include remaining workspace changes

This commit is contained in:
cita
2026-03-16 19:29:26 +08:00
parent 52441f2634
commit bec0457a92
15 changed files with 419 additions and 93 deletions
+40 -64
View File
@@ -52,6 +52,7 @@ vi.mock('../../db/index.js', () => ({
function parseSsePayloads(body: string): Array<Record<string, unknown>> {
return body
.replace(/\r\n/g, '\n')
.split('\n\n')
.map((block) => block.trim())
.filter(Boolean)
@@ -520,21 +521,32 @@ describe('gemini native proxy routes', () => {
});
});
it('keeps non-sse json-array streaming payloads on the wire as chunk responses', async () => {
fetchMock.mockResolvedValue(new Response(JSON.stringify([
it('keeps non-sse json-array streaming payloads on the wire as raw chunk responses', async () => {
const upstreamPayload = [
{
promptFeedback: { blockReason: 'BLOCK_REASON_UNSPECIFIED' },
candidates: [
{
content: { role: 'model', parts: [{ text: 'first' }] },
content: {
role: 'model',
parts: [
{
functionCall: { id: 'tool-1', name: 'lookup_weather', args: { city: 'Shanghai' } },
thoughtSignature: 'sig-tool-1',
},
],
},
groundingMetadata: { source: 'web' },
},
],
},
{
serverContent: { modelTurn: { parts: [{ text: 'tool result received' }] } },
candidates: [
{
content: { role: 'model', parts: [{ text: 'second', thoughtSignature: 'sig-1' }] },
citationMetadata: { citations: [{ startIndex: 0, endIndex: 5 }] },
finishReason: 'STOP',
},
],
usageMetadata: {
@@ -545,7 +557,9 @@ describe('gemini native proxy routes', () => {
thoughtsTokenCount: 3,
},
},
]), {
];
fetchMock.mockResolvedValue(new Response(JSON.stringify(upstreamPayload), {
status: 200,
headers: { 'content-type': 'application/json' },
}));
@@ -567,45 +581,7 @@ describe('gemini native proxy routes', () => {
});
expect(response.statusCode).toBe(200);
expect(response.json()).toEqual([
{
responseId: '',
modelVersion: '',
candidates: [
{
index: 0,
finishReason: 'STOP',
content: {
role: 'model',
parts: [{ text: 'first' }],
},
groundingMetadata: { source: 'web' },
},
],
},
{
responseId: '',
modelVersion: '',
candidates: [
{
index: 0,
finishReason: 'STOP',
content: {
role: 'model',
parts: [{ text: 'second', thoughtSignature: 'sig-1' }],
},
citationMetadata: { citations: [{ startIndex: 0, endIndex: 5 }] },
},
],
usageMetadata: {
promptTokenCount: 11,
candidatesTokenCount: 6,
totalTokenCount: 17,
cachedContentTokenCount: 2,
thoughtsTokenCount: 3,
},
},
]);
expect(response.json()).toEqual(upstreamPayload);
});
it('derives gemini-3 thinkingLevel from OpenAI-style reasoning inputs in the runtime request path', async () => {
@@ -665,12 +641,13 @@ describe('gemini native proxy routes', () => {
});
});
it('streams SSE payloads as normalized chunk events instead of cumulative aggregate snapshots', async () => {
it('streams SSE payloads as raw upstream events without reserializing tool-calling chunks', async () => {
const encoder = new TextEncoder();
const upstreamBody = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(encoder.encode('data: {"responseId":"resp-sse","modelVersion":"gemini-2.5-flash","candidates":[{"content":{"role":"model","parts":[{"text":"first"}]},"groundingMetadata":{"source":"web"}}]}\r\n\r\n'));
controller.enqueue(encoder.encode('data: {"candidates":[{"content":{"role":"model","parts":[{"text":"second","thoughtSignature":"sig-1"}]},"citationMetadata":{"citations":[{"startIndex":0,"endIndex":5}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":11,"candidatesTokenCount":6,"totalTokenCount":17,"cachedContentTokenCount":2,"thoughtsTokenCount":3}}\r\n\r\n'));
controller.enqueue(encoder.encode('data: {"promptFeedback":{"blockReason":"BLOCK_REASON_UNSPECIFIED"},"candidates":[{"content":{"role":"model","parts":[{"functionCall":{"id":"tool-1","name":"lookup_weather","args":{"city":"Shanghai"}},"thoughtSignature":"sig-tool-1"}]},"groundingMetadata":{"source":"web"}}]}\r\n\r\n'));
controller.enqueue(encoder.encode('data: {"serverContent":{"modelTurn":{"parts":[{"text":"tool result received"}]}},"candidates":[{"content":{"role":"model","parts":[{"text":"second","thoughtSignature":"sig-1"}]},"citationMetadata":{"citations":[{"startIndex":0,"endIndex":5}]},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":11,"candidatesTokenCount":6,"totalTokenCount":17,"cachedContentTokenCount":2,"thoughtsTokenCount":3}}\r\n\r\n'));
controller.enqueue(encoder.encode('data: [DONE]\r\n\r\n'));
controller.close();
},
});
@@ -702,25 +679,30 @@ describe('gemini native proxy routes', () => {
expect(events).toHaveLength(2);
expect(events[0]).toMatchObject({
responseId: 'resp-sse',
modelVersion: 'gemini-2.5-flash',
promptFeedback: { blockReason: 'BLOCK_REASON_UNSPECIFIED' },
candidates: [
{
content: {
parts: [{ text: 'first' }],
parts: [
{
functionCall: { id: 'tool-1', name: 'lookup_weather', args: { city: 'Shanghai' } },
thoughtSignature: 'sig-tool-1',
},
],
},
groundingMetadata: { source: 'web' },
},
],
});
expect(events[0]).not.toMatchObject({
candidates: [
{
citationMetadata: expect.anything(),
},
],
});
expect(events[0]).not.toHaveProperty('responseId');
expect(events[0]).not.toHaveProperty('modelVersion');
expect(events[0].candidates?.[0]).not.toHaveProperty('finishReason');
expect(events[1]).toMatchObject({
serverContent: {
modelTurn: {
parts: [{ text: 'tool result received' }],
},
},
usageMetadata: {
promptTokenCount: 11,
candidatesTokenCount: 6,
@@ -738,14 +720,8 @@ describe('gemini native proxy routes', () => {
},
],
});
expect(events[1]).not.toMatchObject({
candidates: [
{
groundingMetadata: expect.anything(),
},
],
});
expect(response.body).not.toContain('\r\n\r\n');
expect(response.body).toContain('\r\n\r\n');
expect(response.body).toContain('data: [DONE]\r\n\r\n');
});
it('falls back to the next channel when first Gemini channel returns 400 before any bytes are written', async () => {