tnega 0.1.0 → 0.3.0
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 +115 -189
- package/dist/agent.js +2907 -0
- package/dist/agent.js.map +7 -0
- package/dist/bin.js +32512 -4419
- package/dist/bin.js.map +4 -4
- package/dist/cli-runtime.js +33404 -0
- package/dist/cli-runtime.js.map +7 -0
- package/dist/coding-agent.js +19088 -0
- package/dist/coding-agent.js.map +7 -0
- package/dist/core.js +1398 -0
- package/dist/core.js.map +7 -0
- package/dist/eval.js +23905 -0
- package/dist/eval.js.map +7 -0
- package/dist/events.js +1215 -0
- package/dist/events.js.map +7 -0
- package/dist/evolve.js +18303 -0
- package/dist/evolve.js.map +7 -0
- package/dist/index.js +33087 -4450
- package/dist/index.js.map +4 -4
- package/dist/llm.js +1016 -0
- package/dist/llm.js.map +7 -0
- package/dist/services.js +1142 -0
- package/dist/services.js.map +7 -0
- package/dist/session.js +1045 -0
- package/dist/session.js.map +7 -0
- package/dist/tools.js +2577 -0
- package/dist/tools.js.map +7 -0
- package/dist/types/packages/agent/src/definition.d.ts +17 -0
- package/dist/types/packages/agent/src/inbox-durable.d.ts +54 -0
- package/dist/types/packages/agent/src/index.d.ts +7 -0
- package/dist/types/packages/agent/src/live.d.ts +115 -0
- package/dist/types/packages/agent/src/llm-service.d.ts +32 -0
- package/dist/types/packages/agent/src/prompt.d.ts +55 -0
- package/dist/types/packages/agent/src/service.d.ts +68 -0
- package/dist/types/packages/agent/src/types.d.ts +215 -0
- package/dist/types/packages/benchmark/src/bigcodebench.d.ts +6 -0
- package/dist/types/packages/benchmark/src/humaneval.d.ts +11 -0
- package/dist/types/packages/benchmark/src/index.d.ts +9 -0
- package/dist/types/packages/benchmark/src/manifest.d.ts +10 -0
- package/dist/types/packages/benchmark/src/mbpp.d.ts +10 -0
- package/dist/types/packages/benchmark/src/parquet.d.ts +2 -0
- package/dist/types/packages/benchmark/src/stdlib.d.ts +3 -0
- package/dist/types/packages/benchmark/src/swebench.d.ts +2 -0
- package/dist/types/packages/benchmark/src/types.d.ts +58 -0
- package/dist/types/packages/cli/src/bin.d.ts +2 -0
- package/dist/types/packages/cli/src/commands.d.ts +146 -0
- package/dist/types/packages/cli/src/config.d.ts +28 -0
- package/dist/types/packages/cli/src/index.d.ts +9 -0
- package/dist/types/packages/cli/src/profile-file.d.ts +26 -0
- package/dist/types/packages/cli/src/profile.d.ts +9 -0
- package/dist/types/packages/cli/src/server.d.ts +14 -0
- package/dist/types/packages/cli/src/store.d.ts +60 -0
- package/dist/types/packages/cli/src/yaml.d.ts +1 -0
- package/dist/types/packages/coding-agent/src/codingAgent.d.ts +25 -0
- package/dist/types/packages/coding-agent/src/index.d.ts +6 -0
- package/dist/types/packages/coding-agent/src/mcp.d.ts +24 -0
- package/dist/types/packages/coding-agent/src/plan.d.ts +13 -0
- package/dist/types/packages/coding-agent/src/skills.d.ts +12 -0
- package/dist/types/packages/coding-agent/src/slash.d.ts +28 -0
- package/dist/types/packages/coding-agent/src/types.d.ts +41 -0
- package/dist/types/packages/core/src/context.d.ts +28 -0
- package/dist/types/packages/core/src/events.d.ts +57 -0
- package/dist/types/packages/core/src/fiber.d.ts +79 -0
- package/dist/types/packages/core/src/index.d.ts +10 -0
- package/dist/types/packages/core/src/invariant.d.ts +45 -0
- package/dist/types/packages/core/src/logger.d.ts +48 -0
- package/dist/types/packages/core/src/reflect.d.ts +42 -0
- package/dist/types/packages/core/src/registry.d.ts +54 -0
- package/dist/types/packages/core/src/service.d.ts +19 -0
- package/dist/types/packages/core/src/utils.d.ts +54 -0
- package/dist/types/packages/eval/src/codingRuntime.d.ts +18 -0
- package/dist/types/packages/eval/src/index.d.ts +5 -0
- package/dist/types/packages/eval/src/policy.d.ts +8 -0
- package/dist/types/packages/eval/src/runner.d.ts +31 -0
- package/dist/types/packages/eval/src/service.d.ts +23 -0
- package/dist/types/packages/eval/src/strategies.d.ts +58 -0
- package/dist/types/packages/eval/src/trace.d.ts +7 -0
- package/dist/types/packages/eval/src/types.d.ts +281 -0
- package/dist/types/packages/eval/src/utils.d.ts +5 -0
- package/dist/types/packages/eval/src/workspace.d.ts +17 -0
- package/dist/types/packages/evolve/src/diagnose.d.ts +9 -0
- package/dist/types/packages/evolve/src/index.d.ts +7 -0
- package/dist/types/packages/evolve/src/llm.d.ts +39 -0
- package/dist/types/packages/evolve/src/log.d.ts +26 -0
- package/dist/types/packages/evolve/src/select.d.ts +3 -0
- package/dist/types/packages/evolve/src/service.d.ts +29 -0
- package/dist/types/packages/evolve/src/types.d.ts +176 -0
- package/dist/types/packages/llm/src/anthropic-messages.d.ts +4 -0
- package/dist/types/packages/llm/src/errors.d.ts +6 -0
- package/dist/types/packages/llm/src/index.d.ts +7 -0
- package/dist/types/packages/llm/src/models.d.ts +29 -0
- package/dist/types/packages/llm/src/openai.d.ts +4 -0
- package/dist/types/packages/llm/src/provider.d.ts +3 -0
- package/dist/types/packages/llm/src/shared.d.ts +15 -0
- package/dist/types/packages/llm/src/types.d.ts +13 -0
- package/dist/types/packages/session/src/index.d.ts +500 -0
- package/dist/types/packages/session/src/invariant.d.ts +33 -0
- package/dist/types/packages/tools/src/builtins.d.ts +25 -0
- package/dist/types/packages/tools/src/calc.d.ts +4 -0
- package/dist/types/packages/tools/src/execution.d.ts +31 -0
- package/dist/types/packages/tools/src/index.d.ts +107 -0
- package/dist/types/packages/tools/src/path.d.ts +4 -0
- package/dist/types/packages/tools/src/policy.d.ts +14 -0
- package/dist/types/src/events.d.ts +1 -0
- package/dist/types/src/index.d.ts +15 -0
- package/dist/types/src/services.d.ts +4 -0
- package/dist/web/assets/index-45pokiBH.js +270 -0
- package/dist/web/assets/index-DCLL69Kz.css +1 -0
- package/dist/web/index.html +13 -0
- package/package.json +92 -35
package/dist/llm.js
ADDED
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
import { createRequire as __tnegaCreateRequire } from 'node:module'; const require = __tnegaCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// packages/llm/src/anthropic-messages.ts
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
|
|
6
|
+
// packages/llm/src/errors.ts
|
|
7
|
+
var OpenAICompatibleError = class extends Error {
|
|
8
|
+
constructor(status, message, detail) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.detail = detail;
|
|
12
|
+
}
|
|
13
|
+
status;
|
|
14
|
+
detail;
|
|
15
|
+
name = "OpenAICompatibleError";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// packages/llm/src/models.ts
|
|
19
|
+
var DEFAULT_MODEL = "deepseek-v4-flash";
|
|
20
|
+
var DEFAULT_DEEPSEEK_MODEL = "deepseek-v4-flash";
|
|
21
|
+
var MODEL_CATALOG = [
|
|
22
|
+
{
|
|
23
|
+
id: "minimax-m3",
|
|
24
|
+
provider: "opencode-go",
|
|
25
|
+
protocol: "anthropic",
|
|
26
|
+
contextWindow: 1e6,
|
|
27
|
+
pricing: {
|
|
28
|
+
rates: {
|
|
29
|
+
input: 0.3,
|
|
30
|
+
output: 1.2,
|
|
31
|
+
cachedRead: 0.06
|
|
32
|
+
},
|
|
33
|
+
monthlyUsage: 60
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "deepseek-v4-flash",
|
|
38
|
+
provider: "opencode-go",
|
|
39
|
+
protocol: "openai",
|
|
40
|
+
contextWindow: 1e6,
|
|
41
|
+
pricing: {
|
|
42
|
+
rates: {
|
|
43
|
+
offPeak: {
|
|
44
|
+
input: 0.22,
|
|
45
|
+
output: 0.66,
|
|
46
|
+
cachedRead: 7e-3
|
|
47
|
+
},
|
|
48
|
+
peak: {
|
|
49
|
+
input: 0.44,
|
|
50
|
+
output: 1.32,
|
|
51
|
+
cachedRead: 0.014
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
monthlyUsage: 30
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "deepseek-v4-pro",
|
|
59
|
+
provider: "opencode-go",
|
|
60
|
+
protocol: "openai",
|
|
61
|
+
contextWindow: 1e6,
|
|
62
|
+
pricing: {
|
|
63
|
+
rates: {
|
|
64
|
+
offPeak: {
|
|
65
|
+
input: 0.66,
|
|
66
|
+
output: 1.98,
|
|
67
|
+
cachedRead: 0.022
|
|
68
|
+
},
|
|
69
|
+
peak: {
|
|
70
|
+
input: 1.32,
|
|
71
|
+
output: 3.96,
|
|
72
|
+
cachedRead: 0.044
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
monthlyUsage: 15
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
function lookupModel(id) {
|
|
80
|
+
if (!id) return void 0;
|
|
81
|
+
const normalized = id.trim().toLowerCase();
|
|
82
|
+
return MODEL_CATALOG.find((model) => model.id === normalized);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// packages/llm/src/shared.ts
|
|
86
|
+
var DEFAULT_OPENCODE_GO_BASE_URL = "https://opencode.ai/zen/go/v1";
|
|
87
|
+
var DEFAULT_LLM_TIMEOUT_MS = 12e4;
|
|
88
|
+
var DEFAULT_LLM_MAX_RETRIES = 2;
|
|
89
|
+
var DEFAULT_LLM_RETRY_DELAY_MS = 500;
|
|
90
|
+
function combineSignal(signal, timeoutMs) {
|
|
91
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
92
|
+
return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
93
|
+
}
|
|
94
|
+
function isRetryableStatus(status) {
|
|
95
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
96
|
+
}
|
|
97
|
+
function isExternalAbort(error, signal) {
|
|
98
|
+
return Boolean(signal?.aborted && isAbortLike(error));
|
|
99
|
+
}
|
|
100
|
+
function isAbortLike(error) {
|
|
101
|
+
if (!error || typeof error !== "object") return false;
|
|
102
|
+
const name = error.name;
|
|
103
|
+
return name === "AbortError" || name === "TimeoutError";
|
|
104
|
+
}
|
|
105
|
+
function errorMessage(error) {
|
|
106
|
+
if (error instanceof Error) return error.message;
|
|
107
|
+
return String(error);
|
|
108
|
+
}
|
|
109
|
+
function sleep(ms) {
|
|
110
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
111
|
+
}
|
|
112
|
+
function normalizeBaseUrl(baseUrl) {
|
|
113
|
+
return (baseUrl ?? DEFAULT_OPENCODE_GO_BASE_URL).replace(/\/+$/, "");
|
|
114
|
+
}
|
|
115
|
+
function stringifyArguments(value) {
|
|
116
|
+
if (typeof value === "string") return value;
|
|
117
|
+
try {
|
|
118
|
+
return JSON.stringify(value) ?? "{}";
|
|
119
|
+
} catch {
|
|
120
|
+
return "{}";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function parseArguments(raw) {
|
|
124
|
+
const text = raw.trim();
|
|
125
|
+
if (!text) return {};
|
|
126
|
+
try {
|
|
127
|
+
return JSON.parse(text);
|
|
128
|
+
} catch {
|
|
129
|
+
return raw;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function assertOk(response, redact) {
|
|
133
|
+
if (response.ok) return;
|
|
134
|
+
let detail;
|
|
135
|
+
try {
|
|
136
|
+
const text = await response.text();
|
|
137
|
+
if (text.trim()) {
|
|
138
|
+
const preview = text.slice(0, 2e3);
|
|
139
|
+
detail = redact && preview.includes(redact) ? preview.replaceAll(redact, "[redacted]") : preview;
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
detail = void 0;
|
|
143
|
+
}
|
|
144
|
+
throw new OpenAICompatibleError(
|
|
145
|
+
response.status,
|
|
146
|
+
`LLM request failed with status ${response.status}`,
|
|
147
|
+
detail
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
async function parseJson(response) {
|
|
151
|
+
try {
|
|
152
|
+
return await response.json();
|
|
153
|
+
} catch {
|
|
154
|
+
throw new OpenAICompatibleError(
|
|
155
|
+
response.status,
|
|
156
|
+
"LLM response was not valid JSON"
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// packages/llm/src/anthropic-messages.ts
|
|
162
|
+
var ANTHROPIC_VERSION = "2023-06-01";
|
|
163
|
+
function anthropicMessagesAdapter(config = {}) {
|
|
164
|
+
return {
|
|
165
|
+
async complete(messages, tools, options) {
|
|
166
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_LLM_TIMEOUT_MS;
|
|
167
|
+
const maxRetries = config.maxRetries ?? DEFAULT_LLM_MAX_RETRIES;
|
|
168
|
+
const retryDelayMs = config.retryDelayMs ?? DEFAULT_LLM_RETRY_DELAY_MS;
|
|
169
|
+
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
|
|
170
|
+
const request = buildRequest(
|
|
171
|
+
messages,
|
|
172
|
+
tools,
|
|
173
|
+
config,
|
|
174
|
+
combineSignal(options.signal, timeoutMs)
|
|
175
|
+
);
|
|
176
|
+
try {
|
|
177
|
+
const response = await fetch(request.url, request.init);
|
|
178
|
+
if (!isRetryableStatus(response.status) || attempt >= maxRetries) {
|
|
179
|
+
return parseResponse(response, config.apiKey);
|
|
180
|
+
}
|
|
181
|
+
await response.body?.cancel();
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (isExternalAbort(error, options.signal)) {
|
|
184
|
+
throw new OpenAICompatibleError(
|
|
185
|
+
0,
|
|
186
|
+
`LLM request aborted: ${errorMessage(error)}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (attempt >= maxRetries) {
|
|
190
|
+
throw toRequestError(error, timeoutMs);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
194
|
+
}
|
|
195
|
+
throw new OpenAICompatibleError(0, "LLM request failed");
|
|
196
|
+
},
|
|
197
|
+
async *stream(messages, tools, options) {
|
|
198
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_LLM_TIMEOUT_MS;
|
|
199
|
+
const maxRetries = config.maxRetries ?? DEFAULT_LLM_MAX_RETRIES;
|
|
200
|
+
const retryDelayMs = config.retryDelayMs ?? DEFAULT_LLM_RETRY_DELAY_MS;
|
|
201
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
202
|
+
const request = buildRequest(
|
|
203
|
+
messages,
|
|
204
|
+
tools,
|
|
205
|
+
config,
|
|
206
|
+
combineSignal(options.signal, timeoutMs),
|
|
207
|
+
true
|
|
208
|
+
);
|
|
209
|
+
let response;
|
|
210
|
+
try {
|
|
211
|
+
response = await fetch(request.url, request.init);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (isExternalAbort(error, options.signal)) {
|
|
214
|
+
throw new OpenAICompatibleError(
|
|
215
|
+
0,
|
|
216
|
+
`LLM stream aborted: ${errorMessage(error)}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (attempt >= maxRetries) {
|
|
220
|
+
throw toStreamError(error, timeoutMs);
|
|
221
|
+
}
|
|
222
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (!response.ok || !response.body) {
|
|
226
|
+
if (isRetryableStatus(response.status) && attempt < maxRetries) {
|
|
227
|
+
await response.body?.cancel();
|
|
228
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (!response.ok) await assertOk(response, config.apiKey);
|
|
232
|
+
throw new OpenAICompatibleError(
|
|
233
|
+
response.status,
|
|
234
|
+
"LLM stream response had no body"
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
let sawEvent = false;
|
|
238
|
+
try {
|
|
239
|
+
for await (const event of parseAnthropicStream(response.body)) {
|
|
240
|
+
sawEvent = true;
|
|
241
|
+
yield event;
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (isExternalAbort(error, options.signal)) {
|
|
246
|
+
throw new OpenAICompatibleError(
|
|
247
|
+
0,
|
|
248
|
+
`LLM stream aborted: ${errorMessage(error)}`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (sawEvent || attempt >= maxRetries) {
|
|
252
|
+
throw toStreamError(error, timeoutMs);
|
|
253
|
+
}
|
|
254
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function buildRequest(messages, tools, config, signal, stream = false) {
|
|
261
|
+
const body = {
|
|
262
|
+
model: config.model ?? DEFAULT_MODEL,
|
|
263
|
+
max_tokens: config.maxTokens ?? 4096,
|
|
264
|
+
messages: toAnthropicMessages(messages)
|
|
265
|
+
};
|
|
266
|
+
if (stream) body.stream = true;
|
|
267
|
+
const system = systemPrompt(messages);
|
|
268
|
+
if (system) body.system = system;
|
|
269
|
+
if (tools.length) {
|
|
270
|
+
body.tools = tools.map(toAnthropicTool);
|
|
271
|
+
}
|
|
272
|
+
if (config.temperature !== void 0) body.temperature = config.temperature;
|
|
273
|
+
return {
|
|
274
|
+
url: anthropicMessagesUrl(config.baseUrl),
|
|
275
|
+
init: {
|
|
276
|
+
method: "POST",
|
|
277
|
+
headers: {
|
|
278
|
+
"content-type": "application/json",
|
|
279
|
+
"anthropic-version": ANTHROPIC_VERSION,
|
|
280
|
+
...config.apiKey ? { "x-api-key": config.apiKey } : {}
|
|
281
|
+
},
|
|
282
|
+
body: JSON.stringify(body),
|
|
283
|
+
...signal ? { signal } : {}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function anthropicMessagesUrl(baseUrl) {
|
|
288
|
+
const normalized = normalizeBaseUrl(baseUrl);
|
|
289
|
+
return normalized.endsWith("/messages") ? normalized : `${normalized}/messages`;
|
|
290
|
+
}
|
|
291
|
+
function systemPrompt(messages) {
|
|
292
|
+
return messages.filter((message) => message.role === "system").map((message) => String(message.content ?? "")).join("\n\n");
|
|
293
|
+
}
|
|
294
|
+
function toAnthropicMessages(messages) {
|
|
295
|
+
const result = [];
|
|
296
|
+
for (const message of messages) {
|
|
297
|
+
if (message.role === "system") continue;
|
|
298
|
+
if (message.role === "tool") {
|
|
299
|
+
const toolResult = {
|
|
300
|
+
type: "tool_result",
|
|
301
|
+
tool_use_id: message.tool_call_id ?? "",
|
|
302
|
+
content: String(message.content ?? "")
|
|
303
|
+
};
|
|
304
|
+
if (message.toolError) toolResult.is_error = true;
|
|
305
|
+
result.push({
|
|
306
|
+
role: "user",
|
|
307
|
+
content: [toolResult]
|
|
308
|
+
});
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (message.role === "assistant" && message.tool_calls?.length) {
|
|
312
|
+
result.push({
|
|
313
|
+
role: "assistant",
|
|
314
|
+
content: [
|
|
315
|
+
...message.content ? [{ type: "text", text: message.content }] : [],
|
|
316
|
+
...message.tool_calls.map((call) => ({
|
|
317
|
+
type: "tool_use",
|
|
318
|
+
id: call.id,
|
|
319
|
+
name: call.name,
|
|
320
|
+
input: typeof call.arguments === "object" && call.arguments !== null ? call.arguments : parseArguments(String(call.arguments ?? ""))
|
|
321
|
+
}))
|
|
322
|
+
]
|
|
323
|
+
});
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
result.push({
|
|
327
|
+
role: message.role,
|
|
328
|
+
content: String(message.content ?? "")
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return result;
|
|
332
|
+
}
|
|
333
|
+
function toAnthropicTool(tool) {
|
|
334
|
+
return {
|
|
335
|
+
name: tool.schema.name,
|
|
336
|
+
...tool.schema.description ? { description: tool.schema.description } : {},
|
|
337
|
+
input_schema: tool.schema.parameters ?? { type: "object", properties: {} }
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
async function parseResponse(response, apiKey) {
|
|
341
|
+
await assertOk(response, apiKey);
|
|
342
|
+
const payload = await parseJson(response);
|
|
343
|
+
return parseCompletion(payload);
|
|
344
|
+
}
|
|
345
|
+
function parseCompletion(payload) {
|
|
346
|
+
const record = payload;
|
|
347
|
+
const blocks = Array.isArray(record?.content) ? record.content : [];
|
|
348
|
+
const text = blocks.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("");
|
|
349
|
+
const toolCalls = parseToolCalls(blocks);
|
|
350
|
+
const finishReason = toFinishReason(
|
|
351
|
+
record?.stop_reason,
|
|
352
|
+
text,
|
|
353
|
+
toolCalls.length > 0
|
|
354
|
+
);
|
|
355
|
+
const completion = { finishReason };
|
|
356
|
+
if (text) completion.content = text;
|
|
357
|
+
if (toolCalls.length) completion.toolCalls = toolCalls;
|
|
358
|
+
return completion;
|
|
359
|
+
}
|
|
360
|
+
function parseToolCalls(blocks) {
|
|
361
|
+
const calls = [];
|
|
362
|
+
for (const block of blocks) {
|
|
363
|
+
if (block.type !== "tool_use") continue;
|
|
364
|
+
const name = typeof block.name === "string" ? block.name : "";
|
|
365
|
+
if (!name) continue;
|
|
366
|
+
const id = typeof block.id === "string" && block.id ? block.id : `toolu_${calls.length + 1}`;
|
|
367
|
+
calls.push({
|
|
368
|
+
id,
|
|
369
|
+
name,
|
|
370
|
+
arguments: block.input ?? {}
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return calls;
|
|
374
|
+
}
|
|
375
|
+
function toFinishReason(raw, content, hasToolCalls) {
|
|
376
|
+
if (raw === "end_turn" || raw === "stop_sequence") return "stop";
|
|
377
|
+
if (raw === "max_tokens") return "length";
|
|
378
|
+
if (raw === "tool_use" || hasToolCalls) return "tool_calls";
|
|
379
|
+
if (content) return "stop";
|
|
380
|
+
return "error";
|
|
381
|
+
}
|
|
382
|
+
function toRequestError(error, timeoutMs) {
|
|
383
|
+
if (isAbortLike(error)) {
|
|
384
|
+
return new OpenAICompatibleError(
|
|
385
|
+
0,
|
|
386
|
+
`LLM request timed out after ${timeoutMs}ms`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
return new OpenAICompatibleError(0, `LLM request failed: ${errorMessage(error)}`);
|
|
390
|
+
}
|
|
391
|
+
async function* parseAnthropicStream(body) {
|
|
392
|
+
const reader = body.getReader();
|
|
393
|
+
const decoder = new TextDecoder();
|
|
394
|
+
let buffer = "";
|
|
395
|
+
let messageId = randomUUID();
|
|
396
|
+
let model;
|
|
397
|
+
let messageStarted = false;
|
|
398
|
+
let text = "";
|
|
399
|
+
let stopReason;
|
|
400
|
+
let stopped = false;
|
|
401
|
+
let textBlockIndex;
|
|
402
|
+
const calls = /* @__PURE__ */ new Map();
|
|
403
|
+
try {
|
|
404
|
+
while (true) {
|
|
405
|
+
const { done, value } = await reader.read();
|
|
406
|
+
if (done) break;
|
|
407
|
+
buffer += decoder.decode(value, { stream: true });
|
|
408
|
+
let newlineIndex;
|
|
409
|
+
while ((newlineIndex = buffer.indexOf("\n")) >= 0) {
|
|
410
|
+
const line = buffer.slice(0, newlineIndex).replace(/\r$/, "");
|
|
411
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
412
|
+
if (line.startsWith("data:")) {
|
|
413
|
+
const data = line.slice(5).trim();
|
|
414
|
+
if (!data) continue;
|
|
415
|
+
let payload;
|
|
416
|
+
try {
|
|
417
|
+
payload = JSON.parse(data);
|
|
418
|
+
} catch {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const record = payload;
|
|
422
|
+
if (record?.type === "error") {
|
|
423
|
+
const message = typeof record.error?.message === "string" ? record.error.message : "LLM stream returned an error event";
|
|
424
|
+
throw new OpenAICompatibleError(0, `LLM stream failed: ${message}`);
|
|
425
|
+
}
|
|
426
|
+
if (record?.type === "ping") continue;
|
|
427
|
+
if (record?.type === "message_start") {
|
|
428
|
+
const start = record;
|
|
429
|
+
messageId = typeof start.message?.id === "string" && start.message.id ? start.message.id : randomUUID();
|
|
430
|
+
model = typeof start.message?.model === "string" ? start.message.model : void 0;
|
|
431
|
+
messageStarted = true;
|
|
432
|
+
yield {
|
|
433
|
+
type: "message_start",
|
|
434
|
+
id: messageId,
|
|
435
|
+
...model ? { model } : {}
|
|
436
|
+
};
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (record?.type === "content_block_start") {
|
|
440
|
+
const block = record.content_block;
|
|
441
|
+
const index = toIndex(record);
|
|
442
|
+
if (block?.type === "text") {
|
|
443
|
+
textBlockIndex = index;
|
|
444
|
+
if (typeof block.text === "string" && block.text) {
|
|
445
|
+
text += block.text;
|
|
446
|
+
yield { type: "message_delta", id: messageId, delta: block.text };
|
|
447
|
+
}
|
|
448
|
+
} else if (block?.type === "tool_use") {
|
|
449
|
+
const call = calls.get(index) ?? {
|
|
450
|
+
index,
|
|
451
|
+
id: typeof block.id === "string" ? block.id : "",
|
|
452
|
+
name: typeof block.name === "string" ? block.name : "",
|
|
453
|
+
rawArguments: typeof block.partial_json === "string" ? block.partial_json : "",
|
|
454
|
+
emitted: false,
|
|
455
|
+
finalized: false
|
|
456
|
+
};
|
|
457
|
+
calls.set(index, call);
|
|
458
|
+
if (!call.emitted && call.id && call.name) {
|
|
459
|
+
call.emitted = true;
|
|
460
|
+
yield {
|
|
461
|
+
type: "toolcall_start",
|
|
462
|
+
id: call.id,
|
|
463
|
+
index,
|
|
464
|
+
name: call.name
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if (record?.type === "content_block_delta") {
|
|
471
|
+
const block = record.delta;
|
|
472
|
+
const index = toIndex(record);
|
|
473
|
+
if (block?.type === "text_delta" && typeof block.text === "string") {
|
|
474
|
+
if (textBlockIndex === void 0) textBlockIndex = index;
|
|
475
|
+
if (textBlockIndex === index && block.text) {
|
|
476
|
+
text += block.text;
|
|
477
|
+
yield { type: "message_delta", id: messageId, delta: block.text };
|
|
478
|
+
}
|
|
479
|
+
} else if (block?.type === "input_json_delta") {
|
|
480
|
+
const call = calls.get(index);
|
|
481
|
+
if (call && typeof block.partial_json === "string") {
|
|
482
|
+
call.rawArguments += block.partial_json;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
if (record?.type === "content_block_stop") {
|
|
488
|
+
const block = record;
|
|
489
|
+
const index = toIndex(block);
|
|
490
|
+
const call = calls.get(index);
|
|
491
|
+
if (call && !call.finalized) {
|
|
492
|
+
call.finalized = true;
|
|
493
|
+
if (!call.name) {
|
|
494
|
+
const partial = parseArguments(call.rawArguments);
|
|
495
|
+
if (partial && typeof partial === "object") {
|
|
496
|
+
const name = partial.name;
|
|
497
|
+
if (typeof name === "string") call.name = name;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (!call.emitted && call.id && call.name) {
|
|
501
|
+
call.emitted = true;
|
|
502
|
+
yield {
|
|
503
|
+
type: "toolcall_start",
|
|
504
|
+
id: call.id,
|
|
505
|
+
index,
|
|
506
|
+
name: call.name
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
yield {
|
|
510
|
+
type: "toolcall_end",
|
|
511
|
+
id: call.id || `toolu_${index + 1}`,
|
|
512
|
+
index,
|
|
513
|
+
name: call.name,
|
|
514
|
+
arguments: parseArguments(call.rawArguments)
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (record?.type === "message_delta") {
|
|
520
|
+
const delta = record.delta;
|
|
521
|
+
if (delta?.stop_reason !== void 0) {
|
|
522
|
+
stopReason = delta.stop_reason;
|
|
523
|
+
}
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
if (record?.type === "message_stop") {
|
|
527
|
+
if (!messageStarted) {
|
|
528
|
+
messageStarted = true;
|
|
529
|
+
yield {
|
|
530
|
+
type: "message_start",
|
|
531
|
+
id: messageId,
|
|
532
|
+
...model ? { model } : {}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
stopped = true;
|
|
536
|
+
yield {
|
|
537
|
+
type: "message_stop",
|
|
538
|
+
id: messageId,
|
|
539
|
+
finishReason: toFinishReason(stopReason, text, calls.size > 0)
|
|
540
|
+
};
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
} finally {
|
|
547
|
+
if (reader.releaseLock) {
|
|
548
|
+
try {
|
|
549
|
+
reader.releaseLock();
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (!messageStarted) {
|
|
555
|
+
yield {
|
|
556
|
+
type: "message_start",
|
|
557
|
+
id: messageId,
|
|
558
|
+
...model ? { model } : {}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
if (!stopped) {
|
|
562
|
+
yield {
|
|
563
|
+
type: "message_stop",
|
|
564
|
+
id: messageId,
|
|
565
|
+
finishReason: toFinishReason(stopReason, text, calls.size > 0)
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function toIndex(block) {
|
|
570
|
+
return typeof block.index === "number" ? block.index : 0;
|
|
571
|
+
}
|
|
572
|
+
function toStreamError(error, timeoutMs) {
|
|
573
|
+
if (isAbortLike(error)) {
|
|
574
|
+
return new OpenAICompatibleError(
|
|
575
|
+
0,
|
|
576
|
+
`LLM stream timed out after ${timeoutMs}ms`
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
return new OpenAICompatibleError(0, `LLM stream failed: ${errorMessage(error)}`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// packages/llm/src/openai.ts
|
|
583
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
584
|
+
function openaiCompatAdapter(config = {}) {
|
|
585
|
+
return {
|
|
586
|
+
async complete(messages, tools, options) {
|
|
587
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_LLM_TIMEOUT_MS;
|
|
588
|
+
const maxRetries = config.maxRetries ?? DEFAULT_LLM_MAX_RETRIES;
|
|
589
|
+
const retryDelayMs = config.retryDelayMs ?? DEFAULT_LLM_RETRY_DELAY_MS;
|
|
590
|
+
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
|
|
591
|
+
const request = buildRequest2(
|
|
592
|
+
messages,
|
|
593
|
+
tools,
|
|
594
|
+
config,
|
|
595
|
+
combineSignal(options.signal, timeoutMs)
|
|
596
|
+
);
|
|
597
|
+
try {
|
|
598
|
+
const response = await fetch(request.url, request.init);
|
|
599
|
+
if (!isRetryableStatus(response.status) || attempt >= maxRetries) {
|
|
600
|
+
return parseResponse2(response, config.apiKey);
|
|
601
|
+
}
|
|
602
|
+
await response.body?.cancel();
|
|
603
|
+
} catch (error) {
|
|
604
|
+
if (isExternalAbort(error, options.signal)) {
|
|
605
|
+
throw new OpenAICompatibleError(
|
|
606
|
+
0,
|
|
607
|
+
`LLM request aborted: ${errorMessage(error)}`
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
if (attempt >= maxRetries) {
|
|
611
|
+
throw toRequestError2(error, timeoutMs);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
615
|
+
}
|
|
616
|
+
throw new OpenAICompatibleError(0, "LLM request failed");
|
|
617
|
+
},
|
|
618
|
+
async *stream(messages, tools, options) {
|
|
619
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_LLM_TIMEOUT_MS;
|
|
620
|
+
const maxRetries = config.maxRetries ?? DEFAULT_LLM_MAX_RETRIES;
|
|
621
|
+
const retryDelayMs = config.retryDelayMs ?? DEFAULT_LLM_RETRY_DELAY_MS;
|
|
622
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
623
|
+
const request = buildRequest2(
|
|
624
|
+
messages,
|
|
625
|
+
tools,
|
|
626
|
+
config,
|
|
627
|
+
combineSignal(options.signal, timeoutMs),
|
|
628
|
+
true
|
|
629
|
+
);
|
|
630
|
+
let response;
|
|
631
|
+
try {
|
|
632
|
+
response = await fetch(request.url, request.init);
|
|
633
|
+
} catch (error) {
|
|
634
|
+
if (isExternalAbort(error, options.signal)) {
|
|
635
|
+
throw new OpenAICompatibleError(
|
|
636
|
+
0,
|
|
637
|
+
`LLM stream aborted: ${errorMessage(error)}`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
if (attempt >= maxRetries) {
|
|
641
|
+
throw toStreamError2(error, timeoutMs);
|
|
642
|
+
}
|
|
643
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
if (!response.ok || !response.body) {
|
|
647
|
+
if (isRetryableStatus(response.status) && attempt < maxRetries) {
|
|
648
|
+
await response.body?.cancel();
|
|
649
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
if (!response.ok) await assertOk(response, config.apiKey);
|
|
653
|
+
throw new OpenAICompatibleError(
|
|
654
|
+
response.status,
|
|
655
|
+
"LLM stream response had no body"
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
let sawEvent = false;
|
|
659
|
+
try {
|
|
660
|
+
for await (const event of parseOpenAIStream(response.body)) {
|
|
661
|
+
sawEvent = true;
|
|
662
|
+
yield event;
|
|
663
|
+
}
|
|
664
|
+
return;
|
|
665
|
+
} catch (error) {
|
|
666
|
+
if (isExternalAbort(error, options.signal)) {
|
|
667
|
+
throw new OpenAICompatibleError(
|
|
668
|
+
0,
|
|
669
|
+
`LLM stream aborted: ${errorMessage(error)}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
if (sawEvent || attempt >= maxRetries) {
|
|
673
|
+
throw toStreamError2(error, timeoutMs);
|
|
674
|
+
}
|
|
675
|
+
await sleep(retryDelayMs * 2 ** attempt);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
async function listModels(config = {}) {
|
|
682
|
+
const baseUrl = normalizeBaseUrl(config.baseUrl);
|
|
683
|
+
const headers = authHeaders(config.apiKey);
|
|
684
|
+
let response;
|
|
685
|
+
try {
|
|
686
|
+
response = await fetch(`${baseUrl}/models`, { headers });
|
|
687
|
+
} catch (error) {
|
|
688
|
+
throw new OpenAICompatibleError(
|
|
689
|
+
0,
|
|
690
|
+
`model list request failed: ${errorMessage(error)}`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
await assertOk(response);
|
|
694
|
+
const payload = await parseJson(response);
|
|
695
|
+
const data = payload?.data;
|
|
696
|
+
if (!Array.isArray(data)) {
|
|
697
|
+
throw new OpenAICompatibleError(
|
|
698
|
+
response.status,
|
|
699
|
+
"model list response did not contain an array in data"
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
return data.map((entry) => {
|
|
703
|
+
const record = entry;
|
|
704
|
+
return typeof record?.id === "string" ? record.id : "";
|
|
705
|
+
}).filter(Boolean);
|
|
706
|
+
}
|
|
707
|
+
function buildRequest2(messages, tools, config, signal, stream = false) {
|
|
708
|
+
const body = {
|
|
709
|
+
model: config.model ?? DEFAULT_DEEPSEEK_MODEL,
|
|
710
|
+
messages: messages.map(toOpenAIMessage)
|
|
711
|
+
};
|
|
712
|
+
if (stream) body.stream = true;
|
|
713
|
+
if (tools.length) {
|
|
714
|
+
body.tools = tools.map(toOpenAITool);
|
|
715
|
+
}
|
|
716
|
+
if (config.temperature !== void 0) body.temperature = config.temperature;
|
|
717
|
+
if (config.maxTokens !== void 0) body.max_tokens = config.maxTokens;
|
|
718
|
+
return {
|
|
719
|
+
url: `${normalizeBaseUrl(config.baseUrl)}/chat/completions`,
|
|
720
|
+
init: {
|
|
721
|
+
method: "POST",
|
|
722
|
+
headers: {
|
|
723
|
+
"content-type": "application/json",
|
|
724
|
+
...authHeaders(config.apiKey)
|
|
725
|
+
},
|
|
726
|
+
body: JSON.stringify(body),
|
|
727
|
+
...signal ? { signal } : {}
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function authHeaders(apiKey) {
|
|
732
|
+
return apiKey ? { authorization: `Bearer ${apiKey}` } : {};
|
|
733
|
+
}
|
|
734
|
+
function toOpenAIMessage(message) {
|
|
735
|
+
const payload = {
|
|
736
|
+
role: message.role,
|
|
737
|
+
content: message.content
|
|
738
|
+
};
|
|
739
|
+
if (message.role === "tool" && message.name) payload.name = message.name;
|
|
740
|
+
if (message.tool_call_id) payload.tool_call_id = message.tool_call_id;
|
|
741
|
+
if (message.tool_calls?.length) {
|
|
742
|
+
payload.tool_calls = message.tool_calls.map((call) => ({
|
|
743
|
+
id: call.id,
|
|
744
|
+
type: "function",
|
|
745
|
+
function: {
|
|
746
|
+
name: call.name,
|
|
747
|
+
arguments: stringifyArguments(call.arguments)
|
|
748
|
+
}
|
|
749
|
+
}));
|
|
750
|
+
}
|
|
751
|
+
return payload;
|
|
752
|
+
}
|
|
753
|
+
function toOpenAITool(tool) {
|
|
754
|
+
return {
|
|
755
|
+
type: "function",
|
|
756
|
+
function: {
|
|
757
|
+
name: tool.schema.name,
|
|
758
|
+
description: tool.schema.description,
|
|
759
|
+
parameters: tool.schema.parameters ?? { type: "object", properties: {} }
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
async function parseResponse2(response, apiKey) {
|
|
764
|
+
await assertOk(response, apiKey);
|
|
765
|
+
const payload = await parseJson(response);
|
|
766
|
+
return parseCompletion2(payload);
|
|
767
|
+
}
|
|
768
|
+
function parseCompletion2(payload) {
|
|
769
|
+
const record = payload;
|
|
770
|
+
const choices = Array.isArray(record?.choices) ? record.choices : [];
|
|
771
|
+
const choice = choices[0];
|
|
772
|
+
const rawContent = choice?.message?.content;
|
|
773
|
+
const content = typeof rawContent === "string" ? rawContent : void 0;
|
|
774
|
+
const toolCalls = parseToolCalls2(choice?.message?.tool_calls);
|
|
775
|
+
const finishReason = toFinishReason2(
|
|
776
|
+
choice?.finish_reason,
|
|
777
|
+
content,
|
|
778
|
+
toolCalls !== void 0
|
|
779
|
+
);
|
|
780
|
+
const completion = {
|
|
781
|
+
finishReason
|
|
782
|
+
};
|
|
783
|
+
if (content !== void 0) completion.content = content;
|
|
784
|
+
if (toolCalls !== void 0) completion.toolCalls = toolCalls;
|
|
785
|
+
return completion;
|
|
786
|
+
}
|
|
787
|
+
function parseToolCalls2(value) {
|
|
788
|
+
if (!Array.isArray(value)) return void 0;
|
|
789
|
+
const calls = [];
|
|
790
|
+
for (const entry of value) {
|
|
791
|
+
if (!entry || typeof entry !== "object") continue;
|
|
792
|
+
const item = entry;
|
|
793
|
+
const name = typeof item.function?.name === "string" ? item.function.name : "";
|
|
794
|
+
if (!name) continue;
|
|
795
|
+
const id = typeof item.id === "string" && item.id ? item.id : `call_${calls.length + 1}`;
|
|
796
|
+
const rawArguments = typeof item.function?.arguments === "string" ? item.function.arguments : "";
|
|
797
|
+
calls.push({
|
|
798
|
+
id,
|
|
799
|
+
name,
|
|
800
|
+
arguments: parseArguments(rawArguments)
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
return calls.length ? calls : void 0;
|
|
804
|
+
}
|
|
805
|
+
function toFinishReason2(raw, content, hasToolCalls) {
|
|
806
|
+
if (raw === "stop" || raw === void 0 && content !== void 0 && !hasToolCalls) {
|
|
807
|
+
return "stop";
|
|
808
|
+
}
|
|
809
|
+
if (raw === "length") return "length";
|
|
810
|
+
if (raw === "tool_calls" || hasToolCalls) return "tool_calls";
|
|
811
|
+
return "error";
|
|
812
|
+
}
|
|
813
|
+
function toRequestError2(error, timeoutMs) {
|
|
814
|
+
if (isAbortLike(error)) {
|
|
815
|
+
return new OpenAICompatibleError(
|
|
816
|
+
0,
|
|
817
|
+
`LLM request timed out after ${timeoutMs}ms`
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
return new OpenAICompatibleError(0, `LLM request failed: ${errorMessage(error)}`);
|
|
821
|
+
}
|
|
822
|
+
async function* parseOpenAIStream(body) {
|
|
823
|
+
const reader = body.getReader();
|
|
824
|
+
const decoder = new TextDecoder();
|
|
825
|
+
let buffer = "";
|
|
826
|
+
let content = "";
|
|
827
|
+
let messageId = randomUUID2();
|
|
828
|
+
let model;
|
|
829
|
+
let messageStarted = false;
|
|
830
|
+
let finishReason;
|
|
831
|
+
const calls = /* @__PURE__ */ new Map();
|
|
832
|
+
try {
|
|
833
|
+
while (true) {
|
|
834
|
+
const { done, value } = await reader.read();
|
|
835
|
+
if (done) break;
|
|
836
|
+
buffer += decoder.decode(value, { stream: true });
|
|
837
|
+
let newlineIndex;
|
|
838
|
+
while ((newlineIndex = buffer.indexOf("\n")) >= 0) {
|
|
839
|
+
const line = buffer.slice(0, newlineIndex).replace(/\r$/, "");
|
|
840
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
841
|
+
if (!line.startsWith("data:")) continue;
|
|
842
|
+
const data = line.slice(5).trim();
|
|
843
|
+
if (!data) continue;
|
|
844
|
+
if (data === "[DONE]") {
|
|
845
|
+
yield* finalizeStreamEvents(messageId, content, calls, finishReason);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
let payload;
|
|
849
|
+
try {
|
|
850
|
+
payload = JSON.parse(data);
|
|
851
|
+
} catch {
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
const events = processStreamChunk(
|
|
855
|
+
payload,
|
|
856
|
+
{ messageId, model, messageStarted },
|
|
857
|
+
{ content, finishReason },
|
|
858
|
+
calls
|
|
859
|
+
);
|
|
860
|
+
messageId = events.messageId;
|
|
861
|
+
model = events.model;
|
|
862
|
+
messageStarted = true;
|
|
863
|
+
content = events.content;
|
|
864
|
+
finishReason = events.finishReason;
|
|
865
|
+
for (const event of events.events) yield event;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
} finally {
|
|
869
|
+
if (reader.releaseLock) {
|
|
870
|
+
try {
|
|
871
|
+
reader.releaseLock();
|
|
872
|
+
} catch {
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
yield* finalizeStreamEvents(messageId, content, calls, finishReason);
|
|
877
|
+
}
|
|
878
|
+
function processStreamChunk(payload, state, progress, calls) {
|
|
879
|
+
const record = payload;
|
|
880
|
+
const events = [];
|
|
881
|
+
let messageId = state.messageId;
|
|
882
|
+
let model = state.model;
|
|
883
|
+
let messageStarted = state.messageStarted;
|
|
884
|
+
let content = progress.content;
|
|
885
|
+
let finishReason = progress.finishReason;
|
|
886
|
+
if (typeof record?.id === "string" && record.id) messageId = record.id;
|
|
887
|
+
if (typeof record?.model === "string" && record.model) model = record.model;
|
|
888
|
+
if (!messageStarted) {
|
|
889
|
+
messageStarted = true;
|
|
890
|
+
const start = {
|
|
891
|
+
type: "message_start",
|
|
892
|
+
id: messageId,
|
|
893
|
+
...model ? { model } : {}
|
|
894
|
+
};
|
|
895
|
+
events.push(start);
|
|
896
|
+
}
|
|
897
|
+
const choices = Array.isArray(record?.choices) ? record.choices : [];
|
|
898
|
+
const choice = choices[0];
|
|
899
|
+
if (choice?.finish_reason !== void 0 && choice.finish_reason !== null) {
|
|
900
|
+
finishReason = choice.finish_reason;
|
|
901
|
+
}
|
|
902
|
+
const delta = choice?.delta;
|
|
903
|
+
if (typeof delta?.content === "string" && delta.content) {
|
|
904
|
+
content += delta.content;
|
|
905
|
+
events.push({ type: "message_delta", id: messageId, delta: delta.content });
|
|
906
|
+
}
|
|
907
|
+
if (Array.isArray(delta?.tool_calls)) {
|
|
908
|
+
for (const entry of delta.tool_calls) {
|
|
909
|
+
const index = typeof entry.index === "number" ? entry.index : 0;
|
|
910
|
+
let call = calls.get(index);
|
|
911
|
+
if (!call) {
|
|
912
|
+
call = { index, id: "", name: "", arguments: "", emitted: false };
|
|
913
|
+
calls.set(index, call);
|
|
914
|
+
}
|
|
915
|
+
if (typeof entry.id === "string" && entry.id) call.id = entry.id;
|
|
916
|
+
if (typeof entry.function?.name === "string" && entry.function.name) {
|
|
917
|
+
call.name = entry.function.name;
|
|
918
|
+
}
|
|
919
|
+
if (typeof entry.function?.arguments === "string") {
|
|
920
|
+
call.arguments += entry.function.arguments;
|
|
921
|
+
}
|
|
922
|
+
if (!call.emitted && call.id && call.name) {
|
|
923
|
+
call.emitted = true;
|
|
924
|
+
events.push({
|
|
925
|
+
type: "toolcall_start",
|
|
926
|
+
id: call.id,
|
|
927
|
+
index,
|
|
928
|
+
name: call.name
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return {
|
|
934
|
+
messageId,
|
|
935
|
+
model,
|
|
936
|
+
messageStarted,
|
|
937
|
+
content,
|
|
938
|
+
finishReason,
|
|
939
|
+
events
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
function finalizeStreamEvents(messageId, content, calls, finishReason) {
|
|
943
|
+
const events = [];
|
|
944
|
+
const toolCalls = [];
|
|
945
|
+
for (const call of calls.values()) {
|
|
946
|
+
if (!call.emitted) continue;
|
|
947
|
+
events.push({
|
|
948
|
+
type: "toolcall_end",
|
|
949
|
+
id: call.id,
|
|
950
|
+
index: call.index,
|
|
951
|
+
name: call.name,
|
|
952
|
+
arguments: parseArguments(call.arguments)
|
|
953
|
+
});
|
|
954
|
+
toolCalls.push({
|
|
955
|
+
id: call.id,
|
|
956
|
+
name: call.name,
|
|
957
|
+
arguments: parseArguments(call.arguments)
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
events.push({
|
|
961
|
+
type: "message_stop",
|
|
962
|
+
id: messageId,
|
|
963
|
+
finishReason: toFinishReason2(finishReason, content, toolCalls.length > 0)
|
|
964
|
+
});
|
|
965
|
+
return events;
|
|
966
|
+
}
|
|
967
|
+
function toStreamError2(error, timeoutMs) {
|
|
968
|
+
if (isAbortLike(error)) {
|
|
969
|
+
return new OpenAICompatibleError(
|
|
970
|
+
0,
|
|
971
|
+
`LLM stream timed out after ${timeoutMs}ms`
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
return new OpenAICompatibleError(0, `LLM stream failed: ${errorMessage(error)}`);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// packages/llm/src/provider.ts
|
|
978
|
+
function createLlmAdapter(config) {
|
|
979
|
+
if (config.protocol === "anthropic") {
|
|
980
|
+
return anthropicMessagesAdapter(config);
|
|
981
|
+
}
|
|
982
|
+
if (config.protocol === "openai") {
|
|
983
|
+
return openaiCompatAdapter(config);
|
|
984
|
+
}
|
|
985
|
+
if (lookupModel(config.model)?.protocol === "anthropic") {
|
|
986
|
+
return anthropicMessagesAdapter(config);
|
|
987
|
+
}
|
|
988
|
+
return openaiCompatAdapter(config);
|
|
989
|
+
}
|
|
990
|
+
export {
|
|
991
|
+
DEFAULT_DEEPSEEK_MODEL,
|
|
992
|
+
DEFAULT_LLM_MAX_RETRIES,
|
|
993
|
+
DEFAULT_LLM_RETRY_DELAY_MS,
|
|
994
|
+
DEFAULT_LLM_TIMEOUT_MS,
|
|
995
|
+
DEFAULT_MODEL,
|
|
996
|
+
DEFAULT_OPENCODE_GO_BASE_URL,
|
|
997
|
+
MODEL_CATALOG,
|
|
998
|
+
OpenAICompatibleError,
|
|
999
|
+
anthropicMessagesAdapter,
|
|
1000
|
+
assertOk,
|
|
1001
|
+
combineSignal,
|
|
1002
|
+
createLlmAdapter,
|
|
1003
|
+
errorMessage,
|
|
1004
|
+
isAbortLike,
|
|
1005
|
+
isExternalAbort,
|
|
1006
|
+
isRetryableStatus,
|
|
1007
|
+
listModels,
|
|
1008
|
+
lookupModel,
|
|
1009
|
+
normalizeBaseUrl,
|
|
1010
|
+
openaiCompatAdapter,
|
|
1011
|
+
parseArguments,
|
|
1012
|
+
parseJson,
|
|
1013
|
+
sleep,
|
|
1014
|
+
stringifyArguments
|
|
1015
|
+
};
|
|
1016
|
+
//# sourceMappingURL=llm.js.map
|