@@ -38,8 +38,7 @@ func Think(ctx context.Context, s Interface, tag, prompt string) (string, []stri
38
38
39
39
var ids []string
40
40
// We handle the first search specially.
41
- // TODO(zeph): skip terminating choices
42
- id , tok , l , err := next (ctx , s , tag , search .Slice ())
41
+ id , tok , err := first (ctx , s , tag , search .Slice ())
43
42
if len (tok ) == 0 {
44
43
return "" , nil , err
45
44
}
@@ -48,7 +47,8 @@ func Think(ctx context.Context, s Interface, tag, prompt string) (string, []stri
48
47
ids = slices .Insert (ids , k , id )
49
48
}
50
49
w = append (w , tok ... )
51
- search = search .DropEnd (search .Len () - l - 1 ).Prepend (ReduceEntropy (tok ))
50
+ search = search .Prepend (ReduceEntropy (tok ))
51
+
52
52
for range 1024 {
53
53
id , tok , l , err := next (ctx , s , tag , search .Slice ())
54
54
if len (tok ) == 0 {
@@ -116,3 +116,47 @@ func term(ctx context.Context, s Interface, tag string, prompt []string, wid, wt
116
116
}
117
117
return n , seen , nil
118
118
}
119
+
120
+ // first finds a single first term from a brain given a prompt.
121
+ // Unlike next, it requires the entire prompt to match, and it skips empty
122
+ // continuations if the prompt is not empty.
123
+ func first (ctx context.Context , s Interface , tag string , prompt []string ) (id , tok string , err error ) {
124
+ wid := make ([]byte , 0 , 64 )
125
+ wtok := make ([]byte , 0 , 64 )
126
+ var skip Skip
127
+ var n uint64
128
+ // Empty and non-empty prompts have different logic. We could merge them
129
+ // into the same loop, but it's easier and probably more efficient to
130
+ // split the control flow.
131
+ if len (prompt ) == 0 {
132
+ _ , _ , err := term (ctx , s , tag , prompt , & wid , & wtok , & skip , 0 )
133
+ if err != nil {
134
+ return "" , "" , fmt .Errorf ("couldn't think of first term: %w" , err )
135
+ }
136
+ return string (wid ), string (wtok ), nil
137
+ }
138
+
139
+ var rid , rtok []byte
140
+ for f := range s .Think (ctx , tag , prompt ) {
141
+ // The downside with a prompt is that we have to read every option so
142
+ // that we only count non-empty continuations.
143
+ wid , wtok = wid [:0 ], wtok [:0 ]
144
+ if err := f (& wid , & wtok ); err != nil {
145
+ return "" , "" , fmt .Errorf ("couldn't think of first term with prompt %q: %w" , prompt , err )
146
+ }
147
+ if len (wtok ) == 0 {
148
+ // Empty suffix. Don't care.
149
+ continue
150
+ }
151
+ if n > 0 {
152
+ n --
153
+ continue
154
+ }
155
+ // Save this result as the potential selection.
156
+ // We could just assign id and tok here, but this reduces allocations.
157
+ rid = append (rid [:0 ], wid ... )
158
+ rtok = append (rtok [:0 ], wtok ... )
159
+ n = skip .N (rand .Uint64 (), rand .Uint64 ())
160
+ }
161
+ return string (rid ), string (rtok ), nil
162
+ }
0 commit comments