Skip to content

Commit 3a13f43

Browse files
committed
Fix tests
1 parent 11a83cb commit 3a13f43

File tree

7 files changed

+252
-257
lines changed

7 files changed

+252
-257
lines changed

integration-test/integration/initialize_test.clj

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
[clojure.test :refer [deftest is testing]]
44
[integration.eca :as eca]
55
[integration.fixture :as fixture]
6-
[matcher-combinators.test :refer [match?]]
7-
[matcher-combinators.matchers :as m]))
6+
[matcher-combinators.matchers :as m]
7+
[matcher-combinators.test :refer [match?]]))
88

99
(eca/clean-after-test)
1010

@@ -13,17 +13,22 @@
1313

1414
(testing "initialize request with default config"
1515
(is (match?
16-
{:models ["claude-3-5-haiku-20241022"
17-
"claude-opus-4-1-20250805"
18-
"claude-opus-4-20250514"
19-
"claude-sonnet-4-20250514"
20-
"gpt-4.1"
21-
"gpt-5"
22-
"gpt-5-mini"
23-
"gpt-5-nano"
24-
"o3"
25-
"o4-mini"]
26-
:chatDefaultModel "claude-sonnet-4-20250514"
16+
{:models ["anthropic/claude-3-5-haiku-20241022"
17+
"anthropic/claude-opus-4-1-20250805"
18+
"anthropic/claude-opus-4-20250514"
19+
"anthropic/claude-sonnet-4-20250514"
20+
"github-copilot/gpt-5-mini"
21+
"github-copilot/gpt-4.1"
22+
"github-copilot/gpt-4o"
23+
"github-copilot/claude-3.5-sonnet"
24+
"github-copilot/gemini-2.0-flash-001"
25+
"openai/gpt-4.1"
26+
"openai/gpt-5"
27+
"openai/gpt-5-mini"
28+
"openai/gpt-5-nano"
29+
"openai/o3"
30+
"openai/o4-mini"]
31+
:chatDefaultModel "anthropic/claude-sonnet-4-20250514"
2732
:chatBehaviors ["agent" "plan"]
2833
:chatDefaultBehavior "plan"
2934
:chatWelcomeMessage "Welcome to ECA!\n\nType '/' for commands\n\n"}
@@ -54,18 +59,23 @@
5459
(eca/start-process!)
5560
(testing "initialize request with custom providers"
5661
(is (match?
57-
{:models ["claude-3-5-haiku-20241022"
58-
"claude-opus-4-1-20250805"
59-
"claude-opus-4-20250514"
60-
"claude-sonnet-4-20250514"
61-
"gpt-4.1"
62-
"gpt-5"
63-
"gpt-5-mini"
64-
"gpt-5-nano"
62+
{:models ["anthropic/claude-3-5-haiku-20241022"
63+
"anthropic/claude-opus-4-1-20250805"
64+
"anthropic/claude-opus-4-20250514"
65+
"anthropic/claude-sonnet-4-20250514"
66+
"github-copilot/gpt-5-mini"
67+
"github-copilot/gpt-4.1"
68+
"github-copilot/gpt-4o"
69+
"github-copilot/claude-3.5-sonnet"
70+
"github-copilot/gemini-2.0-flash-001"
6571
"myCustom/bar-2"
6672
"myCustom/foo-1"
67-
"o3"
68-
"o4-mini"]
73+
"openai/gpt-4.1"
74+
"openai/gpt-5"
75+
"openai/gpt-5-mini"
76+
"openai/gpt-5-nano"
77+
"openai/o3"
78+
"openai/o4-mini"]
6979
:chatDefaultModel "myCustom/bar-2"
7080
:chatBehaviors ["agent" "plan"]
7181
:chatDefaultBehavior "agent"

src/eca/features/chat.clj

Lines changed: 155 additions & 148 deletions
Large diffs are not rendered by default.

src/eca/features/login.clj

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,40 @@
11
(ns eca.features.login
22
(:require
33
[eca.db :as db]
4-
[eca.llm-api :as llm-api]
4+
[eca.llm-providers.copilot :as llm-providers.copilot]
55
[eca.messenger :as messenger]))
66

77
(defn start-login [chat-id provider db*]
8-
(let [{:keys [error-message auth-type] :as result} (llm-api/auth-start {:provider provider})]
9-
(cond
10-
error-message
11-
(do
12-
(swap! db* assoc-in [:chats chat-id :status] :idle)
13-
{:error true
14-
:message error-message})
15-
16-
(= :oauth/simple auth-type)
17-
(do
18-
(swap! db* assoc-in [:chats chat-id :login-provider] provider)
19-
(swap! db* assoc-in [:auth provider] {:step :login/waiting-user-confirmation
20-
:device-code (:device-code result)})
21-
{:message (format "Open your browser at `%s` and authenticate using the code: `%s`\nThen type anything in the chat and send it to continue the authentication."
22-
(:url result)
23-
(:user-code result))}))))
8+
(case provider
9+
"github-copilot"
10+
(let [{:keys [user-code device-code url]} (llm-providers.copilot/oauth-url)]
11+
(swap! db* assoc-in [:chats chat-id :login-provider] provider)
12+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-user-confirmation
13+
:device-code device-code})
14+
{:message (format "Open your browser at `%s` and authenticate using the code: `%s`\nThen type anything in the chat and send it to continue the authentication."
15+
url
16+
user-code)})))
2417

2518
(defn continue [{:keys [chat-id request-id]} db* messenger]
2619
(let [provider (get-in @db* [:chats chat-id :login-provider])
2720
step (get-in @db* [:auth provider :step])]
2821
(case step
2922
:login/waiting-user-confirmation
3023
(case provider
31-
"github-copilot" (let [{:keys [api-token expires-at error-message]} (llm-api/auth-continue {:provider provider
32-
:db* db*})
33-
msg (or error-message "Login successful! You can now use the 'github-copilot' models.")]
34-
(when-not error-message
35-
(swap! db* update-in [:auth provider] merge {:step :login/done
36-
:api-token api-token
37-
:expires-at expires-at}))
24+
"github-copilot" (let [access-token (llm-providers.copilot/oauth-access-token (get-in @db* [:auth provider :device-code]))
25+
{:keys [api-token expires-at]} (llm-providers.copilot/oauth-renew-token access-token)]
26+
(swap! db* update-in [:auth provider] merge {:step :login/done
27+
:access-token access-token
28+
:api-token api-token
29+
:expires-at expires-at})
3830
(swap! db* update-in [:chats chat-id :status] :idle)
3931
(messenger/chat-content-received
4032
messenger
4133
{:chat-id chat-id
4234
:request-id request-id
4335
:role "system"
4436
:content {:type :text
45-
:text msg}})
37+
:text "Login successful! You can now use the 'github-copilot' models."}})
4638
(messenger/chat-content-received
4739
messenger
4840
{:chat-id chat-id
@@ -51,3 +43,11 @@
5143
:content {:type :progress
5244
:state :finished}}))))
5345
(db/update-workspaces-cache! @db*)))
46+
47+
(defn renew-auth! [provider db*]
48+
(case provider
49+
"github-copilot"
50+
(let [access-token (get-in @db* [:auth provider :access-token])
51+
{:keys [api-token expires-at]} (llm-providers.copilot/oauth-renew-token access-token)]
52+
(swap! db* update-in [:auth provider] merge {:api-token api-token
53+
:expires-at expires-at}))))

src/eca/llm_api.clj

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,8 @@
204204
:extra-payload extra-payload}
205205
callbacks)
206206

207-
(contains? custom-models model)
208-
(let [[provider model] (string/split model #"/" 2)
209-
provider-config (get custom-providers (keyword provider))
207+
(contains? custom-models (str provider "/" model))
208+
(let [provider-config (get custom-providers (keyword provider))
210209
provider-fn (case (:api provider-config)
211210
("openai-responses"
212211
"openai") llm-providers.openai/completion!
@@ -234,26 +233,3 @@
234233
(on-error-wrapper {:message (str "ECA Unsupported model: " model)}))
235234
(catch Exception e
236235
(on-error-wrapper {:exception e})))))
237-
238-
(defn auth-start [{:keys [provider]}]
239-
(try
240-
(case provider
241-
"github-copilot" (let [auth (llm-providers.copilot/auth-url)]
242-
{:auth-type :oauth/simple
243-
:url (:url auth)
244-
:device-code (:device-code auth)
245-
:user-code (:user-code auth)})
246-
{:error-message (str "Unknown provider: " provider)})
247-
(catch Exception e
248-
{:error-message (format "Error log into provider %s: %s" provider (.getMessage e))})))
249-
250-
(defn auth-continue [{:keys [provider db*]}]
251-
(try
252-
(case provider
253-
"github-copilot" (let [{:keys [api-token expires-at]} (llm-providers.copilot/auth-exchange (get-in @db* [:auth provider :device-code]))]
254-
{:api-token api-token
255-
:expires-at expires-at})
256-
{:error-message (str "Unknown provider: " provider)})
257-
(catch Exception e
258-
(logger/error logger-tag "Error on login: " e)
259-
{:error-message (format "Error log into provider %s: %s" provider (.getMessage e))})))

src/eca/llm_providers/copilot.clj

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"editor-plugin-version" "eca/*"
1515
"editor-version" (str "eca/" (config/eca-version))})
1616

17-
(defn auth-url []
17+
(defn oauth-url []
1818
(let [{:keys [body]} (http/post
1919
"https://github.com/login/device/code"
2020
{:headers (auth-headers)
@@ -25,40 +25,44 @@
2525
:device-code (:device_code body)
2626
:url (:verification_uri body)}))
2727

28-
(defn auth-exchange [device-code]
28+
(defn oauth-access-token [device-code]
2929
(let [{:keys [status body]} (http/post
3030
"https://github.com/login/oauth/access_token"
3131
{:headers (auth-headers)
3232
:body (json/generate-string {:client_id client-id
3333
:device_code device-code
3434
:grant_type "urn:ietf:params:oauth:grant-type:device_code"})
3535
:throw-exceptions? false
36-
:as :json})
37-
access-token (:access_token body)]
36+
:as :json})]
3837
(if (= 200 status)
39-
(let [{:keys [body]} (http/get
40-
"https://api.github.com/copilot_internal/v2/token"
41-
{:headers (merge (auth-headers)
42-
{"authorization" (str "token " access-token)})
43-
:throw-exceptions? false
44-
:as :json})]
45-
(if-let [token (:token body)]
46-
{:api-token token
47-
:expires-at (:expires_at body)}
48-
(throw (ex-info (format "Error: You may not have access to Github Copilot")
49-
{:status status
50-
:body body}))))
38+
(:access_token body)
5139
(throw (ex-info (format "Github auth failed: %s" (pr-str body))
5240
{:status status
5341
:body body})))))
5442

43+
(defn oauth-renew-token [access-token]
44+
(let [{:keys [status body]} (http/get
45+
"https://api.github.com/copilot_internal/v2/token"
46+
{:headers (merge (auth-headers)
47+
{"authorization" (str "token " access-token)})
48+
:throw-exceptions? false
49+
:as :json})]
50+
(if-let [token (:token body)]
51+
{:api-token token
52+
:expires-at (:expires_at body)}
53+
(throw (ex-info (format "Error on copilot login: %s" body)
54+
{:status status
55+
:body body})))))
56+
5557
(comment
56-
(def a (auth-url))
58+
(def a (oauth-url))
5759
(:user-code a)
5860
(:device-code a)
5961
(:url a)
6062

61-
(def credentials (auth-exchange (:device-code a)))
63+
(def access-token (oauth-access-token (:device-code a)))
64+
65+
(def credentials (oauth-renew-token access-token))
6266

6367
(:api-token credentials)
6468
(:expires-at credentials))

test/eca/llm_api_test.clj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
(deftest default-model-test
1111
(testing "Custom provider default-model? present"
1212
(with-redefs [config/get-env (constantly nil)]
13-
(let [db {:models {"my-model" {:custom-provider? true :default-model? true}}}
13+
(let [db {:models {"my-provider/my-model" {:custom-provider? true :default-model? true}}}
1414
config {}]
15-
(is (= "my-model" (llm-api/default-model db config))))))
15+
(is (= "my-provider/my-model" (llm-api/default-model db config))))))
1616

1717
(testing "Ollama running model present"
1818
(with-redefs [config/get-env (constantly nil)]
@@ -26,28 +26,28 @@
2626
(with-redefs [config/get-env (constantly nil)]
2727
(let [db {:models {}}
2828
config {:anthropicApiKey "something"}]
29-
(is (= "claude-sonnet-4-20250514" (llm-api/default-model db config))))))
29+
(is (= "anthropic/claude-sonnet-4-20250514" (llm-api/default-model db config))))))
3030

3131
(testing "Anthropic API key present in ENV"
3232
(with-redefs [config/get-env (fn [k] (when (= k "ANTHROPIC_API_KEY") "env-anthropic"))]
3333
(let [db {:models {}}
3434
config {}]
35-
(is (= "claude-sonnet-4-20250514" (llm-api/default-model db config))))))
35+
(is (= "anthropic/claude-sonnet-4-20250514" (llm-api/default-model db config))))))
3636

3737
(testing "OpenAI API key present in config"
3838
(with-redefs [config/get-env (constantly nil)]
3939
(let [db {:models {}}
4040
config {:openaiApiKey "yes!"}]
41-
(is (= "gpt-5" (llm-api/default-model db config))))))
41+
(is (= "openai/gpt-5" (llm-api/default-model db config))))))
4242

4343
(testing "OpenAI API key present in ENV"
4444
(with-redefs [config/get-env (fn [k] (when (= k "OPENAI_API_KEY") "env-openai"))]
4545
(let [db {:models {}}
4646
config {}]
47-
(is (= "gpt-5" (llm-api/default-model db config))))))
47+
(is (= "openai/gpt-5" (llm-api/default-model db config))))))
4848

4949
(testing "Fallback default (no keys anywhere)"
5050
(with-redefs [config/get-env (constantly nil)]
5151
(let [db {:models {}}
5252
config {}]
53-
(is (= "claude-sonnet-4-20250514" (llm-api/default-model db config)))))))
53+
(is (= "anthropic/claude-sonnet-4-20250514" (llm-api/default-model db config)))))))

test/eca/shared_test.clj

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,18 @@
3232
(shared/assoc-some {} :a 1 :b)))))
3333

3434
(deftest tokens->cost-test
35-
(let [db {:models {"my-model" {:input-token-cost 0.01
36-
:output-token-cost 0.02
37-
:input-cache-creation-token-cost 0.005
38-
:input-cache-read-token-cost 0.001}}}]
35+
(let [db {:models {"provider/my-model" {:input-token-cost 0.01
36+
:output-token-cost 0.02
37+
:input-cache-creation-token-cost 0.005
38+
:input-cache-read-token-cost 0.001}}}]
3939
(testing "basic input/output cost"
40-
(is (= "0.70" (shared/tokens->cost 30 nil nil 20 "my-model" db))))
40+
(is (= "0.70" (shared/tokens->cost 30 nil nil 20 "provider/my-model" db))))
4141
(testing "with cache creation tokens"
42-
(is (= "0.75" (shared/tokens->cost 30 10 nil 20 "my-model" db))))
42+
(is (= "0.75" (shared/tokens->cost 30 10 nil 20 "provider/my-model" db))))
4343
(testing "with cache read tokens"
44-
(is (= "0.73" (shared/tokens->cost 30 nil 30 20 "my-model" db))))
44+
(is (= "0.73" (shared/tokens->cost 30 nil 30 20 "provider/my-model" db))))
4545
(testing "with both cache creation and read tokens"
46-
(is (= "0.78" (shared/tokens->cost 30 10 30 20 "my-model" db))))
47-
(testing "accepts provider-prefixed model names"
48-
(is (= "0.70" (shared/tokens->cost 30 nil nil 20 "provider/my-model" db))))
46+
(is (= "0.78" (shared/tokens->cost 30 10 30 20 "provider/my-model" db))))
4947
(testing "returns nil when model is missing from db"
5048
(is (nil? (shared/tokens->cost 30 nil nil 20 "unknown" db))))
5149
(testing "returns nil when mandatory costs are missing"

0 commit comments

Comments
 (0)