fix: 套用自訂指示到 session summaries
This commit is contained in:
parent
85239a69b2
commit
9221aa8fbb
|
|
@ -97,12 +97,23 @@ class SummaryType(Enum):
|
|||
LONG = "honcho_chat_summary_long"
|
||||
|
||||
|
||||
def _custom_instructions_section(custom_instructions: str | None) -> str:
|
||||
if not custom_instructions or not custom_instructions.strip():
|
||||
return ""
|
||||
return c(f"""
|
||||
CUSTOM INSTRUCTIONS:
|
||||
{custom_instructions.strip()}
|
||||
""")
|
||||
|
||||
|
||||
def short_summary_prompt(
|
||||
formatted_messages: str,
|
||||
output_words: int,
|
||||
previous_summary_text: str,
|
||||
custom_instructions: str | None = None,
|
||||
) -> str:
|
||||
"""Generate the short summary prompt."""
|
||||
custom_instructions_section = _custom_instructions_section(custom_instructions)
|
||||
return c(f"""
|
||||
You are a system that summarizes parts of a conversation to create a concise and accurate summary. Focus on capturing:
|
||||
|
||||
|
|
@ -117,6 +128,8 @@ Provide a concise, factual summary that captures the essence of the conversation
|
|||
|
||||
Return only the summary without any explanation or meta-commentary.
|
||||
|
||||
{custom_instructions_section}
|
||||
|
||||
<previous_summary>
|
||||
{previous_summary_text}
|
||||
</previous_summary>
|
||||
|
|
@ -133,8 +146,10 @@ def long_summary_prompt(
|
|||
formatted_messages: str,
|
||||
output_words: int,
|
||||
previous_summary_text: str,
|
||||
custom_instructions: str | None = None,
|
||||
) -> str:
|
||||
"""Generate the long summary prompt."""
|
||||
custom_instructions_section = _custom_instructions_section(custom_instructions)
|
||||
return c(f"""
|
||||
You are a system that creates thorough, comprehensive summaries of conversations. Focus on capturing:
|
||||
|
||||
|
|
@ -151,6 +166,8 @@ Provide a thorough and detailed summary that captures the essence of the convers
|
|||
|
||||
Return only the summary without any explanation or meta-commentary.
|
||||
|
||||
{custom_instructions_section}
|
||||
|
||||
<previous_summary>
|
||||
{previous_summary_text}
|
||||
</previous_summary>
|
||||
|
|
@ -201,6 +218,7 @@ async def create_short_summary(
|
|||
input_tokens: int,
|
||||
previous_summary: str | None = None,
|
||||
*,
|
||||
custom_instructions: str | None = None,
|
||||
workspace_name: str | None = None,
|
||||
) -> HonchoLLMCallResponse[str]:
|
||||
# input_tokens indicates how many tokens the message list + previous summary take up
|
||||
|
|
@ -216,7 +234,10 @@ async def create_short_summary(
|
|||
previous_summary_text = "There is no previous summary -- the messages are the beginning of the conversation."
|
||||
|
||||
prompt = short_summary_prompt(
|
||||
formatted_messages, output_words, previous_summary_text
|
||||
formatted_messages,
|
||||
output_words,
|
||||
previous_summary_text,
|
||||
custom_instructions,
|
||||
)
|
||||
|
||||
return await honcho_llm_call(
|
||||
|
|
@ -236,6 +257,7 @@ async def create_long_summary(
|
|||
formatted_messages: str,
|
||||
previous_summary: str | None = None,
|
||||
*,
|
||||
custom_instructions: str | None = None,
|
||||
workspace_name: str | None = None,
|
||||
) -> HonchoLLMCallResponse[str]:
|
||||
# the word/token ratio is roughly 4:3 so we multiply by 0.75.
|
||||
|
|
@ -248,7 +270,10 @@ async def create_long_summary(
|
|||
previous_summary_text = "There is no previous summary -- the messages are the beginning of the conversation."
|
||||
|
||||
prompt = long_summary_prompt(
|
||||
formatted_messages, output_words, previous_summary_text
|
||||
formatted_messages,
|
||||
output_words,
|
||||
previous_summary_text,
|
||||
custom_instructions,
|
||||
)
|
||||
|
||||
return await honcho_llm_call(
|
||||
|
|
@ -453,6 +478,7 @@ async def _create_and_save_summary(
|
|||
last_message_id=last_message_id,
|
||||
last_message_content_preview=last_message_content_preview,
|
||||
message_count=message_count,
|
||||
custom_instructions=configuration.reasoning.custom_instructions,
|
||||
workspace_name=workspace_name,
|
||||
)
|
||||
|
||||
|
|
@ -553,6 +579,7 @@ async def _create_summary(
|
|||
last_message_content_preview: str,
|
||||
message_count: int,
|
||||
*,
|
||||
custom_instructions: str | None = None,
|
||||
workspace_name: str | None = None,
|
||||
) -> tuple[Summary, bool, int, int]:
|
||||
"""
|
||||
|
|
@ -585,12 +612,14 @@ async def _create_summary(
|
|||
formatted_messages,
|
||||
input_tokens,
|
||||
previous_summary_text,
|
||||
custom_instructions=custom_instructions,
|
||||
workspace_name=workspace_name,
|
||||
)
|
||||
else:
|
||||
response = await create_long_summary(
|
||||
formatted_messages,
|
||||
previous_summary_text,
|
||||
custom_instructions=custom_instructions,
|
||||
workspace_name=workspace_name,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ _RUNTIME_MOCK_TEST_BLOCKLIST_PREFIXES = (
|
|||
"tests/llm/",
|
||||
# LLM transport tests mock providers directly and don't need database/runtime setup.
|
||||
"tests/utils/test_length_finish_reason.py",
|
||||
# Summary unit tests patch LLM calls directly and should not require database setup.
|
||||
"tests/utils/test_summarizer.py",
|
||||
"tests/utils/test_clients.py",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ from src.utils.summarizer import (
|
|||
_create_summary, # pyright: ignore[reportPrivateUsage]
|
||||
create_long_summary,
|
||||
create_short_summary,
|
||||
long_summary_prompt,
|
||||
short_summary_prompt,
|
||||
)
|
||||
|
||||
# Common test arguments for _create_summary
|
||||
|
|
@ -222,6 +224,68 @@ class TestCreateSummary:
|
|||
assert summary["token_count"] == 0
|
||||
|
||||
|
||||
class TestSummaryCustomInstructions:
|
||||
def test_short_summary_prompt_includes_custom_instructions(self):
|
||||
prompt = short_summary_prompt(
|
||||
formatted_messages=_FORMATTED_MESSAGES,
|
||||
output_words=50,
|
||||
previous_summary_text="previous context",
|
||||
custom_instructions="Always write the summary in German.",
|
||||
)
|
||||
|
||||
assert "CUSTOM INSTRUCTIONS:" in prompt
|
||||
assert "Always write the summary in German." in prompt
|
||||
assert "previous context" in prompt
|
||||
assert _FORMATTED_MESSAGES in prompt
|
||||
|
||||
def test_long_summary_prompt_omits_blank_custom_instructions(self):
|
||||
prompt = long_summary_prompt(
|
||||
formatted_messages=_FORMATTED_MESSAGES,
|
||||
output_words=250,
|
||||
previous_summary_text="previous context",
|
||||
custom_instructions=" \n\t ",
|
||||
)
|
||||
|
||||
assert "CUSTOM INSTRUCTIONS:" not in prompt
|
||||
assert "previous context" in prompt
|
||||
assert _FORMATTED_MESSAGES in prompt
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_summary_passes_custom_instructions_to_short_summary(self):
|
||||
mock_response = HonchoLLMCallResponse(
|
||||
content="Benutzer bevorzugt kurze Antworten.",
|
||||
input_tokens=100,
|
||||
output_tokens=8,
|
||||
finish_reasons=["STOP"],
|
||||
)
|
||||
|
||||
with patch(
|
||||
"src.utils.summarizer.create_short_summary",
|
||||
new_callable=AsyncMock,
|
||||
return_value=mock_response,
|
||||
) as mock_short:
|
||||
summary, is_fallback, _, _ = await _create_summary(
|
||||
formatted_messages=_FORMATTED_MESSAGES,
|
||||
previous_summary_text=None,
|
||||
summary_type=SummaryType.SHORT,
|
||||
input_tokens=_INPUT_TOKENS,
|
||||
message_public_id=_MESSAGE_PUBLIC_ID,
|
||||
last_message_id=_LAST_MESSAGE_ID,
|
||||
last_message_content_preview=_LAST_MESSAGE_CONTENT_PREVIEW,
|
||||
message_count=_MESSAGE_COUNT,
|
||||
custom_instructions="Always write summaries in German.",
|
||||
)
|
||||
|
||||
assert is_fallback is False
|
||||
assert "Benutzer" in summary["content"]
|
||||
await_args = mock_short.await_args
|
||||
if await_args is None:
|
||||
raise AssertionError("Expected short summary call")
|
||||
assert await_args.kwargs["custom_instructions"] == (
|
||||
"Always write summaries in German."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestSummaryCallerMigration:
|
||||
async def test_create_short_summary_uses_model_config(self):
|
||||
|
|
|
|||
Loading…
Reference in New Issue