The write handler was building a hand-rolled map that dropped the type and domain fields from writeArgs. Pass the struct directly so all fields reach the ingestion server. Strengthen the test to assert the request body contains the type field. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
// internal/skills/brain/handlers.go
|
|
package brain
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
)
|
|
|
|
// Handle dispatches brain_query and brain_write tool calls.
|
|
func (s *Skill) Handle(ctx context.Context, tool string, args json.RawMessage) (json.RawMessage, error) {
|
|
switch tool {
|
|
case "brain_query":
|
|
return s.query(ctx, args)
|
|
case "brain_write":
|
|
return s.write(ctx, args)
|
|
default:
|
|
return nil, fmt.Errorf("unknown brain tool: %s", tool)
|
|
}
|
|
}
|
|
|
|
type queryArgs struct {
|
|
Query string `json:"query"`
|
|
Limit int `json:"limit,omitempty"`
|
|
}
|
|
|
|
func (s *Skill) query(ctx context.Context, args json.RawMessage) (json.RawMessage, error) {
|
|
var a queryArgs
|
|
if err := json.Unmarshal(args, &a); err != nil {
|
|
return nil, fmt.Errorf("parse args: %w", err)
|
|
}
|
|
if a.Query == "" {
|
|
return nil, fmt.Errorf("query is required")
|
|
}
|
|
if a.Limit == 0 {
|
|
a.Limit = 5
|
|
}
|
|
return s.post(ctx, "/query", a)
|
|
}
|
|
|
|
type writeArgs struct {
|
|
Content string `json:"content"`
|
|
Type string `json:"type,omitempty"`
|
|
Domain string `json:"domain,omitempty"`
|
|
Filename string `json:"filename,omitempty"`
|
|
}
|
|
|
|
func (s *Skill) write(ctx context.Context, args json.RawMessage) (json.RawMessage, error) {
|
|
var a writeArgs
|
|
if err := json.Unmarshal(args, &a); err != nil {
|
|
return nil, fmt.Errorf("parse args: %w", err)
|
|
}
|
|
if a.Content == "" {
|
|
return nil, fmt.Errorf("content is required")
|
|
}
|
|
return s.post(ctx, "/write", a)
|
|
}
|
|
|
|
func (s *Skill) post(ctx context.Context, path string, body any) (json.RawMessage, error) {
|
|
b, err := json.Marshal(body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal request: %w", err)
|
|
}
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.cfg.IngestBaseURL+path, bytes.NewReader(b))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("call ingestion server: %w", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
out, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read response: %w", err)
|
|
}
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("ingestion server returned %d: %s", resp.StatusCode, out)
|
|
}
|
|
return json.RawMessage(out), nil
|
|
}
|