|
41 | 41 | (on-error {:exception e}))))) |
42 | 42 |
|
43 | 43 | (defn ^:private normalize-messages [past-messages] |
44 | | - (keep (fn [{:keys [role content] :as msg}] |
45 | | - (case role |
46 | | - "tool_call" {:type "function_call" |
47 | | - :name (:name content) |
48 | | - :call_id (:id content) |
49 | | - :arguments (json/generate-string (:arguments content))} |
50 | | - "tool_call_output" |
51 | | - {:type "function_call_output" |
52 | | - :call_id (:id content) |
53 | | - :output (llm-util/stringfy-tool-result content)} |
54 | | - "reason" nil |
55 | | - (update msg :content (fn [c] |
56 | | - (if (string? c) |
57 | | - c |
58 | | - (mapv #(if (= "text" (name (:type %))) |
59 | | - (assoc % :type (if (= "user" role) |
60 | | - "input_text" |
61 | | - "output_text")) |
62 | | - %) c)))))) |
63 | | - past-messages)) |
| 44 | + (keep-indexed (fn [i {:keys [role content] :as msg}] |
| 45 | + (case role |
| 46 | + "tool_call" {:type "function_call" |
| 47 | + :name (:name content) |
| 48 | + :call_id (:id content) |
| 49 | + :arguments (json/generate-string (:arguments content))} |
| 50 | + "tool_call_output" |
| 51 | + {:type "function_call_output" |
| 52 | + :call_id (:id content) |
| 53 | + :output (llm-util/stringfy-tool-result content)} |
| 54 | + ;; TODO include reason blocks |
| 55 | + "reason" nil |
| 56 | + (update msg :content (fn [c] |
| 57 | + (if (string? c) |
| 58 | + c |
| 59 | + (mapv #(if (= "text" (name (:type %))) |
| 60 | + (assoc % :type (if (= "user" role) |
| 61 | + "input_text" |
| 62 | + "output_text")) |
| 63 | + %) c)))))) |
| 64 | + past-messages)) |
64 | 65 |
|
65 | | -(defn completion! [{:keys [model user-messages instructions temperature api-key api-url |
66 | | - max-output-tokens past-messages tools web-search] |
67 | | - :or {temperature 1.0}} |
| 66 | +(defn completion! [{:keys [model user-messages instructions reason? temperature api-key api-url |
| 67 | + max-output-tokens past-messages tools web-search]} |
68 | 68 | {:keys [on-message-received on-error on-prepare-tool-call on-tool-called on-reason]}] |
69 | 69 | (let [input (concat (normalize-messages past-messages) |
70 | 70 | (normalize-messages user-messages)) |
|
76 | 76 | ;; TODO support parallel |
77 | 77 | :parallel_tool_calls false |
78 | 78 | :instructions instructions |
| 79 | + ;; TODO allow user specify custom temperature (default 1.0) |
79 | 80 | :temperature temperature |
80 | 81 | :tools tools |
| 82 | + :reasoning (when reason? |
| 83 | + {:effort "medium" |
| 84 | + :summary "detailed"}) |
81 | 85 | :stream true |
82 | 86 | :max_output_tokens max-output-tokens} |
83 | 87 | mcp-call-by-item-id* (atom {}) |
|
111 | 115 | :on-response handle-response}) |
112 | 116 | (swap! mcp-call-by-item-id* dissoc (-> data :item :id))) |
113 | 117 | "reasoning" (on-reason {:status :finished |
114 | | - :id (-> data :item :id)}) |
| 118 | + :id (-> data :item :id) |
| 119 | + :external-id (-> data :item :id)}) |
115 | 120 | nil) |
116 | 121 |
|
117 | 122 | ;; URL mentioned |
|
124 | 129 | nil) |
125 | 130 |
|
126 | 131 | ;; reasoning / tools |
| 132 | + "response.reasoning_summary_text.delta" |
| 133 | + (on-reason {:status :thinking |
| 134 | + :id (:item_id data) |
| 135 | + :text (:delta data)}) |
| 136 | + |
127 | 137 | "response.output_item.added" |
128 | 138 | (case (-> data :item :type) |
129 | 139 | "reasoning" (on-reason {:status :started |
|
0 commit comments