Skip to content

Commit 69eebb2

Browse files
authored
Merge pull request #180 from editor-code-assistant/non-stream-support
Non stream support
2 parents ac36460 + 22d5435 commit 69eebb2

File tree

10 files changed

+569
-499
lines changed

10 files changed

+569
-499
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Improved `eca_edit_file` to automatically handle whitespace and indentation differences in single-occurrence edits.
66
- Fix contexts in user prompts (not system contexts) not parsing lines ranges properly.
7+
- Support non-stream providers on openai-chat API. #174
78

89
## 0.73.5
910

src/eca/features/chat.clj

Lines changed: 286 additions & 283 deletions
Large diffs are not rendered by default.

src/eca/llm_api.clj

Lines changed: 183 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -93,140 +93,138 @@
9393
:on-tools-called on-tools-called
9494
:on-reason on-reason
9595
:on-usage-updated on-usage-updated})]
96-
;; We spawn a new future to not block the lsp4clj thread
97-
;; in case a tool call approval is needed
98-
(future
99-
(try
100-
(when-not api-url (throw (ex-info (format "API url not found.\nMake sure you have provider '%s' configured properly." provider) {})))
101-
(cond
102-
(= "openai" provider)
103-
(llm-providers.openai/create-response!
104-
{:model real-model
105-
:instructions instructions
106-
:user-messages user-messages
107-
:max-output-tokens max-output-tokens
108-
:reason? reason?
109-
:supports-image? supports-image?
110-
:past-messages past-messages
111-
:tools tools
112-
:web-search web-search
113-
:extra-payload (merge {:parallel_tool_calls true}
114-
extra-payload)
115-
:api-url api-url
116-
:api-key api-key
117-
:auth-type auth-type}
118-
callbacks)
96+
(try
97+
(when-not api-url (throw (ex-info (format "API url not found.\nMake sure you have provider '%s' configured properly." provider) {})))
98+
(cond
99+
(= "openai" provider)
100+
(llm-providers.openai/create-response!
101+
{:model real-model
102+
:instructions instructions
103+
:user-messages user-messages
104+
:max-output-tokens max-output-tokens
105+
:reason? reason?
106+
:supports-image? supports-image?
107+
:past-messages past-messages
108+
:tools tools
109+
:web-search web-search
110+
:extra-payload (merge {:parallel_tool_calls true}
111+
extra-payload)
112+
:api-url api-url
113+
:api-key api-key
114+
:auth-type auth-type}
115+
callbacks)
119116

120-
(= "anthropic" provider)
121-
(llm-providers.anthropic/chat!
122-
{:model real-model
123-
:instructions instructions
124-
:user-messages user-messages
125-
:max-output-tokens max-output-tokens
126-
:reason? reason?
127-
:supports-image? supports-image?
128-
:past-messages past-messages
129-
:tools tools
130-
:web-search web-search
131-
:extra-payload extra-payload
132-
:api-url api-url
133-
:api-key api-key
134-
:auth-type auth-type}
135-
callbacks)
117+
(= "anthropic" provider)
118+
(llm-providers.anthropic/chat!
119+
{:model real-model
120+
:instructions instructions
121+
:user-messages user-messages
122+
:max-output-tokens max-output-tokens
123+
:reason? reason?
124+
:supports-image? supports-image?
125+
:past-messages past-messages
126+
:tools tools
127+
:web-search web-search
128+
:extra-payload extra-payload
129+
:api-url api-url
130+
:api-key api-key
131+
:auth-type auth-type}
132+
callbacks)
136133

137-
(= "github-copilot" provider)
138-
(llm-providers.openai-chat/chat-completion!
139-
{:model real-model
140-
:instructions instructions
141-
:user-messages user-messages
142-
:max-output-tokens max-output-tokens
143-
:reason? reason?
144-
:supports-image? supports-image?
145-
:past-messages past-messages
146-
:tools tools
147-
:extra-payload (merge {:parallel_tool_calls true}
148-
extra-payload)
149-
:api-url api-url
150-
:api-key api-key
151-
:extra-headers {"openai-intent" "conversation-panel"
152-
"x-request-id" (str (random-uuid))
153-
"vscode-sessionid" ""
154-
"vscode-machineid" ""
155-
"Copilot-Vision-Request" "true"
156-
"copilot-integration-id" "vscode-chat"}}
157-
callbacks)
134+
(= "github-copilot" provider)
135+
(llm-providers.openai-chat/chat-completion!
136+
{:model real-model
137+
:instructions instructions
138+
:user-messages user-messages
139+
:max-output-tokens max-output-tokens
140+
:reason? reason?
141+
:supports-image? supports-image?
142+
:past-messages past-messages
143+
:tools tools
144+
:extra-payload (merge {:parallel_tool_calls true}
145+
extra-payload)
146+
:api-url api-url
147+
:api-key api-key
148+
:extra-headers {"openai-intent" "conversation-panel"
149+
"x-request-id" (str (random-uuid))
150+
"vscode-sessionid" ""
151+
"vscode-machineid" ""
152+
"Copilot-Vision-Request" "true"
153+
"copilot-integration-id" "vscode-chat"}}
154+
callbacks)
155+
156+
(= "google" provider)
157+
(llm-providers.openai-chat/chat-completion!
158+
{:model real-model
159+
:instructions instructions
160+
:user-messages user-messages
161+
:max-output-tokens max-output-tokens
162+
:reason? reason?
163+
:supports-image? supports-image?
164+
:past-messages past-messages
165+
:tools tools
166+
:thinking-tag "thought"
167+
:extra-payload (merge {:parallel_tool_calls false}
168+
(when reason?
169+
{:extra_body {:google {:thinking_config {:include_thoughts true}}}})
170+
extra-payload)
171+
:api-url api-url
172+
:api-key api-key}
173+
callbacks)
174+
175+
(= "ollama" provider)
176+
(llm-providers.ollama/chat!
177+
{:api-url api-url
178+
:reason? (:reason? model-capabilities)
179+
:supports-image? supports-image?
180+
:model real-model
181+
:instructions instructions
182+
:user-messages user-messages
183+
:past-messages past-messages
184+
:tools tools
185+
:extra-payload extra-payload}
186+
callbacks)
158187

159-
(= "google" provider)
160-
(llm-providers.openai-chat/chat-completion!
188+
model-config
189+
(let [provider-fn (case (:api provider-config)
190+
("openai-responses"
191+
"openai") llm-providers.openai/create-response!
192+
"anthropic" llm-providers.anthropic/chat!
193+
"openai-chat" llm-providers.openai-chat/chat-completion!
194+
(on-error {:message (format "Unknown model %s for provider %s" (:api provider-config) provider)}))
195+
url-relative-path (:completionUrlRelativePath provider-config)]
196+
(provider-fn
161197
{:model real-model
162198
:instructions instructions
163199
:user-messages user-messages
164200
:max-output-tokens max-output-tokens
201+
:web-search web-search
165202
:reason? reason?
166203
:supports-image? supports-image?
167204
:past-messages past-messages
168205
:tools tools
169-
:thinking-tag "thought"
170-
:extra-payload (merge {:parallel_tool_calls false}
171-
(when reason?
172-
{:extra_body {:google {:thinking_config {:include_thoughts true}}}})
173-
extra-payload)
206+
:extra-payload extra-payload
207+
:url-relative-path url-relative-path
174208
:api-url api-url
175209
:api-key api-key}
176-
callbacks)
210+
callbacks))
177211

178-
(= "ollama" provider)
179-
(llm-providers.ollama/chat!
180-
{:api-url api-url
181-
:reason? (:reason? model-capabilities)
182-
:supports-image? supports-image?
183-
:model real-model
184-
:instructions instructions
185-
:user-messages user-messages
186-
:past-messages past-messages
187-
:tools tools
188-
:extra-payload extra-payload}
189-
callbacks)
190-
191-
model-config
192-
(let [provider-fn (case (:api provider-config)
193-
("openai-responses"
194-
"openai") llm-providers.openai/create-response!
195-
"anthropic" llm-providers.anthropic/chat!
196-
"openai-chat" llm-providers.openai-chat/chat-completion!
197-
(on-error {:message (format "Unknown model %s for provider %s" (:api provider-config) provider)}))
198-
url-relative-path (:completionUrlRelativePath provider-config)]
199-
(provider-fn
200-
{:model real-model
201-
:instructions instructions
202-
:user-messages user-messages
203-
:max-output-tokens max-output-tokens
204-
:web-search web-search
205-
:reason? reason?
206-
:supports-image? supports-image?
207-
:past-messages past-messages
208-
:tools tools
209-
:extra-payload extra-payload
210-
:url-relative-path url-relative-path
211-
:api-url api-url
212-
:api-key api-key}
213-
callbacks))
212+
:else
213+
(on-error {:message (format "ECA Unsupported model %s for provider %s" real-model provider)}))
214+
(catch Exception e
215+
(on-error {:exception e})))))
214216

215-
:else
216-
(on-error {:message (format "ECA Unsupported model %s for provider %s" real-model provider)}))
217-
(catch Exception e
218-
(on-error {:exception e}))))))
219-
220-
(defn async-prompt! [{:keys [provider model model-capabilities instructions user-messages config on-first-response-received
221-
on-message-received on-error on-prepare-tool-call on-tools-called on-reason on-usage-updated
222-
past-messages tools provider-auth]
223-
:or {on-first-response-received identity
224-
on-message-received identity
225-
on-error identity
226-
on-prepare-tool-call identity
227-
on-tools-called identity
228-
on-reason identity
229-
on-usage-updated identity}}]
217+
(defn sync-or-async-prompt!
218+
[{:keys [provider model model-capabilities instructions user-messages config on-first-response-received
219+
on-message-received on-error on-prepare-tool-call on-tools-called on-reason on-usage-updated
220+
past-messages tools provider-auth]
221+
:or {on-first-response-received identity
222+
on-message-received identity
223+
on-error identity
224+
on-prepare-tool-call identity
225+
on-tools-called identity
226+
on-reason identity
227+
on-usage-updated identity}}]
230228
(let [first-response-received* (atom false)
231229
emit-first-message-fn (fn [& args]
232230
(when-not @first-response-received*
@@ -244,38 +242,73 @@
244242
on-error-wrapper (fn [{:keys [exception] :as args}]
245243
(when-not (:silent? (ex-data exception))
246244
(logger/error args)
247-
(on-error args)))]
248-
(prompt!
249-
{:sync? false
250-
:provider provider
251-
:model model
252-
:model-capabilities model-capabilities
253-
:instructions instructions
254-
:tools tools
255-
:provider-auth provider-auth
256-
:past-messages past-messages
257-
:user-messages user-messages
258-
:on-message-received on-message-received-wrapper
259-
:on-prepare-tool-call on-prepare-tool-call-wrapper
260-
:on-tools-called on-tools-called
261-
:on-usage-updated on-usage-updated
262-
:on-reason on-reason-wrapper
263-
:on-error on-error-wrapper
264-
:config config})))
245+
(on-error args)))
246+
provider-config (get-in config [:providers provider])
247+
model-config (get-in provider-config [:models model])
248+
extra-payload (:extraPayload model-config)
249+
stream? (if (not (nil? (:stream extra-payload)))
250+
(:stream extra-payload)
251+
true)]
252+
(if (not stream?)
253+
(loop [result (prompt!
254+
{:sync? true
255+
:provider provider
256+
:model model
257+
:model-capabilities model-capabilities
258+
:instructions instructions
259+
:tools tools
260+
:provider-auth provider-auth
261+
:past-messages past-messages
262+
:user-messages user-messages
263+
:on-error on-error-wrapper
264+
:config config})]
265+
(let [{:keys [error output-text reason-text tools-to-call call-tools-fn reason-id usage]} result]
266+
(if error
267+
(on-error-wrapper error)
268+
(do
269+
(when reason-text
270+
(on-reason-wrapper {:status :started :id reason-id})
271+
(on-reason-wrapper {:status :thinking :id reason-id :text reason-text})
272+
(on-reason-wrapper {:status :finished :id reason-id}))
273+
(on-message-received-wrapper {:type :text :text output-text})
274+
(some-> usage (on-usage-updated))
275+
(if-let [new-result (when (seq tools-to-call)
276+
(doseq [tool-to-call tools-to-call]
277+
(on-prepare-tool-call tool-to-call))
278+
(call-tools-fn on-tools-called))]
279+
(recur new-result)
280+
(on-message-received-wrapper {:type :finish :finish-reason "stop"}))))))
281+
(prompt!
282+
{:sync? false
283+
:provider provider
284+
:model model
285+
:model-capabilities model-capabilities
286+
:instructions instructions
287+
:tools tools
288+
:provider-auth provider-auth
289+
:past-messages past-messages
290+
:user-messages user-messages
291+
:on-message-received on-message-received-wrapper
292+
:on-prepare-tool-call on-prepare-tool-call-wrapper
293+
:on-tools-called on-tools-called
294+
:on-usage-updated on-usage-updated
295+
:on-reason on-reason-wrapper
296+
:on-error on-error-wrapper
297+
:config config}))))
265298

266299
(defn sync-prompt!
267300
[{:keys [provider model model-capabilities instructions
268301
prompt past-messages user-messages config tools provider-auth]}]
269-
@(prompt!
270-
{:sync? true
271-
:provider provider
272-
:model model
273-
:model-capabilities model-capabilities
274-
:instructions instructions
275-
:tools tools
276-
:provider-auth provider-auth
277-
:past-messages past-messages
278-
:user-messages (or user-messages
279-
[{:role "user" :content [{:type :text :text prompt}]}])
280-
:config config
281-
:on-error (fn [error] {:error error})}))
302+
(prompt!
303+
{:sync? true
304+
:provider provider
305+
:model model
306+
:model-capabilities model-capabilities
307+
:instructions instructions
308+
:tools tools
309+
:provider-auth provider-auth
310+
:past-messages past-messages
311+
:user-messages (or user-messages
312+
[{:role "user" :content [{:type :text :text prompt}]}])
313+
:config config
314+
:on-error (fn [error] {:error error})}))

src/eca/llm_providers/anthropic.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@
103103
(do
104104
(llm-util/log-response logger-tag rid "response" body)
105105
(reset! response*
106-
{:result (:text (last (:content body)))}))))
106+
{:output-text (:text (last (:content body)))}))))
107107
(catch Exception e
108108
(on-error {:exception e}))))
109109
(fn [e]

src/eca/llm_providers/ollama.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
(do
8282
(llm-util/log-response logger-tag rid "response" body)
8383
(reset! response*
84-
{:result (:content (:message body))}))))
84+
{:output-text (:content (:message body))}))))
8585
(catch Exception e
8686
(on-error {:exception e}))))
8787
(fn [e]

0 commit comments

Comments
 (0)