Skip to content

Commit 895be05

Browse files
committed
Migrate models to inside profiles
1 parent 32334b0 commit 895be05

File tree

10 files changed

+181
-76
lines changed

10 files changed

+181
-76
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Refactor config for better UX and understanding:
6+
- Move `models` to inside `providers`.
7+
58
## 0.31.0
69

710
- Update copilot models

docs/configuration.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ There are 3 possible ways to configure rules following this order of priority:
156156
providers: {[key: string]: {
157157
url?: string;
158158
key?: string; // when provider supports api key.
159+
models: {[key: string]: {
160+
extraPayload?: {[key: string]: any}
161+
}};
159162
}};
160163
defaultModel?: string;
161164
rules: [{path: string;}];
@@ -186,9 +189,6 @@ There are 3 possible ways to configure rules following this order of priority:
186189
key?: string;
187190
keyEnv?: string;
188191
}};
189-
models: {[key: string]: {
190-
extraPayload: {[key: string]: any}
191-
}};
192192
chat?: {
193193
welcomeMessage: string;
194194
};
@@ -211,7 +211,7 @@ There are 3 possible ways to configure rules following this order of priority:
211211
{
212212
"providers": {
213213
"openai": {"key": null,
214-
"url": "https://api.openai.com"},
214+
"url": "https://api.openai.com"},
215215
"anthropic": {"key": null,
216216
"url": "https://api.anthropic.com"},
217217
"github-copilot": {"url": "https://api.githubcopilot.com"},
@@ -231,7 +231,6 @@ There are 3 possible ways to configure rules following this order of priority:
231231
"mcpTimeoutSeconds" : 60,
232232
"mcpServers" : {},
233233
"customProviders": {},
234-
"models": {},
235234
"chat" : {
236235
"welcomeMessage" : "Welcome to ECA!\n\nType '/' for commands\n\n"
237236
},

docs/development.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ Run with `bb integration-test`, it will use your `eca` binary project root to sp
5858
There are several ways of finding and fixing a bug or implementing a new feature:
5959

6060
- Create a test for your bug/feature, then implement the code following the test (TDD).
61-
- Build a local `eca` JVM embedded binary using `bb debug-cli` (requires `babashka`), and test it manually in your client pointing to it. After started, you can connect to the nrepl port mentioned in eca stderr buffer, do you changes, evaluate and it will be affected on the running eca.
62-
- Using a debug binary you can check eca's stderr buffer and look for a nrepl port, and connect to the REPL, make changes to the running eca process (really handy).
61+
- Build a local `eca` JVM embedded binary using `bb debug-cli` (requires `babashka`), and test it manually in your client pointing to it. After started, you can connect to the nrepl port `9990`, do you changes, evaluate and it will be affected on the running eca.
62+
- Using a debug binary you can connect to the REPL, make changes to the running eca process (really handy).
6363

6464
## Supporting a new editor
6565

integration-test/integration/initialize_test.clj

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,20 @@
1313

1414
(testing "initialize request with default config"
1515
(is (match?
16-
{:models (m/in-any-order
17-
["anthropic/claude-3-5-haiku-20241022"
18-
"anthropic/claude-opus-4-1-20250805"
19-
"anthropic/claude-opus-4-20250514"
20-
"anthropic/claude-sonnet-4-20250514"
21-
"github-copilot/gpt-5"
22-
"github-copilot/gpt-5-mini"
23-
"github-copilot/gpt-4.1"
24-
"github-copilot/claude-sonnet-4"
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"])
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/claude-sonnet-4"
21+
"github-copilot/gpt-4.1"
22+
"github-copilot/gpt-5"
23+
"github-copilot/gpt-5-mini"
24+
"openai/gpt-4.1"
25+
"openai/gpt-5"
26+
"openai/gpt-5-mini"
27+
"openai/gpt-5-nano"
28+
"openai/o3"
29+
"openai/o4-mini"]
3130
:chatDefaultModel "anthropic/claude-sonnet-4-20250514"
3231
:chatBehaviors ["agent" "plan"]
3332
:chatDefaultBehavior "plan"
@@ -59,23 +58,22 @@
5958
(eca/start-process!)
6059
(testing "initialize request with custom providers"
6160
(is (match?
62-
{:models (m/in-any-order
63-
["anthropic/claude-3-5-haiku-20241022"
64-
"anthropic/claude-opus-4-1-20250805"
65-
"anthropic/claude-opus-4-20250514"
66-
"anthropic/claude-sonnet-4-20250514"
67-
"github-copilot/gpt-5"
68-
"github-copilot/gpt-5-mini"
69-
"github-copilot/gpt-4.1"
70-
"github-copilot/claude-sonnet-4"
71-
"myCustom/bar-2"
72-
"myCustom/foo-1"
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"])
61+
{:models ["anthropic/claude-3-5-haiku-20241022"
62+
"anthropic/claude-opus-4-1-20250805"
63+
"anthropic/claude-opus-4-20250514"
64+
"anthropic/claude-sonnet-4-20250514"
65+
"github-copilot/claude-sonnet-4"
66+
"github-copilot/gpt-4.1"
67+
"github-copilot/gpt-5"
68+
"github-copilot/gpt-5-mini"
69+
"myCustom/bar-2"
70+
"myCustom/foo-1"
71+
"openai/gpt-4.1"
72+
"openai/gpt-5"
73+
"openai/gpt-5-mini"
74+
"openai/gpt-5-nano"
75+
"openai/o3"
76+
"openai/o4-mini"]
7977
:chatDefaultModel "myCustom/bar-2"
8078
:chatBehaviors ["agent" "plan"]
8179
:chatDefaultBehavior "agent"

src/eca/config.clj

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,24 @@
2020

2121
(def initial-config
2222
{:providers {"openai" {:key nil
23-
:url "https://api.openai.com"}
23+
:url "https://api.openai.com"
24+
:models {"gpt-5" {}
25+
"gpt-5-mini" {}
26+
"gpt-5-nano" {}
27+
"gpt-4.1" {}
28+
"o4-mini" {}
29+
"o3" {}}}
2430
"anthropic" {:key nil
25-
:url "https://api.anthropic.com"}
26-
"github-copilot" {:url "https://api.githubcopilot.com"}
31+
:url "https://api.anthropic.com"
32+
:models {"claude-sonnet-4-20250514" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
33+
"claude-opus-4-1-20250805" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
34+
"claude-opus-4-20250514" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
35+
"claude-3-5-haiku-20241022" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}}}
36+
"github-copilot" {:url "https://api.githubcopilot.com"
37+
:models {"gpt-5" {}
38+
"gpt-5-mini" {}
39+
"gpt-4.1" {}
40+
"claude-sonnet-4" {}}}
2741
"ollama" {:url "http://localhost:11434"}}
2842
:defaultModel nil
2943
:rules []
@@ -35,20 +49,6 @@
3549
:disabledTools []
3650
:mcpTimeoutSeconds 60
3751
:mcpServers {}
38-
:models {"openai/gpt-5" {}
39-
"openai/gpt-5-mini" {}
40-
"openai/gpt-5-nano" {}
41-
"openai/gpt-4.1" {}
42-
"openai/o4-mini" {}
43-
"openai/o3" {}
44-
"github-copilot/gpt-5" {}
45-
"github-copilot/gpt-5-mini" {}
46-
"github-copilot/gpt-4.1" {}
47-
"github-copilot/claude-sonnet-4" {}
48-
"anthropic/claude-sonnet-4-20250514" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
49-
"anthropic/claude-opus-4-1-20250805" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
50-
"anthropic/claude-opus-4-20250514" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
51-
"anthropic/claude-3-5-haiku-20241022" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}}
5252
:chat {:welcomeMessage "Welcome to ECA!\n\nType '/' for commands\n\n"}
5353
:agentFileRelativePath "AGENT.md"
5454
:customProviders {}
@@ -116,9 +116,22 @@
116116

117117
(def ollama-model-prefix "ollama/")
118118

119+
(defn ^:private normalize-providers [providers]
120+
(letfn [(norm-key [k]
121+
(csk/->kebab-case (string/replace-first (str k) ":" "")))]
122+
(reduce-kv (fn [m k v]
123+
(let [nk (norm-key k)]
124+
(if (contains? m nk)
125+
(update m nk #(deep-merge % v))
126+
(assoc m nk v))))
127+
{}
128+
providers)))
129+
119130
(defn ^:private normalize-fields [config]
120131
(-> config
121-
(update-in [:providers] update-keys #(csk/->kebab-case (string/replace-first (str %) ":" "")))))
132+
(update-in [:providers] (fn [providers]
133+
(when providers
134+
(normalize-providers providers))))))
122135

123136
(defn all [db]
124137
(let [initialization-config @initialization-config*

src/eca/handlers.clj

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
(ns eca.handlers
22
(:require
3+
[clojure.string :as string]
34
[eca.config :as config]
45
[eca.db :as db]
56
[eca.features.chat :as f.chat]
@@ -8,15 +9,17 @@
89
[eca.features.tools.mcp :as f.mcp]
910
[eca.llm-api :as llm-api]
1011
[eca.logger :as logger]
11-
[eca.models :as models]))
12+
[eca.models :as models]
13+
[eca.shared :as shared]))
1214

1315
(set! *warn-on-reflection* true)
1416

1517
(defn ^:private initialize-models! [db* config]
1618
(let [all-models (models/all)
1719
eca-models (filter
18-
(fn [[model _config]]
19-
(get-in config [:models model]))
20+
(fn [[full-model _config]]
21+
(let [[provider model] (string/split full-model #"/" 2)]
22+
(get-in config [:providers provider :models model])))
2023
all-models)]
2124
(swap! db* update :models merge eca-models)
2225
(when-let [custom-providers (seq (:customProviders config))]
@@ -48,21 +51,24 @@
4851
ollama-models)]
4952
(swap! db* update :models merge models))))
5053

51-
(defn initialize [{:keys [db* config]} params]
54+
(defn initialize [{:keys [db*]} params]
5255
(logger/logging-task
5356
:eca/initialize
54-
(swap! db* assoc
55-
:client-info (:client-info params)
56-
:workspace-folders (:workspace-folders params)
57-
:client-capabilities (:capabilities params)
58-
:chat-default-behavior (or (-> params :initialization-options :chat-behavior) (:chat-default-behavior @db*)))
59-
(initialize-models! db* config)
60-
(db/load-db-from-cache! db*)
61-
{:models (sort (keys (:models @db*)))
62-
:chat-default-model (f.chat/default-model @db* config)
63-
:chat-behaviors (:chat-behaviors @db*)
64-
:chat-default-behavior (:chat-default-behavior @db*)
65-
:chat-welcome-message (:welcomeMessage (:chat config))}))
57+
(reset! config/initialization-config* (shared/map->camel-cased-map (:initialization-options params)))
58+
(let [config (config/all @db*)]
59+
(logger/info "--->" config)
60+
(swap! db* assoc
61+
:client-info (:client-info params)
62+
:workspace-folders (:workspace-folders params)
63+
:client-capabilities (:capabilities params)
64+
:chat-default-behavior (or (-> params :initialization-options :chat-behavior) (:chat-default-behavior @db*)))
65+
(initialize-models! db* config)
66+
(db/load-db-from-cache! db*)
67+
{:models (sort (keys (:models @db*)))
68+
:chat-default-model (f.chat/default-model @db* config)
69+
:chat-behaviors (:chat-behaviors @db*)
70+
:chat-default-behavior (:chat-default-behavior @db*)
71+
:chat-welcome-message (:welcomeMessage (:chat config))})))
6672

6773
(defn initialized [{:keys [db* messenger config]}]
6874
(future

src/eca/nrepl.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
(def cider-nrepl-handler (dynaload 'cider.nrepl/cider-nrepl-handler))
1111

1212
(defn ^:private repl-port []
13-
(:port (start-server :handler cider-nrepl-handler)))
13+
(:port (start-server :handler cider-nrepl-handler :port 9990)))
1414

1515
(defn setup-nrepl []
1616
(try

src/eca/server.clj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131
(defmethod lsp.server/receive-request "initialize" [_ {:keys [server] :as components} params]
3232
(when-let [parent-process-id (:process-id params)]
3333
(liveness-probe/start! parent-process-id log-wrapper-fn #(exit server)))
34-
(reset! config/initialization-config* (shared/map->camel-cased-map (:initialization-options params)))
35-
(handlers/initialize (with-config components) params))
34+
(handlers/initialize components params))
3635

3736
(defmethod lsp.server/receive-notification "initialized" [_ components _params]
3837
(handlers/initialized (with-config components)))

test/eca/config_test.clj

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
(ns eca.config-test
2+
(:require
3+
[clojure.test :refer [deftest is testing]]
4+
[eca.config :as config]
5+
[eca.test-helper :as h]
6+
[matcher-combinators.matchers :as m]
7+
[matcher-combinators.test :refer [match?]]))
8+
9+
(h/reset-components-before-test)
10+
11+
(deftest all-test
12+
(testing "Default config"
13+
(reset! config/initialization-config* {})
14+
(is (match?
15+
{:providers {"github-copilot" {:key m/absent
16+
:models {"gpt-5" {}}}}}
17+
(config/all {}))))
18+
(testing "deep merging initializationOptions with initial config"
19+
(reset! config/initialization-config* {:pureConfig true
20+
:providers {"githubCopilot" {:key "123"}}})
21+
(is (match?
22+
{:pureConfig true
23+
:providers {"github-copilot" {:key "123"
24+
:models {"gpt-5" {}}}}}
25+
(config/all {})))))
26+
27+
(deftest deep-merge-test
28+
(testing "basic merge"
29+
(is (match?
30+
{:a 1
31+
:b 4
32+
:c 3
33+
:d 1}
34+
(#'config/deep-merge {:a 1}
35+
{:b 2}
36+
{:c 3}
37+
{:b 4 :d 1}))))
38+
(testing "deep merging"
39+
(is (match?
40+
{:a 1
41+
:b {:c {:d 3
42+
:e 4}}}
43+
(#'config/deep-merge {:a 1
44+
:b {:c {:d 3}}}
45+
{:b {:c {:e 4}}}))))
46+
(testing "deep merging maps with other keys"
47+
(is (match?
48+
{:a 1
49+
:b {:c {:e 3
50+
:f 4}
51+
:d 2}}
52+
(#'config/deep-merge {:a 1
53+
:b {:c {:e 3}
54+
:d 2}}
55+
{:b {:c {:f 4}}})))
56+
(is (match?
57+
{:pureConfig true
58+
:providers {"github-copilot" {:models {"gpt-5" {}}
59+
:key "123"}}}
60+
(#'config/deep-merge {:providers {"github-copilot" {:models {"gpt-5" {}}}}}
61+
{:pureConfig true
62+
:providers {"github-copilot" {:key "123"}}})))))

test/eca/handlers_test.clj

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(ns eca.handlers-test
2+
(:require
3+
[clojure.test :refer [deftest is testing]]
4+
[eca.config :as config]
5+
[eca.db :as db]
6+
[eca.handlers :as handlers]
7+
[matcher-combinators.test :refer [match?]]))
8+
9+
(deftest initialize-test
10+
(testing "initializationOptions config is merged properly with default init config"
11+
(let [db* (atom {})]
12+
(with-redefs [handlers/initialize-models! (constantly nil)
13+
db/load-db-from-cache! (constantly nil)]
14+
(is (match?
15+
{}
16+
(handlers/initialize {:db* db*} {:initialization-options
17+
{:pureConfig true
18+
:providers {"github-copilot" {:key "123"
19+
:models {"gpt-5" {:a 1}}}}}})))
20+
(is (match?
21+
{:providers {"github-copilot" {:key "123"
22+
:models {"gpt-5" {:a 1}
23+
"gpt-5-mini" {}}
24+
:url string?}}}
25+
(config/all @db*)))))))

0 commit comments

Comments
 (0)