Skip to content

Commit ebb8bdc

Browse files
ericdalloeca-agent
andcommitted
Add LiteLLM, LM Studio, Mistral, and Moonshot as built-in providers
Add provider modules with login-step support for four new providers: - LiteLLM (openai-responses API, user-provided URL) - LM Studio (openai-chat, local, no API key) - Mistral (openai-chat, api.mistral.ai) - Moonshot (openai-chat, api.kimi.com) Also fix Z-AI provider-configs using wrong API type (openai-chat → anthropic) and wrong URL (api.x.ai → api.z.ai/api/anthropic). 🤖 Generated with [eca](https://eca.dev) Co-Authored-By: eca <git@eca.dev>
1 parent 463072c commit ebb8bdc

File tree

8 files changed

+149
-1
lines changed

8 files changed

+149
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## Unreleased
44

55
- Add `providers/list`, `providers/login`, `providers/loginInput`, `providers/logout` requests and `providers/updated` notification for settings-based provider/model management.
6+
- Add LiteLLM, LM Studio, Mistral, and Moonshot as built-in providers with login support.
7+
- Fix Z-AI provider config using wrong API type and URL.
68

79
## 0.120.1
810

src/eca/db.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@
120120
"deepseek" {}
121121
"github-copilot" {}
122122
"google" {}
123+
"litellm" {}
124+
"lmstudio" {}
125+
"mistral" {}
126+
"moonshot" {}
123127
"openai" {}
124128
"openrouter" {}
125129
"z-ai" {}}

src/eca/features/providers.clj

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
"google" "Google"
1919
"azure" "Azure"
2020
"deepseek" "DeepSeek"
21+
"litellm" "LiteLLM"
22+
"lmstudio" "LM Studio"
23+
"mistral" "Mistral"
24+
"moonshot" "Moonshot"
2125
"openrouter" "OpenRouter"
2226
"z-ai" "Z-AI"
2327
"ollama" "Ollama"})
@@ -32,13 +36,25 @@
3236
"google" [{:key "api-key" :label "Enter API key"}]
3337
"azure" [{:key "api-key" :label "Enter API key, URL & models"}]
3438
"deepseek" [{:key "api-key" :label "Enter API key & models"}]
39+
"litellm" [{:key "api-key" :label "Enter API key, URL & models"}]
40+
"lmstudio" [{:key "api-key" :label "Enter models"}]
41+
"mistral" [{:key "api-key" :label "Enter API key & models"}]
42+
"moonshot" [{:key "api-key" :label "Enter API key & models"}]
3543
"openrouter" [{:key "api-key" :label "Enter API key & models"}]
3644
"z-ai" [{:key "api-key" :label "Enter API key & models"}]})
3745

3846
(def ^:private provider-login-fields
3947
{"google" [{:key "api-key" :label "API Key" :type "secret"}]
4048
"deepseek" [{:key "api-key" :label "API Key" :type "secret"}
4149
{:key "models" :label "Model names (comma-separated)" :type "text"}]
50+
"litellm" [{:key "api-key" :label "API Key" :type "secret"}
51+
{:key "url" :label "API URL (e.g. https://litellm.my-company.com)" :type "text"}
52+
{:key "models" :label "Model names (comma-separated)" :type "text"}]
53+
"lmstudio" [{:key "models" :label "Model names (comma-separated)" :type "text"}]
54+
"mistral" [{:key "api-key" :label "API Key" :type "secret"}
55+
{:key "models" :label "Model names (comma-separated)" :type "text"}]
56+
"moonshot" [{:key "api-key" :label "API Key" :type "secret"}
57+
{:key "models" :label "Model names (comma-separated)" :type "text"}]
4258
"openrouter" [{:key "api-key" :label "API Key" :type "secret"}
4359
{:key "models" :label "Model names (comma-separated)" :type "text"}]
4460
"z-ai" [{:key "api-key" :label "API Key" :type "secret"}
@@ -49,8 +65,14 @@
4965

5066
(def ^:private provider-configs
5167
{"deepseek" {:api "openai-chat" :url "https://api.deepseek.com"}
68+
"litellm" {:api "openai-responses"}
69+
"lmstudio" {:api "openai-chat" :url "http://localhost:1234"
70+
:completionUrlRelativePath "/v1/chat/completions"
71+
:httpClient {:version "http-1.1"}}
72+
"mistral" {:api "openai-chat" :url "https://api.mistral.ai/v1"}
73+
"moonshot" {:api "openai-chat" :url "https://api.kimi.com/coding/v1"}
5274
"openrouter" {:api "openai-chat" :url "https://openrouter.ai/api/v1"}
53-
"z-ai" {:api "openai-chat" :url "https://api.x.ai"}
75+
"z-ai" {:api "anthropic" :url "https://api.z.ai/api/anthropic"}
5476
"azure" {:api "openai-chat"}})
5577

5678
;; --- Auth resolution ---

src/eca/llm_api.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
[eca.llm-providers.deepseek]
1010
[eca.llm-providers.errors :as llm-providers.errors]
1111
[eca.llm-providers.google]
12+
[eca.llm-providers.litellm]
13+
[eca.llm-providers.lmstudio]
14+
[eca.llm-providers.mistral]
15+
[eca.llm-providers.moonshot]
1216
[eca.llm-providers.ollama :as llm-providers.ollama]
1317
[eca.llm-providers.openai :as llm-providers.openai]
1418
[eca.llm-providers.openai-chat :as llm-providers.openai-chat]

src/eca/llm_providers/litellm.clj

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(ns eca.llm-providers.litellm
2+
(:require
3+
[clojure.string :as string]
4+
[eca.config :as config]
5+
[eca.features.login :as f.login]))
6+
7+
(defmethod f.login/login-step ["litellm" :login/start] [{:keys [db* chat-id provider send-msg!]}]
8+
(swap! db* assoc-in [:chats chat-id :login-provider] provider)
9+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-api-key})
10+
(send-msg! "Paste your API Key"))
11+
12+
(defmethod f.login/login-step ["litellm" :login/waiting-api-key] [{:keys [input db* provider send-msg!]}]
13+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-url
14+
:api-key input})
15+
(send-msg! "Inform your LiteLLM API URL (e.g. https://litellm.my-company.com):"))
16+
17+
(defmethod f.login/login-step ["litellm" :login/waiting-url] [{:keys [input db* provider send-msg!]}]
18+
(swap! db* assoc-in [:auth provider :url] (string/trim input))
19+
(swap! db* assoc-in [:auth provider :step] :login/waiting-models)
20+
(send-msg! "Inform one or more models (separated by `,`):"))
21+
22+
(defmethod f.login/login-step ["litellm" :login/waiting-models] [{:keys [input db* provider send-msg!] :as ctx}]
23+
(let [{:keys [api-key url]} (get-in @db* [:auth provider])]
24+
(config/update-global-config! {:providers {"litellm" {:api "openai-responses"
25+
:url url
26+
:models (reduce
27+
(fn [models model-str]
28+
(assoc models (string/trim model-str) {}))
29+
{}
30+
(string/split input #","))
31+
:key api-key}}}))
32+
(swap! db* assoc-in [:auth provider] {:step :login/done :type :auth/token})
33+
(send-msg! (format "API key and models saved to %s" (.getCanonicalPath (config/global-config-file))))
34+
(f.login/login-done! ctx))

src/eca/llm_providers/lmstudio.clj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
(ns eca.llm-providers.lmstudio
2+
(:require
3+
[clojure.string :as string]
4+
[eca.config :as config]
5+
[eca.features.login :as f.login]))
6+
7+
(defmethod f.login/login-step ["lmstudio" :login/start] [{:keys [db* chat-id provider send-msg!]}]
8+
(swap! db* assoc-in [:chats chat-id :login-provider] provider)
9+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-models})
10+
(send-msg! "Inform one or more models (separated by `,`):"))
11+
12+
(defmethod f.login/login-step ["lmstudio" :login/waiting-models] [{:keys [input db* provider send-msg!] :as ctx}]
13+
(config/update-global-config! {:providers {"lmstudio" {:api "openai-chat"
14+
:url "http://localhost:1234"
15+
:completionUrlRelativePath "/v1/chat/completions"
16+
:httpClient {:version "http-1.1"}
17+
:models (reduce
18+
(fn [models model-str]
19+
(assoc models (string/trim model-str) {}))
20+
{}
21+
(string/split input #","))}}})
22+
(swap! db* assoc-in [:auth provider] {:step :login/done :type :auth/token})
23+
(send-msg! (format "Models saved to %s" (.getCanonicalPath (config/global-config-file))))
24+
(f.login/login-done! ctx))

src/eca/llm_providers/mistral.clj

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
(ns eca.llm-providers.mistral
2+
(:require
3+
[clojure.string :as string]
4+
[eca.config :as config]
5+
[eca.features.login :as f.login]))
6+
7+
(defmethod f.login/login-step ["mistral" :login/start] [{:keys [db* chat-id provider send-msg!]}]
8+
(swap! db* assoc-in [:chats chat-id :login-provider] provider)
9+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-api-key})
10+
(send-msg! "Paste your API Key"))
11+
12+
(defmethod f.login/login-step ["mistral" :login/waiting-api-key] [{:keys [input db* provider send-msg!]}]
13+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-models
14+
:api-key input})
15+
(send-msg! "Inform one or more models (separated by `,`):"))
16+
17+
(defmethod f.login/login-step ["mistral" :login/waiting-models] [{:keys [input db* provider send-msg!] :as ctx}]
18+
(let [api-key (get-in @db* [:auth provider :api-key])]
19+
(config/update-global-config! {:providers {"mistral" {:api "openai-chat"
20+
:url "https://api.mistral.ai/v1"
21+
:models (reduce
22+
(fn [models model-str]
23+
(assoc models (string/trim model-str) {}))
24+
{}
25+
(string/split input #","))
26+
:key api-key}}}))
27+
(swap! db* assoc-in [:auth provider] {:step :login/done :type :auth/token})
28+
(send-msg! (format "API key and models saved to %s" (.getCanonicalPath (config/global-config-file))))
29+
(f.login/login-done! ctx))

src/eca/llm_providers/moonshot.clj

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
(ns eca.llm-providers.moonshot
2+
(:require
3+
[clojure.string :as string]
4+
[eca.config :as config]
5+
[eca.features.login :as f.login]))
6+
7+
(defmethod f.login/login-step ["moonshot" :login/start] [{:keys [db* chat-id provider send-msg!]}]
8+
(swap! db* assoc-in [:chats chat-id :login-provider] provider)
9+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-api-key})
10+
(send-msg! "Paste your API Key"))
11+
12+
(defmethod f.login/login-step ["moonshot" :login/waiting-api-key] [{:keys [input db* provider send-msg!]}]
13+
(swap! db* assoc-in [:auth provider] {:step :login/waiting-models
14+
:api-key input})
15+
(send-msg! "Inform one or more models (separated by `,`):"))
16+
17+
(defmethod f.login/login-step ["moonshot" :login/waiting-models] [{:keys [input db* provider send-msg!] :as ctx}]
18+
(let [api-key (get-in @db* [:auth provider :api-key])]
19+
(config/update-global-config! {:providers {"moonshot" {:api "openai-chat"
20+
:url "https://api.kimi.com/coding/v1"
21+
:models (reduce
22+
(fn [models model-str]
23+
(assoc models (string/trim model-str) {}))
24+
{}
25+
(string/split input #","))
26+
:key api-key}}}))
27+
(swap! db* assoc-in [:auth provider] {:step :login/done :type :auth/token})
28+
(send-msg! (format "API key and models saved to %s" (.getCanonicalPath (config/global-config-file))))
29+
(f.login/login-done! ctx))

0 commit comments

Comments
 (0)