pi-freerouter 0.1.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 +79 -0
- package/dist/discovery.d.ts +2 -0
- package/dist/discovery.js +47 -0
- package/dist/discovery.js.map +1 -0
- package/dist/discovery.test.d.ts +1 -0
- package/dist/discovery.test.js +68 -0
- package/dist/discovery.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +321 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +12 -0
- package/dist/router.js +53 -0
- package/dist/router.js.map +1 -0
- package/dist/router.test.d.ts +1 -0
- package/dist/router.test.js +74 -0
- package/dist/router.test.js.map +1 -0
- package/dist/stream.d.ts +11 -0
- package/dist/stream.js +292 -0
- package/dist/stream.js.map +1 -0
- package/dist/stream.test.d.ts +1 -0
- package/dist/stream.test.js +129 -0
- package/dist/stream.test.js.map +1 -0
- package/dist/types.d.ts +186 -0
- package/dist/types.js +88 -0
- package/dist/types.js.map +1 -0
- package/package.json +23 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { strict as assert } from "node:assert";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { FreeRouter } from "./router.js";
|
|
4
|
+
describe("FreeRouter", () => {
|
|
5
|
+
it("returns the first model when none exhausted", () => {
|
|
6
|
+
const r = new FreeRouter(["a:free", "b:free", "c:free"]);
|
|
7
|
+
assert.equal(r.nextModel(), "a:free");
|
|
8
|
+
});
|
|
9
|
+
it("skips exhausted models", () => {
|
|
10
|
+
const r = new FreeRouter(["a:free", "b:free", "c:free"]);
|
|
11
|
+
r.markExhausted("a:free");
|
|
12
|
+
assert.equal(r.nextModel(), "b:free");
|
|
13
|
+
});
|
|
14
|
+
it("returns null when all models exhausted", () => {
|
|
15
|
+
const r = new FreeRouter(["a:free", "b:free"]);
|
|
16
|
+
r.markExhausted("a:free");
|
|
17
|
+
r.markExhausted("b:free");
|
|
18
|
+
assert.equal(r.nextModel(), null);
|
|
19
|
+
});
|
|
20
|
+
it("always returns first available in insertion order", () => {
|
|
21
|
+
const r = new FreeRouter(["a:free", "b:free", "c:free"]);
|
|
22
|
+
r.markExhausted("a:free");
|
|
23
|
+
r.markExhausted("b:free");
|
|
24
|
+
assert.equal(r.nextModel(), "c:free");
|
|
25
|
+
assert.equal(r.nextModel(), "c:free"); // stable
|
|
26
|
+
});
|
|
27
|
+
it("handles empty model list", () => {
|
|
28
|
+
const r = new FreeRouter([]);
|
|
29
|
+
assert.equal(r.nextModel(), null);
|
|
30
|
+
});
|
|
31
|
+
it("ignores markExhausted with unknown ID", () => {
|
|
32
|
+
const r = new FreeRouter(["a:free", "b:free"]);
|
|
33
|
+
r.markExhausted("unknown:free"); // not in models list
|
|
34
|
+
assert.equal(r.nextModel(), "a:free"); // unchanged
|
|
35
|
+
});
|
|
36
|
+
it("markExhausted is idempotent", () => {
|
|
37
|
+
const r = new FreeRouter(["a:free", "b:free"]);
|
|
38
|
+
r.markExhausted("a:free");
|
|
39
|
+
r.markExhausted("a:free"); // second call, same ID
|
|
40
|
+
assert.equal(r.nextModel(), "b:free"); // still correct
|
|
41
|
+
});
|
|
42
|
+
it("nextModels returns up to count non-exhausted models in order", () => {
|
|
43
|
+
const r = new FreeRouter(["a:free", "b:free", "c:free", "d:free"]);
|
|
44
|
+
r.markExhausted("b:free");
|
|
45
|
+
assert.deepEqual(r.nextModels(2), ["a:free", "c:free"]);
|
|
46
|
+
});
|
|
47
|
+
it("nextModels returns fewer than count when not enough available", () => {
|
|
48
|
+
const r = new FreeRouter(["a:free", "b:free"]);
|
|
49
|
+
r.markExhausted("b:free");
|
|
50
|
+
assert.deepEqual(r.nextModels(3), ["a:free"]);
|
|
51
|
+
});
|
|
52
|
+
it("exhausted model returns after TTL expires", async () => {
|
|
53
|
+
const r = new FreeRouter(["a:free", "b:free"], 20); // 20ms TTL for test speed
|
|
54
|
+
r.markExhausted("a:free");
|
|
55
|
+
assert.equal(r.nextModel(), "b:free"); // a is excluded
|
|
56
|
+
await new Promise((res) => setTimeout(res, 30)); // wait past TTL
|
|
57
|
+
assert.equal(r.nextModel(), "a:free"); // a is back at front of list
|
|
58
|
+
});
|
|
59
|
+
it("markSlow skips model with short TTL", async () => {
|
|
60
|
+
const r = new FreeRouter(["a:free", "b:free"]);
|
|
61
|
+
r.markSlow("a:free");
|
|
62
|
+
assert.equal(r.nextModel(), "b:free"); // a temporarily skipped
|
|
63
|
+
// Note: full 15s slow TTL not tested to keep tests fast; TTL logic is shared with markExhausted
|
|
64
|
+
});
|
|
65
|
+
it("markSlow does not downgrade an exhausted model to slow TTL", () => {
|
|
66
|
+
const r = new FreeRouter(["a:free", "b:free"], 90_000);
|
|
67
|
+
r.markExhausted("a:free");
|
|
68
|
+
r.markSlow("a:free"); // should keep the longer 90s TTL
|
|
69
|
+
// Both markExhausted and markSlow skip the model; the key invariant is that
|
|
70
|
+
// markSlow won't replace a long-TTL entry with a short one.
|
|
71
|
+
assert.equal(r.nextModel(), "b:free"); // a still excluded
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=router.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.test.js","sourceRoot":"","sources":["../src/router.test.ts"],"names":[],"mappings":"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"}
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AssistantMessageEventStream, Context } from "./types.js";
|
|
2
|
+
export declare class ModelExhaustedError extends Error {
|
|
3
|
+
readonly modelId: string;
|
|
4
|
+
readonly status: number;
|
|
5
|
+
constructor(modelId: string, status: number);
|
|
6
|
+
}
|
|
7
|
+
/** Thrown when the API key has insufficient credits (HTTP 402). Not retriable. */
|
|
8
|
+
export declare class ModelFatalError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare function streamFreeModel(modelId: string, context: Context, apiKey: string, outStream: AssistantMessageEventStream, signal?: AbortSignal, maxTokens?: number): Promise<void>;
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
export class ModelExhaustedError extends Error {
|
|
2
|
+
modelId;
|
|
3
|
+
status;
|
|
4
|
+
constructor(modelId, status) {
|
|
5
|
+
super(`Model ${modelId} quota exceeded (HTTP ${status})`);
|
|
6
|
+
this.modelId = modelId;
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.name = "ModelExhaustedError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Thrown when the API key has insufficient credits (HTTP 402). Not retriable. */
|
|
12
|
+
export class ModelFatalError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "ModelFatalError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Fix 6: Helper — normalize OpenRouter finish_reason to Pi StopReason
|
|
19
|
+
function normalizeStopReason(finishReason) {
|
|
20
|
+
if (finishReason === "tool_calls")
|
|
21
|
+
return "toolUse";
|
|
22
|
+
if (finishReason === "length")
|
|
23
|
+
return "length";
|
|
24
|
+
return "stop";
|
|
25
|
+
}
|
|
26
|
+
// Fix 7: Helper — deep-clone output for partial snapshots (content items are shallow-cloned)
|
|
27
|
+
function snapshot(output) {
|
|
28
|
+
return { ...output, content: output.content.map((c) => ({ ...c })) };
|
|
29
|
+
}
|
|
30
|
+
// Fix 6: Helper — get current text for text_end content field
|
|
31
|
+
function getCurrentText(output, textIndex) {
|
|
32
|
+
const block = output.content[textIndex];
|
|
33
|
+
return block?.type === "text" ? block.text : "";
|
|
34
|
+
}
|
|
35
|
+
// Fix 6: Helper — emit text_end event
|
|
36
|
+
function emitTextEnd(outStream, output, contentIndex) {
|
|
37
|
+
outStream.push({
|
|
38
|
+
type: "text_end",
|
|
39
|
+
contentIndex,
|
|
40
|
+
content: getCurrentText(output, contentIndex),
|
|
41
|
+
partial: snapshot(output),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Convert Pi's internal message format to OpenRouter's chat-completion format.
|
|
46
|
+
*
|
|
47
|
+
* Pi uses role:"toolResult" and embeds tool calls inside content[]; OpenRouter
|
|
48
|
+
* expects role:"tool" and tool_calls as a top-level array on assistant messages.
|
|
49
|
+
*/
|
|
50
|
+
function normalizeMessages(messages) {
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const msg of messages) {
|
|
53
|
+
if (msg.role === "user") {
|
|
54
|
+
if (typeof msg.content === "string") {
|
|
55
|
+
out.push({ role: "user", content: msg.content });
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const parts = msg.content.map((c) => {
|
|
59
|
+
if (c.type === "text")
|
|
60
|
+
return { type: "text", text: c.text };
|
|
61
|
+
// image → data URL
|
|
62
|
+
return { type: "image_url", image_url: { url: `data:${c.mimeType};base64,${c.data}` } };
|
|
63
|
+
});
|
|
64
|
+
out.push({ role: "user", content: parts });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (msg.role === "assistant") {
|
|
68
|
+
const textParts = msg.content
|
|
69
|
+
.filter((c) => c.type === "text")
|
|
70
|
+
.map((c) => c.text)
|
|
71
|
+
.join("");
|
|
72
|
+
const toolCalls = msg.content
|
|
73
|
+
.filter((c) => c.type === "toolCall")
|
|
74
|
+
.map((tc) => ({
|
|
75
|
+
id: tc.id,
|
|
76
|
+
type: "function",
|
|
77
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.arguments) },
|
|
78
|
+
}));
|
|
79
|
+
if (toolCalls.length > 0) {
|
|
80
|
+
out.push({ role: "assistant", content: textParts || null, tool_calls: toolCalls });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
out.push({ role: "assistant", content: textParts });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (msg.role === "toolResult") {
|
|
87
|
+
const tr = msg;
|
|
88
|
+
const text = tr.content
|
|
89
|
+
.filter((c) => c.type === "text")
|
|
90
|
+
.map((c) => c.text)
|
|
91
|
+
.join("\n");
|
|
92
|
+
out.push({ role: "tool", tool_call_id: tr.toolCallId, content: text });
|
|
93
|
+
}
|
|
94
|
+
// unknown roles: silently drop (forward compatibility)
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
export async function streamFreeModel(modelId, context, apiKey, outStream, signal, maxTokens) {
|
|
99
|
+
const messages = [
|
|
100
|
+
...(context.systemPrompt ? [{ role: "system", content: context.systemPrompt }] : []),
|
|
101
|
+
...normalizeMessages(context.messages),
|
|
102
|
+
];
|
|
103
|
+
const body = { model: modelId, stream: true, messages };
|
|
104
|
+
if (maxTokens !== undefined)
|
|
105
|
+
body.max_tokens = maxTokens;
|
|
106
|
+
// Forward tool definitions if Pi provided them — free models that support
|
|
107
|
+
// function calling will use them; those that don't will return 400 and be
|
|
108
|
+
// temporarily skipped by the router.
|
|
109
|
+
if (context.tools && context.tools.length > 0) {
|
|
110
|
+
body.tools = context.tools.map((t) => ({
|
|
111
|
+
type: "function",
|
|
112
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: {
|
|
118
|
+
Authorization: `Bearer ${apiKey}`,
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
"X-Title": "pi-freerouter",
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify(body),
|
|
123
|
+
signal,
|
|
124
|
+
});
|
|
125
|
+
if (response.status === 402) {
|
|
126
|
+
throw new ModelFatalError("OpenRouter API key has insufficient credits. Add credits at openrouter.ai/credits.");
|
|
127
|
+
}
|
|
128
|
+
if (response.status === 429 || response.status >= 500) {
|
|
129
|
+
throw new ModelExhaustedError(modelId, response.status);
|
|
130
|
+
}
|
|
131
|
+
// 400/422: model rejected the request (unsupported features, content policy, etc.)
|
|
132
|
+
// Treat like exhaustion so the router skips this model for the current batch.
|
|
133
|
+
if (response.status === 400 || response.status === 422) {
|
|
134
|
+
throw new ModelExhaustedError(modelId, response.status);
|
|
135
|
+
}
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error(`OpenRouter error: ${response.status} ${response.statusText}`);
|
|
138
|
+
}
|
|
139
|
+
// Fix 4: Guard response.body null
|
|
140
|
+
if (!response.body) {
|
|
141
|
+
throw new Error(`OpenRouter returned empty body for model ${modelId}`);
|
|
142
|
+
}
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
const output = {
|
|
145
|
+
role: "assistant",
|
|
146
|
+
content: [],
|
|
147
|
+
api: "openrouter",
|
|
148
|
+
provider: "openrouter",
|
|
149
|
+
model: modelId,
|
|
150
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
|
|
151
|
+
stopReason: "stop",
|
|
152
|
+
timestamp: now,
|
|
153
|
+
};
|
|
154
|
+
outStream.push({ type: "start", partial: snapshot(output) });
|
|
155
|
+
let textStarted = false;
|
|
156
|
+
// Text content is always at index 0.
|
|
157
|
+
const TEXT_INDEX = 0;
|
|
158
|
+
const pendingToolCalls = new Map();
|
|
159
|
+
function flushToolCalls() {
|
|
160
|
+
for (const [, p] of pendingToolCalls) {
|
|
161
|
+
let args = {};
|
|
162
|
+
try {
|
|
163
|
+
args = JSON.parse(p.argsBuffer);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
args = { _raw: p.argsBuffer };
|
|
167
|
+
}
|
|
168
|
+
const block = output.content[p.contentIndex];
|
|
169
|
+
if (block?.type === "toolCall")
|
|
170
|
+
block.arguments = args;
|
|
171
|
+
outStream.push({
|
|
172
|
+
type: "toolcall_end",
|
|
173
|
+
contentIndex: p.contentIndex,
|
|
174
|
+
toolCall: { type: "toolCall", id: p.id, name: p.name, arguments: args },
|
|
175
|
+
partial: snapshot(output),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
pendingToolCalls.clear();
|
|
179
|
+
}
|
|
180
|
+
const reader = response.body.getReader();
|
|
181
|
+
const decoder = new TextDecoder();
|
|
182
|
+
let buffer = "";
|
|
183
|
+
try {
|
|
184
|
+
while (true) {
|
|
185
|
+
const { done, value } = await reader.read();
|
|
186
|
+
if (done)
|
|
187
|
+
break;
|
|
188
|
+
buffer += decoder.decode(value, { stream: true });
|
|
189
|
+
const lines = buffer.split("\n");
|
|
190
|
+
buffer = lines.pop();
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
if (!line.startsWith("data: "))
|
|
193
|
+
continue;
|
|
194
|
+
const data = line.slice(6).trim();
|
|
195
|
+
if (data === "[DONE]") {
|
|
196
|
+
if (textStarted)
|
|
197
|
+
emitTextEnd(outStream, output, TEXT_INDEX);
|
|
198
|
+
flushToolCalls();
|
|
199
|
+
outStream.push({ type: "done", reason: output.stopReason, message: snapshot(output) });
|
|
200
|
+
outStream.end();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let chunk;
|
|
204
|
+
try {
|
|
205
|
+
chunk = JSON.parse(data);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// OpenRouter sometimes streams errors inline as {"error":{"code":N,"message":"..."}}
|
|
211
|
+
// instead of returning a non-200 HTTP status.
|
|
212
|
+
if (chunk.error) {
|
|
213
|
+
const code = chunk.error.code ?? chunk.error.status ?? 0;
|
|
214
|
+
if (code === 402)
|
|
215
|
+
throw new ModelFatalError(chunk.error.message ?? "insufficient credits");
|
|
216
|
+
if (code === 429 || code >= 500)
|
|
217
|
+
throw new ModelExhaustedError(modelId, code);
|
|
218
|
+
// Other inline errors (e.g. content policy): treat as exhausted so we skip this model.
|
|
219
|
+
throw new ModelExhaustedError(modelId, code || 400);
|
|
220
|
+
}
|
|
221
|
+
const choice = chunk.choices?.[0];
|
|
222
|
+
const delta = choice?.delta;
|
|
223
|
+
const finishReason = choice?.finish_reason;
|
|
224
|
+
const usage = chunk.usage;
|
|
225
|
+
if (usage) {
|
|
226
|
+
output.usage.input = usage.prompt_tokens ?? 0;
|
|
227
|
+
output.usage.output = usage.completion_tokens ?? 0;
|
|
228
|
+
output.usage.totalTokens = usage.total_tokens ?? 0;
|
|
229
|
+
}
|
|
230
|
+
if (finishReason) {
|
|
231
|
+
output.stopReason = normalizeStopReason(finishReason);
|
|
232
|
+
}
|
|
233
|
+
// ── Text content ──────────────────────────────────────────────────────
|
|
234
|
+
if (delta?.content) {
|
|
235
|
+
if (!textStarted) {
|
|
236
|
+
output.content.push({ type: "text", text: "" });
|
|
237
|
+
outStream.push({ type: "text_start", contentIndex: TEXT_INDEX, partial: snapshot(output) });
|
|
238
|
+
textStarted = true;
|
|
239
|
+
}
|
|
240
|
+
const textBlock = output.content[TEXT_INDEX];
|
|
241
|
+
if (textBlock?.type === "text")
|
|
242
|
+
textBlock.text += delta.content;
|
|
243
|
+
outStream.push({ type: "text_delta", contentIndex: TEXT_INDEX, delta: delta.content, partial: snapshot(output) });
|
|
244
|
+
}
|
|
245
|
+
// ── Tool calls ────────────────────────────────────────────────────────
|
|
246
|
+
if (delta?.tool_calls) {
|
|
247
|
+
for (const tc of delta.tool_calls) {
|
|
248
|
+
const tcIdx = tc.index ?? 0;
|
|
249
|
+
if (tc.id) {
|
|
250
|
+
// First chunk for this tool call — open it.
|
|
251
|
+
const contentIndex = output.content.length;
|
|
252
|
+
const name = tc.function?.name ?? "";
|
|
253
|
+
output.content.push({ type: "toolCall", id: tc.id, name, arguments: {} });
|
|
254
|
+
pendingToolCalls.set(tcIdx, { contentIndex, id: tc.id, name, argsBuffer: tc.function?.arguments ?? "" });
|
|
255
|
+
outStream.push({ type: "toolcall_start", contentIndex, partial: snapshot(output) });
|
|
256
|
+
}
|
|
257
|
+
else if (tc.function?.arguments) {
|
|
258
|
+
// Continuation chunk — stream argument JSON fragment.
|
|
259
|
+
const p = pendingToolCalls.get(tcIdx);
|
|
260
|
+
if (p) {
|
|
261
|
+
p.argsBuffer += tc.function.arguments;
|
|
262
|
+
outStream.push({ type: "toolcall_delta", contentIndex: p.contentIndex, delta: tc.function.arguments, partial: snapshot(output) });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Flush TextDecoder
|
|
270
|
+
const remaining = decoder.decode();
|
|
271
|
+
if (remaining)
|
|
272
|
+
buffer += remaining;
|
|
273
|
+
// Fallback: stream ended without [DONE]
|
|
274
|
+
if (textStarted)
|
|
275
|
+
emitTextEnd(outStream, output, TEXT_INDEX);
|
|
276
|
+
flushToolCalls();
|
|
277
|
+
outStream.push({ type: "done", reason: output.stopReason, message: snapshot(output) });
|
|
278
|
+
outStream.end();
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
// Close any in-progress text/tool-call blocks so consumers don't see
|
|
282
|
+
// dangling text_start or toolcall_start events without their closers.
|
|
283
|
+
if (textStarted)
|
|
284
|
+
emitTextEnd(outStream, output, TEXT_INDEX);
|
|
285
|
+
flushToolCalls();
|
|
286
|
+
const isAbort = err instanceof Error && err.name === "AbortError";
|
|
287
|
+
outStream.push({ type: "error", reason: isAbort ? "aborted" : "error", error: output });
|
|
288
|
+
outStream.end();
|
|
289
|
+
throw err;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { strict as assert } from "node:assert";
|
|
2
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
3
|
+
import { ModelExhaustedError, ModelFatalError } from "./stream.js";
|
|
4
|
+
function makeStream() {
|
|
5
|
+
const events = [];
|
|
6
|
+
let ended = false;
|
|
7
|
+
return {
|
|
8
|
+
push: (e) => events.push(e),
|
|
9
|
+
end: () => { ended = true; },
|
|
10
|
+
get events() { return events; },
|
|
11
|
+
get ended() { return ended; },
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function makeSseBody(lines) {
|
|
15
|
+
const encoder = new TextEncoder();
|
|
16
|
+
const text = lines.map((l) => `data: ${l}\n\n`).join("");
|
|
17
|
+
return new ReadableStream({
|
|
18
|
+
start(controller) {
|
|
19
|
+
controller.enqueue(encoder.encode(text));
|
|
20
|
+
controller.close();
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
describe("streamFreeModel", () => {
|
|
25
|
+
let originalFetch;
|
|
26
|
+
beforeEach(() => { originalFetch = globalThis.fetch; });
|
|
27
|
+
afterEach(() => { globalThis.fetch = originalFetch; });
|
|
28
|
+
it("throws ModelExhaustedError on 429", async () => {
|
|
29
|
+
globalThis.fetch = async () => ({ ok: false, status: 429, statusText: "Too Many Requests", body: null });
|
|
30
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
31
|
+
const stream = makeStream();
|
|
32
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelExhaustedError);
|
|
33
|
+
});
|
|
34
|
+
it("throws ModelFatalError on 402", async () => {
|
|
35
|
+
globalThis.fetch = async () => ({ ok: false, status: 402, statusText: "Payment Required", body: null });
|
|
36
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
37
|
+
const stream = makeStream();
|
|
38
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelFatalError);
|
|
39
|
+
});
|
|
40
|
+
it("throws ModelExhaustedError on 503", async () => {
|
|
41
|
+
globalThis.fetch = async () => ({ ok: false, status: 503, statusText: "Service Unavailable", body: null });
|
|
42
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
43
|
+
const stream = makeStream();
|
|
44
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelExhaustedError);
|
|
45
|
+
});
|
|
46
|
+
it("throws ModelExhaustedError on 400 (model rejects request)", async () => {
|
|
47
|
+
globalThis.fetch = async () => ({ ok: false, status: 400, statusText: "Bad Request", body: null });
|
|
48
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
49
|
+
const stream = makeStream();
|
|
50
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelExhaustedError);
|
|
51
|
+
});
|
|
52
|
+
it("throws ModelExhaustedError on 422 (unprocessable)", async () => {
|
|
53
|
+
globalThis.fetch = async () => ({ ok: false, status: 422, statusText: "Unprocessable Entity", body: null });
|
|
54
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
55
|
+
const stream = makeStream();
|
|
56
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelExhaustedError);
|
|
57
|
+
});
|
|
58
|
+
it("throws ModelExhaustedError on inline SSE 429 error", async () => {
|
|
59
|
+
const sseLines = [
|
|
60
|
+
JSON.stringify({ error: { code: 429, message: "rate limit exceeded" } }),
|
|
61
|
+
];
|
|
62
|
+
globalThis.fetch = async () => ({ ok: true, status: 200, body: makeSseBody(sseLines) });
|
|
63
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
64
|
+
const stream = makeStream();
|
|
65
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelExhaustedError);
|
|
66
|
+
});
|
|
67
|
+
it("throws ModelFatalError on inline SSE 402 error", async () => {
|
|
68
|
+
const sseLines = [
|
|
69
|
+
JSON.stringify({ error: { code: 402, message: "insufficient credits" } }),
|
|
70
|
+
];
|
|
71
|
+
globalThis.fetch = async () => ({ ok: true, status: 200, body: makeSseBody(sseLines) });
|
|
72
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
73
|
+
const stream = makeStream();
|
|
74
|
+
await assert.rejects(() => streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream), ModelFatalError);
|
|
75
|
+
});
|
|
76
|
+
it("emits toolcall events for tool call responses", async () => {
|
|
77
|
+
const sseLines = [
|
|
78
|
+
JSON.stringify({ choices: [{ delta: { role: "assistant", content: null, tool_calls: [{ index: 0, id: "call_abc", type: "function", function: { name: "bash", arguments: "" } }] }, finish_reason: null }] }),
|
|
79
|
+
JSON.stringify({ choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: '{"command' } }] }, finish_reason: null }] }),
|
|
80
|
+
JSON.stringify({ choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: '":"ls"}' } }] }, finish_reason: null }] }),
|
|
81
|
+
JSON.stringify({ choices: [{ delta: {}, finish_reason: "tool_calls" }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 } }),
|
|
82
|
+
"[DONE]",
|
|
83
|
+
];
|
|
84
|
+
globalThis.fetch = async () => ({ ok: true, status: 200, body: makeSseBody(sseLines) });
|
|
85
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
86
|
+
const stream = makeStream();
|
|
87
|
+
await streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream);
|
|
88
|
+
const types = stream.events.map((e) => e.type);
|
|
89
|
+
assert.ok(types.includes("start"), "missing start event");
|
|
90
|
+
assert.ok(types.includes("toolcall_start"), "missing toolcall_start");
|
|
91
|
+
assert.ok(types.includes("toolcall_delta"), "missing toolcall_delta");
|
|
92
|
+
assert.ok(types.includes("toolcall_end"), "missing toolcall_end");
|
|
93
|
+
assert.ok(types.includes("done"), "missing done event");
|
|
94
|
+
assert.ok(stream.ended, "stream.end() was not called");
|
|
95
|
+
const tcEnd = stream.events.find((e) => e.type === "toolcall_end");
|
|
96
|
+
assert.equal(tcEnd.toolCall.id, "call_abc");
|
|
97
|
+
assert.equal(tcEnd.toolCall.name, "bash");
|
|
98
|
+
assert.deepEqual(tcEnd.toolCall.arguments, { command: "ls" });
|
|
99
|
+
const done = stream.events.find((e) => e.type === "done");
|
|
100
|
+
assert.equal(done.reason, "toolUse");
|
|
101
|
+
});
|
|
102
|
+
it("pushes text events and done on success", async () => {
|
|
103
|
+
const sseLines = [
|
|
104
|
+
JSON.stringify({ choices: [{ delta: { role: "assistant", content: "" }, finish_reason: null }] }),
|
|
105
|
+
JSON.stringify({ choices: [{ delta: { content: "Hello" }, finish_reason: null }] }),
|
|
106
|
+
JSON.stringify({ choices: [{ delta: { content: " world" }, finish_reason: null }] }),
|
|
107
|
+
JSON.stringify({ choices: [{ delta: {}, finish_reason: "stop" }], usage: { prompt_tokens: 5, completion_tokens: 2, total_tokens: 7 } }),
|
|
108
|
+
"[DONE]",
|
|
109
|
+
];
|
|
110
|
+
globalThis.fetch = async () => ({ ok: true, status: 200, body: makeSseBody(sseLines) });
|
|
111
|
+
const { streamFreeModel } = await import("./stream.js");
|
|
112
|
+
const stream = makeStream();
|
|
113
|
+
await streamFreeModel("model:free", { messages: [] }, "sk-or-test", stream);
|
|
114
|
+
const types = stream.events.map((e) => e.type);
|
|
115
|
+
assert.ok(types.includes("start"), "missing start event");
|
|
116
|
+
assert.ok(types.includes("text_start"), "missing text_start");
|
|
117
|
+
assert.ok(types.includes("text_delta"), "missing text_delta");
|
|
118
|
+
assert.ok(types.includes("done"), "missing done event");
|
|
119
|
+
assert.ok(stream.ended, "stream.end() was not called");
|
|
120
|
+
const deltas = stream.events
|
|
121
|
+
.filter((e) => e.type === "text_delta")
|
|
122
|
+
.map((e) => e.delta);
|
|
123
|
+
assert.deepEqual(deltas, ["Hello", " world"]);
|
|
124
|
+
const done = stream.events.find((e) => e.type === "done");
|
|
125
|
+
assert.equal(done.message.usage.input, 5);
|
|
126
|
+
assert.equal(done.message.usage.output, 2);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=stream.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.test.js","sourceRoot":"","sources":["../src/stream.test.ts"],"names":[],"mappings":"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"}
|