Compare commits

...

1 Commits

Author SHA1 Message Date
Teknium
bdbc4086b6 feat(titles): support language-aware title generation
Make auxiliary title prompts match the user language by default, with an optional pinned `auxiliary.title_generation.language` config.
2026-06-13 06:52:13 -07:00
4 changed files with 74 additions and 1 deletions

View File

@@ -22,9 +22,31 @@ TitleCallback = Callable[[str], None]
_TITLE_PROMPT = (
"Generate a short, descriptive title (3-7 words) for a conversation that starts with the "
"following exchange. The title should capture the main topic or intent. "
"Write the title in the same language the user is writing in. "
"Return ONLY the title text, nothing else. No quotes, no punctuation at the end, no prefixes."
)
_TITLE_PROMPT_PINNED_LANGUAGE = (
"Generate a short, descriptive title (3-7 words) for a conversation that starts with the "
"following exchange. The title should capture the main topic or intent. "
"Write the title in {language}. "
"Return ONLY the title text, nothing else. No quotes, no punctuation at the end, no prefixes."
)
def _title_language() -> str:
"""Return configured title language, or empty string to match the user."""
try:
from hermes_cli.config import load_config
return str(
((load_config() or {}).get("auxiliary") or {})
.get("title_generation", {})
.get("language", "")
).strip()
except Exception:
return ""
def generate_title(
user_message: str,
@@ -48,8 +70,11 @@ def generate_title(
user_snippet = user_message[:500] if user_message else ""
assistant_snippet = assistant_response[:500] if assistant_response else ""
language = _title_language()
prompt = _TITLE_PROMPT_PINNED_LANGUAGE.format(language=language) if language else _TITLE_PROMPT
messages = [
{"role": "system", "content": _TITLE_PROMPT},
{"role": "system", "content": prompt},
{"role": "user", "content": f"User: {user_snippet}\n\nAssistant: {assistant_snippet}"},
]

View File

@@ -1309,6 +1309,7 @@ DEFAULT_CONFIG = {
"api_key": "",
"timeout": 30,
"extra_body": {},
"language": "",
},
"tts_audio_tags": {
"provider": "auto",

View File

@@ -7,6 +7,7 @@ from agent.title_generator import (
generate_title,
auto_title_session,
maybe_auto_title,
_title_language,
)
@@ -22,6 +23,42 @@ class TestGenerateTitle:
title = generate_title("help me fix this import", "Sure, let me check...")
assert title == "Debugging Python Import Errors"
def test_default_prompt_matches_user_language(self):
mock_response = MagicMock()
mock_response.choices = [MagicMock()]
mock_response.choices[0].message.content = "Some Title"
with patch("agent.title_generator.call_llm", return_value=mock_response) as llm:
generate_title("質問です", "回答です")
system_prompt = llm.call_args.kwargs["messages"][0]["content"]
assert "same language the user is writing in" in system_prompt
def test_configured_language_pins_prompt(self):
mock_response = MagicMock()
mock_response.choices = [MagicMock()]
mock_response.choices[0].message.content = "Some Title"
with (
patch("agent.title_generator.call_llm", return_value=mock_response) as llm,
patch("agent.title_generator._title_language", return_value="Japanese"),
):
generate_title("hello", "hi")
system_prompt = llm.call_args.kwargs["messages"][0]["content"]
assert "Write the title in Japanese" in system_prompt
assert "same language the user" not in system_prompt
def test_title_language_reads_config(self):
cfg = {"auxiliary": {"title_generation": {"language": " French "}}}
with patch("hermes_cli.config.load_config", return_value=cfg):
assert _title_language() == "French"
with patch("hermes_cli.config.load_config", return_value={}):
assert _title_language() == ""
with patch("hermes_cli.config.load_config", side_effect=RuntimeError("bad config")):
assert _title_language() == ""
def test_strips_quotes(self):
mock_response = MagicMock()
mock_response.choices = [MagicMock()]

View File

@@ -938,6 +938,16 @@ auxiliary:
compression:
timeout: 120 # seconds — compression summarizes long conversations, needs more time
# Auto-generated session titles. Empty language follows the conversation;
# set e.g. "English" or "Japanese" to pin titles to one language.
title_generation:
provider: "auto"
model: ""
base_url: ""
api_key: ""
timeout: 30
language: ""
# Skills hub — skill matching and search
skills_hub:
provider: "auto"