@agxnte/reidx 2.0.9 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "reidx"
|
|
3
|
-
version = "2.0
|
|
4
|
-
description = "Terminal-native personal intelligence and coding CLI (agent-first runtime)"
|
|
3
|
+
version = "2.1.0"
|
|
4
|
+
description = "Terminal-native personal intelligence and coding CLI (agent-first runtime)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
license = { text = "MIT" }
|
package/src/reidx/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Speaks the OpenAI Chat Completions API (POST /v1/chat/completions). The same
|
|
4
4
|
wire format is spoken by llama.cpp's server (`/v1/chat/completions`) and by
|
|
5
|
-
LM Studio's local server
|
|
5
|
+
LM Studio's local server - see `OpenAICompatibleProvider` below for the
|
|
6
6
|
local-endpoint variant that skips the auth header when no key is set.
|
|
7
7
|
|
|
8
8
|
Tool schemas ReidX produces (`ToolBase.schema()`) are already OpenAI-style,
|
|
@@ -24,6 +24,7 @@ class OpenAIProvider(BaseProvider):
|
|
|
24
24
|
name = "openai"
|
|
25
25
|
DEFAULT_BASE_URL = "https://api.openai.com"
|
|
26
26
|
DEFAULT_MODEL = "gpt-4o-mini"
|
|
27
|
+
MODELS_PATH = "/v1/models"
|
|
27
28
|
|
|
28
29
|
def __init__(
|
|
29
30
|
self,
|
|
@@ -126,7 +127,7 @@ class OpenAIProvider(BaseProvider):
|
|
|
126
127
|
return self._parse(body, model)
|
|
127
128
|
|
|
128
129
|
def fetch_models(self) -> list[str]:
|
|
129
|
-
url = f"{self.base_url}
|
|
130
|
+
url = f"{self.base_url}{self.MODELS_PATH}"
|
|
130
131
|
body = get_json(url, self._headers())
|
|
131
132
|
models: list[str] = []
|
|
132
133
|
for item in body.get("data", []):
|
|
@@ -148,6 +149,7 @@ class OpenAICompatibleProvider(OpenAIProvider):
|
|
|
148
149
|
name = "openai-compatible"
|
|
149
150
|
DEFAULT_BASE_URL = "http://localhost:8080"
|
|
150
151
|
DEFAULT_MODEL = "local"
|
|
152
|
+
MODELS_PATH = "/v1/models"
|
|
151
153
|
|
|
152
154
|
def __init__(
|
|
153
155
|
self,
|
|
@@ -174,4 +176,4 @@ class OpenAICompatibleProvider(OpenAIProvider):
|
|
|
174
176
|
|
|
175
177
|
def _json_dump(obj) -> str: # type: ignore[no-untyped-def]
|
|
176
178
|
import json
|
|
177
|
-
return json.dumps(obj, ensure_ascii=False)
|
|
179
|
+
return json.dumps(obj, ensure_ascii=False)
|
|
@@ -153,6 +153,8 @@ def validate_provider(
|
|
|
153
153
|
msg = str(exc)
|
|
154
154
|
if msg.startswith("HTTP 401") or msg.startswith("HTTP 403"):
|
|
155
155
|
return False, f"authentication failed ({msg})"
|
|
156
|
+
if msg.startswith("HTTP 404"):
|
|
157
|
+
return False, f"endpoint not found - check base URL ({msg})"
|
|
156
158
|
if msg.startswith("HTTP "):
|
|
157
159
|
return False, f"provider error: {msg}"
|
|
158
160
|
return False, f"{msg} (use --skip-verify to save anyway)"
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Provider catalog: canonical metadata for known providers.
|
|
2
|
+
|
|
3
|
+
All OpenAI-compatible providers use `base_url` without a `/v1` suffix -
|
|
4
|
+
the client appends `/v1` when building request URLs. For Anthropic, the
|
|
5
|
+
client uses the base URL as-is with `/v1/messages` and `/v1/models`.
|
|
6
|
+
Ollama uses its native `/api` endpoints.
|
|
7
|
+
"""
|
|
1
8
|
from __future__ import annotations
|
|
2
9
|
|
|
3
10
|
from dataclasses import dataclass, field
|
|
@@ -22,276 +29,277 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
22
29
|
ProviderDefinition(
|
|
23
30
|
id="openai", name="OpenAI", description="GPT-4o, o-series, and DALL-E models",
|
|
24
31
|
kind="openai", base_url="https://api.openai.com", default_model="gpt-4o-mini",
|
|
25
|
-
aliases=["gpt", "chatgpt"], popular=True, icon="
|
|
32
|
+
aliases=["gpt", "chatgpt"], popular=True, icon="OAI",
|
|
26
33
|
),
|
|
27
34
|
ProviderDefinition(
|
|
28
35
|
id="anthropic", name="Anthropic", description="Claude Sonnet, Opus, and Haiku models",
|
|
29
36
|
kind="anthropic", base_url="https://api.anthropic.com", default_model="claude-sonnet-4-20250514",
|
|
30
|
-
aliases=["claude"], auth_method="x-api-key", popular=True, icon="
|
|
37
|
+
aliases=["claude"], auth_method="x-api-key", popular=True, icon="ANT",
|
|
31
38
|
),
|
|
32
39
|
ProviderDefinition(
|
|
33
40
|
id="google-ai", name="Google AI", description="Gemini Pro, Flash, and Flash-Lite models",
|
|
34
41
|
kind="openai-compatible", base_url="https://generativelanguage.googleapis.com/v1beta/openai",
|
|
35
42
|
default_model="gemini-2.0-flash", aliases=["gemini", "google", "google-gemini"],
|
|
36
|
-
popular=True, icon="
|
|
43
|
+
popular=True, icon="GAI",
|
|
37
44
|
),
|
|
38
|
-
|
|
45
|
+
ProviderDefinition(
|
|
39
46
|
id="openrouter", name="OpenRouter", description="Unified API for 300+ models from all providers",
|
|
40
|
-
kind="openai-compatible", base_url="https://openrouter.ai/api
|
|
41
|
-
default_model="auto", aliases=["or"], popular=True, icon="
|
|
47
|
+
kind="openai-compatible", base_url="https://openrouter.ai/api",
|
|
48
|
+
default_model="auto", aliases=["or"], popular=True, icon="ORT",
|
|
42
49
|
),
|
|
43
50
|
ProviderDefinition(
|
|
44
51
|
id="zai", name="Z.ai", description="GLM-4 and GLM-4.5 series models",
|
|
45
52
|
kind="openai-compatible", base_url="https://open.bigmodel.cn/api/paas/v4",
|
|
46
|
-
default_model="glm-4", aliases=["z-ai", "zhipu", "glm", "bigmodel"], popular=True, icon="
|
|
53
|
+
default_model="glm-4", aliases=["z-ai", "zhipu", "glm", "bigmodel"], popular=True, icon="ZAI",
|
|
47
54
|
),
|
|
48
55
|
ProviderDefinition(
|
|
49
56
|
id="agentrouter", name="AgentRouter", description="Multi-model routing for AI agents",
|
|
50
57
|
kind="openai-compatible", base_url="https://api.agentrouter.io/v1",
|
|
51
|
-
default_model="auto", aliases=["agent-router"], icon="
|
|
58
|
+
default_model="auto", aliases=["agent-router"], icon="AGT",
|
|
52
59
|
),
|
|
53
60
|
ProviderDefinition(
|
|
54
61
|
id="together", name="Together AI", description="Open-source Llama, Mixtral, and Qwen models",
|
|
55
62
|
kind="openai-compatible", base_url="https://api.together.xyz/v1",
|
|
56
63
|
default_model="meta-llama/Llama-3.3-70B-Instruct-Turbo", aliases=["together-ai"],
|
|
57
|
-
popular=True, icon="
|
|
64
|
+
popular=True, icon="TGT",
|
|
58
65
|
),
|
|
59
66
|
ProviderDefinition(
|
|
60
67
|
id="fireworks", name="Fireworks AI", description="Fast inference for open-source models",
|
|
61
68
|
kind="openai-compatible", base_url="https://api.fireworks.ai/inference/v1",
|
|
62
69
|
default_model="accounts/fireworks/models/llama-v3p1-70b-instruct",
|
|
63
|
-
aliases=["fireworks-ai"], popular=True, icon="
|
|
70
|
+
aliases=["fireworks-ai"], popular=True, icon="FRW",
|
|
64
71
|
),
|
|
65
72
|
ProviderDefinition(
|
|
66
73
|
id="groq", name="Groq", description="Ultra-fast LPU inference for Llama and Mixtral",
|
|
67
74
|
kind="openai-compatible", base_url="https://api.groq.com/openai/v1",
|
|
68
|
-
default_model="llama-3.3-70b-versatile", aliases=[], popular=True, icon="
|
|
75
|
+
default_model="llama-3.3-70b-versatile", aliases=[], popular=True, icon="GRQ",
|
|
69
76
|
),
|
|
70
77
|
ProviderDefinition(
|
|
71
78
|
id="deepinfra", name="DeepInfra", description="Cost-effective inference for open models",
|
|
72
79
|
kind="openai-compatible", base_url="https://api.deepinfra.com/v1/openai",
|
|
73
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
80
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="DPI",
|
|
74
81
|
),
|
|
75
82
|
ProviderDefinition(
|
|
76
83
|
id="cerebras", name="Cerebras", description="Fastest AI inference with Wafer-Scale Engines",
|
|
77
84
|
kind="openai-compatible", base_url="https://api.cerebras.ai/v1",
|
|
78
|
-
default_model="llama3.1-70b", aliases=[], icon="
|
|
85
|
+
default_model="llama3.1-70b", aliases=[], icon="CER",
|
|
79
86
|
),
|
|
80
87
|
ProviderDefinition(
|
|
81
88
|
id="cohere", name="Cohere", description="Command R+ and Aya models for enterprise",
|
|
82
89
|
kind="openai-compatible", base_url="https://api.cohere.ai/v1",
|
|
83
|
-
default_model="command-r-plus-08-2024", aliases=[], icon="
|
|
90
|
+
default_model="command-r-plus-08-2024", aliases=[], icon="COH",
|
|
84
91
|
),
|
|
85
92
|
ProviderDefinition(
|
|
86
93
|
id="xai", name="xAI", description="Grok models from xAI",
|
|
87
94
|
kind="openai-compatible", base_url="https://api.x.ai/v1",
|
|
88
|
-
default_model="grok-2-latest", aliases=["grok", "x-ai"], popular=True, icon="
|
|
95
|
+
default_model="grok-2-latest", aliases=["grok", "x-ai"], popular=True, icon="XAI",
|
|
89
96
|
),
|
|
90
97
|
ProviderDefinition(
|
|
91
98
|
id="mistral", name="Mistral", description="Mistral Large, Codestral, and Pixtral models",
|
|
92
99
|
kind="openai-compatible", base_url="https://api.mistral.ai/v1",
|
|
93
|
-
default_model="mistral-large-latest", aliases=["mistral-ai"], popular=True, icon="
|
|
100
|
+
default_model="mistral-large-latest", aliases=["mistral-ai"], popular=True, icon="MIS",
|
|
94
101
|
),
|
|
95
102
|
ProviderDefinition(
|
|
96
103
|
id="perplexity", name="Perplexity", description="Sonar models with built-in web search",
|
|
97
104
|
kind="openai-compatible", base_url="https://api.perplexity.ai",
|
|
98
|
-
default_model="llama-3.1-sonar-large-128k-online", aliases=[], icon="
|
|
105
|
+
default_model="llama-3.1-sonar-large-128k-online", aliases=[], icon="PPL",
|
|
99
106
|
),
|
|
100
107
|
ProviderDefinition(
|
|
101
|
-
id="ollama", name="Ollama", description="Run models locally
|
|
108
|
+
id="ollama", name="Ollama", description="Run models locally Llama, Qwen, Phi, and more",
|
|
102
109
|
kind="ollama", base_url="http://localhost:11434",
|
|
103
|
-
default_model="llama3.2", aliases=[], auth_method="none", popular=True, icon="
|
|
110
|
+
default_model="llama3.2", aliases=[], auth_method="none", popular=True, icon="OLL",
|
|
104
111
|
),
|
|
105
112
|
ProviderDefinition(
|
|
106
113
|
id="lm-studio", name="LM Studio", description="Local model server with OpenAI-compatible API",
|
|
107
114
|
kind="openai-compatible", base_url="http://localhost:1234/v1",
|
|
108
|
-
default_model="local", aliases=["lmstudio"], auth_method="none", icon="
|
|
115
|
+
default_model="local", aliases=["lmstudio"], auth_method="none", icon="LMS",
|
|
109
116
|
),
|
|
110
117
|
ProviderDefinition(
|
|
111
118
|
id="vllm", name="vLLM", description="High-throughput local inference server",
|
|
112
119
|
kind="openai-compatible", base_url="http://localhost:8000/v1",
|
|
113
|
-
default_model="auto", aliases=[], auth_method="none", icon="
|
|
120
|
+
default_model="auto", aliases=[], auth_method="none", icon="VLL",
|
|
114
121
|
),
|
|
115
122
|
ProviderDefinition(
|
|
116
123
|
id="azure-openai", name="Azure OpenAI", description="OpenAI models via Azure cloud (set base URL to your endpoint)",
|
|
117
124
|
kind="openai", base_url="https://api.openai.com", default_model="gpt-4o-mini",
|
|
118
|
-
aliases=["azure"], icon="
|
|
125
|
+
aliases=["azure"], icon="AZO",
|
|
119
126
|
),
|
|
120
127
|
ProviderDefinition(
|
|
121
128
|
id="huggingface", name="HuggingFace", description="Inference API for 100k+ models",
|
|
122
129
|
kind="openai-compatible", base_url="https://api-inference.huggingface.co/v1",
|
|
123
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=["hf"], icon="
|
|
130
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=["hf"], icon="HF",
|
|
124
131
|
),
|
|
125
132
|
ProviderDefinition(
|
|
126
133
|
id="sambanova", name="SambaNova", description="Fast RDU-based inference for open models",
|
|
127
134
|
kind="openai-compatible", base_url="https://api.sambanova.ai/v1",
|
|
128
|
-
default_model="Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
135
|
+
default_model="Meta-Llama-3.1-70B-Instruct", aliases=[], icon="SMB",
|
|
129
136
|
),
|
|
130
137
|
ProviderDefinition(
|
|
131
138
|
id="novita", name="Novita", description="Affordable API for 100+ open models",
|
|
132
139
|
kind="openai-compatible", base_url="https://api.novita.ai/v3/openai",
|
|
133
|
-
default_model="meta-llama/llama-3.1-70b-instruct", aliases=[], icon="
|
|
140
|
+
default_model="meta-llama/llama-3.1-70b-instruct", aliases=[], icon="NVT",
|
|
134
141
|
),
|
|
135
142
|
ProviderDefinition(
|
|
136
143
|
id="featherless", name="Featherless", description="Serverless inference for open models",
|
|
137
144
|
kind="openai-compatible", base_url="https://api.featherless.ai/v1/openai",
|
|
138
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
145
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="FTL",
|
|
139
146
|
),
|
|
140
147
|
ProviderDefinition(
|
|
141
148
|
id="chutes", name="Chutes", description="GPU-accelerated inference for AI workloads",
|
|
142
149
|
kind="openai-compatible", base_url="https://api.chutes.ai/v1",
|
|
143
|
-
default_model="deepseek-ai/DeepSeek-V3", aliases=[], icon="
|
|
150
|
+
default_model="deepseek-ai/DeepSeek-V3", aliases=[], icon="CHT",
|
|
144
151
|
),
|
|
145
152
|
ProviderDefinition(
|
|
146
153
|
id="aimlapi", name="AI/ML API", description="Unified API for 200+ AI models",
|
|
147
154
|
kind="openai-compatible", base_url="https://api.aimlapi.com/v1",
|
|
148
|
-
default_model="gpt-4o", aliases=["ai-ml-api"], icon="
|
|
155
|
+
default_model="gpt-4o", aliases=["ai-ml-api"], icon="AML",
|
|
149
156
|
),
|
|
150
157
|
ProviderDefinition(
|
|
151
158
|
id="hyperbolic", name="Hyperbolic", description="GPU marketplace for AI inference",
|
|
152
159
|
kind="openai-compatible", base_url="https://api.hyperbolic.xyz/v1",
|
|
153
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
160
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="HYP",
|
|
154
161
|
),
|
|
155
162
|
ProviderDefinition(
|
|
156
163
|
id="deepseek", name="DeepSeek", description="DeepSeek-V3 and DeepSeek-R1 reasoning models",
|
|
157
164
|
kind="openai-compatible", base_url="https://api.deepseek.com/v1",
|
|
158
|
-
default_model="deepseek-chat", aliases=[], popular=True, icon="
|
|
165
|
+
default_model="deepseek-chat", aliases=[], popular=True, icon="DSK",
|
|
159
166
|
),
|
|
160
167
|
ProviderDefinition(
|
|
161
168
|
id="moonshot", name="Moonshot", description="Kimi K1.5 and Moonshot models",
|
|
162
169
|
kind="openai-compatible", base_url="https://api.moonshot.cn/v1",
|
|
163
|
-
default_model="moonshot-v1-8k", aliases=["kimi"], icon="
|
|
170
|
+
default_model="moonshot-v1-8k", aliases=["kimi"], icon="MSH",
|
|
164
171
|
),
|
|
165
172
|
ProviderDefinition(
|
|
166
173
|
id="voyage-ai", name="Voyage AI", description="Embeddings and reranking models",
|
|
167
174
|
kind="openai-compatible", base_url="https://api.voyageai.com/v1",
|
|
168
|
-
default_model="voyage-large-2", aliases=[], icon="
|
|
175
|
+
default_model="voyage-large-2", aliases=[], icon="VOY",
|
|
169
176
|
),
|
|
170
177
|
ProviderDefinition(
|
|
171
178
|
id="nvidia-nim", name="NVIDIA NIM", description="NVIDIA inference microservices for open models",
|
|
172
179
|
kind="openai-compatible", base_url="https://integrate.api.nvidia.com/v1",
|
|
173
|
-
default_model="meta/llama-3.1-70b-instruct", aliases=["nvidia"], icon="
|
|
180
|
+
default_model="meta/llama-3.1-70b-instruct", aliases=["nvidia"], icon="NVD",
|
|
174
181
|
),
|
|
175
182
|
ProviderDefinition(
|
|
176
183
|
id="ai21", name="AI21 Labs", description="Jamba models with long context windows",
|
|
177
184
|
kind="openai-compatible", base_url="https://api.ai21.com/v1",
|
|
178
|
-
default_model="jamba-1.5-large", aliases=[], icon="
|
|
185
|
+
default_model="jamba-1.5-large", aliases=[], icon="A21",
|
|
179
186
|
),
|
|
180
187
|
ProviderDefinition(
|
|
181
188
|
id="databricks", name="Databricks", description="Serving endpoints for fine-tuned models (set base URL to your workspace)",
|
|
182
189
|
kind="openai-compatible", base_url="https://dbc-xxxxxxxx.cloud.databricks.com/serving-endpoints",
|
|
183
190
|
default_model="databricks-dbrx-instruct",
|
|
184
|
-
aliases=[], icon="
|
|
191
|
+
aliases=[], icon="DBS",
|
|
185
192
|
),
|
|
186
193
|
ProviderDefinition(
|
|
187
194
|
id="replicate", name="Replicate", description="Run open-source models in the cloud",
|
|
188
195
|
kind="openai-compatible", base_url="https://api.replicate.com/v1",
|
|
189
|
-
default_model="meta/llama-2-70b-chat", aliases=[], icon="
|
|
196
|
+
default_model="meta/llama-2-70b-chat", aliases=[], icon="RPL",
|
|
190
197
|
),
|
|
191
198
|
ProviderDefinition(
|
|
192
199
|
id="siliconflow", name="SiliconFlow", description="Cost-effective inference for 200+ models",
|
|
193
200
|
kind="openai-compatible", base_url="https://api.siliconflow.cn/v1",
|
|
194
|
-
default_model="deepseek-ai/DeepSeek-V3", aliases=[], icon="
|
|
201
|
+
default_model="deepseek-ai/DeepSeek-V3", aliases=[], icon="SFL",
|
|
195
202
|
),
|
|
196
203
|
ProviderDefinition(
|
|
197
204
|
id="nebius", name="Nebius", description="Cloud GPU platform for AI inference",
|
|
198
205
|
kind="openai-compatible", base_url="https://api.studio.nebius.ai/v1",
|
|
199
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
206
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="NEB",
|
|
200
207
|
),
|
|
201
208
|
ProviderDefinition(
|
|
202
209
|
id="lepton", name="Lepton AI", description="Serverless inference with auto-scaling",
|
|
203
210
|
kind="openai-compatible", base_url="https://api.lepton.ai/v1",
|
|
204
|
-
default_model="meta-llama/Llama-3.2-70B-Instruct", aliases=[], icon="
|
|
211
|
+
default_model="meta-llama/Llama-3.2-70B-Instruct", aliases=[], icon="LPT",
|
|
205
212
|
),
|
|
206
213
|
ProviderDefinition(
|
|
207
214
|
id="lambda", name="Lambda Labs", description="GPU cloud for AI training and inference",
|
|
208
215
|
kind="openai-compatible", base_url="https://api.lambdalabs.com/v1",
|
|
209
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=["lambda-labs"], icon="
|
|
216
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=["lambda-labs"], icon="LDA",
|
|
210
217
|
),
|
|
211
218
|
ProviderDefinition(
|
|
212
219
|
id="kluster", name="Kluster AI", description="Distributed GPU network for inference",
|
|
213
220
|
kind="openai-compatible", base_url="https://api.kluster.ai/v1",
|
|
214
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
221
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="KLU",
|
|
215
222
|
),
|
|
216
223
|
ProviderDefinition(
|
|
217
224
|
id="upstage", name="Upstage", description="Solar Mini and Pro models for Korean and English",
|
|
218
225
|
kind="openai-compatible", base_url="https://api.upstage.ai/v1",
|
|
219
|
-
default_model="solar-pro", aliases=[], icon="
|
|
226
|
+
default_model="solar-pro", aliases=[], icon="UPS",
|
|
220
227
|
),
|
|
221
228
|
ProviderDefinition(
|
|
222
229
|
id="yi", name="Yi (01.AI)", description="Yi-Large and Yi-34B models",
|
|
223
230
|
kind="openai-compatible", base_url="https://api.01.ai/v1",
|
|
224
|
-
default_model="yi-large", aliases=["01-ai", "yi-ai"], icon="
|
|
231
|
+
default_model="yi-large", aliases=["01-ai", "yi-ai"], icon="YI",
|
|
225
232
|
),
|
|
226
233
|
ProviderDefinition(
|
|
227
234
|
id="volcengine", name="Volcengine", description="ByteDance's Ark platform for Doubao models",
|
|
228
235
|
kind="openai-compatible", base_url="https://ark.cn-beijing.volces.com/api/v3",
|
|
229
|
-
default_model="doubao-pro-32k", aliases=["ark", "bytedance"], icon="
|
|
236
|
+
default_model="doubao-pro-32k", aliases=["ark", "bytedance"], icon="VOL",
|
|
230
237
|
),
|
|
231
238
|
ProviderDefinition(
|
|
232
239
|
id="hunyuan", name="Tencent Hunyuan", description="Tencent's Hunyuan large language models",
|
|
233
240
|
kind="openai-compatible", base_url="https://api.hunyuan.cloud.tencent.com/v1",
|
|
234
|
-
default_model="hunyuan-turbos-latest", aliases=["tencent"], icon="
|
|
241
|
+
default_model="hunyuan-turbos-latest", aliases=["tencent"], icon="HUN",
|
|
235
242
|
),
|
|
236
243
|
ProviderDefinition(
|
|
237
244
|
id="cloudflare-ai", name="Cloudflare Workers AI", description="Serverless AI inference at the edge",
|
|
238
245
|
kind="openai-compatible", base_url="https://api.cloudflare.com/client/v4",
|
|
239
246
|
default_model="@cf/meta/llama-3.1-70b-instruct",
|
|
240
|
-
aliases=["cf-ai"], icon="
|
|
247
|
+
aliases=["cf-ai"], icon="CFL",
|
|
241
248
|
),
|
|
242
249
|
ProviderDefinition(
|
|
243
250
|
id="monsterapi", name="Monster API", description="Affordable GPU inference for open models",
|
|
244
251
|
kind="openai-compatible", base_url="https://api.monsterapi.ai/v1",
|
|
245
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
252
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="MST",
|
|
246
253
|
),
|
|
247
254
|
ProviderDefinition(
|
|
248
255
|
id="baseten", name="Baseten", description="Model deployment and serving platform",
|
|
249
256
|
kind="openai-compatible", base_url="https://inference.baseten.co/v1",
|
|
250
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
257
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="BST",
|
|
251
258
|
),
|
|
252
259
|
ProviderDefinition(
|
|
253
260
|
id="anyscale", name="Anyscale", description="Ray-based scalable model serving",
|
|
254
261
|
kind="openai-compatible", base_url="https://api.endpoints.anyscale.com/v1",
|
|
255
|
-
default_model="meta-llama/Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
262
|
+
default_model="meta-llama/Llama-3.1-70B-Instruct", aliases=[], icon="ANY",
|
|
256
263
|
),
|
|
257
264
|
ProviderDefinition(
|
|
258
265
|
id="jan", name="Jan", description="Local AI model server with web UI",
|
|
259
266
|
kind="openai-compatible", base_url="http://localhost:1337/v1",
|
|
260
|
-
default_model="local", aliases=[], auth_method="none", icon="
|
|
267
|
+
default_model="local", aliases=[], auth_method="none", icon="JAN",
|
|
261
268
|
),
|
|
262
269
|
ProviderDefinition(
|
|
263
270
|
id="llamacpp", name="Llama.cpp Server", description="Local C++ inference server",
|
|
264
271
|
kind="openai-compatible", base_url="http://localhost:8080",
|
|
265
|
-
default_model="local", aliases=["llama-cpp"], auth_method="none", icon="
|
|
272
|
+
default_model="local", aliases=["llama-cpp"], auth_method="none", icon="LPP",
|
|
266
273
|
),
|
|
267
274
|
ProviderDefinition(
|
|
268
275
|
id="watsonx", name="IBM Watsonx", description="Enterprise AI platform with Granite models",
|
|
269
276
|
kind="openai-compatible", base_url="https://us-south.ml.cloud.ibm.com",
|
|
270
277
|
default_model="ibm/granite-3-8b-instruct",
|
|
271
|
-
aliases=["ibm"], icon="
|
|
278
|
+
aliases=["ibm"], icon="IBM",
|
|
272
279
|
),
|
|
273
280
|
ProviderDefinition(
|
|
274
|
-
id="aleph-alpha", name="Aleph Alpha", description="European sovereign AI
|
|
281
|
+
id="aleph-alpha", name="Aleph Alpha", description="European sovereign AI Luminous models",
|
|
275
282
|
kind="openai-compatible", base_url="https://api.aleph-alpha.eu/v1",
|
|
276
|
-
default_model="luminous-supreme-control", aliases=[], icon="
|
|
283
|
+
default_model="luminous-supreme-control", aliases=[], icon="ALP",
|
|
277
284
|
),
|
|
278
285
|
ProviderDefinition(
|
|
279
286
|
id="predibase", name="Predibase", description="Fine-tune and serve open-source LLMs",
|
|
280
287
|
kind="openai-compatible", base_url="https://serving.app.predibase.com/v1",
|
|
281
|
-
default_model="meta-llama/Meta-Llama-3.1-8B-Instruct", aliases=[], icon="
|
|
288
|
+
default_model="meta-llama/Meta-Llama-3.1-8B-Instruct", aliases=[], icon="PDB",
|
|
282
289
|
),
|
|
283
290
|
ProviderDefinition(
|
|
284
291
|
id="gravity", name="Gravity API", description="Multi-model API gateway",
|
|
285
292
|
kind="openai-compatible", base_url="https://api.gravityapi.com/v1",
|
|
286
|
-
default_model="auto", aliases=[], icon="
|
|
293
|
+
default_model="auto", aliases=[], icon="GRV",
|
|
287
294
|
),
|
|
288
295
|
ProviderDefinition(
|
|
289
296
|
id="infermatic", name="Infermatic", description="Fast inference for popular open models",
|
|
290
297
|
kind="openai-compatible", base_url="https://api.infermatic.ai/v1",
|
|
291
|
-
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="
|
|
298
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="INF",
|
|
292
299
|
),
|
|
293
300
|
]
|
|
294
301
|
|
|
302
|
+
|
|
295
303
|
_BY_ID: dict[str, ProviderDefinition] = {p.id: p for p in _BUILTIN}
|
|
296
304
|
|
|
297
305
|
|
|
@@ -330,4 +338,4 @@ def search(query: str) -> list[ProviderDefinition]:
|
|
|
330
338
|
if score > 0:
|
|
331
339
|
results.append((score, p))
|
|
332
340
|
results.sort(key=lambda x: (-x[0], x[1].name))
|
|
333
|
-
return [p for _, p in results]
|
|
341
|
+
return [p for _, p in results]
|
|
@@ -96,6 +96,7 @@ class ProviderPalette:
|
|
|
96
96
|
CONFIRM = "confirm"
|
|
97
97
|
WIZARD = "wizard"
|
|
98
98
|
MESSAGE = "message"
|
|
99
|
+
MODELS = "models"
|
|
99
100
|
|
|
100
101
|
def __init__(
|
|
101
102
|
self,
|
|
@@ -163,7 +164,7 @@ class ProviderPalette:
|
|
|
163
164
|
def is_list_screen(self) -> bool:
|
|
164
165
|
return self._active and self.screen in (
|
|
165
166
|
self.LIST, self.KEYS, self.MANAGE, self.RENAME_SEL,
|
|
166
|
-
self.DELETE_SEL, self.CONFIRM, self.WIZARD,
|
|
167
|
+
self.DELETE_SEL, self.CONFIRM, self.WIZARD, self.MODELS,
|
|
167
168
|
) and not self.is_input_screen()
|
|
168
169
|
|
|
169
170
|
def activate(self) -> None:
|
|
@@ -221,6 +222,10 @@ class ProviderPalette:
|
|
|
221
222
|
self.screen = self._message_next
|
|
222
223
|
self.selected_index = 0
|
|
223
224
|
self._scroll_offset = 0
|
|
225
|
+
elif self.screen == self.MODELS:
|
|
226
|
+
self.screen = self.MANAGE
|
|
227
|
+
self.selected_index = 0
|
|
228
|
+
self._scroll_offset = 0
|
|
224
229
|
elif self.screen == self.WIZARD:
|
|
225
230
|
if self.wizard_step_idx > 0:
|
|
226
231
|
self.wizard_step_idx -= 1
|
|
@@ -311,6 +316,8 @@ class ProviderPalette:
|
|
|
311
316
|
if step.field == "auth_method":
|
|
312
317
|
return AUTH_OPTIONS
|
|
313
318
|
return []
|
|
319
|
+
if self.screen == self.MODELS:
|
|
320
|
+
return self._models_items()
|
|
314
321
|
return []
|
|
315
322
|
|
|
316
323
|
def _list_items(self) -> list[PaletteItem]:
|
|
@@ -327,14 +334,14 @@ class ProviderPalette:
|
|
|
327
334
|
if d.name.lower() in stored:
|
|
328
335
|
sp = stored[d.name.lower()]
|
|
329
336
|
active_key = sp.active_key()
|
|
330
|
-
icon = "
|
|
337
|
+
icon = "?" if active_key else " "
|
|
331
338
|
items.append(PaletteItem(
|
|
332
339
|
label=d.name, description=d.description, icon=icon,
|
|
333
340
|
kind="stored", data=sp,
|
|
334
341
|
))
|
|
335
342
|
else:
|
|
336
343
|
items.append(PaletteItem(
|
|
337
|
-
label=d.name, description=d.description, icon="
|
|
344
|
+
label=d.name, description=d.description, icon="?",
|
|
338
345
|
kind="catalog", data=d,
|
|
339
346
|
))
|
|
340
347
|
|
|
@@ -342,7 +349,7 @@ class ProviderPalette:
|
|
|
342
349
|
if sp.name.lower() not in {d.name.lower() for d in defs}:
|
|
343
350
|
if not q or q in sp.name.lower() or q in sp.kind.lower() or q in sp.base_url.lower():
|
|
344
351
|
active_key = sp.active_key()
|
|
345
|
-
icon = "
|
|
352
|
+
icon = "?" if active_key else " "
|
|
346
353
|
items.append(PaletteItem(
|
|
347
354
|
label=sp.name, description=f"{sp.kind} {sp.base_url}", icon=icon,
|
|
348
355
|
kind="stored", data=sp,
|
|
@@ -359,7 +366,7 @@ class ProviderPalette:
|
|
|
359
366
|
if self.current_provider:
|
|
360
367
|
for k in self.current_provider.keys:
|
|
361
368
|
is_active = k.id == self.current_provider.active_key_id
|
|
362
|
-
icon = "
|
|
369
|
+
icon = "?" if is_active else " "
|
|
363
370
|
desc = "active" if is_active else ""
|
|
364
371
|
items.append(PaletteItem(label=k.label, description=desc, icon=icon, kind="action", data=k))
|
|
365
372
|
items.append(PaletteItem(label="Add API Key", icon="+", kind="action"))
|
|
@@ -369,13 +376,14 @@ class ProviderPalette:
|
|
|
369
376
|
items: list[PaletteItem] = []
|
|
370
377
|
sp = self.current_provider
|
|
371
378
|
if sp and len(sp.keys) > 1:
|
|
372
|
-
items.append(PaletteItem("Switch Key", "Change the active API key", icon="
|
|
379
|
+
items.append(PaletteItem("Switch Key", "Change the active API key", icon=""))
|
|
373
380
|
items.append(PaletteItem("Add Key", "Add another API key", icon="+"))
|
|
374
381
|
if sp and sp.keys:
|
|
375
|
-
items.append(PaletteItem("Rename Key", "Rename a key label", icon="
|
|
376
|
-
items.append(PaletteItem("Delete Key", "Remove a key", icon="
|
|
377
|
-
items.append(PaletteItem("
|
|
378
|
-
items.append(PaletteItem("
|
|
382
|
+
items.append(PaletteItem("Rename Key", "Rename a key label", icon="?"))
|
|
383
|
+
items.append(PaletteItem("Delete Key", "Remove a key", icon="?"))
|
|
384
|
+
items.append(PaletteItem("View Models", "Browse and set default model", icon="M"))
|
|
385
|
+
items.append(PaletteItem("Edit Provider", "Change base URL, model, etc.", icon="?"))
|
|
386
|
+
items.append(PaletteItem("Remove Provider", "Delete provider and all keys", icon="?", kind="action"))
|
|
379
387
|
return items
|
|
380
388
|
|
|
381
389
|
def _key_select_items(self, _mode: str) -> list[PaletteItem]:
|
|
@@ -383,10 +391,43 @@ class ProviderPalette:
|
|
|
383
391
|
if self.current_provider:
|
|
384
392
|
for k in self.current_provider.keys:
|
|
385
393
|
is_active = k.id == self.current_provider.active_key_id
|
|
386
|
-
icon = "
|
|
394
|
+
icon = "?" if is_active else " "
|
|
387
395
|
items.append(PaletteItem(label=k.label, icon=icon, kind="action", data=k))
|
|
388
396
|
return items
|
|
389
397
|
|
|
398
|
+
def _models_items(self) -> list[PaletteItem]:
|
|
399
|
+
items: list[PaletteItem] = []
|
|
400
|
+
if not self.current_provider:
|
|
401
|
+
return items
|
|
402
|
+
|
|
403
|
+
sp = self.current_provider
|
|
404
|
+
try:
|
|
405
|
+
provider = build_provider(ProviderRecord(
|
|
406
|
+
name=sp.name, kind=sp.kind, base_url=sp.base_url,
|
|
407
|
+
api_key=sp.decrypted_api_key(), default_model=sp.default_model,
|
|
408
|
+
auth_method=sp.auth_method,
|
|
409
|
+
))
|
|
410
|
+
models = provider.fetch_models()
|
|
411
|
+
except Exception as exc:
|
|
412
|
+
log.warning("failed to fetch models for %s: %s", sp.name, exc)
|
|
413
|
+
items.append(PaletteItem("Error loading models", str(exc), icon="!", kind="action"))
|
|
414
|
+
return items
|
|
415
|
+
|
|
416
|
+
if not models:
|
|
417
|
+
items.append(PaletteItem("No models available", "Provider returned empty model list", icon="!", kind="action"))
|
|
418
|
+
return items
|
|
419
|
+
|
|
420
|
+
current_model = sp.default_model or ""
|
|
421
|
+
for model in models:
|
|
422
|
+
is_current = model == current_model
|
|
423
|
+
icon = "?" if is_current else " "
|
|
424
|
+
desc = "current" if is_current else ""
|
|
425
|
+
items.append(PaletteItem(
|
|
426
|
+
label=model, description=desc, icon=icon, kind="model", data=model
|
|
427
|
+
))
|
|
428
|
+
|
|
429
|
+
return items
|
|
430
|
+
|
|
390
431
|
def _handle_list_enter(self, item: PaletteItem) -> None:
|
|
391
432
|
if self.screen == self.LIST:
|
|
392
433
|
self._on_list_select(item)
|
|
@@ -402,6 +443,8 @@ class ProviderPalette:
|
|
|
402
443
|
self._on_confirm_select(item)
|
|
403
444
|
elif self.screen == self.WIZARD:
|
|
404
445
|
self._on_wizard_select(item)
|
|
446
|
+
elif self.screen == self.MODELS:
|
|
447
|
+
self._on_models_select(item)
|
|
405
448
|
|
|
406
449
|
def _on_list_select(self, item: PaletteItem) -> None:
|
|
407
450
|
if item.kind == "action":
|
|
@@ -472,6 +515,11 @@ class ProviderPalette:
|
|
|
472
515
|
self.selected_index = 0
|
|
473
516
|
self._scroll_offset = 0
|
|
474
517
|
self._invalidate()
|
|
518
|
+
elif label == "View Models":
|
|
519
|
+
self.screen = self.MODELS
|
|
520
|
+
self.selected_index = 0
|
|
521
|
+
self._scroll_offset = 0
|
|
522
|
+
self._invalidate()
|
|
475
523
|
elif label == "Edit Provider":
|
|
476
524
|
self._start_wizard(edit=True)
|
|
477
525
|
elif label == "Remove Provider":
|
|
@@ -503,6 +551,46 @@ class ProviderPalette:
|
|
|
503
551
|
self._scroll_offset = 0
|
|
504
552
|
self._invalidate()
|
|
505
553
|
|
|
554
|
+
def _models_items(self) -> list[PaletteItem]:
|
|
555
|
+
items: list[PaletteItem] = []
|
|
556
|
+
if not self.current_provider:
|
|
557
|
+
return items
|
|
558
|
+
|
|
559
|
+
sp = self.current_provider
|
|
560
|
+
try:
|
|
561
|
+
provider = build_provider(ProviderRecord(
|
|
562
|
+
name=sp.name, kind=sp.kind, base_url=sp.base_url,
|
|
563
|
+
api_key=sp.decrypted_api_key(), default_model=sp.default_model,
|
|
564
|
+
auth_method=sp.auth_method,
|
|
565
|
+
))
|
|
566
|
+
models = provider.fetch_models()
|
|
567
|
+
except Exception as exc:
|
|
568
|
+
log.warning("failed to fetch models for %s: %s", sp.name, exc)
|
|
569
|
+
items.append(PaletteItem("Error loading models", str(exc), icon="!", kind="action"))
|
|
570
|
+
return items
|
|
571
|
+
|
|
572
|
+
if not models:
|
|
573
|
+
items.append(PaletteItem("No models available", "Provider returned empty model list", icon="!", kind="action"))
|
|
574
|
+
return items
|
|
575
|
+
|
|
576
|
+
current_model = sp.default_model or ""
|
|
577
|
+
for model in models:
|
|
578
|
+
is_current = model == current_model
|
|
579
|
+
icon = "?" if is_current else " "
|
|
580
|
+
desc = "current" if is_current else ""
|
|
581
|
+
items.append(PaletteItem(
|
|
582
|
+
label=model, description=desc, icon=icon, kind="model", data=model
|
|
583
|
+
))
|
|
584
|
+
|
|
585
|
+
return items
|
|
586
|
+
|
|
587
|
+
def _on_models_select(self, item: PaletteItem) -> None:
|
|
588
|
+
if item.kind == "model" and item.data:
|
|
589
|
+
self.current_provider.default_model = item.data
|
|
590
|
+
self.db.save_provider(self.current_provider)
|
|
591
|
+
self._register_current()
|
|
592
|
+
self._show_message(f"Set default model to '{item.data}'", self.MANAGE)
|
|
593
|
+
|
|
506
594
|
def _on_confirm_select(self, item: PaletteItem) -> None:
|
|
507
595
|
if item.label == "Confirm":
|
|
508
596
|
if self._pending_action:
|
|
@@ -524,6 +612,13 @@ class ProviderPalette:
|
|
|
524
612
|
self.wizard_data[step.field] = item.label
|
|
525
613
|
self._wizard_advance()
|
|
526
614
|
|
|
615
|
+
def _on_models_select(self, item: PaletteItem) -> None:
|
|
616
|
+
if item.kind == "model" and item.data:
|
|
617
|
+
self.current_provider.default_model = item.data
|
|
618
|
+
self.db.save_provider(self.current_provider)
|
|
619
|
+
self._register_current()
|
|
620
|
+
self._show_message(f"Set default model to '{item.data}'", self.MANAGE)
|
|
621
|
+
|
|
527
622
|
def _handle_input_enter(self) -> None:
|
|
528
623
|
text = self.input_buf.text
|
|
529
624
|
if self.screen == self.KEY_LABEL:
|
|
@@ -820,6 +915,9 @@ class ProviderPalette:
|
|
|
820
915
|
return [(f"bold {ACCENT}", " + Custom Provider"), ("", " "), (DIM, f"Step {self.wizard_step_idx + 1}/{total}: {step.prompt}")]
|
|
821
916
|
if self.screen == self.MESSAGE:
|
|
822
917
|
return [(f"bold {OK}", " ✓ Done")]
|
|
918
|
+
if self.screen == self.MODELS:
|
|
919
|
+
name = self.current_provider.name if self.current_provider else ""
|
|
920
|
+
return [(f"bold {ACCENT}", f" {name}"), ("", " "), (DIM, "Models")]
|
|
823
921
|
return [(f"bold {ACCENT}", " Provider")]
|
|
824
922
|
|
|
825
923
|
def content_fragments(self) -> list[tuple[str, str]]:
|
|
@@ -829,7 +927,7 @@ class ProviderPalette:
|
|
|
829
927
|
return [(f"{WARN}", f" {self._confirm_text}")]
|
|
830
928
|
if self.screen == self.WIZARD and self.is_input_screen():
|
|
831
929
|
step = WIZARD_STEPS[self.wizard_step_idx]
|
|
832
|
-
hint = " (optional
|
|
930
|
+
hint = " (optional - press Enter to skip)" if step.optional else ""
|
|
833
931
|
return [(DIM, f" {step.prompt}{hint}")]
|
|
834
932
|
if self.is_input_screen():
|
|
835
933
|
return [(DIM, f" {self.input_prompt_text}")]
|
|
@@ -850,7 +948,7 @@ class ProviderPalette:
|
|
|
850
948
|
desc = item.description
|
|
851
949
|
remaining = max(1, inner - len(label_part) - 1)
|
|
852
950
|
if len(desc) > remaining:
|
|
853
|
-
desc = desc[: remaining - 1] + "
|
|
951
|
+
desc = desc[: remaining - 1] + "."
|
|
854
952
|
desc_part = f" {desc}" if desc else ""
|
|
855
953
|
pad = max(0, inner - len(label_part) - len(desc_part))
|
|
856
954
|
if is_sel:
|
|
@@ -878,12 +976,14 @@ class ProviderPalette:
|
|
|
878
976
|
if self.is_input_screen():
|
|
879
977
|
return [(DIM, " Enter to continue Esc to go back")]
|
|
880
978
|
if self.screen == self.CONFIRM:
|
|
881
|
-
return [(DIM, "
|
|
979
|
+
return [(DIM, " Select Enter Confirm Esc Cancel")]
|
|
882
980
|
if self.screen == self.MESSAGE:
|
|
883
981
|
return [(DIM, " Enter to continue")]
|
|
884
982
|
if self.screen == self.LIST:
|
|
885
|
-
return [(DIM, "
|
|
886
|
-
|
|
983
|
+
return [(DIM, " Navigate Enter Select Esc Close Type to search")]
|
|
984
|
+
if self.screen == self.MODELS:
|
|
985
|
+
return [(DIM, " Navigate Enter Select Esc Back")]
|
|
986
|
+
return [(DIM, " Navigate Enter Select Esc Back")]
|
|
887
987
|
|
|
888
988
|
def input_label(self) -> str:
|
|
889
989
|
if self.screen == self.KEY_LABEL:
|
|
@@ -899,8 +999,8 @@ class ProviderPalette:
|
|
|
899
999
|
|
|
900
1000
|
def search_label(self) -> str:
|
|
901
1001
|
if self.screen == self.LIST:
|
|
902
|
-
return "
|
|
903
|
-
return "
|
|
1002
|
+
return " ? "
|
|
1003
|
+
return " > "
|
|
904
1004
|
|
|
905
1005
|
def content_height(self) -> int:
|
|
906
1006
|
if self.screen == self.MESSAGE:
|
|
@@ -913,4 +1013,4 @@ class ProviderPalette:
|
|
|
913
1013
|
return max(1, min(len(items), self.max_content_lines()))
|
|
914
1014
|
|
|
915
1015
|
def total_height(self) -> int:
|
|
916
|
-
return self.content_height() + 6
|
|
1016
|
+
return self.content_height() + 6
|