u
This commit is contained in:
@@ -87,3 +87,124 @@ test('chatCompletions sends session_id in JSON body and x-session-id in HTTP hea
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test('OpenRouterProvider resolves apiUrl from constructor option, OPENROUTER_API_URL env, or default', () => {
|
||||
const originalEnv = process.env.OPENROUTER_API_URL;
|
||||
try {
|
||||
delete process.env.OPENROUTER_API_URL;
|
||||
const defaultProvider = new OpenRouterProvider({ apiKey: 'test-key' });
|
||||
assert.equal(defaultProvider.apiUrl, 'https://openrouter.ai/api/v1/chat/completions');
|
||||
|
||||
process.env.OPENROUTER_API_URL = 'http://127.0.0.1:8787/api/v1/chat/completions';
|
||||
const envProvider = new OpenRouterProvider({ apiKey: 'test-key' });
|
||||
assert.equal(envProvider.apiUrl, 'http://127.0.0.1:8787/api/v1/chat/completions');
|
||||
|
||||
const explicitProvider = new OpenRouterProvider({
|
||||
apiKey: 'test-key',
|
||||
apiUrl: 'http://127.0.0.1:9999/custom',
|
||||
});
|
||||
assert.equal(explicitProvider.apiUrl, 'http://127.0.0.1:9999/custom');
|
||||
} finally {
|
||||
if (originalEnv !== undefined) {
|
||||
process.env.OPENROUTER_API_URL = originalEnv;
|
||||
} else {
|
||||
delete process.env.OPENROUTER_API_URL;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('OpenRouterProvider emits connected, firstevent, reasoning, and content status in order', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const statuses = [];
|
||||
const chunks = [
|
||||
': OPENROUTER PROCESSING\n\n',
|
||||
'data: {"choices":[{"delta":{"content":"","role":"assistant","reasoning":"Thinking..."}}]}\n\n',
|
||||
'data: {"choices":[{"delta":{"content":"","role":"assistant","reasoning":" more"}}]}\n\n',
|
||||
'data: {"choices":[{"delta":{"content":"Hello","role":"assistant"}}]}\n\n',
|
||||
'data: {"choices":[{"delta":{"content":" world","role":"assistant"}}]}\n\n',
|
||||
'data: [DONE]\n\n',
|
||||
];
|
||||
|
||||
globalThis.fetch = async () => {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
body: {
|
||||
getReader() {
|
||||
let idx = 0;
|
||||
return {
|
||||
async read() {
|
||||
if (idx >= chunks.length) return { done: true, value: undefined };
|
||||
const encoder = new TextEncoder();
|
||||
const val = encoder.encode(chunks[idx++]);
|
||||
return { done: false, value: val };
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
const provider = new OpenRouterProvider({ apiKey: 'sk-test' });
|
||||
const res = await provider.chatCompletions({
|
||||
model: 'test/model',
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
onStatus: (s) => statuses.push(s),
|
||||
});
|
||||
|
||||
assert.equal(res.content, 'Hello world');
|
||||
assert.equal(res.reasoning, 'Thinking... more');
|
||||
assert.deepEqual(statuses, ['connected', 'firstevent', 'reasoning', 'content']);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test('OpenRouterProvider emits working status on first tool call delta', async () => {
|
||||
const originalFetch = globalThis.fetch;
|
||||
const statuses = [];
|
||||
const chunks = [
|
||||
': OPENROUTER PROCESSING\n\n',
|
||||
'data: {"choices":[{"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"foo","arguments":""}}]}}]}\n\n',
|
||||
'data: {"choices":[{"delta":{"role":"assistant","tool_calls":[{"index":0,"function":{"arguments":"{}"}}]}}]}\n\n',
|
||||
'data: [DONE]\n\n',
|
||||
];
|
||||
|
||||
globalThis.fetch = async () => {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
body: {
|
||||
getReader() {
|
||||
let idx = 0;
|
||||
return {
|
||||
async read() {
|
||||
if (idx >= chunks.length) return { done: true, value: undefined };
|
||||
const encoder = new TextEncoder();
|
||||
const val = encoder.encode(chunks[idx++]);
|
||||
return { done: false, value: val };
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
const provider = new OpenRouterProvider({ apiKey: 'sk-test' });
|
||||
const res = await provider.chatCompletions({
|
||||
model: 'test/model',
|
||||
messages: [{ role: 'user', content: 'hello' }],
|
||||
onStatus: (s) => statuses.push(s),
|
||||
});
|
||||
|
||||
assert.equal(res.tool_calls?.length, 1);
|
||||
assert.equal(res.tool_calls[0].function.name, 'foo');
|
||||
assert.deepEqual(statuses, ['connected', 'firstevent', 'working']);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -102,3 +102,54 @@ test('LLMOrbiter finishes immediately in 1 iteration when content is returned wi
|
||||
assert.equal(result.iterations, 1);
|
||||
assert.equal(result.content, 'Hello there!');
|
||||
});
|
||||
|
||||
test('LLMOrbiter calls onToolResult before onUsage in a tool-calling iteration', async () => {
|
||||
const events = [];
|
||||
const testTool = defineTool({
|
||||
name: 'test_tool',
|
||||
description: 'Test tool',
|
||||
parameters: { type: 'object', properties: {} },
|
||||
invoke: async () => {
|
||||
return { ok: true, data: 42 };
|
||||
},
|
||||
});
|
||||
|
||||
const mock = new MockProvider([
|
||||
{
|
||||
tool_calls: [
|
||||
{
|
||||
id: 'call_1',
|
||||
type: 'function',
|
||||
function: { name: 'test_tool', arguments: '{}' },
|
||||
},
|
||||
],
|
||||
content: null,
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5 },
|
||||
},
|
||||
{
|
||||
tool_calls: undefined,
|
||||
content: 'All done.',
|
||||
usage: { prompt_tokens: 20, completion_tokens: 8 },
|
||||
},
|
||||
]);
|
||||
|
||||
const orbiter = new LLMOrbiter(mock, {
|
||||
onToolCall: ({ name }) => events.push(`tool_call:${name}`),
|
||||
onToolResult: ({ name }) => events.push(`tool_result:${name}`),
|
||||
onUsage: (u) => events.push(`usage:${u.input}:${u.output}`),
|
||||
});
|
||||
|
||||
const result = await orbiter.run({
|
||||
prompt: 'Run tool',
|
||||
tools: [testTool],
|
||||
});
|
||||
|
||||
assert.equal(result.content, 'All done.');
|
||||
assert.deepEqual(events, [
|
||||
'tool_call:test_tool',
|
||||
'tool_result:test_tool',
|
||||
'usage:10:5',
|
||||
'usage:20:8',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user