This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/TOOLS_IMPL.md
2026-02-21 19:03:19 +00:00

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_id
  • GET /notes — list with folder/tag filter, pagination
  • GET /notes/:id — get single note
  • PUT /notes/:id — update with replace/append/prepend modes
  • DELETE /notes/:id — delete
  • GET /notes/search?q= — full-text search with ts_rank + ts_headline
  • GET /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)
  • Tool interface — 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 name
  • AllDefinitions() — all registered tool schemas for LLM requests
  • ExecuteCall() — runs a single tool, returns ToolResult (never errors)
  • ExecuteAll() — batch execution
  • HasTools() — check if any tools registered

Note Tools (tools/notes.go)

  • note_create — create with title, content, folder, tags; links source_channel_id
  • note_search — full-text search with relevance ranking and excerpts
  • note_update — update by note_id with replace/append/prepend modes
  • note_list — list by folder/tag filter
  • All registered via init()

Provider Tool Calling Support

  • providers.Message — extended with ToolCalls, ToolCallID, Name fields
  • providers.CompletionRequest — Tools field for function definitions
  • providers.CompletionResponse — ToolCalls field for function calls
  • providers.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_use and event: 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 = 10 prevents 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

  1. Separate packagestools/ is independent of handlers/. Tools don't know about HTTP. The completion handler bridges them.
  2. init() registration — Tools self-register. Adding a new tool = create file with init(), done. No central wiring.
  3. JSON Schema parameters — Tool definitions use standard JSON Schema, matching OpenAI's function calling spec.
  4. Non-streaming tool loop — After tool execution, re-calls use streaming. Each iteration streams text deltas.
  5. Anthropic content blocks — Full translation: tool_use blocks, tool_result as user messages, consecutive user message merging for alternating-role requirement.
  6. Folder paths — Normalized to /path/ format. Root is /. Tree structure without a folders table.
  7. 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_result SSE events
  • Display tool activity in message bubbles