Skip to content

Commit 614c207

Browse files
committed
Check models and configs from models.dev
1 parent 51b13d9 commit 614c207

File tree

9 files changed

+137
-51
lines changed

9 files changed

+137
-51
lines changed

CHANGELOG.md

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

33
## Unreleased
44

5+
- Get models and configs from models.dev instead of hardcoding in eca.
6+
- Allow custom models addition via `models <modelName>` config.
7+
- Add `/resume` command to resume previous chats.
8+
59
## 0.23.1
610

711
- Fix openai reasoning not being included in messages.

docs/configuration.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,19 @@ Just add to your config the `commands` pointing to `.md` files that will be sear
156156
}
157157
```
158158

159-
## Overriding model payloads
159+
## Adding models / overriding model payloads
160160

161-
It's possible to override the payload sent to LLMs via `models <modelName> extraPayload` config, this way you can configure custom LLM settings like `temperature`, `reasoning_effort`, `verbosity` etc.
161+
It's possible to add new models just adding `models <yourmodel>`, example:
162+
163+
```javascript
164+
{
165+
"models": {
166+
"o1": {}
167+
}
168+
}
169+
```
170+
171+
To override the payload sent to LLMs via `models <modelName> extraPayload` config, this way you can configure custom LLM settings like `temperature`, `reasoning_effort`, `verbosity` etc.
162172
This config will be merged with current default used by ECA.
163173

164174
Example:

src/eca/config.clj

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,16 @@
2626
:disabledTools []
2727
:mcpTimeoutSeconds 60
2828
:mcpServers {}
29-
:models {}
29+
:models {"gpt-5" {}
30+
"gpt-5-mini" {}
31+
"gpt-5-nano" {}
32+
"gpt-4.1" {}
33+
"o4-mini" {}
34+
"o3" {}
35+
"claude-sonnet-4-20250514" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
36+
"claude-opus-4-1-20250805" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
37+
"claude-opus-4-20250514" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}
38+
"claude-3-5-haiku-20241022" {:extraPayload {:thinking {:type "enabled" :budget_tokens 2048}}}}
3039
:ollama {:host "http://localhost"
3140
:port 11434
3241
:useTools true

src/eca/features/chat.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
[eca.llm-api :as llm-api]
1515
[eca.logger :as logger]
1616
[eca.messenger :as messenger]
17+
[eca.models :as models]
1718
[eca.shared :as shared :refer [assoc-some]]))
1819

1920
(set! *warn-on-reflection* true)
@@ -107,6 +108,8 @@
107108
:text "Parsing given context"}))
108109
(let [db @db*
109110
manual-approval? (get-in config [:toolCall :manualApproval] false)
111+
all-models (models/all)
112+
provider (get-in all-models [model :provider])
110113
rules (f.rules/all config (:workspace-folders db))
111114
refined-contexts (f.context/raw-contexts->refined contexts db)
112115
repo-map* (delay (f.index/repo-map db {:as-string? true}))
@@ -124,6 +127,7 @@
124127
:text "Waiting model"})
125128
(llm-api/complete!
126129
{:model model
130+
:provider provider
127131
:model-config (get-in db [:models model])
128132
:user-messages user-messages
129133
:instructions instructions

src/eca/handlers.clj

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,51 @@
66
[eca.features.tools :as f.tools]
77
[eca.features.tools.mcp :as f.mcp]
88
[eca.llm-api :as llm-api]
9-
[eca.logger :as logger]))
9+
[eca.logger :as logger]
10+
[eca.models :as models]))
1011

1112
(set! *warn-on-reflection* true)
1213

13-
(defn ^:private initialize-extra-models! [db* config]
14-
(when-let [custom-providers (seq (:customProviders config))]
15-
(swap! db* update :models merge
16-
(reduce
17-
(fn [models [provider {provider-models :models default-model :defaultModel}]]
18-
(reduce
19-
(fn [m model]
20-
(assoc m
21-
(str (name provider) "/" model)
22-
;; TODO avoid hardcoding these capabilities
23-
{:tools true
24-
:web-search true
25-
:custom-provider? true
26-
:default-model? (= model default-model)}))
27-
models
28-
provider-models))
29-
{}
30-
custom-providers)))
14+
;; (:models @eca.db/db*)
15+
16+
(def a (models/all))
17+
18+
19+
20+
(defn ^:private initialize-models! [db* config]
21+
(let [all-models (models/all)
22+
eca-models (filter
23+
(fn [[model _config]]
24+
(get-in config [:models model]))
25+
all-models)]
26+
(swap! db* update :models merge eca-models)
27+
(when-let [custom-providers (seq (:customProviders config))]
28+
(let [models (reduce
29+
(fn [models [provider {provider-models :models default-model :defaultModel}]]
30+
(reduce
31+
(fn [m model]
32+
(let [known-model (get all-models model)]
33+
(assoc m
34+
(str (name provider) "/" model)
35+
{:tools (or (:tools known-model) true)
36+
:reason? (or (:reason? known-model) true)
37+
:web-search (or (:web-search known-model) true)
38+
:max-output-tokens (:max-output-tokens known-model)
39+
:custom-provider? true
40+
:default-model? (= model default-model)})))
41+
models
42+
provider-models))
43+
{}
44+
custom-providers)]
45+
(swap! db* update :models merge models))))
3146
(when-let [ollama-models (seq (llm-api/extra-models config))]
3247
(let [models (reduce
33-
(fn [models {:keys [model] :as ollama-model}]
34-
(assoc models
35-
(str config/ollama-model-prefix model)
36-
(select-keys ollama-model [:tools :reason?])))
37-
{}
38-
ollama-models)]
48+
(fn [models {:keys [model] :as ollama-model}]
49+
(assoc models
50+
(str config/ollama-model-prefix model)
51+
(select-keys ollama-model [:tools :reason?])))
52+
{}
53+
ollama-models)]
3954
(swap! db* update :models merge models))))
4055

4156
(defn initialize [{:keys [db* config]} params]

src/eca/llm_api.clj

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
:type "function"))
8181

8282
(defn complete!
83-
[{:keys [model model-config instructions reason? user-messages config on-first-response-received
83+
[{:keys [model provider model-config instructions reason? user-messages config on-first-response-received
8484
on-message-received on-error on-prepare-tool-call on-tools-called on-reason on-usage-updated
8585
past-messages tools]}]
8686
(let [first-response-received* (atom false)
@@ -105,7 +105,6 @@
105105
(mapv tool->llm-tool tools))
106106
web-search (:web-search model-config)
107107
max-output-tokens (:max-output-tokens model-config)
108-
reason-tokens (:reason-tokens model-config)
109108
custom-providers (:customProviders config)
110109
custom-models (set (mapcat (fn [[k v]]
111110
(map #(str (name k) "/" %) (:models v)))
@@ -118,18 +117,12 @@
118117
:on-reason on-reason-wrapper
119118
:on-usage-updated on-usage-updated}]
120119
(cond
121-
(contains? #{"o4-mini"
122-
"o3"
123-
"gpt-4.1"
124-
"gpt-5"
125-
"gpt-5-mini"
126-
"gpt-5-nano"} model)
120+
(= "openai" provider)
127121
(llm-providers.openai/completion!
128122
{:model model
129123
:instructions instructions
130124
:user-messages user-messages
131125
:max-output-tokens max-output-tokens
132-
:reason-tokens reason-tokens
133126
:reason? (and reason? (:reason? model-config))
134127
:past-messages past-messages
135128
:tools tools
@@ -139,16 +132,12 @@
139132
:api-key (openai-api-key config)}
140133
callbacks)
141134

142-
(contains? #{"claude-sonnet-4-0"
143-
"claude-opus-4-0"
144-
"claude-opus-4-1"
145-
"claude-3-5-haiku-latest"} model)
135+
(= "anthropic" provider)
146136
(llm-providers.anthropic/completion!
147137
{:model model
148138
:instructions instructions
149139
:user-messages user-messages
150140
:max-output-tokens max-output-tokens
151-
:reason-tokens reason-tokens
152141
:reason? (and reason? (:reason? model-config))
153142
:past-messages past-messages
154143
:tools tools
@@ -177,15 +166,14 @@
177166
provider-fn (case (:api provider-config)
178167
"openai" llm-providers.openai/completion!
179168
"anthropic" llm-providers.anthropic/completion!
180-
(on-error-wrapper {:msg (format "Unknown custom model %s for provider %s" (:api provider-config) provider)}))
169+
(on-error-wrapper {:message (format "Unknown custom model %s for provider %s" (:api provider-config) provider)}))
181170
url (or (:url provider-config) (config/get-env (:urlEnv provider-config)))
182171
key (or (:key provider-config) (config/get-env (:keyEnv provider-config)))]
183172
(provider-fn
184173
{:model model
185174
:instructions instructions
186175
:user-messages user-messages
187176
:max-output-tokens max-output-tokens
188-
:reason-tokens reason-tokens
189177
:reason? (and reason? (:reason? model-config))
190178
:past-messages past-messages
191179
:web-search web-search
@@ -196,4 +184,4 @@
196184
callbacks))
197185

198186
:else
199-
(on-error-wrapper {:msg (str "ECA Unsupported model: " model)}))))
187+
(on-error-wrapper {:message (str "ECA Unsupported model: " model)}))))

src/eca/llm_providers/anthropic.clj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,12 @@
9696

9797
(defn completion!
9898
[{:keys [model user-messages temperature instructions max-output-tokens
99-
api-url api-key reason? reason-tokens past-messages tools web-search extra-payload]
99+
api-url api-key reason? past-messages tools web-search extra-payload]
100100
:or {temperature 1.0}}
101101
{:keys [on-message-received on-error on-reason on-prepare-tool-call on-tools-called on-usage-updated]}]
102102
(let [messages (concat (normalize-messages past-messages)
103103
(normalize-messages user-messages))
104+
thinking (:thinking extra-payload)
104105
body (merge (assoc-some
105106
{:model model
106107
:messages (add-cache-to-last-message messages)
@@ -109,9 +110,8 @@
109110
:stream true
110111
:tools (->tools tools web-search)
111112
:system [{:type "text" :text instructions :cache_control {:type "ephemeral"}}]}
112-
:thinking (when (and reason? reason-tokens (> reason-tokens 0))
113-
{:type "enabled"
114-
:budget_tokens reason-tokens}))
113+
:thinking (when (and reason? thinking)
114+
thinking))
115115
extra-payload)
116116

117117
on-response-fn

src/eca/models.clj

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
(ns eca.models
2+
(:require
3+
[cheshire.core :as json]
4+
[eca.logger :as logger]
5+
[eca.shared :refer [assoc-some]]))
6+
7+
(set! *warn-on-reflection* true)
8+
9+
(def ^:private logger-tag "[MODELS]")
10+
11+
(defn ^:private models-dev* []
12+
(try
13+
(let [response (slurp "https://models.dev/api.json")
14+
data (json/parse-string response keyword)]
15+
data)
16+
(catch Exception e
17+
(logger/error logger-tag " Error fetching models from models.dev:" (.getMessage e))
18+
{})))
19+
20+
(def ^:private models-dev (memoize models-dev*))
21+
22+
(def ^:private one-million 1000000)
23+
24+
(def ^:private models-with-web-search-support
25+
#{"gpt-4.1"
26+
"gpt-5"
27+
"gpt-5-mini"
28+
"gpt-5-nano"
29+
"claude-sonnet-4-20250514"
30+
"claude-opus-4-20250514"
31+
"claude-opus-4-1-20250805"
32+
"claude-3-5-haiku-20241022"})
33+
34+
(defn all
35+
"Return all known existing models with their capabilities and configs."
36+
[]
37+
(reduce
38+
(fn [m [provider provider-config]]
39+
(merge m
40+
(reduce
41+
(fn [p [model model-config]]
42+
(assoc p (name model) (assoc-some
43+
{:provider (or (namespace model) (name provider))
44+
:reason? (:reasoning model-config)
45+
;; TODO how to check for web-search mode dynamically
46+
:web-search (contains? models-with-web-search-support (name model))
47+
:tools (:tool_call model-config)
48+
:max-output-tokens (-> model-config :limit :output)}
49+
:input-token-cost (some-> (:input (:cost model-config)) float (/ one-million))
50+
:output-token-cost (some-> (:output (:cost model-config)) float (/ one-million))
51+
:input-cache-creation-token-cost (some-> (:cache_write (:cost model-config)) float (/ one-million))
52+
:input-cache-read-token-cost (some-> (:cache_read (:cost model-config)) float (/ one-million)))))
53+
{}
54+
(:models provider-config))))
55+
{}
56+
(models-dev)))

src/eca/shared.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@
5252
{:keys [input-token-cost output-token-cost
5353
input-cache-creation-token-cost input-cache-read-token-cost]} (get-in db [:models normalized-model])
5454
input-cost (* input-tokens input-token-cost)
55-
input-cost (if input-cache-creation-tokens
55+
input-cost (if (and input-cache-creation-tokens input-cache-creation-token-cost)
5656
(+ input-cost (* input-cache-creation-tokens input-cache-creation-token-cost))
5757
input-cost)
58-
input-cost (if input-cache-read-tokens
58+
input-cost (if (and input-cache-read-tokens input-cache-read-token-cost)
5959
(+ input-cost (* input-cache-read-tokens input-cache-read-token-cost))
6060
input-cost)]
6161
(when (and input-token-cost output-token-cost)

0 commit comments

Comments
 (0)