Skip to content

Commit ce26637

Browse files
committed
brain/sqlbrain: implement think
For #94.
1 parent 14465d5 commit ce26637

File tree

4 files changed

+523
-2
lines changed

4 files changed

+523
-2
lines changed

brain/brain.go

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type Interface interface {
2525
//
2626
// Yielded closures replace id and suffix with successive messages' contents.
2727
// The iterating loop may not call the yielded closure on every iteration.
28+
// When it does, it uses the closure fully within each loop, allowing the
29+
// sequence to yield the same closure on each call.
30+
// Conversely, iterators should expect that the arguments to the closure
31+
// will be the same on each iteration and must not retain them.
2832
Think(ctx context.Context, tag string, prefix []string) iter.Seq[func(id, suf *[]byte) error]
2933

3034
// Speak generates a full message and appends it to w.

brain/braintest/thoughts.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package braintest
2+
3+
import "iter"
4+
5+
// Collect gathers all thoughts from a think.
6+
func Collect(it iter.Seq[func(id, suf *[]byte) error]) (ids, sufs []string, err error) {
7+
var id, suf []byte
8+
for f := range it {
9+
if err := f(&id, &suf); err != nil {
10+
return ids, sufs, err
11+
}
12+
ids = append(ids, string(id))
13+
sufs = append(sufs, string(suf))
14+
}
15+
return ids, sufs, nil
16+
}

brain/sqlbrain/speak.go

+52-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,58 @@ import (
1515

1616
var prependerPool tpool.Pool[deque.Deque[string]]
1717

18-
func (br *Brain) Think(ctx context.Context, tag string, prefix []string) iter.Seq[func(id *[]byte, suf *[]byte) error] {
19-
panic("unimplemented")
18+
func (br *Brain) Think(ctx context.Context, tag string, prompt []string) iter.Seq[func(id *[]byte, suf *[]byte) error] {
19+
return func(yield func(func(id, suf *[]byte) error) bool) {
20+
erf := func(err error) { yield(func(id, suf *[]byte) error { return err }) }
21+
conn, err := br.db.Take(ctx)
22+
defer br.db.Put(conn)
23+
if err != nil {
24+
erf(fmt.Errorf("couldn't get connection to speak: %w", err))
25+
return
26+
}
27+
var s *sqlite.Stmt
28+
if len(prompt) != 0 {
29+
s, err = conn.Prepare(`SELECT id, suffix FROM knowledge WHERE tag = :tag AND prefix >= :lower AND prefix < :upper AND LIKELY(deleted IS NULL)`)
30+
if err != nil {
31+
erf(fmt.Errorf("couldn't prepare term selection: %w", err))
32+
return
33+
}
34+
b := prefix(make([]byte, 0, 128), prompt)
35+
b, d := searchbounds(b)
36+
s.SetBytes(":lower", b)
37+
s.SetBytes(":upper", d)
38+
} else {
39+
s, err = conn.Prepare(`SELECT id, suffix FROM knowledge WHERE tag = :tag AND prefix = x'00' AND LIKELY(deleted IS NULL)`)
40+
if err != nil {
41+
erf(fmt.Errorf("couldn't prepare first term selection: %w", err))
42+
return
43+
}
44+
}
45+
s.SetText(":tag", tag)
46+
f := func(id, suf *[]byte) error {
47+
*id = bytecol(*id, s, 0)
48+
*suf = bytecol(*suf, s, 1)
49+
return nil
50+
}
51+
for {
52+
ok, err := s.Step()
53+
if err != nil {
54+
erf(fmt.Errorf("couldn't step term selection: %w", err))
55+
return
56+
}
57+
if !ok || !yield(f) {
58+
break
59+
}
60+
}
61+
}
62+
}
63+
64+
func bytecol(d []byte, s *sqlite.Stmt, col int) []byte {
65+
n := s.ColumnLen(col)
66+
if cap(d) < n {
67+
d = make([]byte, n)
68+
}
69+
return d[:s.ColumnBytes(col, d[:n])]
2070
}
2171

2272
// Speak generates a full message and appends it to w.

0 commit comments

Comments
 (0)