This commit is contained in:
Oleg Maslov
2026-09-02 10:14:22 +02:00
parent 0c3e2ead3b
commit b20b138fe4
771 changed files with 161561 additions and 9027 deletions

View File

@@ -11,7 +11,8 @@
// embeddingProvider + LiteLLM) are deferred to Phase 5 e2e validation —
// current critical-path coverage is module + side-effect validation.
import { describe, it, expect } from 'vitest';
import Fastify from 'fastify';
import { describe, it, expect, vi } from 'vitest';
describe('agent-run.ts route module', () => {
it('exports agentRunRoutes plugin function', async () => {
@@ -67,4 +68,70 @@ describe('agent-run.ts route module', () => {
expect(expectedNames.every((n) => n.includes('-gen1-v1'))).toBe(true);
expect(expectedNames.every((n) => !n.includes('::'))).toBe(true);
});
it('accepts only complete text responses and preserves usage on failure', async () => {
const { parseAgentRunCompletion } = await import('../../src/local/routes/agent-run.js');
const usage = { prompt_tokens: 21, completion_tokens: 8, total_cost: 0.018 };
expect(parseAgentRunCompletion({
choices: [{ finish_reason: 'stop', message: { content: 'Final answer.' } }],
usage,
}, 17)).toEqual({
content: 'Final answer.',
inTokens: 21,
outTokens: 8,
costUsd: 0.018,
latencyMs: 17,
});
for (const choice of [
{ message: { content: 'Missing terminal reason.' } },
{ finish_reason: 'length', message: { content: 'Truncated answer.' } },
{
finish_reason: 'stop',
message: { content: 'Unsafe mismatch.', tool_calls: [{ id: 'call_1' }] },
},
]) {
expect(() => parseAgentRunCompletion({ choices: [choice], usage }, 19)).toThrowError(
expect.objectContaining({
code: 'INCOMPLETE_COMPLETION',
usage: { inputTokens: 21, outputTokens: 8, totalCostUsd: 0.018 },
message: expect.stringMatching(/partial content was rejected/i),
}),
);
}
expect(() => parseAgentRunCompletion(null, 19)).toThrowError(
expect.objectContaining({ code: 'INCOMPLETE_COMPLETION' }),
);
});
it('does not replay a malformed HTTP-200 completion and reports done.ok=false', async () => {
const fetchImpl = vi.fn(async () => new Response('{', { status: 200 }));
vi.stubGlobal('fetch', fetchImpl);
const server = Fastify({ logger: false });
server.decorate('multiMind', { personal: {} } as never);
server.decorate('embeddingProvider', { dimensions: 3 } as never);
server.decorate('agentState', { litellmApiKey: 'test-key' } as never);
server.decorate('localConfig', { litellmUrl: 'http://127.0.0.1:43123/v1' } as never);
try {
const { agentRunRoutes } = await import('../../src/local/routes/agent-run.js');
await server.register(agentRunRoutes);
const response = await server.inject({
method: 'POST',
url: '/api/agent/run',
payload: { question: 'Give me a complete answer.' },
});
expect(fetchImpl).toHaveBeenCalledOnce();
expect(response.body).toContain('event: error');
expect(response.body).toContain('INCOMPLETE_COMPLETION');
expect(response.body).toContain('data: {"ok":false}');
expect(response.body).not.toContain('event: finalized');
} finally {
vi.unstubAllGlobals();
await server.close();
}
});
});