Skip to content

Commit

Permalink
brain/*: remove speaking
Browse files Browse the repository at this point in the history
Fixes #94.
  • Loading branch information
zephyrtronium committed Dec 22, 2024
1 parent 09e645f commit 327458e
Show file tree
Hide file tree
Showing 11 changed files with 0 additions and 1,121 deletions.
5 changes: 0 additions & 5 deletions brain/brain.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ type Interface interface {
// will be the same on each iteration and must not retain them.
Think(ctx context.Context, tag string, prefix []string) iter.Seq[func(id, suf *[]byte) error]

// Speak generates a full message and appends it to w.
//
// The prompt is in reverse order and has entropy reduction applied.
Speak(ctx context.Context, tag string, prompt []string, w *Builder) error

// Forget forgets everything learned from a single given message.
// If nothing has been learned from the message, it must prevent anything
// from being learned from a message with that ID.
Expand Down
47 changes: 0 additions & 47 deletions brain/braintest/braintest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package braintest_test
import (
"context"
"iter"
"math/rand/v2"
"slices"
"strings"
"sync"
Expand Down Expand Up @@ -88,52 +87,6 @@ func (m *membrain) Think(ctx context.Context, tag string, prompt []string) iter.
}
}

func (m *membrain) Speak(ctx context.Context, tag string, prompt []string, w *brain.Builder) error {
m.mu.Lock()
defer m.mu.Unlock()
var s string
if len(prompt) == 0 {
u := slices.Clone(m.tups[tag].tups[""])
d := 0
for k, v := range u {
if m.tups[tag].forgort[v[0]] {
u[d], u[k] = u[k], u[d]
d++
}
}
u = u[d:]
if len(u) == 0 {
return nil
}
t := u[rand.IntN(len(u))]
w.Append(t[0], []byte(t[1]))
s = brain.ReduceEntropy(t[1])
} else {
s = brain.ReduceEntropy(prompt[len(prompt)-1])
}
for range 256 {
u := slices.Clone(m.tups[tag].tups[s])
d := 0
for k, v := range u {
if m.tups[tag].forgort[v[0]] {
u[d], u[k] = u[k], u[d]
d++
}
}
u = u[d:]
if len(u) == 0 {
break
}
t := u[rand.IntN(len(u))]
if t[1] == "" {
break
}
w.Append(t[0], []byte(t[1]))
s = brain.ReduceEntropy(t[1])
}
return nil
}

func TestTests(t *testing.T) {
braintest.Test(context.Background(), t, func(ctx context.Context) brain.Interface { return new(membrain) })
}
50 changes: 0 additions & 50 deletions brain/builder.go

This file was deleted.

116 changes: 0 additions & 116 deletions brain/builder_test.go

This file was deleted.

120 changes: 0 additions & 120 deletions brain/kvbrain/speak.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,10 @@ import (
"context"
"fmt"
"iter"
"math/rand/v2"

"github.com/dgraph-io/badger/v4"

"github.com/zephyrtronium/robot/brain"
"github.com/zephyrtronium/robot/deque"
"github.com/zephyrtronium/robot/tpool"
)

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

func (br *Brain) Think(ctx context.Context, tag string, prompt []string) iter.Seq[func(id *[]byte, suf *[]byte) error] {
return func(yield func(func(id *[]byte, suf *[]byte) error) bool) {
erf := func(err error) { yield(func(id, suf *[]byte) error { return err }) }
Expand Down Expand Up @@ -79,116 +72,3 @@ func (br *Brain) Think(ctx context.Context, tag string, prompt []string) iter.Se
}
}
}

// Speak generates a full message and appends it to w.
// The prompt is in reverse order and has entropy reduction applied.
func (br *Brain) Speak(ctx context.Context, tag string, prompt []string, w *brain.Builder) error {
search := prependerPool.Get().Prepend(prompt...)
defer func() { prependerPool.Put(search.Reset()) }()

tb := hashTag(make([]byte, 0, tagHashLen), tag)
b := make([]byte, 0, 128)
var id string
opts := badger.DefaultIteratorOptions
// We don't actually need to iterate over values, only the single value
// that we decide to use per suffix. So, we can disable value prefetch.
opts.PrefetchValues = false
opts.Prefix = hashTag(nil, tag)
for range 1024 {
var err error
var l int
b = append(b[:0], tb...)
b, id, l, err = br.next(b, search.Slice(), opts)
if err != nil {
return err
}
if len(b) == 0 {
break
}
w.Append(id, b)
search = search.DropEnd(search.Len() - l - 1).Prepend(brain.ReduceEntropy(string(b)))
}
return nil
}

// next finds a single token to continue a prompt.
// The returned values are, in order,
// b with its contents replaced with the new term,
// the ID of the message used for the term,
// the number of terms of the prompt which matched to produce the new term,
// and any error.
// If the returned term is the empty string, generation should end.
func (br *Brain) next(b []byte, prompt []string, opts badger.IteratorOptions) ([]byte, string, int, error) {
// These definitions are outside the loop to ensure we don't bias toward
// smaller contexts.
var (
key []byte
skip brain.Skip
n uint64
)
b = appendPrefix(b, prompt)
if len(prompt) == 0 {
// If we have no prompt, then we want to make sure we select only
// options that start a message.
b = append(b, '\xff')
}
for {
var seen uint64
err := br.knowledge.View(func(txn *badger.Txn) error {
it := txn.NewIterator(opts)
defer it.Close()
it.Seek(b)
for it.ValidForPrefix(b) {
if n == 0 {
item := it.Item()
// TODO(zeph): for #43, check deleted uuids so we never
// pick a message that has been deleted
key = item.KeyCopy(key[:0])
n = skip.N(rand.Uint64(), rand.Uint64())
}
it.Next()
n--
seen++
}
return nil
})
if err != nil {
return nil, "", len(prompt), fmt.Errorf("couldn't read knowledge: %w", err)
}
// Try to lose context.
// We want to do so when we have a long context and almost no options,
// or at random with even a short context.
// Note that in the latter case we use a 1/2 chance; it seems high, but
// n.b. the caller will recover the last token that we discard.
if len(prompt) > 4 && seen <= 2 || len(prompt) > 2 && rand.Uint32()&1 == 0 {
// We haven't seen enough options, and we have context we could
// lose. Do so and try again from the beginning.
prompt = prompt[:len(prompt)-1]
b = appendPrefix(b[:tagHashLen], prompt)
continue
}
if key == nil {
// We never saw any options. Since we always select the first, this
// means there were no options. Don't look for nothing in the DB.
return b[:0], "", len(prompt), nil
}
err = br.knowledge.View(func(txn *badger.Txn) error {
item, err := txn.Get(key)
if err != nil {
return fmt.Errorf("couldn't get item for key %q: %w", key, err)
}
b, err = item.ValueCopy(b[:0])
if err != nil {
return fmt.Errorf("couldn't get value for key %q: %w", key, err)
}
return nil
})
// The id is everything after the first byte following the hash for
// empty prefixes, and everything after the first \xff\xff otherwise.
id := key[tagHashLen+1:]
if len(prompt) > 0 {
_, id, _ = bytes.Cut(key, []byte{0xff, 0xff})
}
return b, string(id), len(prompt), err
}
}
Loading

0 comments on commit 327458e

Please sign in to comment.