| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- """Task summarization utilities."""
- from __future__ import annotations
- from collections.abc import Callable, Iterator
- from typing import Tuple
- from hello_agents import ToolAwareSimpleAgent
- from models import SummaryState, TodoItem
- from config import Configuration
- from utils import strip_thinking_tokens
- from services.notes import build_note_guidance
- from services.text_processing import strip_tool_calls
- class SummarizationService:
- """Handles synchronous and streaming task summarization."""
- def __init__(
- self,
- summarizer_factory: Callable[[], ToolAwareSimpleAgent],
- config: Configuration,
- ) -> None:
- self._agent_factory = summarizer_factory
- self._config = config
- def summarize_task(self, state: SummaryState, task: TodoItem, context: str) -> str:
- """Generate a task-specific summary using the summarizer agent."""
- prompt = self._build_prompt(state, task, context)
- agent = self._agent_factory()
- try:
- response = agent.run(prompt)
- finally:
- agent.clear_history()
- summary_text = response.strip()
- if self._config.strip_thinking_tokens:
- summary_text = strip_thinking_tokens(summary_text)
- summary_text = strip_tool_calls(summary_text).strip()
- return summary_text or "暂无可用信息"
- def stream_task_summary(
- self, state: SummaryState, task: TodoItem, context: str
- ) -> Tuple[Iterator[str], Callable[[], str]]:
- """Stream the summary text for a task while collecting full output."""
- prompt = self._build_prompt(state, task, context)
- remove_thinking = self._config.strip_thinking_tokens
- raw_buffer = ""
- visible_output = ""
- emit_index = 0
- agent = self._agent_factory()
- def flush_visible() -> Iterator[str]:
- nonlocal emit_index, raw_buffer
- while True:
- start = raw_buffer.find("<think>", emit_index)
- if start == -1:
- if emit_index < len(raw_buffer):
- segment = raw_buffer[emit_index:]
- emit_index = len(raw_buffer)
- if segment:
- yield segment
- break
- if start > emit_index:
- segment = raw_buffer[emit_index:start]
- emit_index = start
- if segment:
- yield segment
- end = raw_buffer.find("</think>", start)
- if end == -1:
- break
- emit_index = end + len("</think>")
- def generator() -> Iterator[str]:
- nonlocal raw_buffer, visible_output, emit_index
- try:
- for chunk in agent.stream_run(prompt):
- raw_buffer += chunk
- if remove_thinking:
- for segment in flush_visible():
- visible_output += segment
- if segment:
- yield segment
- else:
- visible_output += chunk
- if chunk:
- yield chunk
- finally:
- if remove_thinking:
- for segment in flush_visible():
- visible_output += segment
- if segment:
- yield segment
- agent.clear_history()
- def get_summary() -> str:
- if remove_thinking:
- cleaned = strip_thinking_tokens(visible_output)
- else:
- cleaned = visible_output
- return strip_tool_calls(cleaned).strip()
- return generator(), get_summary
- def _build_prompt(self, state: SummaryState, task: TodoItem, context: str) -> str:
- """Construct the summarization prompt shared by both modes."""
- return (
- f"任务主题:{state.research_topic}\n"
- f"任务名称:{task.title}\n"
- f"任务目标:{task.intent}\n"
- f"检索查询:{task.query}\n"
- f"任务上下文:\n{context}\n"
- f"{build_note_guidance(task)}\n"
- "请按照以上协作要求先同步笔记,然后返回一份面向用户的 Markdown 总结(仍遵循任务总结模板)。"
- )
|