Repository navigation
Expand file tree
/
Copy pathopenrouter_c.c
More file actions
142 lines (126 loc) · 4 KB
/
Copy pathopenrouter_c.c
File metadata and controls
142 lines (126 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// OpenRouter / OpenAI-compatible chat completion helper for Bend2 using curl
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct {
char* data;
size_t len;
} OpenRouterRespBuffer;
// Model routing applied to every OpenRouter request. OpenRouter tries the
// payload's "model" field first, then this "models" list in order.
// OPENROUTER_MODEL and OPENROUTER_BACKUP_MODEL override the defaults.
static const char* openrouter_model(void) {
const char* m = getenv("OPENROUTER_MODEL");
return (m && m[0]) ? m : "openai/gpt-oss-120b:nitro";
}
static const char* openrouter_backup_model(void) {
const char* m = getenv("OPENROUTER_BACKUP_MODEL");
return (m && m[0]) ? m : "deepseek/deepseek-v4-flash-0731:free";
}
// Insert a "models" fallback list after the payload's opening brace.
static char* openrouter_payload_with_fallback(const char* payload) {
const char* primary = openrouter_model();
const char* backup = openrouter_backup_model();
if (!payload || payload[0] != '{') {
return strdup(payload ? payload : "{}");
}
size_t need = strlen(payload) + strlen(primary) + strlen(backup) + 32;
char* out = malloc(need);
if (!out) return strdup(payload);
snprintf(out, need, "{\"models\":[\"%s\",\"%s\"],%s", primary, backup,
payload + 1);
return out;
}
static void openrouter_call_worker(IoWork* w) {
const char* env_var_name = "OPENROUTER_API_KEY";
const char* env_key = getenv(env_var_name);
char file_key[256] = {0};
const char* key = env_key;
if (!key || strlen(key) == 0) {
FILE* kf = fopen(".env.openrouter", "r");
if (kf) {
char line[256];
while (fgets(line, sizeof(line), kf)) {
if (line[0] == '#' || line[0] == '\n') continue;
char* nl = strchr(line, '\r');
if (nl) *nl = '\0';
nl = strchr(line, '\n');
if (nl) *nl = '\0';
if (strlen(line) > 0) {
strncpy(file_key, line, sizeof(file_key) - 1);
key = file_key;
break;
}
}
fclose(kf);
}
}
if (!key || strlen(key) == 0 || strcmp(key, "YOUR_OPENROUTER_API_KEY") == 0) {
const char* err_msg = "{\"error\": \"Missing OpenRouter API key. Please set OPENROUTER_API_KEY in .env.openrouter or environment.\"}";
w->data = strdup(err_msg);
w->size = strlen(err_msg);
return;
}
// Write payload to temporary file
char tmp_payload[] = "/tmp/openrouter_req_XXXXXX";
int fd = mkstemp(tmp_payload);
if (fd < 0) {
w->data = strdup("{\"error\":\"Failed to create temp request file\"}");
w->size = strlen(w->data);
return;
}
char* payload = openrouter_payload_with_fallback(w->data);
FILE* pf = fdopen(fd, "w");
if (pf) {
fputs(payload, pf);
fclose(pf);
} else {
close(fd);
}
free(payload);
free(w->data);
// Use curl with the payload file
char cmd[1024];
snprintf(cmd, sizeof(cmd),
"curl -s -X POST https://openrouter.ai/api/v1/chat/completions "
"-H 'Authorization: Bearer %s' "
"-H 'Content-Type: application/json' "
"-d @%s",
key, tmp_payload);
FILE* pipe = popen(cmd, "r");
OpenRouterRespBuffer resp = {NULL, 0};
char chunk[1024];
size_t bytes;
if (pipe) {
while ((bytes = fread(chunk, 1, sizeof(chunk), pipe)) > 0) {
char* n = realloc(resp.data, resp.len + bytes + 1);
if (!n) break;
resp.data = n;
memcpy(resp.data + resp.len, chunk, bytes);
resp.len += bytes;
resp.data[resp.len] = '\0';
}
pclose(pipe);
}
unlink(tmp_payload);
if (!resp.data) {
w->data = strdup("{}");
w->size = 2;
} else {
w->data = resp.data;
w->size = resp.len;
}
}
static Term openrouter_post_pack(Env e, IoWork* w) {
Term str = io_str(e, w->data, w->size);
free(w->data);
return str;
}
Term openrouter_post_run(Env e, Term* f, IoWork* w) {
w->data = io_cstr(e, f[0], &w->size);
return io_work(w, openrouter_call_worker, openrouter_post_pack);
}
static void __attribute__((constructor)) openrouter_post_use(void) {
io_eff(CID_OPENROUTER_POST, openrouter_post_run, 0);
}