mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-01 21:20:44 +00:00
fix(cli): use live context for /btw side questions (#3429)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
This commit is contained in:
parent
6ebe28453d
commit
8ad9a5b467
2 changed files with 146 additions and 1 deletions
|
|
@ -52,6 +52,12 @@ describe('btwCommand', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockGetCacheSafeParams.mockReturnValue({
|
||||
generationConfig: {},
|
||||
history: [],
|
||||
model: 'test-model',
|
||||
version: 1,
|
||||
});
|
||||
mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: createConfig(),
|
||||
|
|
@ -157,6 +163,107 @@ describe('btwCommand', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should fall back to live Gemini client context when no saved cache params exist', async () => {
|
||||
mockGetCacheSafeParams.mockReturnValue(null);
|
||||
mockRunForkedAgent.mockResolvedValue({
|
||||
text: 'answer',
|
||||
usage: { inputTokens: 5, outputTokens: 2, cacheHitTokens: 0 },
|
||||
});
|
||||
|
||||
const geminiClient = {
|
||||
getHistory: vi
|
||||
.fn()
|
||||
.mockReturnValue([
|
||||
{ role: 'user', parts: [{ text: '杭州天气如何?' }] },
|
||||
]),
|
||||
getChat: vi.fn().mockReturnValue({
|
||||
getGenerationConfig: vi.fn().mockReturnValue({
|
||||
systemInstruction: 'You are helpful',
|
||||
tools: [],
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
const liveContext = createMockCommandContext({
|
||||
services: {
|
||||
config: createConfig({
|
||||
getGeminiClient: () => geminiClient,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
await btwCommand.action!(liveContext, 'how ?');
|
||||
await flushPromises();
|
||||
|
||||
expect(mockRunForkedAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cacheSafeParams: expect.objectContaining({
|
||||
generationConfig: expect.objectContaining({
|
||||
systemInstruction: 'You are helpful',
|
||||
}),
|
||||
history: [{ role: 'user', parts: [{ text: '杭州天气如何?' }] }],
|
||||
model: 'test-model',
|
||||
}),
|
||||
userMessage: expect.stringContaining('how ?'),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should prefer live Gemini client history over a stale saved cache snapshot', async () => {
|
||||
mockGetCacheSafeParams.mockReturnValue({
|
||||
generationConfig: {
|
||||
systemInstruction: 'stale system prompt',
|
||||
tools: [],
|
||||
},
|
||||
history: [{ role: 'user', parts: [{ text: '旧问题' }] }],
|
||||
model: 'stale-model',
|
||||
version: 99,
|
||||
});
|
||||
mockRunForkedAgent.mockResolvedValue({
|
||||
text: 'answer',
|
||||
usage: { inputTokens: 5, outputTokens: 2, cacheHitTokens: 0 },
|
||||
});
|
||||
|
||||
const geminiClient = {
|
||||
getHistory: vi.fn().mockReturnValue([
|
||||
{ role: 'user', parts: [{ text: '杭州天气如何?' }] },
|
||||
{ role: 'user', parts: [{ text: '请顺便解释一下湿度怎么看' }] },
|
||||
]),
|
||||
getChat: vi.fn().mockReturnValue({
|
||||
getGenerationConfig: vi.fn().mockReturnValue({
|
||||
systemInstruction: 'live system prompt',
|
||||
tools: [],
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
||||
const liveContext = createMockCommandContext({
|
||||
services: {
|
||||
config: createConfig({
|
||||
getGeminiClient: () => geminiClient,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
await btwCommand.action!(liveContext, 'how ?');
|
||||
await flushPromises();
|
||||
|
||||
expect(mockRunForkedAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cacheSafeParams: expect.objectContaining({
|
||||
generationConfig: expect.objectContaining({
|
||||
systemInstruction: 'live system prompt',
|
||||
}),
|
||||
history: [
|
||||
{ role: 'user', parts: [{ text: '杭州天气如何?' }] },
|
||||
{ role: 'user', parts: [{ text: '请顺便解释一下湿度怎么看' }] },
|
||||
],
|
||||
model: 'test-model',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should add error item on failure and clear btwItem', async () => {
|
||||
mockRunForkedAgent.mockRejectedValue(new Error('API error'));
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,44 @@ function buildBtwPrompt(question: string): string {
|
|||
].join('\n');
|
||||
}
|
||||
|
||||
function getBtwCacheSafeParams(
|
||||
context: CommandContext,
|
||||
): ReturnType<typeof getCacheSafeParams> {
|
||||
const geminiClient = context.services.config?.getGeminiClient();
|
||||
if (
|
||||
geminiClient &&
|
||||
typeof geminiClient === 'object' &&
|
||||
typeof geminiClient.getChat === 'function' &&
|
||||
typeof geminiClient.getHistory === 'function'
|
||||
) {
|
||||
const chat = geminiClient.getChat();
|
||||
if (
|
||||
chat &&
|
||||
typeof chat === 'object' &&
|
||||
typeof chat.getGenerationConfig === 'function'
|
||||
) {
|
||||
const generationConfig = chat.getGenerationConfig();
|
||||
if (generationConfig) {
|
||||
const fullHistory = geminiClient.getHistory(true);
|
||||
const maxHistoryEntries = 40;
|
||||
const history =
|
||||
fullHistory.length > maxHistoryEntries
|
||||
? fullHistory.slice(-maxHistoryEntries)
|
||||
: fullHistory;
|
||||
|
||||
return {
|
||||
generationConfig,
|
||||
history,
|
||||
model: context.services.config?.getModel() ?? '',
|
||||
version: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return getCacheSafeParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a side question using runForkedAgent (cache path).
|
||||
*
|
||||
|
|
@ -63,7 +101,7 @@ async function askBtw(
|
|||
const { config } = context.services;
|
||||
if (!config) throw new Error('Config not loaded');
|
||||
|
||||
const cacheSafeParams = getCacheSafeParams();
|
||||
const cacheSafeParams = getBtwCacheSafeParams(context);
|
||||
if (!cacheSafeParams)
|
||||
throw new Error(t('No conversation context available for /btw'));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue