|
4 | 4 | (agent/subagent/commands), strips YAML frontmatter, discovers skills |
5 | 5 | for the {{SKILLS}} placeholder, assembles the effective system prompt |
6 | 6 | from project context files + task-completion rules + agent prompt, and |
7 | | -provides user_prompt_texts() for the compaction flow (summarize the |
8 | | -conversation and rebuild the history with every user prompt preserved |
9 | | -verbatim). |
| 7 | +provides the compaction flow helpers (summarize the conversation with |
| 8 | +the compact prompt and rebuild the history with every user prompt |
| 9 | +preserved verbatim), shared by the in-loop compaction and the manual |
| 10 | +/compact command. |
10 | 11 | """ |
11 | 12 |
|
12 | 13 | from __future__ import annotations |
13 | 14 |
|
14 | 15 | import os |
15 | 16 | import re |
16 | 17 | import subprocess |
| 18 | +from collections.abc import Callable |
17 | 19 | from pathlib import Path |
| 20 | +from typing import Any |
18 | 21 |
|
19 | 22 | from . import config |
| 23 | +from .models import Message |
20 | 24 |
|
21 | 25 |
|
22 | 26 | def read_prompt_file(name: str) -> str: |
@@ -450,3 +454,45 @@ def user_prompt_texts(messages: list) -> list[str]: |
450 | 454 | continue |
451 | 455 | prompts.append(text) |
452 | 456 | return prompts |
| 457 | + |
| 458 | + |
| 459 | +def compact_summary( |
| 460 | + client: Any, |
| 461 | + conversation: str, |
| 462 | + cancel_check: Callable[[], bool] | None = None, |
| 463 | +) -> str | None: |
| 464 | + """Ask the model to summarize *conversation* using the compact prompt. |
| 465 | +
|
| 466 | + Shared by the in-loop compaction (``AgentLoop.compact``) and the |
| 467 | + manual /compact command (``AgentSession.compact_conversation``). |
| 468 | + Returns the summary text with the reasoning preamble stripped, or |
| 469 | + None when the response carries no text. Client exceptions |
| 470 | + propagate to the caller, which owns the failure handling |
| 471 | + (log/notify/status message). |
| 472 | + """ |
| 473 | + system = read_prompt_file("compact.md") |
| 474 | + kwargs: dict[str, Any] = {} |
| 475 | + if cancel_check is not None: |
| 476 | + kwargs["cancel_check"] = cancel_check |
| 477 | + resp, _ = client.chat_sync( |
| 478 | + [Message(role="user", content=conversation)], |
| 479 | + system=system, |
| 480 | + **kwargs, |
| 481 | + ) |
| 482 | + summary = resp.text_without_reasoning() |
| 483 | + return summary or None |
| 484 | + |
| 485 | + |
| 486 | +def compacted_messages(summary: str, prompts: list[str]) -> list[Message]: |
| 487 | + """The post-compaction history: the summary frame as a user message |
| 488 | + followed by every preserved user prompt, oldest first. |
| 489 | +
|
| 490 | + The summary lives in the user turn (the system prompt is passed |
| 491 | + separately and stays untouched); *prompts* are the real user |
| 492 | + requests (see ``user_prompt_texts``) that must survive compaction. |
| 493 | + """ |
| 494 | + frame = (config.COMPACT_HEADER + summary + config.COMPACT_SEPARATOR).strip() |
| 495 | + return [ |
| 496 | + Message(role="user", content=frame), |
| 497 | + *[Message(role="user", content=p) for p in prompts], |
| 498 | + ] |
0 commit comments