|
45 | 45 |
|
46 | 46 | (facts |
47 | 47 | "about user speech aggregation" |
48 | | - (let [config {:messages/role "user" |
49 | | - :llm/context {:messages [{:role :assistant :content "You are a helpful assistant"}]} |
50 | | - :aggregator/start-frame? frame/user-speech-start? |
51 | | - :aggregator/end-frame? frame/user-speech-stop? |
52 | | - :aggregator/accumulator-frame? frame/transcription? |
53 | | - :aggregator/interim-results-frame? frame/transcription-interim? |
54 | | - :aggregator/handles-interrupt? false ;; User speaking shouldn't be interrupted |
55 | | - :aggregator/debug? false} |
| 48 | + (let [config {:llm/context {:messages [{:role :assistant :content "You are a helpful assistant"}]}} |
56 | 49 | state (partial merge config) |
57 | 50 | sstate (state {:aggregating? true |
58 | 51 | :aggregation "" |
|
85 | 78 | :seen-start-frame? false})] |
86 | 79 |
|
87 | 80 | (fact "S T E -> X" |
88 | | - (sut/aggregator-transform config nil |
89 | | - (frame/user-speech-start true)) => [sstate] |
90 | | - (sut/aggregator-transform sstate nil (frame/transcription "Hello there")) => [ststate] |
91 | | - (let [[next-state {:keys [out]}] (sut/aggregator-transform ststate nil (frame/user-speech-stop true)) |
| 81 | + (sut/user-aggregator-transform config nil |
| 82 | + (frame/user-speech-start true)) => [sstate] |
| 83 | + (sut/user-aggregator-transform sstate nil (frame/transcription "Hello there")) => [ststate] |
| 84 | + (let [[next-state {:keys [out]}] (sut/user-aggregator-transform ststate nil (frame/user-speech-stop true)) |
92 | 85 | frame (first out)] |
93 | 86 | next-state => stestate |
94 | 87 | (:frame/type frame) => :frame.llm/context |
|
101 | 94 | :seen-end-frame? true |
102 | 95 | :seen-interim-results? false |
103 | 96 | :seen-start-frame? false})] |
104 | | - (sut/aggregator-transform sstate nil (frame/user-speech-stop true)) => [sestate] |
| 97 | + (sut/user-aggregator-transform sstate nil (frame/user-speech-stop true)) => [sestate] |
105 | 98 |
|
106 | | - (let [[next-state {:keys [out]}] (sut/aggregator-transform sestate nil (frame/transcription "Hello there")) |
| 99 | + (let [[next-state {:keys [out]}] (sut/user-aggregator-transform sestate nil (frame/transcription "Hello there")) |
107 | 100 | frame (first out)] |
108 | 101 | next-state => (state stestate) |
109 | 102 | (:frame/type frame) => :frame.llm/context |
110 | 103 | (:frame/data frame) => {:messages [{:content "You are a helpful assistant" :role :assistant} |
111 | 104 | {:content "Hello there" :role "user"}]}))) |
112 | 105 | (fact "S I T E -> X" |
113 | | - (sut/aggregator-transform sstate nil (frame/transcription-interim "Hello there")) => [sistate] |
114 | | - (sut/aggregator-transform sistate nil (frame/transcription "Hello there")) => [ststate]) |
| 106 | + (sut/user-aggregator-transform sstate nil (frame/transcription-interim "Hello there")) => [sistate] |
| 107 | + (sut/user-aggregator-transform sistate nil (frame/transcription "Hello there")) => [ststate]) |
115 | 108 |
|
116 | 109 | (fact "S I E I T -> X" |
117 | | - (sut/aggregator-transform sistate nil (frame/user-speech-stop true)) => [siestate] |
118 | | - (sut/aggregator-transform siestate nil (frame/transcription-interim "Hello, there")) => [siestate] |
119 | | - (let [[next-state {:keys [out]}] (sut/aggregator-transform siestate nil (frame/transcription "Hello there")) |
| 110 | + (sut/user-aggregator-transform sistate nil (frame/user-speech-stop true)) => [siestate] |
| 111 | + (sut/user-aggregator-transform siestate nil (frame/transcription-interim "Hello, there")) => [siestate] |
| 112 | + (let [[next-state {:keys [out]}] (sut/user-aggregator-transform siestate nil (frame/transcription "Hello there")) |
120 | 113 | frame (first out)] |
121 | 114 | next-state => (state stestate) |
122 | 115 | (:frame/type frame) => :frame.llm/context |
123 | 116 | (:frame/data frame) => {:messages [{:content "You are a helpful assistant" :role :assistant} |
124 | 117 | {:content "Hello there" :role "user"}]})) |
125 | | - (fact "updates current context if a frame is received" |
| 118 | + (fact "updates current context if a context frame is received" |
126 | 119 | (let [new-context {:messages [{:content "You are a helpful assistant" :role :assistant} |
127 | 120 | {:content "Hello there" :role "user"} |
128 | 121 | {:content "How can I help" :role :assistant}]}] |
129 | | - (sut/aggregator-transform ststate nil (frame/llm-context new-context)) => [(assoc ststate :llm/context new-context)])))) |
| 122 | + (sut/user-aggregator-transform ststate nil (frame/llm-context new-context)) => [(assoc ststate :llm/context new-context) nil])))) |
130 | 123 |
|
131 | 124 | (facts |
132 | 125 | "about assistant response aggregation" |
133 | | - (let [config {:messages/role "assistant" |
134 | | - :llm/context {:messages [{:role "assistant" :content "You are a helpful assistant"} |
135 | | - {:role "user" :content "Hello there"}]} |
136 | | - :aggregator/start-frame? frame/llm-full-response-start? |
137 | | - :aggregator/end-frame? frame/llm-full-response-end? |
138 | | - :aggregator/accumulator-frame? frame/llm-text-chunk?} |
| 126 | + (let [config {:llm/context {:messages [{:role "assistant" :content "You are a helpful assistant"} |
| 127 | + {:role "user" :content "Hello there"}]}} |
139 | 128 | state (partial merge config) |
140 | 129 | ;; State after start frame |
141 | 130 | sstate (state {:aggregating? true |
142 | | - :aggregation "" |
| 131 | + :function-arguments nil |
| 132 | + :function-name nil |
| 133 | + :tool-call-id nil |
| 134 | + :content-aggregation nil |
143 | 135 | :seen-end-frame? false |
144 | 136 | :seen-interim-results? false |
145 | 137 | :seen-start-frame? true}) |
146 | 138 | ;; State after text accumulation |
147 | 139 | ststate (state {:aggregating? true |
148 | | - :aggregation "Hi! How can I help you?" |
| 140 | + :content-aggregation "Hi! How can I help you?" |
| 141 | + :function-arguments nil |
| 142 | + :function-name nil |
| 143 | + :tool-call-id nil |
149 | 144 | :seen-end-frame? false |
150 | 145 | :seen-interim-results? false |
151 | 146 | :seen-start-frame? true}) |
152 | 147 | ;; State after complete sequence (final state) |
153 | 148 | stestate (state {:aggregating? false |
154 | | - :aggregation "" |
| 149 | + :content-aggregation nil |
| 150 | + :function-arguments nil |
| 151 | + :function-name nil |
| 152 | + :tool-call-id nil |
155 | 153 | :seen-end-frame? false |
156 | 154 | :seen-interim-results? false |
157 | 155 | :seen-start-frame? false |
158 | 156 | :llm/context {:messages [{:role "assistant" :content "You are a helpful assistant"} |
159 | 157 | {:role "user" :content "Hello there"} |
160 | | - {:role "assistant" :content "Hi! How can I help you?"}]}})] |
| 158 | + {:role :assistant |
| 159 | + :content [{:text "Hi! How can I help you?" :type :text}]}]}})] |
161 | 160 |
|
162 | 161 | (fact "S T E -> X" |
163 | | - (sut/aggregator-transform config nil |
164 | | - (frame/llm-full-response-start true)) => [sstate] |
165 | | - (sut/aggregator-transform sstate nil |
166 | | - (frame/llm-text-chunk "Hi! How can I help you?")) => [ststate] |
167 | | - (let [[next-state {:keys [out]}] (sut/aggregator-transform ststate nil |
168 | | - (frame/llm-full-response-end true)) |
| 162 | + (sut/assistant-aggregator-transform config nil |
| 163 | + (frame/llm-full-response-start true)) => [sstate] |
| 164 | + (sut/assistant-aggregator-transform sstate nil |
| 165 | + (frame/llm-text-chunk "Hi! How can I help you?")) => [ststate] |
| 166 | + (let [[next-state {:keys [out]}] (sut/assistant-aggregator-transform ststate nil |
| 167 | + (frame/llm-full-response-end true)) |
169 | 168 | frame (first out)] |
170 | 169 | next-state => stestate |
171 | 170 | (:frame/type frame) => :frame.llm/context |
172 | 171 | (:frame/data frame) => {:messages [{:role "assistant" :content "You are a helpful assistant"} |
173 | 172 | {:role "user" :content "Hello there"} |
174 | | - {:role "assistant" :content "Hi! How can I help you?"}]})) |
175 | | - |
176 | | - (fact "S E T -> X" |
177 | | - (let [sestate (state {:aggregating? true |
178 | | - :aggregation "" |
179 | | - :seen-end-frame? true |
180 | | - :seen-interim-results? false |
181 | | - :seen-start-frame? false})] |
182 | | - (sut/aggregator-transform sstate nil |
183 | | - (frame/llm-full-response-end true)) => [sestate] |
184 | | - |
185 | | - (let [[next-state {:keys [out]}] (sut/aggregator-transform sestate nil |
186 | | - (frame/llm-text-chunk "Hi! How can I help you?")) |
187 | | - frame (first out)] |
188 | | - next-state => stestate |
189 | | - (:frame/type frame) => :frame.llm/context |
190 | | - (:frame/data frame) => {:messages [{:role "assistant" :content "You are a helpful assistant"} |
191 | | - {:role "user" :content "Hello there"} |
192 | | - {:role "assistant" :content "Hi! How can I help you?"}]}))) |
| 173 | + {:role :assistant |
| 174 | + :content [{:text "Hi! How can I help you?" :type :text}]}]})) |
193 | 175 |
|
194 | 176 | (fact "S T T T T T E -> X (streaming tokens pattern)" |
195 | 177 | (let [token-chunks ["Hi" "!" " How" " can" " I" " help" " you" "?"] |
196 | 178 | expected-response "Hi! How can I help you?" |
197 | 179 |
|
198 | 180 | ;; Accumulate all token chunks |
199 | 181 | final-state (reduce (fn [current-state frame] |
200 | | - (let [[next-state] (sut/aggregator-transform |
| 182 | + (let [[next-state] (sut/assistant-aggregator-transform |
201 | 183 | current-state |
202 | 184 | nil |
203 | 185 | frame)] |
|
206 | 188 | (map frame/llm-text-chunk token-chunks)) |
207 | 189 |
|
208 | 190 | ;; Final state after end frame |
209 | | - [next-state {:keys [out]}] (sut/aggregator-transform |
| 191 | + [next-state {:keys [out]}] (sut/assistant-aggregator-transform |
210 | 192 | final-state |
211 | 193 | nil |
212 | 194 | (frame/llm-full-response-end true)) |
213 | 195 | frame (first out)] |
214 | 196 |
|
215 | 197 | ;; Verify intermediate state has accumulated all tokens |
216 | | - (get final-state :aggregation) => expected-response |
| 198 | + (get final-state :content-aggregation) => expected-response |
217 | 199 |
|
218 | 200 | ;; Verify final state and output |
219 | 201 | next-state => (state {:aggregating? false |
220 | | - :aggregation "" |
| 202 | + :content-aggregation nil |
| 203 | + :function-arguments nil |
| 204 | + :function-name nil |
| 205 | + :tool-call-id nil |
221 | 206 | :seen-end-frame? false |
222 | 207 | :seen-interim-results? false |
223 | 208 | :seen-start-frame? false |
224 | 209 | :llm/context {:messages [{:role "assistant" |
225 | 210 | :content "You are a helpful assistant"} |
226 | 211 | {:role "user" |
227 | 212 | :content "Hello there"} |
228 | | - {:role "assistant" |
229 | | - :content expected-response}]}}) |
| 213 | + {:role :assistant |
| 214 | + :content [{:text expected-response :type :text}]}]}}) |
230 | 215 |
|
231 | 216 | (:frame/type frame) => :frame.llm/context |
232 | 217 | (:frame/data frame) => {:messages [{:role "assistant" |
233 | 218 | :content "You are a helpful assistant"} |
234 | 219 | {:role "user" |
235 | 220 | :content "Hello there"} |
236 | | - {:role "assistant" |
237 | | - :content expected-response}]})) |
| 221 | + {:role :assistant |
| 222 | + :content [{:text expected-response :type :text}]}]})) |
238 | 223 |
|
239 | 224 | (fact "updates current context if a frame is received" |
240 | 225 | (let [new-context {:messages [{:content "You are a helpful assistant" :role :assistant} |
241 | 226 | {:content "Hello there" :role "user"} |
242 | 227 | {:content "How can I help" :role :assistant}]}] |
243 | | - (sut/aggregator-transform ststate nil (frame/llm-context new-context)) => [(assoc ststate :llm/context new-context)])))) |
| 228 | + (sut/assistant-aggregator-transform ststate nil (frame/llm-context new-context)) => [(assoc ststate :llm/context new-context)])))) |
0 commit comments