5.0 KiB
5.0 KiB
Notes & Tool Execution Framework — Implementation
Version: 0.7.2 (Phase 2 per ARCHITECTURE.md §10) Depends on: channels (006), messages with tree (013) — both deployed
Status: Complete
Migration
014_notes.sql— notes table, tsvector full-text search, folder/tag indexes, auto-update trigger
Notes API (handlers/notes.go)
POST /notes— create note with title, content, folder, tags, source_channel_idGET /notes— list with folder/tag filter, paginationGET /notes/:id— get single notePUT /notes/:id— update with replace/append/prepend modesDELETE /notes/:id— deleteGET /notes/search?q=— full-text search with ts_rank + ts_headlineGET /notes/folders— list distinct folder paths with counts
Tool Type System (tools/types.go)
ToolDef— name, description, JSON Schema parameters (sent to LLM)ToolCall— ID, name, arguments (from LLM response)ToolResult— tool_call_id, name, content, is_error (fed back)Toolinterface —Definition()+Execute(ctx, execCtx, argsJSON)ExecutionContext— userID, channelID for scoped execution- JSON Schema helpers:
Prop,PropEnum,PropArray,JSONSchema
Tool Registry (tools/registry.go)
Register(Tool)— global registry, called from init()Get(name)— lookup by nameAllDefinitions()— all registered tool schemas for LLM requestsExecuteCall()— runs a single tool, returns ToolResult (never errors)ExecuteAll()— batch executionHasTools()— check if any tools registered
Note Tools (tools/notes.go)
note_create— create with title, content, folder, tags; links source_channel_idnote_search— full-text search with relevance ranking and excerptsnote_update— update by note_id with replace/append/prepend modesnote_list— list by folder/tag filter- All registered via init()
Provider Tool Calling Support
providers.Message— extended with ToolCalls, ToolCallID, Name fieldsproviders.CompletionRequest— Tools field for function definitionsproviders.CompletionResponse— ToolCalls field for function callsproviders.StreamEvent— ToolCalls accumulated on final event- OpenAI provider — full tool_calls in request/response/stream
- Anthropic provider — tool_use/tool_result content blocks, input_json_delta streaming, consecutive user message merging
- OpenRouter — inherits from OpenAI (delegation)
- Venice — inherits from OpenAI (delegation)
Completion Handler Tool Loop (handlers/completion.go)
buildToolDefs()— converts registered tools to provider format- Tools attached when
caps.ToolCalling && tools.HasTools() - Streaming: tool loop with max 10 iterations
- Stream text deltas normally
- On
finish_reason: "tool_calls": execute tools, send SSE events - Custom SSE events:
event: tool_useandevent: tool_result - Re-call provider with tool results appended to messages
- Final text response streamed normally
- Non-streaming: same loop, tools executed synchronously
- Safety:
maxToolIterations = 10prevents infinite loops
SSE Events for Tool Execution
The streaming completion sends custom named SSE events during tool execution. Standard data events continue to carry OpenAI-compatible content deltas.
| Event | Payload | When |
|---|---|---|
tool_use |
[{"id":"...","type":"function","function":{"name":"note_create","arguments":"..."}}] |
LLM requests tool calls |
tool_result |
{"tool_call_id":"...","name":"note_create","content":"...","is_error":false} |
Tool execution complete |
The frontend can listen for these to show tool execution status.
Existing frontends that only handle data: events are unaffected.
Design Decisions
- Separate packages —
tools/is independent ofhandlers/. Tools don't know about HTTP. The completion handler bridges them. - init() registration — Tools self-register. Adding a new tool = create file with init(), done. No central wiring.
- JSON Schema parameters — Tool definitions use standard JSON Schema, matching OpenAI's function calling spec.
- Non-streaming tool loop — After tool execution, re-calls use streaming. Each iteration streams text deltas.
- Anthropic content blocks — Full translation:
tool_useblocks,tool_resultas user messages, consecutive user message merging for alternating-role requirement. - Folder paths — Normalized to
/path/format. Root is/. Tree structure without a folders table. - Search — PostgreSQL tsvector with weighted ranks (title=A, content=B). No external search infrastructure needed.
Frontend Work
- Notes panel/modal in UI
- Notes CRUD forms (create, edit, list, search)
- Tool execution indicators during streaming
- Parse
event: tool_use/event: tool_resultSSE events - Display tool activity in message bubbles