feat(journal): LLM tools (record_moment, search_journal) + system prompt wiring
- services/tools/journal.py — record_moment + search_journal tool handlers - services/tools/_registry.py: add `journal` flag on ToolDef + tool() decorator - get_tools_for_user(user_id, conversation_type='chat'|'journal') — exclude journal-only tools from chat sessions; exclude set_rag_scope from journal sessions - services/tools/__init__.py: register the new journal module; drop the unused get_briefing_tools export - services/llm.py build_context: short-circuit for journal conversations, using journal_pipeline.build_journal_system_prompt and skipping all notes-RAG injection (preserves the journal/notes isolation invariant) - services/generation_task.py: pass conversation_type into get_tools_for_user Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,7 @@ class ToolDef:
|
||||
handler: ToolHandler
|
||||
read_only: bool = False
|
||||
briefing: bool = False
|
||||
journal: bool = False
|
||||
requires: str | None = None
|
||||
required_params: list[str] = field(default_factory=list)
|
||||
|
||||
@@ -57,6 +58,7 @@ def tool(
|
||||
required: list[str] | None = None,
|
||||
read_only: bool = False,
|
||||
briefing: bool = False,
|
||||
journal: bool = False,
|
||||
requires: str | None = None,
|
||||
) -> Callable[[ToolHandler], ToolHandler]:
|
||||
"""Register an async tool handler with its schema metadata."""
|
||||
@@ -71,6 +73,7 @@ def tool(
|
||||
handler=fn,
|
||||
read_only=read_only,
|
||||
briefing=briefing,
|
||||
journal=journal,
|
||||
requires=requires,
|
||||
required_params=required or [],
|
||||
)
|
||||
@@ -90,27 +93,29 @@ async def _check_requires(user_id: int, requires: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def get_tools_for_user(user_id: int) -> list[dict]:
|
||||
"""Build the tool schema list for a user based on configured integrations."""
|
||||
async def get_tools_for_user(
|
||||
user_id: int, *, conversation_type: str = "chat"
|
||||
) -> list[dict]:
|
||||
"""Build the tool schema list for a user, scoped by conversation type.
|
||||
|
||||
- 'chat': all tools except those marked journal-only.
|
||||
- 'journal': all tools except set_rag_scope (scope is implicit in journal).
|
||||
"""
|
||||
tools: list[dict] = []
|
||||
for td in _REGISTRY.values():
|
||||
if td.requires and not await _check_requires(user_id, td.requires):
|
||||
continue
|
||||
if conversation_type == "journal":
|
||||
if td.name == "set_rag_scope":
|
||||
continue
|
||||
else:
|
||||
if td.journal:
|
||||
continue
|
||||
tools.append(td.schema())
|
||||
logger.debug("User %d: %d tools available", user_id, len(tools))
|
||||
return tools
|
||||
|
||||
|
||||
async def get_briefing_tools(user_id: int) -> list[dict]:
|
||||
"""Return only the tool schemas marked ``briefing=True``."""
|
||||
all_tools = await get_tools_for_user(user_id)
|
||||
names = {td.name for td in _REGISTRY.values() if td.briefing}
|
||||
filtered = [t for t in all_tools if t["function"]["name"] in names]
|
||||
logger.debug(
|
||||
"Briefing tools for user %d: %d of %d selected",
|
||||
user_id, len(filtered), len(all_tools),
|
||||
"User %d / %s: %d tools available", user_id, conversation_type, len(tools)
|
||||
)
|
||||
return filtered
|
||||
return tools
|
||||
|
||||
|
||||
async def execute_tool(
|
||||
|
||||
Reference in New Issue
Block a user