xiaodcs-copilot-api 2.3.9-public.3 → 2.5.1-public.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -17
- package/README.zh-CN.md +46 -17
- package/dist/{auth-BiVetBYt.js → auth-D-M3qCeV.js} +79 -16
- package/dist/auth-DIGdDJx3.js +2 -0
- package/dist/{config-SytZjLq8.js → config-DnMKoKrg.js} +43 -29
- package/dist/{debug-BFadhEB4.js → debug-CuLfQpTq.js} +3 -3
- package/dist/main.js +6 -6
- package/dist/{mcp-fpSlKZxK.js → mcp-DqslOG3W.js} +1 -1
- package/dist/{mcp-server-BeNu_Edl.js → mcp-server-B-_dWuXe.js} +1 -1
- package/dist/mcp-server-Byq5DomJ.js +2 -0
- package/dist/protocol-BTFLbI8m.js +257 -0
- package/dist/provider-proxy-BbLwvtD_.js +263 -0
- package/dist/runtime-CWU5BNBf.js +442 -0
- package/dist/server-DmKNMBLd.js +13628 -0
- package/dist/{start-FFVCi8su.js → start-WdcRgBSL.js} +110 -11
- package/dist/{token-D9svRIYW.js → token-CQftvMU4.js} +48 -31
- package/dist/transport-DE8Q0wU-.js +252 -0
- package/dist/transport-HOWgkXsv.js +2 -0
- package/package.json +20 -8
- package/pages/index.html +106 -32
- package/qoder-bridge/README.md +112 -0
- package/dist/auth-Bu4MXadr.js +0 -2
- package/dist/mcp-server-DQ4r-fAy.js +0 -2
- package/dist/models-YMUf33c-.js +0 -88
- package/dist/server-CFQmvoAJ.js +0 -11710
- /package/dist/{electron-fetch-BRX-ug5E.js → electron-fetch-D5uOld7F.js} +0 -0
- /package/dist/{tls-Aq1Dd8E2.js → tls-B5VJAJJM.js} +0 -0
- /package/dist/{tool-search-Ds1vbmGG.js → tool-search-DCb3HbWj.js} +0 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { c as makeImage, d as BridgeError, f as record, l as validateImageUrl, s as decodeDataImage, u as clientToolServer } from "./protocol-BTFLbI8m.js";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { accessToken, qodercliAuth, query } from "@qoder-ai/qoder-agent-sdk";
|
|
6
|
+
import { lookup } from "node:dns/promises";
|
|
7
|
+
import { request } from "node:https";
|
|
8
|
+
import { BlockList, isIP } from "node:net";
|
|
9
|
+
import { Readable } from "node:stream";
|
|
10
|
+
//#region qoder-bridge/model-policy.ts
|
|
11
|
+
function qoderModelPolicy(input) {
|
|
12
|
+
return (context) => {
|
|
13
|
+
const model = context.availableModels.find((m) => m.value === input.model);
|
|
14
|
+
if (input.contextWindow && !model?.availableContextWindows?.includes(input.contextWindow)) throw new BridgeError("Unsupported Qoder context window");
|
|
15
|
+
const efforts = model?.efforts ?? [];
|
|
16
|
+
let effort = input.reasoningEffort;
|
|
17
|
+
if (!efforts.length || effort === "none") effort = void 0;
|
|
18
|
+
else if (effort && !efforts.includes(effort)) {
|
|
19
|
+
if (![
|
|
20
|
+
"ultra",
|
|
21
|
+
"max",
|
|
22
|
+
"xhigh"
|
|
23
|
+
].includes(effort)) throw new BridgeError("Unsupported Qoder reasoning effort");
|
|
24
|
+
effort = [
|
|
25
|
+
"max",
|
|
26
|
+
"xhigh",
|
|
27
|
+
"high",
|
|
28
|
+
"medium",
|
|
29
|
+
"low"
|
|
30
|
+
].find((e) => efforts.includes(e));
|
|
31
|
+
if (!effort) throw new BridgeError("Unsupported Qoder reasoning effort");
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
model: input.model,
|
|
35
|
+
parameters: {
|
|
36
|
+
...input.contextWindow ? { contextWindow: input.contextWindow } : {},
|
|
37
|
+
...effort ? { reasoningEffort: effort } : {}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region qoder-bridge/remote-images.ts
|
|
44
|
+
const denied = new BlockList();
|
|
45
|
+
for (const [address, prefix] of [
|
|
46
|
+
["0.0.0.0", 8],
|
|
47
|
+
["10.0.0.0", 8],
|
|
48
|
+
["100.64.0.0", 10],
|
|
49
|
+
["127.0.0.0", 8],
|
|
50
|
+
["169.254.0.0", 16],
|
|
51
|
+
["172.16.0.0", 12],
|
|
52
|
+
["192.0.0.0", 24],
|
|
53
|
+
["192.0.2.0", 24],
|
|
54
|
+
["192.88.99.0", 24],
|
|
55
|
+
["192.168.0.0", 16],
|
|
56
|
+
["198.18.0.0", 15],
|
|
57
|
+
["198.51.100.0", 24],
|
|
58
|
+
["203.0.113.0", 24],
|
|
59
|
+
["224.0.0.0", 4],
|
|
60
|
+
["240.0.0.0", 4]
|
|
61
|
+
]) denied.addSubnet(address, prefix, "ipv4");
|
|
62
|
+
for (const [address, prefix] of [
|
|
63
|
+
["2001::", 23],
|
|
64
|
+
["2001:db8::", 32],
|
|
65
|
+
["2002::", 16],
|
|
66
|
+
["3fff::", 20]
|
|
67
|
+
]) denied.addSubnet(address, prefix, "ipv6");
|
|
68
|
+
function isPublicAddress(address) {
|
|
69
|
+
const family = isIP(address);
|
|
70
|
+
if (family === 4) return !denied.check(address, "ipv4");
|
|
71
|
+
return family === 6 && /^[23][0-9a-f]{3}:/i.test(address) && !denied.check(address, "ipv6");
|
|
72
|
+
}
|
|
73
|
+
function fetchPinned(url, address, signal, send = request) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
76
|
+
const req = send({
|
|
77
|
+
hostname: address,
|
|
78
|
+
port: 443,
|
|
79
|
+
servername: isIP(hostname) ? void 0 : hostname,
|
|
80
|
+
path: url.pathname + url.search,
|
|
81
|
+
method: "GET",
|
|
82
|
+
headers: {
|
|
83
|
+
host: url.host,
|
|
84
|
+
accept: "image/png,image/jpeg,image/webp,image/gif"
|
|
85
|
+
},
|
|
86
|
+
agent: false,
|
|
87
|
+
signal
|
|
88
|
+
}, (response) => {
|
|
89
|
+
const headers = new Headers();
|
|
90
|
+
for (const [key, value] of Object.entries(response.headers)) if (value !== void 0) headers.set(key, Array.isArray(value) ? value.join(",") : value);
|
|
91
|
+
try {
|
|
92
|
+
const status = response.statusCode ?? 502;
|
|
93
|
+
const empty = [
|
|
94
|
+
204,
|
|
95
|
+
205,
|
|
96
|
+
304
|
|
97
|
+
].includes(status);
|
|
98
|
+
if (empty) response.resume();
|
|
99
|
+
resolve(new Response(empty ? null : Readable.toWeb(response), {
|
|
100
|
+
status,
|
|
101
|
+
headers
|
|
102
|
+
}));
|
|
103
|
+
} catch (error) {
|
|
104
|
+
response.destroy();
|
|
105
|
+
reject(error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid image response"));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
req.on("error", reject);
|
|
109
|
+
req.end();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function resolveImageHost(hostname) {
|
|
113
|
+
return lookup(hostname, { all: true });
|
|
114
|
+
}
|
|
115
|
+
const defaults = {
|
|
116
|
+
resolve: resolveImageHost,
|
|
117
|
+
fetchPinned
|
|
118
|
+
};
|
|
119
|
+
async function loadRemoteImage(raw, signal, deps = defaults) {
|
|
120
|
+
const bounded = AbortSignal.any([signal, AbortSignal.timeout(15e3)]);
|
|
121
|
+
let url = validateImageUrl(raw);
|
|
122
|
+
try {
|
|
123
|
+
for (let redirects = 0; redirects <= 3; redirects++) {
|
|
124
|
+
bounded.throwIfAborted();
|
|
125
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
126
|
+
const addresses = await deps.resolve(hostname);
|
|
127
|
+
bounded.throwIfAborted();
|
|
128
|
+
if (!addresses.length || addresses.some((item) => !isPublicAddress(item.address))) throw new BridgeError("Image URL must resolve exclusively to public IP addresses");
|
|
129
|
+
const response = await deps.fetchPinned(url, addresses[0].address, bounded);
|
|
130
|
+
if ([
|
|
131
|
+
301,
|
|
132
|
+
302,
|
|
133
|
+
303,
|
|
134
|
+
307,
|
|
135
|
+
308
|
|
136
|
+
].includes(response.status)) {
|
|
137
|
+
await response.body?.cancel();
|
|
138
|
+
const location = response.headers.get("location");
|
|
139
|
+
if (!location || redirects === 3) throw new BridgeError("Image URL redirect limit exceeded or missing location");
|
|
140
|
+
url = validateImageUrl(new URL(location, url).href);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
await response.body?.cancel();
|
|
145
|
+
throw new BridgeError("Image URL returned an unsuccessful HTTP status");
|
|
146
|
+
}
|
|
147
|
+
if (Number(response.headers.get("content-length")) > 5242880) {
|
|
148
|
+
await response.body?.cancel();
|
|
149
|
+
throw new BridgeError("Image exceeds the 5 MiB limit", 413);
|
|
150
|
+
}
|
|
151
|
+
if (!response.body) throw new BridgeError("Image URL returned an empty body");
|
|
152
|
+
const reader = response.body.getReader();
|
|
153
|
+
const chunks = [];
|
|
154
|
+
let size = 0;
|
|
155
|
+
try {
|
|
156
|
+
while (true) {
|
|
157
|
+
bounded.throwIfAborted();
|
|
158
|
+
const { done, value } = await reader.read();
|
|
159
|
+
if (done) break;
|
|
160
|
+
size += value.byteLength;
|
|
161
|
+
if (size > 5242880) throw new BridgeError("Image exceeds the 5 MiB limit", 413);
|
|
162
|
+
chunks.push(value);
|
|
163
|
+
}
|
|
164
|
+
} finally {
|
|
165
|
+
await reader.cancel().catch(() => {});
|
|
166
|
+
reader.releaseLock();
|
|
167
|
+
}
|
|
168
|
+
return makeImage(Buffer.concat(chunks), response.headers.get("content-type")?.split(";", 1)[0].trim() ?? "");
|
|
169
|
+
}
|
|
170
|
+
throw new BridgeError("Image URL redirect limit exceeded");
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (error instanceof BridgeError) throw error;
|
|
173
|
+
throw new BridgeError(bounded.aborted ? "Image download cancelled or timed out" : "Image download failed");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region qoder-bridge/image-prompt.ts
|
|
178
|
+
async function buildSdkPrompt(input, signal, download = loadRemoteImage) {
|
|
179
|
+
if (!input.images?.length) return input.prompt;
|
|
180
|
+
const content = [{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: input.prompt
|
|
183
|
+
}];
|
|
184
|
+
let total = 0;
|
|
185
|
+
for (const item of input.images) {
|
|
186
|
+
signal.throwIfAborted();
|
|
187
|
+
const image = item.url.startsWith("data:") ? decodeDataImage(item.url) : await download(item.url, signal);
|
|
188
|
+
total += Buffer.from(image.source.data, "base64").byteLength;
|
|
189
|
+
if (total > 15728640) throw new BridgeError("Combined images exceed the 15 MiB limit", 413);
|
|
190
|
+
content.push({
|
|
191
|
+
type: "text",
|
|
192
|
+
text: `[${item.label}]`
|
|
193
|
+
}, image);
|
|
194
|
+
}
|
|
195
|
+
return (async function* () {
|
|
196
|
+
signal.throwIfAborted();
|
|
197
|
+
yield {
|
|
198
|
+
type: "user",
|
|
199
|
+
message: {
|
|
200
|
+
role: "user",
|
|
201
|
+
content
|
|
202
|
+
},
|
|
203
|
+
parent_tool_use_id: null
|
|
204
|
+
};
|
|
205
|
+
})();
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region qoder-bridge/runtime.ts
|
|
209
|
+
function sdkOptions(cwd, controller, personalAccessToken) {
|
|
210
|
+
const token = (personalAccessToken ?? process.env.QODER_PERSONAL_ACCESS_TOKEN)?.trim();
|
|
211
|
+
return {
|
|
212
|
+
auth: token ? accessToken(token) : qodercliAuth(),
|
|
213
|
+
cwd,
|
|
214
|
+
abortController: controller,
|
|
215
|
+
tools: [],
|
|
216
|
+
allowedTools: [],
|
|
217
|
+
skills: [],
|
|
218
|
+
plugins: [],
|
|
219
|
+
extensions: [],
|
|
220
|
+
agents: {},
|
|
221
|
+
mcpServers: {},
|
|
222
|
+
allowedMcpServerNames: [],
|
|
223
|
+
strictMcpConfig: true,
|
|
224
|
+
settingSources: [],
|
|
225
|
+
persistSession: false,
|
|
226
|
+
includePartialMessages: true,
|
|
227
|
+
maxTurns: 1,
|
|
228
|
+
permissionMode: "default",
|
|
229
|
+
canUseTool: () => Promise.resolve({
|
|
230
|
+
behavior: "deny",
|
|
231
|
+
message: "Tools are disabled in this gateway"
|
|
232
|
+
}),
|
|
233
|
+
memory: {
|
|
234
|
+
mode: "custom",
|
|
235
|
+
projectScope: false,
|
|
236
|
+
userScope: false,
|
|
237
|
+
generation: { enabled: false },
|
|
238
|
+
consumption: { enabled: false }
|
|
239
|
+
},
|
|
240
|
+
evolution: { skill: {
|
|
241
|
+
mode: "custom",
|
|
242
|
+
generation: { enabled: false }
|
|
243
|
+
} },
|
|
244
|
+
proxy: process.env.QODER_PROXY ?? process.env.HTTPS_PROXY ?? process.env.HTTP_PROXY,
|
|
245
|
+
...process.env.QODERCLI_PATH ? { pathToQoderCLIExecutable: process.env.QODERCLI_PATH } : {}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
var EventTranslator = class {
|
|
249
|
+
clientNames;
|
|
250
|
+
constructor(clientNames = /* @__PURE__ */ new Map()) {
|
|
251
|
+
this.clientNames = clientNames;
|
|
252
|
+
}
|
|
253
|
+
streamed = false;
|
|
254
|
+
completed = false;
|
|
255
|
+
*accept(value) {
|
|
256
|
+
if (this.completed) return;
|
|
257
|
+
const message = record(value);
|
|
258
|
+
if (message.type === "stream_event") {
|
|
259
|
+
const event = record(message.event);
|
|
260
|
+
if (event.type === "message_start") this.streamed = false;
|
|
261
|
+
const delta = record(event.delta);
|
|
262
|
+
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
263
|
+
this.streamed = true;
|
|
264
|
+
yield {
|
|
265
|
+
type: "text",
|
|
266
|
+
text: delta.text
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (record(event.content_block).type === "tool_use" && !this.clientNames.has(String(record(event.content_block).name))) throw new BridgeError("Qoder attempted an unsupported tool call", 502, "unsupported_tool_call");
|
|
270
|
+
}
|
|
271
|
+
if (message.type === "assistant") {
|
|
272
|
+
const content = record(message.message).content;
|
|
273
|
+
const calls = [];
|
|
274
|
+
if (Array.isArray(content)) for (const part of content) {
|
|
275
|
+
const block = record(part);
|
|
276
|
+
if (block.type === "tool_use" && this.clientNames.has(String(block.name))) {
|
|
277
|
+
if (typeof block.id !== "string") throw new BridgeError("Qoder tool call is missing an id", 502);
|
|
278
|
+
calls.push({
|
|
279
|
+
id: block.id,
|
|
280
|
+
type: "function",
|
|
281
|
+
function: {
|
|
282
|
+
name: this.clientNames.get(String(block.name)),
|
|
283
|
+
arguments: JSON.stringify(block.input ?? {})
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (block.type === "tool_use") throw new BridgeError("Qoder attempted an unsupported tool call", 502, "unsupported_tool_call");
|
|
289
|
+
if (!this.streamed && block.type === "text" && typeof block.text === "string") yield {
|
|
290
|
+
type: "text",
|
|
291
|
+
text: block.text
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (calls.length) yield {
|
|
295
|
+
type: "tool_calls",
|
|
296
|
+
calls
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
if (message.type === "result") {
|
|
300
|
+
if (message.subtype !== "success" || message.is_error === true) throw new BridgeError("Qoder did not complete the request successfully", 502, "qoder_result_error");
|
|
301
|
+
this.completed = true;
|
|
302
|
+
const info = { qoder_usage: {} };
|
|
303
|
+
if (typeof message.total_credits === "number" && Number.isFinite(message.total_credits) && message.total_credits >= 0) info.qoder_usage.total_credits = message.total_credits;
|
|
304
|
+
if (message.modelUsage) info.qoder_usage.model_usage = record(message.modelUsage);
|
|
305
|
+
const usage = record(message.usage);
|
|
306
|
+
const counters = [
|
|
307
|
+
usage.input_tokens,
|
|
308
|
+
usage.output_tokens,
|
|
309
|
+
usage.cache_read_input_tokens ?? 0,
|
|
310
|
+
usage.cache_creation_input_tokens ?? 0
|
|
311
|
+
];
|
|
312
|
+
if (counters.every((n) => typeof n === "number" && Number.isFinite(n) && n >= 0)) {
|
|
313
|
+
const [input, output, cacheRead, cacheWrite] = counters;
|
|
314
|
+
if (input + output + cacheRead + cacheWrite > 0) info.usage = {
|
|
315
|
+
prompt_tokens: input + cacheRead + cacheWrite,
|
|
316
|
+
completion_tokens: output,
|
|
317
|
+
total_tokens: input + output + cacheRead + cacheWrite
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
yield {
|
|
321
|
+
type: "complete",
|
|
322
|
+
info
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
assertComplete() {
|
|
327
|
+
if (!this.completed) throw new BridgeError("Qoder stream ended without a successful result", 502, "incomplete_stream");
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
async function workspace(signal, token) {
|
|
331
|
+
signal.throwIfAborted();
|
|
332
|
+
const cwd = await mkdtemp(join(tmpdir(), "copilot-qoder-"));
|
|
333
|
+
const controller = new AbortController();
|
|
334
|
+
const abort = () => controller.abort(signal.reason);
|
|
335
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
336
|
+
if (signal.aborted) abort();
|
|
337
|
+
return {
|
|
338
|
+
options: sdkOptions(cwd, controller, token),
|
|
339
|
+
cleanup: async () => {
|
|
340
|
+
signal.removeEventListener("abort", abort);
|
|
341
|
+
await rm(cwd, {
|
|
342
|
+
recursive: true,
|
|
343
|
+
force: true
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function createBackend(runQuery = query, token) {
|
|
349
|
+
return {
|
|
350
|
+
async catalog(signal) {
|
|
351
|
+
const session = await workspace(signal, token);
|
|
352
|
+
let release = () => {};
|
|
353
|
+
const idle = new Promise((resolve) => {
|
|
354
|
+
release = resolve;
|
|
355
|
+
});
|
|
356
|
+
let q;
|
|
357
|
+
try {
|
|
358
|
+
q = runQuery({
|
|
359
|
+
prompt: (async function* () {
|
|
360
|
+
await idle;
|
|
361
|
+
})(),
|
|
362
|
+
options: session.options
|
|
363
|
+
});
|
|
364
|
+
await q.initializationResult();
|
|
365
|
+
const models = await q.getAvailableModels({ fetchStrategy: "live" });
|
|
366
|
+
const usage = await q.getUsageInfo();
|
|
367
|
+
return {
|
|
368
|
+
models: models.filter((m) => m.isEnabled !== false).map((m) => ({
|
|
369
|
+
id: m.value,
|
|
370
|
+
name: m.displayName,
|
|
371
|
+
context_window: m.defaultContextWindow ?? Math.min(...m.availableContextWindows?.length ? m.availableContextWindows : [m.maxInputTokens ?? 128e3]),
|
|
372
|
+
max_input_tokens: m.maxInputTokens,
|
|
373
|
+
max_output_tokens: m.maxOutputTokens,
|
|
374
|
+
available_context_windows: m.availableContextWindows,
|
|
375
|
+
input_modalities: m.isVl ? ["text", "image"] : ["text"]
|
|
376
|
+
})),
|
|
377
|
+
usage
|
|
378
|
+
};
|
|
379
|
+
} finally {
|
|
380
|
+
release();
|
|
381
|
+
try {
|
|
382
|
+
await q?.close();
|
|
383
|
+
} finally {
|
|
384
|
+
await session.cleanup();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
async *turn(input, signal) {
|
|
389
|
+
const session = await workspace(signal, token);
|
|
390
|
+
let q;
|
|
391
|
+
const client = clientToolServer(input.tools ?? []);
|
|
392
|
+
const translator = new EventTranslator(client.names);
|
|
393
|
+
try {
|
|
394
|
+
q = runQuery({
|
|
395
|
+
prompt: await buildSdkPrompt(input, signal),
|
|
396
|
+
options: {
|
|
397
|
+
...session.options,
|
|
398
|
+
model: input.model,
|
|
399
|
+
...input.tools?.length ? {
|
|
400
|
+
mcpServers: { client: client.config },
|
|
401
|
+
allowedMcpServerNames: ["client"],
|
|
402
|
+
allowedTools: [...client.names.keys()],
|
|
403
|
+
canUseTool: (name, args) => Promise.resolve(client.names.has(name) ? {
|
|
404
|
+
behavior: "allow",
|
|
405
|
+
updatedInput: args
|
|
406
|
+
} : {
|
|
407
|
+
behavior: "deny",
|
|
408
|
+
message: "Only client tools are allowed"
|
|
409
|
+
})
|
|
410
|
+
} : {},
|
|
411
|
+
...input.contextWindow || input.reasoningEffort ? { resolveModel: qoderModelPolicy(input) } : {},
|
|
412
|
+
systemPrompt: input.systemPrompt
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
for await (const message of q) {
|
|
416
|
+
signal.throwIfAborted();
|
|
417
|
+
for (const event of translator.accept(message)) {
|
|
418
|
+
yield event;
|
|
419
|
+
if (event.type === "tool_calls") {
|
|
420
|
+
yield {
|
|
421
|
+
type: "complete",
|
|
422
|
+
info: { qoder_usage: {} }
|
|
423
|
+
};
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
translator.assertComplete();
|
|
429
|
+
} finally {
|
|
430
|
+
try {
|
|
431
|
+
session.options.abortController?.abort();
|
|
432
|
+
await client.close();
|
|
433
|
+
await q?.close();
|
|
434
|
+
} finally {
|
|
435
|
+
await session.cleanup();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
//#endregion
|
|
442
|
+
export { createBackend };
|