Revert "fix(core): skip auto-title generation when history has no user messag…" (#5200)

This reverts commit 99d4c1246e.
This commit is contained in:
yuanyuanAli 2026-06-17 10:42:58 +08:00 committed by GitHub
parent 05d17cfd5a
commit e5655b4bd7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 0 additions and 108 deletions

View file

@ -549,61 +549,4 @@ describe('ChatRecordingService - auto-title trigger', () => {
expect(chatRecordingService.getCurrentCustomTitle()).toBe('user-chosen');
expect(chatRecordingService.getCurrentTitleSource()).toBe('manual');
});
it('skips title generation when history has no user message', async () => {
/**
* This test verifies that auto-title is not triggered when the history
* contains only model messages (e.g., initialization context) without
* any user message. This prevents titles like "初始化项目上下文" when
* the user hasn't sent any message yet.
*/
// Simulate tryGenerateSessionTitle returning empty_history because
// the history only has model messages (initialization context)
tryGenerateSessionTitleMock.mockResolvedValueOnce({
ok: false,
reason: 'empty_history',
});
// Simulate the first assistant turn completing with initialization context
chatRecordingService.recordAssistantTurn({
model: 'qwen-plus',
message: [{ text: '已加载项目上下文,准备就绪。' }],
});
await flushMicrotasks();
// Verify: the mock was called and returned empty_history
expect(tryGenerateSessionTitleMock).toHaveBeenCalledOnce();
// Verify: NO title was generated because history had no user message
const titleRecord = findCustomTitleRecord();
expect(titleRecord).toBeUndefined();
expect(chatRecordingService.getCurrentCustomTitle()).toBeUndefined();
// Now simulate user sends "你好" and gets a response
vi.mocked(jsonl.writeLine).mockClear();
tryGenerateSessionTitleMock.mockClear();
// This time, tryGenerateSessionTitle succeeds because history now has user message
tryGenerateSessionTitleMock.mockResolvedValueOnce({
ok: true,
title: '问候会话',
modelUsed: 'qwen-turbo',
});
chatRecordingService.recordAssistantTurn({
model: 'qwen-plus',
message: [{ text: '你好!有什么可以帮助你的?' }],
});
await flushMicrotasks();
// Now the title should be generated based on the actual conversation
const newTitleRecord = findCustomTitleRecord();
expect(newTitleRecord).toBeDefined();
expect(
(newTitleRecord?.systemPayload as { customTitle?: string })?.customTitle,
).toBe('问候会话');
expect(chatRecordingService.getCurrentCustomTitle()).toBe('问候会话');
});
});

View file

@ -75,39 +75,6 @@ describe('tryGenerateSessionTitle', () => {
expect(outcome).toEqual({ ok: false, reason: 'empty_history' });
});
it('returns {ok:false, reason:"empty_history"} when history has only startup prelude (no real user message)', async () => {
// Guard: auto-title should not be generated based solely on the
// initialization prelude — a role:'user' <system-reminder> message
// seeded by getInitialChatHistory. Without the guard, the title model
// would be called with only the startup context and produce a title
// like "初始化项目上下文" instead of one based on the user's actual
// first message.
const { config, generateJson } = makeConfig({
fastModel: 'qwen-turbo',
history: [
{
role: 'user',
parts: [
{
text: "<system-reminder>\nThis is the Qwen Code. We are setting up the context for our chat.\nToday's date is Monday, June 15, 2026.\nMy operating system is: linux\nI'm currently working in the directory: /home/me/project\n</system-reminder>",
},
],
},
{
role: 'model',
parts: [{ text: 'Got it! How can I help you today?' }],
},
],
});
const outcome = await tryGenerateSessionTitle(
config,
new AbortController().signal,
);
expect(outcome).toEqual({ ok: false, reason: 'empty_history' });
// The title model must NOT be called — the guard should short-circuit.
expect(generateJson).not.toHaveBeenCalled();
});
it('returns {ok:false, reason:"model_error"} when the LLM throws', async () => {
const { config } = makeConfig({
fastModel: 'qwen-turbo',

View file

@ -7,7 +7,6 @@
import type { Content } from '@google/genai';
import type { Config } from '../config/config.js';
import { createDebugLogger } from '../utils/debugLogger.js';
import { isSystemReminderContent } from '../utils/environmentContext.js';
import { runSideQuery } from '../utils/sideQuery.js';
import { stripTerminalControlSequences } from '../utils/terminalSafe.js';
import { SESSION_TITLE_MAX_LENGTH } from './sessionService.js';
@ -117,23 +116,6 @@ export async function tryGenerateSessionTitle(
const fullHistory = geminiClient.getHistoryShallow();
if (fullHistory.length < 2) return { ok: false, reason: 'empty_history' };
// Guard: skip title generation if no real user message exists in history.
// This prevents the title from being generated based solely on
// initialization context (e.g., workspace loading, system prompts)
// before the user has actually said anything. Without this guard, a
// session that sends a prompt immediately after creation could get a
// title like "初始化项目上下文" instead of one based on the user's
// actual message.
//
// The startup prelude is a role:'user' message wrapping
// <system-reminder> content (see getInitialChatHistory), so we must
// exclude it via isSystemReminderContent to avoid misclassifying it
// as a real user turn.
const hasUserMessage = fullHistory.some(
(msg) => msg.role === 'user' && !isSystemReminderContent(msg),
);
if (!hasUserMessage) return { ok: false, reason: 'empty_history' };
const dialog = filterToDialog(fullHistory);
const recentHistory = takeRecentDialog(dialog, RECENT_MESSAGE_WINDOW);
if (recentHistory.length === 0) {