mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-24 10:39:17 +00:00
Compare commits
1 Commits
mingholy/f
...
dev/topp-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77bae3ffc0 |
@@ -913,21 +913,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* For AuthDialog it is required to complete the authentication process,
|
||||
* otherwise user cannot proceed to the next step.
|
||||
* So a quit on AuthDialog should go with normal two press quit
|
||||
* and without quit-confirm dialog.
|
||||
*/
|
||||
if (isAuthDialogOpen) {
|
||||
setPressedOnce(true);
|
||||
timerRef.current = setTimeout(() => {
|
||||
setPressedOnce(false);
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
//1. Close other dialogs (highest priority)
|
||||
// 1. Close other dialogs (highest priority)
|
||||
if (closeAnyOpenDialog()) {
|
||||
return; // Dialog closed, end processing
|
||||
}
|
||||
@@ -948,7 +934,6 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||
handleSlashCommand('/quit-confirm');
|
||||
},
|
||||
[
|
||||
isAuthDialogOpen,
|
||||
handleSlashCommand,
|
||||
quitConfirmationRequest,
|
||||
closeAnyOpenDialog,
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('QwenOAuthProgress', () => {
|
||||
const output = lastFrame();
|
||||
expect(output).toContain('MockSpinner(dots)');
|
||||
expect(output).toContain('Waiting for Qwen OAuth authentication...');
|
||||
expect(output).toContain('(Press ESC or CTRL+C to cancel)');
|
||||
expect(output).toContain('(Press ESC to cancel)');
|
||||
});
|
||||
|
||||
it('should render loading state with gray border', () => {
|
||||
@@ -105,7 +105,7 @@ describe('QwenOAuthProgress', () => {
|
||||
expect(output).toContain('MockSpinner(dots)');
|
||||
expect(output).toContain('Waiting for authorization');
|
||||
expect(output).toContain('Time remaining: 5:00');
|
||||
expect(output).toContain('(Press ESC or CTRL+C to cancel)');
|
||||
expect(output).toContain('(Press ESC to cancel)');
|
||||
});
|
||||
|
||||
it('should display correct URL in Static component when QR code is generated', async () => {
|
||||
|
||||
@@ -110,7 +110,7 @@ function StatusDisplay({
|
||||
<Text color={Colors.Gray}>
|
||||
Time remaining: {formatTime(timeRemaining)}
|
||||
</Text>
|
||||
<Text color={Colors.AccentPurple}>(Press ESC or CTRL+C to cancel)</Text>
|
||||
<Text color={Colors.AccentPurple}>(Press ESC to cancel)</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
@@ -132,7 +132,7 @@ export function QwenOAuthProgress({
|
||||
if (authStatus === 'timeout') {
|
||||
// Any key press in timeout state should trigger cancel to return to auth dialog
|
||||
onCancel();
|
||||
} else if (key.escape || (key.ctrl && input === 'c')) {
|
||||
} else if (key.escape) {
|
||||
onCancel();
|
||||
}
|
||||
});
|
||||
@@ -250,9 +250,7 @@ export function QwenOAuthProgress({
|
||||
Time remaining: {Math.floor(timeRemaining / 60)}:
|
||||
{(timeRemaining % 60).toString().padStart(2, '0')}
|
||||
</Text>
|
||||
<Text color={Colors.AccentPurple}>
|
||||
(Press ESC or CTRL+C to cancel)
|
||||
</Text>
|
||||
<Text color={Colors.AccentPurple}>(Press ESC to cancel)</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -61,6 +61,16 @@ export function useDialogClose(options: DialogCloseOptions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.isAuthDialogOpen) {
|
||||
// Mimic ESC behavior: only close if already authenticated (same as AuthDialog ESC logic)
|
||||
if (options.selectedAuthType !== undefined) {
|
||||
// Note: We don't await this since we want non-blocking behavior like ESC
|
||||
void options.handleAuthSelect(undefined, SettingScope.User);
|
||||
}
|
||||
// Note: AuthDialog prevents ESC exit if not authenticated, we follow same logic
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options.isEditorDialogOpen) {
|
||||
// Mimic ESC behavior: call onExit() directly
|
||||
options.exitEditorDialog();
|
||||
|
||||
@@ -161,9 +161,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
top_p: 0.9,
|
||||
max_tokens: 1000,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
signal: undefined,
|
||||
}),
|
||||
);
|
||||
expect(mockConverter.convertOpenAIResponseToGemini).toHaveBeenCalledWith(
|
||||
mockOpenAIResponse,
|
||||
@@ -241,9 +238,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
expect.objectContaining({
|
||||
tools: mockTools,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
signal: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -280,30 +274,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
request,
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass abort signal to OpenAI client when provided', async () => {
|
||||
const abortController = new AbortController();
|
||||
const request: GenerateContentParameters = {
|
||||
model: 'test-model',
|
||||
contents: [{ parts: [{ text: 'Hello' }], role: 'user' }],
|
||||
config: { abortSignal: abortController.signal },
|
||||
};
|
||||
|
||||
(mockConverter.convertGeminiRequestToOpenAI as Mock).mockReturnValue([]);
|
||||
(mockConverter.convertOpenAIResponseToGemini as Mock).mockReturnValue(
|
||||
new GenerateContentResponse(),
|
||||
);
|
||||
(mockClient.chat.completions.create as Mock).mockResolvedValue({
|
||||
choices: [{ message: { content: 'response' } }],
|
||||
});
|
||||
|
||||
await pipeline.execute(request, 'test-id');
|
||||
|
||||
expect(mockClient.chat.completions.create).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.objectContaining({ signal: abortController.signal }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeStream', () => {
|
||||
@@ -368,9 +338,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
}),
|
||||
expect.objectContaining({
|
||||
signal: undefined,
|
||||
}),
|
||||
);
|
||||
expect(mockTelemetryService.logStreamingSuccess).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -503,42 +470,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass abort signal to OpenAI client for streaming requests', async () => {
|
||||
const abortController = new AbortController();
|
||||
const request: GenerateContentParameters = {
|
||||
model: 'test-model',
|
||||
contents: [{ parts: [{ text: 'Hello' }], role: 'user' }],
|
||||
config: { abortSignal: abortController.signal },
|
||||
};
|
||||
|
||||
const mockStream = {
|
||||
async *[Symbol.asyncIterator]() {
|
||||
yield {
|
||||
id: 'chunk-1',
|
||||
choices: [{ delta: { content: 'Hello' }, finish_reason: 'stop' }],
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
(mockConverter.convertGeminiRequestToOpenAI as Mock).mockReturnValue([]);
|
||||
(mockConverter.convertOpenAIChunkToGemini as Mock).mockReturnValue(
|
||||
new GenerateContentResponse(),
|
||||
);
|
||||
(mockClient.chat.completions.create as Mock).mockResolvedValue(
|
||||
mockStream,
|
||||
);
|
||||
|
||||
const resultGenerator = await pipeline.executeStream(request, 'test-id');
|
||||
for await (const _result of resultGenerator) {
|
||||
// Consume stream
|
||||
}
|
||||
|
||||
expect(mockClient.chat.completions.create).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
expect.objectContaining({ signal: abortController.signal }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should merge finishReason and usageMetadata from separate chunks', async () => {
|
||||
// Arrange
|
||||
const request: GenerateContentParameters = {
|
||||
@@ -993,9 +924,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
top_p: 0.9, // Config parameter used since request overrides are not being applied in current implementation
|
||||
max_tokens: 1000, // Config parameter used since request overrides are not being applied in current implementation
|
||||
}),
|
||||
expect.objectContaining({
|
||||
signal: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1032,9 +960,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
top_p: 0.9, // From config
|
||||
max_tokens: 1000, // From config
|
||||
}),
|
||||
expect.objectContaining({
|
||||
signal: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1084,9 +1009,6 @@ describe('ContentGenerationPipeline', () => {
|
||||
expect.objectContaining({
|
||||
metadata: { promptId: userPromptId },
|
||||
}),
|
||||
expect.objectContaining({
|
||||
signal: undefined,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,9 +48,6 @@ export class ContentGenerationPipeline {
|
||||
async (openaiRequest, context) => {
|
||||
const openaiResponse = (await this.client.chat.completions.create(
|
||||
openaiRequest,
|
||||
{
|
||||
signal: request.config?.abortSignal,
|
||||
},
|
||||
)) as OpenAI.Chat.ChatCompletion;
|
||||
|
||||
const geminiResponse =
|
||||
@@ -81,9 +78,6 @@ export class ContentGenerationPipeline {
|
||||
// Stage 1: Create OpenAI stream
|
||||
const stream = (await this.client.chat.completions.create(
|
||||
openaiRequest,
|
||||
{
|
||||
signal: request.config?.abortSignal,
|
||||
},
|
||||
)) as AsyncIterable<OpenAI.Chat.ChatCompletionChunk>;
|
||||
|
||||
// Stage 2: Process stream with conversion and logging
|
||||
|
||||
Reference in New Issue
Block a user