Changeset 0.7.2 (#40)
This commit is contained in:
102
TOOLS_IMPL.md
Normal file
102
TOOLS_IMPL.md
Normal 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
|
||||
Reference in New Issue
Block a user