orchard
mirrorEvery 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
1// The memory overlay's endpoints.
2//
3// JSON rather than pages, because this lives in a panel over the conversation
4// and navigating away from a thread to delete a fact is a worse trade than the
5// handful of endpoints here.
6package main
7
8import (
9 "context"
10 "encoding/json"
11 "net/http"
12 "strconv"
13 "strings"
14 "time"
15)
16
17type memoryView struct {
18 Facts []Fact `json:"facts"`
19 Note string `json:"note,omitempty"`
20 Problem string `json:"problem,omitempty"`
21 Applied []memChange `json:"applied,omitempty"`
22}
23
24func (s *site) memoryList(w http.ResponseWriter, r *http.Request) {
25 facts, err := s.store.Facts()
26 if err != nil {
27 http.Error(w, err.Error(), 500)
28 return
29 }
30 writeJSON(w, memoryView{Facts: facts})
31}
32
33// memoryTeach is the write path, and the only one. What is typed here is not
34// stored: it goes to the model with the same rules the automatic pass uses, and
35// what comes back is what gets written.
36func (s *site) memoryTeach(w http.ResponseWriter, r *http.Request) {
37 var body struct {
38 Said string `json:"said"`
39 }
40 _ = json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&body)
41 said := strings.TrimSpace(body.Said)
42 if said == "" {
43 s.memoryProblem(w, "Say what should change.")
44 return
45 }
46
47 ctx, cancel := context.WithTimeout(r.Context(), 2*time.Minute)
48 defer cancel()
49
50 existing, err := s.store.Facts()
51 if err != nil {
52 s.memoryProblem(w, err.Error())
53 return
54 }
55 changes, err := s.proposeChanges(ctx, existing,
56 "Isaac is editing his memory himself and said:\n"+said,
57 "Do what he asked, following the rules above.")
58 if err != nil {
59 s.memoryProblem(w, "the model did not answer: "+err.Error())
60 return
61 }
62 applied := s.applyChanges(changes, existing)
63 s.store.Checkpoint()
64
65 facts, _ := s.store.Facts()
66 if len(applied) == 0 {
67 writeJSON(w, memoryView{Facts: facts,
68 Problem: "Nothing changed. It read that as not worth keeping, or as already known."})
69 return
70 }
71 var parts []string
72 for _, c := range applied {
73 switch c.Op {
74 case "add":
75 parts = append(parts, "remembered that")
76 case "replace":
77 parts = append(parts, "rewrote one")
78 case "delete":
79 parts = append(parts, "forgot one")
80 }
81 }
82 writeJSON(w, memoryView{Facts: facts, Applied: applied, Note: strings.Join(parts, ", ") + "."})
83}
84
85func (s *site) memoryProblem(w http.ResponseWriter, msg string) {
86 facts, _ := s.store.Facts()
87 writeJSON(w, memoryView{Facts: facts, Problem: msg})
88}
89
90func (s *site) memoryDelete(w http.ResponseWriter, r *http.Request) {
91 id, _ := strconv.ParseInt(r.PathValue("id"), 10, 64)
92 if err := s.store.DeleteFact(id); err != nil {
93 http.Error(w, err.Error(), 500)
94 return
95 }
96 s.store.Checkpoint()
97 s.memoryList(w, r)
98}
99
100func (s *site) memoryForget(w http.ResponseWriter, r *http.Request) {
101 if err := s.store.ForgetEverything(); err != nil {
102 http.Error(w, err.Error(), 500)
103 return
104 }
105 s.store.Checkpoint()
106 s.memoryList(w, r)
107}