Reject reasoning text in session titles
This commit is contained in:
+28
-1
@@ -1725,6 +1725,9 @@ def create_app(state: AgentState) -> FastAPI:
|
|||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
title = session_metadata.get('title')
|
title = session_metadata.get('title')
|
||||||
|
title_source = session_metadata.get('title_source')
|
||||||
|
if isinstance(title, str) and title_source != 'manual':
|
||||||
|
title = _clean_session_title(title)
|
||||||
if not isinstance(title, str) or not title.strip():
|
if not isinstance(title, str) or not title.strip():
|
||||||
title = None
|
title = None
|
||||||
preview = ''
|
preview = ''
|
||||||
@@ -3917,6 +3920,8 @@ def _read_session_title(directory: Path, session_id: str | None) -> str | None:
|
|||||||
return None
|
return None
|
||||||
title = metadata.get('title')
|
title = metadata.get('title')
|
||||||
if isinstance(title, str) and title.strip():
|
if isinstance(title, str) and title.strip():
|
||||||
|
if metadata.get('title_source') != 'manual':
|
||||||
|
return _clean_session_title(title)
|
||||||
return title.strip()
|
return title.strip()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -4044,8 +4049,30 @@ def _generate_session_title(
|
|||||||
|
|
||||||
|
|
||||||
def _clean_session_title(value: str) -> str | None:
|
def _clean_session_title(value: str) -> str | None:
|
||||||
title = ' '.join(value.strip().strip('"\'“”‘’`').split())
|
raw = value.strip()
|
||||||
|
if not raw:
|
||||||
|
return None
|
||||||
|
raw = re.sub(r'(?is)<analysis>.*?</analysis>', '', raw)
|
||||||
|
raw = re.sub(r'(?is)<think>.*?</think>', '', raw)
|
||||||
|
if re.search(r'(?i)<\s*(analysis|think)\b', raw):
|
||||||
|
return None
|
||||||
|
lines = [line.strip() for line in raw.replace('\r', '\n').split('\n')]
|
||||||
|
title = next((line for line in reversed(lines) if line), '')
|
||||||
|
title = re.sub(r'^\s*(?:[-*#>\d.、\s]+)', '', title)
|
||||||
|
title = re.sub(
|
||||||
|
r'^(?:会话标题|标题|短标题|生成标题|session title)\s*[::]\s*',
|
||||||
|
'',
|
||||||
|
title,
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
title = title.strip().strip('"\'“”‘’`*_ ')
|
||||||
|
title = re.sub(r'\*\*(.*?)\*\*', r'\1', title)
|
||||||
|
title = ' '.join(title.split())
|
||||||
title = title.rstrip('。.!!??')
|
title = title.rstrip('。.!!??')
|
||||||
|
if re.search(r'(?i)\b(generating|generate|thinking|reasoning)\b', title):
|
||||||
|
return None
|
||||||
|
if '<' in title or '>' in title:
|
||||||
|
return None
|
||||||
if not title:
|
if not title:
|
||||||
return None
|
return None
|
||||||
return title[:24].rstrip()
|
return title[:24].rstrip()
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from backend.api.server import (
|
|||||||
AgentState,
|
AgentState,
|
||||||
create_app,
|
create_app,
|
||||||
_bash_bg_manager,
|
_bash_bg_manager,
|
||||||
|
_clean_session_title,
|
||||||
_derive_initial_session_title,
|
_derive_initial_session_title,
|
||||||
_ensure_session_title,
|
_ensure_session_title,
|
||||||
_generate_session_title,
|
_generate_session_title,
|
||||||
@@ -860,6 +861,35 @@ class GuiServerTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertIsNone(_read_session_title(directory, session_id))
|
self.assertIsNone(_read_session_title(directory, session_id))
|
||||||
|
|
||||||
|
def test_invalid_llm_title_is_not_preserved_as_previous_title(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
root = Path(d)
|
||||||
|
_, state = _build_client(root)
|
||||||
|
session_id = 'invalid-llm-title'
|
||||||
|
directory = state.account_paths('alice')['sessions']
|
||||||
|
agent = state.agent_for('alice', session_id)
|
||||||
|
|
||||||
|
_save_in_progress_session(
|
||||||
|
directory=directory,
|
||||||
|
agent=agent,
|
||||||
|
session_id=session_id,
|
||||||
|
prompt='帮我生成一批地图导航测试数据。',
|
||||||
|
)
|
||||||
|
stored = load_agent_session(session_id, directory=directory)
|
||||||
|
save_agent_session(
|
||||||
|
replace(
|
||||||
|
stored,
|
||||||
|
session_metadata={
|
||||||
|
**(stored.session_metadata or {}),
|
||||||
|
'title': '<think>**Generating short title',
|
||||||
|
'title_source': 'llm',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
directory=directory,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIsNone(_read_session_title(directory, session_id))
|
||||||
|
|
||||||
def test_session_title_refines_first_message_title_after_run(self) -> None:
|
def test_session_title_refines_first_message_title_after_run(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
root = Path(d)
|
root = Path(d)
|
||||||
@@ -960,6 +990,20 @@ class GuiServerTests(unittest.TestCase):
|
|||||||
self.assertEqual(config.base_url, 'http://model.mify.ai.srv/v1')
|
self.assertEqual(config.base_url, 'http://model.mify.ai.srv/v1')
|
||||||
client.complete.assert_called_once()
|
client.complete.assert_called_once()
|
||||||
|
|
||||||
|
def test_clean_session_title_rejects_reasoning_leak(self) -> None:
|
||||||
|
self.assertIsNone(_clean_session_title('<think>**Generating short title'))
|
||||||
|
self.assertIsNone(
|
||||||
|
_clean_session_title(
|
||||||
|
'<think>reasoning</think>\nGenerating short Chinese title'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_clean_session_title_strips_markdown_and_prefix(self) -> None:
|
||||||
|
self.assertEqual(
|
||||||
|
_clean_session_title('**标题:地图导航测试数据。**'),
|
||||||
|
'地图导航测试数据',
|
||||||
|
)
|
||||||
|
|
||||||
def test_clear_runtime_state(self) -> None:
|
def test_clear_runtime_state(self) -> None:
|
||||||
with tempfile.TemporaryDirectory() as d:
|
with tempfile.TemporaryDirectory() as d:
|
||||||
client, _ = _build_client(Path(d))
|
client, _ = _build_client(Path(d))
|
||||||
|
|||||||
Reference in New Issue
Block a user