|
70 | 70 | (let [tools-to-call (->> (:choices body) |
71 | 71 | (mapcat (comp :tool_calls :message)) |
72 | 72 | (map (fn [tool-call] |
73 | | - {:id (:id tool-call) |
74 | | - :full-name (:name (:function tool-call)) |
75 | | - :arguments (json/parse-string (:arguments (:function tool-call)))})))] |
| 73 | + (cond-> {:id (:id tool-call) |
| 74 | + :full-name (:name (:function tool-call)) |
| 75 | + :arguments (json/parse-string (:arguments (:function tool-call)))} |
| 76 | + ;; Preserve Google Gemini thought signatures |
| 77 | + (get-in tool-call [:extra_content :google :thought_signature]) |
| 78 | + (assoc :thought-signature |
| 79 | + (get-in tool-call [:extra_content :google :thought_signature]))))))] |
76 | 80 | {:usage (parse-usage (:usage body)) |
77 | 81 | :reason-id (str (random-uuid)) |
78 | 82 | :tools-to-call tools-to-call |
|
129 | 133 |
|
130 | 134 | (defn ^:private transform-message |
131 | 135 | "Transform a single ECA message to OpenAI format. Returns nil for unsupported roles." |
132 | | - [{:keys [role content] :as _msg} supports-image? think-tag-start think-tag-end] |
| 136 | + [{:keys [role content] :as _msg} supports-image? think-tag-start think-tag-end skip-thought-signature-validator?] |
133 | 137 | (case role |
134 | 138 | "tool_call" {:type :tool-call ; Special marker for accumulation |
135 | | - :data {:id (:id content) |
136 | | - :type "function" |
137 | | - :function {:name (:full-name content) |
138 | | - :arguments (json/generate-string (:arguments content))}}} |
| 139 | + :data (cond-> {:id (:id content) |
| 140 | + :type "function" |
| 141 | + :function {:name (:full-name content) |
| 142 | + :arguments (json/generate-string (:arguments content))}} |
| 143 | + ;; Preserve Google Gemini thought signatures if present |
| 144 | + (:thought-signature content) |
| 145 | + (assoc-in [:extra_content :google :thought_signature] |
| 146 | + (:thought-signature content)) |
| 147 | + ;; Use bypass signature when thought signature is missing and bypass is enabled |
| 148 | + (and skip-thought-signature-validator? |
| 149 | + (not (:thought-signature content))) |
| 150 | + (assoc-in [:extra_content :google :thought_signature] |
| 151 | + "skip_thought_signature_validator"))} |
139 | 152 | "tool_call_output" {:role "tool" |
140 | 153 | :tool_call_id (:id content) |
141 | 154 | :content (llm-util/stringfy-tool-result content)} |
|
196 | 209 | 'assistant' role message, not as separate messages. This function ensures compliance |
197 | 210 | with that requirement by accumulating tool calls and flushing them into assistant |
198 | 211 | messages when a non-tool_call message is encountered." |
199 | | - [messages supports-image? think-tag-start think-tag-end] |
| 212 | + [messages supports-image? think-tag-start think-tag-end skip-thought-signature-validator?] |
200 | 213 | (->> messages |
201 | | - (map #(transform-message % supports-image? think-tag-start think-tag-end)) |
| 214 | + (map #(transform-message % supports-image? think-tag-start think-tag-end skip-thought-signature-validator?)) |
202 | 215 | (remove nil?) |
203 | 216 | accumulate-tool-calls |
204 | 217 | (filter valid-message?))) |
|
295 | 308 | Compatible with OpenRouter and other OpenAI-compatible providers." |
296 | 309 | [{:keys [model user-messages instructions temperature api-key api-url url-relative-path |
297 | 310 | past-messages tools extra-payload extra-headers supports-image? |
298 | | - think-tag-start think-tag-end http-client]} |
| 311 | + think-tag-start think-tag-end skip-thought-signature-validator? http-client]} |
299 | 312 | {:keys [on-message-received on-error on-prepare-tool-call on-tools-called on-reason on-usage-updated] :as callbacks}] |
300 | 313 | (let [think-tag-start (or think-tag-start "<think>") |
301 | 314 | think-tag-end (or think-tag-end "</think>") |
302 | 315 | stream? (boolean callbacks) |
303 | 316 | messages (vec (concat |
304 | 317 | (when instructions [{:role "system" :content instructions}]) |
305 | | - (normalize-messages past-messages supports-image? think-tag-start think-tag-end) |
306 | | - (normalize-messages user-messages supports-image? think-tag-start think-tag-end))) |
| 318 | + (normalize-messages past-messages supports-image? think-tag-start think-tag-end skip-thought-signature-validator?) |
| 319 | + (normalize-messages user-messages supports-image? think-tag-start think-tag-end skip-thought-signature-validator?))) |
307 | 320 |
|
308 | 321 | body (deep-merge |
309 | 322 | (assoc-some |
|
333 | 346 | (when-let [{:keys [new-messages]} (on-tools-called tools-to-call)] |
334 | 347 | (let [new-messages-list (vec (concat |
335 | 348 | (when instructions [{:role "system" :content instructions}]) |
336 | | - (normalize-messages new-messages supports-image? think-tag-start think-tag-end))) |
| 349 | + (normalize-messages new-messages supports-image? think-tag-start think-tag-end skip-thought-signature-validator?))) |
337 | 350 | new-rid (llm-util/gen-rid)] |
338 | 351 | (reset! tool-calls* {}) |
339 | 352 | (base-chat-request! |
|
406 | 419 | (on-message-received {:type :text :text buf})) |
407 | 420 | (reset! content-buffer* ""))) |
408 | 421 | (doseq [tool-call (:tool_calls delta)] |
409 | | - (let [{:keys [index id function]} tool-call |
| 422 | + (let [{:keys [index id function extra_content]} tool-call |
410 | 423 | {name :name args :arguments} function |
| 424 | + ;; Extract Google Gemini thought signature if present |
| 425 | + thought-signature (get-in extra_content [:google :thought_signature]) |
411 | 426 | ;; Use RID as key to avoid collisions between API requests |
412 | 427 | tool-key (str rid "-" index) |
413 | 428 | ;; Create globally unique tool call ID for client |
|
421 | 436 | (cond-> (or existing {:index index}) |
422 | 437 | unique-id (assoc :id unique-id) |
423 | 438 | name (assoc :full-name name) |
424 | | - args (update :arguments-text (fnil str "") args)))) |
| 439 | + args (update :arguments-text (fnil str "") args) |
| 440 | + ;; Store thought signature for Google Gemini |
| 441 | + thought-signature (assoc :thought-signature thought-signature)))) |
425 | 442 | (when-let [updated-tool-call (get @tool-calls* tool-key)] |
426 | 443 | (when (and (:id updated-tool-call) |
427 | 444 | (:full-name updated-tool-call) |
|
0 commit comments