|
126 | 126 |
|
127 | 127 | (defn ^:private transform-message |
128 | 128 | "Transform a single ECA message to OpenAI format. Returns nil for unsupported roles." |
129 | | - [{:keys [role content] :as _msg} supports-image? thinking-start-tag thinking-end-tag] |
| 129 | + [{:keys [role content] :as _msg} supports-image? think-tag-start think-tag-end] |
130 | 130 | (case role |
131 | 131 | "tool_call" {:type :tool-call ; Special marker for accumulation |
132 | 132 | :data {:id (:id content) |
|
140 | 140 | :content (extract-content content supports-image?)} |
141 | 141 | "reason" {:role "assistant" |
142 | 142 | :content [{:type "text" |
143 | | - :text (str thinking-start-tag (:text content) thinking-end-tag)}]} |
| 143 | + :text (str think-tag-start (:text content) think-tag-end)}]} |
144 | 144 | "assistant" {:role "assistant" |
145 | 145 | :content (extract-content content supports-image?)} |
146 | 146 | "system" {:role "system" |
|
193 | 193 | 'assistant' role message, not as separate messages. This function ensures compliance |
194 | 194 | with that requirement by accumulating tool calls and flushing them into assistant |
195 | 195 | messages when a non-tool_call message is encountered." |
196 | | - [messages supports-image? thinking-start-tag thinking-end-tag] |
| 196 | + [messages supports-image? think-tag-start think-tag-end] |
197 | 197 | (->> messages |
198 | | - (map #(transform-message % supports-image? thinking-start-tag thinking-end-tag)) |
| 198 | + (map #(transform-message % supports-image? think-tag-start think-tag-end)) |
199 | 199 | (remove nil?) |
200 | 200 | accumulate-tool-calls |
201 | 201 | (filter valid-message?))) |
|
228 | 228 | - Inside thinking: emit reasoning up to </think> and keep a small tail to detect split tags |
229 | 229 | - When a tag boundary is found, open/close the reasoning block accordingly" |
230 | 230 | [text content-buffer* reasoning-type* current-reason-id* |
231 | | - reasoning-started* thinking-start-tag thinking-end-tag on-message-received on-reason] |
232 | | - (let [start-len (count thinking-start-tag) |
233 | | - end-len (count thinking-end-tag) |
| 231 | + reasoning-started* think-tag-start think-tag-end on-message-received on-reason] |
| 232 | + (let [start-len (count think-tag-start) |
| 233 | + end-len (count think-tag-end) |
234 | 234 | ;; Keep a small tail to detect tags split across chunk boundaries. |
235 | 235 | start-tail (max 0 (dec start-len)) |
236 | 236 | end-tail (max 0 (dec end-len)) |
|
258 | 258 | (let [^String buf @content-buffer*] |
259 | 259 | (if (= @reasoning-type* :tag) |
260 | 260 | ;; Inside a thinking block; look for end tag |
261 | | - (let [idx (.indexOf buf ^String thinking-end-tag)] |
| 261 | + (let [idx (.indexOf buf ^String think-tag-end)] |
262 | 262 | (if (>= idx 0) |
263 | 263 | (let [before (.substring buf 0 idx) |
264 | 264 | after (.substring buf (+ idx end-len))] |
|
271 | 271 | (emit-think! (.substring buf 0 emit-len)) |
272 | 272 | (reset! content-buffer* (.substring buf emit-len)))))) |
273 | 273 | ;; Outside a thinking block; look for start tag |
274 | | - (let [idx (.indexOf buf ^String thinking-start-tag)] |
| 274 | + (let [idx (.indexOf buf ^String think-tag-start)] |
275 | 275 | (if (>= idx 0) |
276 | 276 | (let [before (.substring buf 0 idx) |
277 | 277 | after (.substring buf (+ idx start-len))] |
|
292 | 292 | Compatible with OpenRouter and other OpenAI-compatible providers." |
293 | 293 | [{:keys [model user-messages instructions temperature api-key api-url url-relative-path |
294 | 294 | past-messages tools extra-payload extra-headers supports-image? |
295 | | - thinking-tag] |
296 | | - :or {thinking-tag "think"}} |
| 295 | + think-tag-start think-tag-end] |
| 296 | + :or {think-tag-start "<think>" |
| 297 | + think-tag-end "</think>"}} |
297 | 298 | {:keys [on-message-received on-error on-prepare-tool-call on-tools-called on-reason on-usage-updated] :as callbacks}] |
298 | | - (let [thinking-start-tag (str "<" thinking-tag ">") |
299 | | - thinking-end-tag (str "</" thinking-tag ">") |
300 | | - stream? (boolean callbacks) |
| 299 | + (let [stream? (boolean callbacks) |
301 | 300 | messages (vec (concat |
302 | 301 | (when instructions [{:role "system" :content instructions}]) |
303 | | - (normalize-messages past-messages supports-image? thinking-start-tag thinking-end-tag) |
304 | | - (normalize-messages user-messages supports-image? thinking-start-tag thinking-end-tag))) |
| 302 | + (normalize-messages past-messages supports-image? think-tag-start think-tag-end) |
| 303 | + (normalize-messages user-messages supports-image? think-tag-start think-tag-end))) |
305 | 304 |
|
306 | 305 | body (deep-merge |
307 | 306 | (assoc-some |
|
331 | 330 | (when-let [{:keys [new-messages]} (on-tools-called tools-to-call)] |
332 | 331 | (let [new-messages-list (vec (concat |
333 | 332 | (when instructions [{:role "system" :content instructions}]) |
334 | | - (normalize-messages new-messages supports-image? thinking-start-tag thinking-end-tag))) |
| 333 | + (normalize-messages new-messages supports-image? think-tag-start think-tag-end))) |
335 | 334 | new-rid (llm-util/gen-rid)] |
336 | 335 | (reset! tool-calls* {}) |
337 | 336 | (base-chat-request! |
|
366 | 365 | ;; Process content if present (with thinking blocks support) |
367 | 366 | (when-let [ct (:content delta)] |
368 | 367 | (process-text-think-aware ct content-buffer* reasoning-type* current-reason-id* reasoning-started* |
369 | | - thinking-start-tag thinking-end-tag on-message-received on-reason)) |
| 368 | + think-tag-start think-tag-end on-message-received on-reason)) |
370 | 369 |
|
371 | 370 | ;; Process reasoning if present (o1 models and compatible providers) |
372 | 371 | (when-let [reasoning-text (or (:reasoning delta) |
|
0 commit comments