repos
/ orchard main

orchard

mirror

Every site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.

blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics

2.5 KB · 69 lines · Go Raw History
 1package tools
 2
 3// Searching earlier conversations.
 4//
 5// The other half of memory. Memory keeps one sentence facts about Isaac, this
 6// reaches what was actually said, which matters because a tool result dies with
 7// the turn that fetched it and only the answer survives. Without it a question
 8// about something settled last week had nothing to reach for.
 9
10import (
11	"context"
12	"fmt"
13	"strings"
14	"time"
15)
16
17// PastExchange is one earlier question and its answer.
18type PastExchange struct {
19	ConvID   string    `json:"conversation_id"`
20	Title    string    `json:"title"`
21	When     time.Time `json:"when"`
22	Question string    `json:"question"`
23	Answer   string    `json:"answer"`
24}
25
26// History is chat's own conversation store, an interface for the same reason
27// Memory is: this package cannot see the database the site owns.
28type History interface {
29	SearchPast(query string, limit int) ([]PastExchange, error)
30}
31
32var ChatHistory = Tool{
33	Name: "chat_history",
34	Description: "Search earlier conversations with Isaac for what was actually said. Use it when he " +
35		"refers to something from another chat, asks what was decided or discussed before, or when a " +
36		"question only makes sense against something already settled. It returns whole exchanges, so " +
37		"quote the answer as something said earlier rather than as a fresh fact, and check anything " +
38		"that has since changed. It does not search the web. Read only.",
39	Schema: obj(map[string]any{
40		"query": str("what to look for, the subject words rather than the whole question"),
41		"n":     integer("how many exchanges to return, default 6"),
42	}, "query"),
43	Run: func(ctx context.Context, d *Deps, a map[string]any) (any, error) {
44		if d.History == nil {
45			return nil, fmt.Errorf("the conversation history is not available in this turn")
46		}
47		q := strings.TrimSpace(argStr(a, "query"))
48		if q == "" {
49			return nil, fmt.Errorf("query is required, and it should be the subject rather than the whole question")
50		}
51		n := int(argNum(a, "n", 6))
52		if n < 1 || n > 15 {
53			n = 6
54		}
55		hits, err := d.History.SearchPast(q, n)
56		if err != nil {
57			return nil, err
58		}
59		if len(hits) == 0 {
60			return map[string]any{"query": q, "exchanges": []any{}, "count": 0,
61				"note": "Nothing earlier matched. Say so rather than inventing what was said, " +
62					"and answer the question on its own."}, nil
63		}
64		return map[string]any{"query": q, "exchanges": hits, "count": len(hits),
65			"note": "These are earlier exchanges, not current facts. Anything dated or priced in " +
66				"them may have moved since, so look it up again before repeating it."}, nil
67	},
68}