mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
openspec/lightweight-tasks/task1-2-4-1-1.md
Add user envelope handling in runNonInteractive function
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
|||||||
import type { Part } from '@google/genai';
|
import type { Part } from '@google/genai';
|
||||||
import { runNonInteractive } from './nonInteractiveCli.js';
|
import { runNonInteractive } from './nonInteractiveCli.js';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
|
import type { StreamJsonUserEnvelope } from './streamJson/types.js';
|
||||||
import type { LoadedSettings } from './config/settings.js';
|
import type { LoadedSettings } from './config/settings.js';
|
||||||
|
|
||||||
// Mock core modules
|
// Mock core modules
|
||||||
@@ -943,6 +944,63 @@ describe('runNonInteractive', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should emit a single user envelope when userEnvelope is provided', async () => {
|
||||||
|
(mockConfig.getOutputFormat as vi.Mock).mockReturnValue('stream-json');
|
||||||
|
(mockConfig.getIncludePartialMessages as vi.Mock).mockReturnValue(false);
|
||||||
|
|
||||||
|
const writes: string[] = [];
|
||||||
|
processStdoutSpy.mockImplementation((chunk: string | Uint8Array) => {
|
||||||
|
if (typeof chunk === 'string') {
|
||||||
|
writes.push(chunk);
|
||||||
|
} else {
|
||||||
|
writes.push(Buffer.from(chunk).toString('utf8'));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
mockGeminiClient.sendMessageStream.mockReturnValue(
|
||||||
|
createStreamFromEvents([
|
||||||
|
{ type: GeminiEventType.Content, value: 'Handled once' },
|
||||||
|
{
|
||||||
|
type: GeminiEventType.Finished,
|
||||||
|
value: { reason: undefined, usageMetadata: { totalTokenCount: 2 } },
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const userEnvelope = {
|
||||||
|
type: 'user',
|
||||||
|
message: {
|
||||||
|
role: 'user',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: '来自 envelope 的消息',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} as unknown as StreamJsonUserEnvelope;
|
||||||
|
|
||||||
|
await runNonInteractive(
|
||||||
|
mockConfig,
|
||||||
|
mockSettings,
|
||||||
|
'ignored input',
|
||||||
|
'prompt-envelope',
|
||||||
|
{
|
||||||
|
userEnvelope,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const envelopes = writes
|
||||||
|
.join('')
|
||||||
|
.split('\n')
|
||||||
|
.filter((line) => line.trim().length > 0)
|
||||||
|
.map((line) => JSON.parse(line));
|
||||||
|
|
||||||
|
const userEnvelopes = envelopes.filter((env) => env.type === 'user');
|
||||||
|
expect(userEnvelopes).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('should include usage metadata and API duration in stream-json result', async () => {
|
it('should include usage metadata and API duration in stream-json result', async () => {
|
||||||
(mockConfig.getOutputFormat as vi.Mock).mockReturnValue('stream-json');
|
(mockConfig.getOutputFormat as vi.Mock).mockReturnValue('stream-json');
|
||||||
(mockConfig.getIncludePartialMessages as vi.Mock).mockReturnValue(false);
|
(mockConfig.getIncludePartialMessages as vi.Mock).mockReturnValue(false);
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export async function runNonInteractive(
|
|||||||
let initialPartList: PartListUnion | null = extractPartsFromEnvelope(
|
let initialPartList: PartListUnion | null = extractPartsFromEnvelope(
|
||||||
options.userEnvelope,
|
options.userEnvelope,
|
||||||
);
|
);
|
||||||
|
let usedEnvelopeInput = initialPartList !== null;
|
||||||
|
|
||||||
if (!initialPartList) {
|
if (!initialPartList) {
|
||||||
let slashHandled = false;
|
let slashHandled = false;
|
||||||
@@ -215,6 +216,7 @@ export async function runNonInteractive(
|
|||||||
// A slash command can replace the prompt entirely; fall back to @-command processing otherwise.
|
// A slash command can replace the prompt entirely; fall back to @-command processing otherwise.
|
||||||
initialPartList = slashCommandResult as PartListUnion;
|
initialPartList = slashCommandResult as PartListUnion;
|
||||||
slashHandled = true;
|
slashHandled = true;
|
||||||
|
usedEnvelopeInput = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,17 +238,19 @@ export async function runNonInteractive(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
initialPartList = processedQuery as PartListUnion;
|
initialPartList = processedQuery as PartListUnion;
|
||||||
|
usedEnvelopeInput = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initialPartList) {
|
if (!initialPartList) {
|
||||||
initialPartList = [{ text: input }];
|
initialPartList = [{ text: input }];
|
||||||
|
usedEnvelopeInput = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialParts = normalizePartList(initialPartList);
|
const initialParts = normalizePartList(initialPartList);
|
||||||
let currentMessages: Content[] = [{ role: 'user', parts: initialParts }];
|
let currentMessages: Content[] = [{ role: 'user', parts: initialParts }];
|
||||||
|
|
||||||
if (streamJsonWriter) {
|
if (streamJsonWriter && !usedEnvelopeInput) {
|
||||||
streamJsonWriter.emitUserMessageFromParts(initialParts);
|
streamJsonWriter.emitUserMessageFromParts(initialParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user