Changeset 0.7.2 (#40)

This commit is contained in:
2026-02-21 19:03:19 +00:00
parent 494b1aa981
commit 416e5439ea
28 changed files with 3813 additions and 138 deletions

102
TOOLS_IMPL.md Normal file
View File

@@ -0,0 +1,102 @@
# 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
- [x] `014_notes.sql` — notes table, tsvector full-text search, folder/tag indexes, auto-update trigger
### Notes API (handlers/notes.go)
- [x] `POST /notes` — create note with title, content, folder, tags, source_channel_id
- [x] `GET /notes` — list with folder/tag filter, pagination
- [x] `GET /notes/:id` — get single note
- [x] `PUT /notes/:id` — update with replace/append/prepend modes
- [x] `DELETE /notes/:id` — delete
- [x] `GET /notes/search?q=` — full-text search with ts_rank + ts_headline
- [x] `GET /notes/folders` — list distinct folder paths with counts
### Tool Type System (tools/types.go)
- [x] `ToolDef` — name, description, JSON Schema parameters (sent to LLM)
- [x] `ToolCall` — ID, name, arguments (from LLM response)
- [x] `ToolResult` — tool_call_id, name, content, is_error (fed back)
- [x] `Tool` interface — `Definition()` + `Execute(ctx, execCtx, argsJSON)`
- [x] `ExecutionContext` — userID, channelID for scoped execution
- [x] JSON Schema helpers: `Prop`, `PropEnum`, `PropArray`, `JSONSchema`
### Tool Registry (tools/registry.go)
- [x] `Register(Tool)` — global registry, called from init()
- [x] `Get(name)` — lookup by name
- [x] `AllDefinitions()` — all registered tool schemas for LLM requests
- [x] `ExecuteCall()` — runs a single tool, returns ToolResult (never errors)
- [x] `ExecuteAll()` — batch execution
- [x] `HasTools()` — check if any tools registered
### Note Tools (tools/notes.go)
- [x] `note_create` — create with title, content, folder, tags; links source_channel_id
- [x] `note_search` — full-text search with relevance ranking and excerpts
- [x] `note_update` — update by note_id with replace/append/prepend modes
- [x] `note_list` — list by folder/tag filter
- All registered via init()
### Provider Tool Calling Support
- [x] `providers.Message` — extended with ToolCalls, ToolCallID, Name fields
- [x] `providers.CompletionRequest` — Tools field for function definitions
- [x] `providers.CompletionResponse` — ToolCalls field for function calls
- [x] `providers.StreamEvent` — ToolCalls accumulated on final event
- [x] OpenAI provider — full tool_calls in request/response/stream
- [x] Anthropic provider — tool_use/tool_result content blocks, input_json_delta streaming, consecutive user message merging
- [x] OpenRouter — inherits from OpenAI (delegation)
- [x] Venice — inherits from OpenAI (delegation)
### Completion Handler Tool Loop (handlers/completion.go)
- [x] `buildToolDefs()` — converts registered tools to provider format
- [x] Tools attached when `caps.ToolCalling && tools.HasTools()`
- [x] 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
- [x] Non-streaming: same loop, tools executed synchronously
- [x] 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 packages**`tools/` 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
- [x] Notes panel/modal in UI
- [x] Notes CRUD forms (create, edit, list, search)
- [x] Tool execution indicators during streaming
- [x] Parse `event: tool_use` / `event: tool_result` SSE events
- [x] Display tool activity in message bubbles