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 { runNonInteractive } from './nonInteractiveCli.js';
|
||||
import { vi } from 'vitest';
|
||||
import type { StreamJsonUserEnvelope } from './streamJson/types.js';
|
||||
import type { LoadedSettings } from './config/settings.js';
|
||||
|
||||
// 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 () => {
|
||||
(mockConfig.getOutputFormat as vi.Mock).mockReturnValue('stream-json');
|
||||
(mockConfig.getIncludePartialMessages as vi.Mock).mockReturnValue(false);
|
||||
|
||||
@@ -201,6 +201,7 @@ export async function runNonInteractive(
|
||||
let initialPartList: PartListUnion | null = extractPartsFromEnvelope(
|
||||
options.userEnvelope,
|
||||
);
|
||||
let usedEnvelopeInput = initialPartList !== null;
|
||||
|
||||
if (!initialPartList) {
|
||||
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.
|
||||
initialPartList = slashCommandResult as PartListUnion;
|
||||
slashHandled = true;
|
||||
usedEnvelopeInput = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,17 +238,19 @@ export async function runNonInteractive(
|
||||
);
|
||||
}
|
||||
initialPartList = processedQuery as PartListUnion;
|
||||
usedEnvelopeInput = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialPartList) {
|
||||
initialPartList = [{ text: input }];
|
||||
usedEnvelopeInput = false;
|
||||
}
|
||||
|
||||
const initialParts = normalizePartList(initialPartList);
|
||||
let currentMessages: Content[] = [{ role: 'user', parts: initialParts }];
|
||||
|
||||
if (streamJsonWriter) {
|
||||
if (streamJsonWriter && !usedEnvelopeInput) {
|
||||
streamJsonWriter.emitUserMessageFromParts(initialParts);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user