@agxnte/reidx 2.0.3 → 2.0.5
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 +1 -1
- package/pyproject.toml +1 -1
- package/src/reidx/__init__.py +1 -1
- package/src/reidx/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/app/commands.py +1 -1
- package/src/reidx/config/__pycache__/models.cpython-312.pyc +0 -0
- package/src/reidx/config/__pycache__/settings.cpython-312.pyc +0 -0
- package/src/reidx/config/models.py +1 -0
- package/src/reidx/config/settings.py +10 -9
- package/src/reidx/provider/__pycache__/anthropic.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/ollama.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/openai.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/registry.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/store.cpython-312.pyc +0 -0
- package/src/reidx/provider/anthropic.py +22 -29
- package/src/reidx/provider/ollama.py +1 -5
- package/src/reidx/provider/openai.py +26 -7
- package/src/reidx/provider/registry.py +1 -0
- package/src/reidx/provider/store.py +9 -3
- package/src/reidx/provider_manager/catalog.py +11 -8
- package/src/reidx/provider_manager/keychain.py +20 -3
- package/src/reidx/provider_manager/palette.py +5 -3
- package/src/reidx/ui/__pycache__/commands.cpython-312.pyc +0 -0
- package/src/reidx/ui/commands.py +2 -1
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/reidx/__init__.py
CHANGED
|
Binary file
|
|
@@ -207,7 +207,7 @@ def doctor() -> None:
|
|
|
207
207
|
console.print(f"storage {cfg.storage_root}")
|
|
208
208
|
console.print(f"provider {cfg.default_provider}")
|
|
209
209
|
console.print(f"mode {cfg.policy.default_mode.value}")
|
|
210
|
-
console.print(f"providers {orch.provider.name} (active), {orch.tools.definitions()
|
|
210
|
+
console.print(f"providers {orch.provider.name} (active), {len(orch.tools.definitions())} tools")
|
|
211
211
|
import os
|
|
212
212
|
|
|
213
213
|
has_key = bool(os.environ.get("ANTHROPIC_API_KEY", "").strip())
|
|
Binary file
|
|
Binary file
|
|
@@ -10,13 +10,14 @@ ignored harmlessly.
|
|
|
10
10
|
Path resolution (first hit wins):
|
|
11
11
|
1. $REIDCHAT_SETTINGS (explicit override)
|
|
12
12
|
2. ./settings.json (project-local, when it exists)
|
|
13
|
-
3.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
even when the shell's
|
|
13
|
+
3. ~/.reidx/settings.json (global default)
|
|
14
|
+
4. ~/Reidchat.json (legacy fallback)
|
|
15
|
+
|
|
16
|
+
Project-local wins over the global file so a project can bake in a different
|
|
17
|
+
backend, permission mode, or provider set without editing the global file.
|
|
18
|
+
The env block is authoritative for the process (overrides any ambient env)
|
|
19
|
+
— this is how ReidX picks up proxy credentials even when the shell's
|
|
20
|
+
ANTHROPIC_* vars point somewhere else.
|
|
20
21
|
"""
|
|
21
22
|
from __future__ import annotations
|
|
22
23
|
|
|
@@ -29,9 +30,9 @@ from reidx.diagnostics.logger import get_logger
|
|
|
29
30
|
|
|
30
31
|
log = get_logger("reidx.config.settings")
|
|
31
32
|
|
|
32
|
-
LEGACY_SETTINGS_PATH = Path("E:/leech/Reidchat.json")
|
|
33
33
|
GLOBAL_SETTINGS_PATH = app_data_dir() / "settings.json"
|
|
34
34
|
PROJECT_SETTINGS_FILENAME = "settings.json"
|
|
35
|
+
LEGACY_SETTINGS_PATH = Path.home() / "Reidchat.json"
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def _walk_upward_for_project_settings(start: Path) -> Path | None:
|
|
@@ -62,7 +63,7 @@ def settings_path() -> Path:
|
|
|
62
63
|
1. $REIDCHAT_SETTINGS (explicit override)
|
|
63
64
|
2. project settings.json (walk upward from CWD)
|
|
64
65
|
3. ~/.reidx/settings.json (global default)
|
|
65
|
-
4.
|
|
66
|
+
4. ~/Reidchat.json (legacy fallback)
|
|
66
67
|
|
|
67
68
|
Returns the last fallback even if it doesn't exist, so `doctor` can
|
|
68
69
|
report it as "missing" rather than crashing on a None.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -11,13 +11,11 @@ Env vars consumed:
|
|
|
11
11
|
"""
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
import json
|
|
15
14
|
import os
|
|
16
|
-
import urllib.error
|
|
17
|
-
import urllib.request
|
|
18
15
|
from typing import Any
|
|
19
16
|
|
|
20
17
|
from reidx.diagnostics.logger import get_logger
|
|
18
|
+
from reidx.provider._http import get_json, post_json
|
|
21
19
|
from reidx.provider.base import BaseProvider, Message, ProviderResponse, ToolCall, Usage
|
|
22
20
|
|
|
23
21
|
log = get_logger("reidx.provider.anthropic")
|
|
@@ -25,7 +23,6 @@ log = get_logger("reidx.provider.anthropic")
|
|
|
25
23
|
DEFAULT_BASE_URL = "https://api.anthropic.com"
|
|
26
24
|
API_VERSION = "2023-06-01"
|
|
27
25
|
MAX_TOKENS = 4096
|
|
28
|
-
TIMEOUT_SECONDS = 120
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
class AnthropicProvider(BaseProvider):
|
|
@@ -53,6 +50,12 @@ class AnthropicProvider(BaseProvider):
|
|
|
53
50
|
model = os.environ.get("ANTHROPIC_MODEL", "").strip()
|
|
54
51
|
return cls(api_key=key, base_url=base, default_model=model)
|
|
55
52
|
|
|
53
|
+
def _headers(self) -> dict[str, str]:
|
|
54
|
+
return {
|
|
55
|
+
"x-api-key": self.api_key,
|
|
56
|
+
"anthropic-version": API_VERSION,
|
|
57
|
+
}
|
|
58
|
+
|
|
56
59
|
def _to_anthropic_messages(self, messages: list[Message]) -> tuple[str | None, list[dict]]:
|
|
57
60
|
"""Convert ReidX Messages to Anthropic format. Returns (system, messages)."""
|
|
58
61
|
system: str | None = None
|
|
@@ -62,7 +65,6 @@ class AnthropicProvider(BaseProvider):
|
|
|
62
65
|
system = m.content
|
|
63
66
|
continue
|
|
64
67
|
if m.role == "tool":
|
|
65
|
-
# Tool results -> Anthropic tool_result content block.
|
|
66
68
|
out.append({
|
|
67
69
|
"role": "user",
|
|
68
70
|
"content": [{
|
|
@@ -73,7 +75,6 @@ class AnthropicProvider(BaseProvider):
|
|
|
73
75
|
})
|
|
74
76
|
continue
|
|
75
77
|
if m.role == "assistant" and m.tool_calls:
|
|
76
|
-
# Assistant turn with tool calls -> content blocks.
|
|
77
78
|
blocks: list[dict] = []
|
|
78
79
|
if m.content:
|
|
79
80
|
blocks.append({"type": "text", "text": m.content})
|
|
@@ -86,7 +87,6 @@ class AnthropicProvider(BaseProvider):
|
|
|
86
87
|
})
|
|
87
88
|
out.append({"role": "assistant", "content": blocks})
|
|
88
89
|
continue
|
|
89
|
-
# Plain user or assistant text.
|
|
90
90
|
out.append({"role": m.role, "content": m.content})
|
|
91
91
|
return system, out
|
|
92
92
|
|
|
@@ -150,27 +150,20 @@ class AnthropicProvider(BaseProvider):
|
|
|
150
150
|
if anthropic_tools:
|
|
151
151
|
payload["tools"] = anthropic_tools
|
|
152
152
|
|
|
153
|
-
body = json.dumps(payload).encode("utf-8")
|
|
154
153
|
url = f"{self.base_url}/v1/messages"
|
|
155
|
-
req = urllib.request.Request(
|
|
156
|
-
url,
|
|
157
|
-
data=body,
|
|
158
|
-
headers={
|
|
159
|
-
"x-api-key": self.api_key,
|
|
160
|
-
"anthropic-version": API_VERSION,
|
|
161
|
-
"content-type": "application/json",
|
|
162
|
-
},
|
|
163
|
-
method="POST",
|
|
164
|
-
)
|
|
165
154
|
try:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
155
|
+
body = post_json(url, payload, self._headers())
|
|
156
|
+
except RuntimeError:
|
|
157
|
+
log.exception("Anthropic API request failed")
|
|
158
|
+
raise
|
|
159
|
+
return self._parse_response(body, model)
|
|
160
|
+
|
|
161
|
+
def fetch_models(self) -> list[str]:
|
|
162
|
+
url = f"{self.base_url}/v1/models"
|
|
163
|
+
body = get_json(url, self._headers())
|
|
164
|
+
models: list[str] = []
|
|
165
|
+
for item in body.get("data", []):
|
|
166
|
+
mid = item.get("id", "")
|
|
167
|
+
if mid:
|
|
168
|
+
models.append(mid)
|
|
169
|
+
return sorted(models)
|
|
@@ -109,11 +109,7 @@ class OllamaProvider(BaseProvider):
|
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
def fetch_models(self) -> list[str]:
|
|
112
|
-
|
|
113
|
-
body = get_json(f"{self.base_url}/api/tags", {})
|
|
114
|
-
except RuntimeError:
|
|
115
|
-
log.debug("failed to fetch models from ollama")
|
|
116
|
-
return []
|
|
112
|
+
body = get_json(f"{self.base_url}/api/tags", {})
|
|
117
113
|
models: list[str] = []
|
|
118
114
|
for item in body.get("models", []):
|
|
119
115
|
name = item.get("name", "")
|
|
@@ -127,11 +127,7 @@ class OpenAIProvider(BaseProvider):
|
|
|
127
127
|
|
|
128
128
|
def fetch_models(self) -> list[str]:
|
|
129
129
|
url = f"{self.base_url}/v1/models"
|
|
130
|
-
|
|
131
|
-
body = get_json(url, self._headers())
|
|
132
|
-
except RuntimeError:
|
|
133
|
-
log.debug("failed to fetch models from %s", url)
|
|
134
|
-
return []
|
|
130
|
+
body = get_json(url, self._headers())
|
|
135
131
|
models: list[str] = []
|
|
136
132
|
for item in body.get("data", []):
|
|
137
133
|
mid = item.get("id", "")
|
|
@@ -144,14 +140,37 @@ class OpenAICompatibleProvider(OpenAIProvider):
|
|
|
144
140
|
"""OpenAI-compatible endpoints (llama.cpp server, LM Studio, vLLM, etc.).
|
|
145
141
|
|
|
146
142
|
Same wire format as OpenAI; only default base_url differs and the API key
|
|
147
|
-
is optional.
|
|
148
|
-
|
|
143
|
+
is optional. Auth method is configurable so providers that use x-api-key
|
|
144
|
+
instead of Bearer are handled correctly. Kept as a subclass so `/providers`
|
|
145
|
+
can show the kind separately in the list.
|
|
149
146
|
"""
|
|
150
147
|
|
|
151
148
|
name = "openai-compatible"
|
|
152
149
|
DEFAULT_BASE_URL = "http://localhost:8080"
|
|
153
150
|
DEFAULT_MODEL = "local"
|
|
154
151
|
|
|
152
|
+
def __init__(
|
|
153
|
+
self,
|
|
154
|
+
api_key: str,
|
|
155
|
+
base_url: str = "",
|
|
156
|
+
default_model: str = "",
|
|
157
|
+
auth_method: str = "bearer",
|
|
158
|
+
) -> None:
|
|
159
|
+
self.auth_method = auth_method
|
|
160
|
+
super().__init__(api_key=api_key, base_url=base_url, default_model=default_model)
|
|
161
|
+
|
|
162
|
+
def _headers(self) -> dict[str, str]:
|
|
163
|
+
h = {}
|
|
164
|
+
if not self.api_key:
|
|
165
|
+
return h
|
|
166
|
+
if self.auth_method == "x-api-key":
|
|
167
|
+
h["x-api-key"] = self.api_key
|
|
168
|
+
elif self.auth_method == "none":
|
|
169
|
+
pass
|
|
170
|
+
else:
|
|
171
|
+
h["authorization"] = f"Bearer {self.api_key}"
|
|
172
|
+
return h
|
|
173
|
+
|
|
155
174
|
|
|
156
175
|
def _json_dump(obj) -> str: # type: ignore[no-untyped-def]
|
|
157
176
|
import json
|
|
@@ -79,6 +79,7 @@ def default_registry(config: Config) -> ProviderRegistry:
|
|
|
79
79
|
base_url=pc.base_url or "",
|
|
80
80
|
api_key=pc.api_key.get_secret_value() if pc.api_key else "",
|
|
81
81
|
default_model=pc.default_model,
|
|
82
|
+
auth_method=pc.auth_method,
|
|
82
83
|
))
|
|
83
84
|
except (ValueError, TypeError):
|
|
84
85
|
log.warning("provider '%s' (kind=%s) configured but could not be built; skipping", name, kind)
|
|
@@ -39,6 +39,7 @@ class ProviderRecord:
|
|
|
39
39
|
base_url: str = ""
|
|
40
40
|
api_key: str = ""
|
|
41
41
|
default_model: str = ""
|
|
42
|
+
auth_method: str = "bearer"
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
def build_provider(record: ProviderRecord) -> BaseProvider:
|
|
@@ -60,6 +61,7 @@ def build_provider(record: ProviderRecord) -> BaseProvider:
|
|
|
60
61
|
api_key=record.api_key,
|
|
61
62
|
base_url=record.base_url,
|
|
62
63
|
default_model=record.default_model,
|
|
64
|
+
auth_method=record.auth_method,
|
|
63
65
|
)
|
|
64
66
|
if kind == "ollama":
|
|
65
67
|
return OllamaProvider(
|
|
@@ -130,12 +132,15 @@ def validate_provider(record: ProviderRecord) -> tuple[bool, str]:
|
|
|
130
132
|
provider = build_provider(record)
|
|
131
133
|
except (ValueError, TypeError) as exc:
|
|
132
134
|
return False, str(exc)
|
|
133
|
-
|
|
135
|
+
_keyless_kinds = ("ollama", "openai-compatible")
|
|
136
|
+
if not record.api_key and record.kind not in _keyless_kinds:
|
|
137
|
+
return False, "API key required for this provider kind"
|
|
138
|
+
if not record.api_key:
|
|
134
139
|
return True, "no key required"
|
|
135
140
|
try:
|
|
136
141
|
models = provider.fetch_models()
|
|
137
|
-
except Exception:
|
|
138
|
-
return False, "could not connect to provider"
|
|
142
|
+
except Exception as exc:
|
|
143
|
+
return False, f"could not connect to provider: {exc}"
|
|
139
144
|
if models:
|
|
140
145
|
return True, f"ok ({len(models)} models available)"
|
|
141
146
|
if record.kind == "ollama":
|
|
@@ -170,6 +175,7 @@ def load_from_database(registry: ProviderRegistry, storage_root: Path) -> list[s
|
|
|
170
175
|
base_url=sp.base_url,
|
|
171
176
|
api_key=api_key,
|
|
172
177
|
default_model=sp.default_model,
|
|
178
|
+
auth_method=sp.auth_method,
|
|
173
179
|
)
|
|
174
180
|
try:
|
|
175
181
|
provider = build_provider(record)
|
|
@@ -113,8 +113,8 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
113
113
|
default_model="auto", aliases=[], auth_method="none", icon="🚀",
|
|
114
114
|
),
|
|
115
115
|
ProviderDefinition(
|
|
116
|
-
id="azure-openai", name="Azure OpenAI", description="OpenAI models via Azure cloud",
|
|
117
|
-
kind="openai", base_url="", default_model="gpt-4o-mini",
|
|
116
|
+
id="azure-openai", name="Azure OpenAI", description="OpenAI models via Azure cloud (set base URL to your endpoint)",
|
|
117
|
+
kind="openai", base_url="https://api.openai.com", default_model="gpt-4o-mini",
|
|
118
118
|
aliases=["azure"], icon="☁️",
|
|
119
119
|
),
|
|
120
120
|
ProviderDefinition(
|
|
@@ -178,8 +178,9 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
178
178
|
default_model="jamba-1.5-large", aliases=[], icon="🃏",
|
|
179
179
|
),
|
|
180
180
|
ProviderDefinition(
|
|
181
|
-
id="databricks", name="Databricks", description="Serving endpoints for fine-tuned models",
|
|
182
|
-
kind="openai-compatible", base_url="
|
|
181
|
+
id="databricks", name="Databricks", description="Serving endpoints for fine-tuned models (set base URL to your workspace)",
|
|
182
|
+
kind="openai-compatible", base_url="https://dbc-xxxxxxxx.cloud.databricks.com/serving-endpoints",
|
|
183
|
+
default_model="databricks-dbrx-instruct",
|
|
183
184
|
aliases=[], icon="🧱",
|
|
184
185
|
),
|
|
185
186
|
ProviderDefinition(
|
|
@@ -205,7 +206,7 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
205
206
|
ProviderDefinition(
|
|
206
207
|
id="lambda", name="Lambda Labs", description="GPU cloud for AI training and inference",
|
|
207
208
|
kind="openai-compatible", base_url="https://api.lambdalabs.com/v1",
|
|
208
|
-
default_model="", aliases=["lambda-labs"], icon="λ",
|
|
209
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=["lambda-labs"], icon="λ",
|
|
209
210
|
),
|
|
210
211
|
ProviderDefinition(
|
|
211
212
|
id="kluster", name="Kluster AI", description="Distributed GPU network for inference",
|
|
@@ -234,7 +235,8 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
234
235
|
),
|
|
235
236
|
ProviderDefinition(
|
|
236
237
|
id="cloudflare-ai", name="Cloudflare Workers AI", description="Serverless AI inference at the edge",
|
|
237
|
-
kind="openai-compatible", base_url="
|
|
238
|
+
kind="openai-compatible", base_url="https://api.cloudflare.com/client/v4",
|
|
239
|
+
default_model="@cf/meta/llama-3.1-70b-instruct",
|
|
238
240
|
aliases=["cf-ai"], icon="orange",
|
|
239
241
|
),
|
|
240
242
|
ProviderDefinition(
|
|
@@ -245,7 +247,7 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
245
247
|
ProviderDefinition(
|
|
246
248
|
id="baseten", name="Baseten", description="Model deployment and serving platform",
|
|
247
249
|
kind="openai-compatible", base_url="https://inference.baseten.co/v1",
|
|
248
|
-
default_model="", aliases=[], icon="🏗️",
|
|
250
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="🏗️",
|
|
249
251
|
),
|
|
250
252
|
ProviderDefinition(
|
|
251
253
|
id="anyscale", name="Anyscale", description="Ray-based scalable model serving",
|
|
@@ -264,7 +266,8 @@ _BUILTIN: list[ProviderDefinition] = [
|
|
|
264
266
|
),
|
|
265
267
|
ProviderDefinition(
|
|
266
268
|
id="watsonx", name="IBM Watsonx", description="Enterprise AI platform with Granite models",
|
|
267
|
-
kind="openai-compatible", base_url="
|
|
269
|
+
kind="openai-compatible", base_url="https://us-south.ml.cloud.ibm.com",
|
|
270
|
+
default_model="ibm/granite-3-8b-instruct",
|
|
268
271
|
aliases=["ibm"], icon="🔷",
|
|
269
272
|
),
|
|
270
273
|
ProviderDefinition(
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
|
+
import getpass
|
|
5
|
+
import hashlib
|
|
4
6
|
import os
|
|
7
|
+
import socket
|
|
5
8
|
import sys
|
|
6
9
|
from pathlib import Path
|
|
7
10
|
|
|
@@ -70,6 +73,21 @@ if _IS_WINDOWS:
|
|
|
70
73
|
ctypes.windll.kernel32.LocalFree(blob_out.pbData)
|
|
71
74
|
|
|
72
75
|
|
|
76
|
+
def _machine_key() -> bytes:
|
|
77
|
+
parts = [
|
|
78
|
+
getpass.getuser(),
|
|
79
|
+
socket.gethostname(),
|
|
80
|
+
str(Path.home()),
|
|
81
|
+
sys.platform,
|
|
82
|
+
]
|
|
83
|
+
raw = "|".join(parts).encode("utf-8")
|
|
84
|
+
return hashlib.pbkdf2_hmac("sha256", raw, b"reidx-keychain-v1", 100000, dklen=32)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _xor_cipher(data: bytes, key: bytes) -> bytes:
|
|
88
|
+
return bytes(data[i] ^ key[i % len(key)] for i in range(len(data)))
|
|
89
|
+
|
|
90
|
+
|
|
73
91
|
def encrypt(plaintext: str) -> str:
|
|
74
92
|
if not plaintext:
|
|
75
93
|
return ""
|
|
@@ -77,8 +95,7 @@ def encrypt(plaintext: str) -> str:
|
|
|
77
95
|
if _IS_WINDOWS:
|
|
78
96
|
encrypted = _dpapi_encrypt(raw)
|
|
79
97
|
else:
|
|
80
|
-
|
|
81
|
-
encrypted = raw
|
|
98
|
+
encrypted = _xor_cipher(raw, _machine_key())
|
|
82
99
|
return base64.b64encode(encrypted).decode("ascii")
|
|
83
100
|
|
|
84
101
|
|
|
@@ -89,7 +106,7 @@ def decrypt(ciphertext: str) -> str:
|
|
|
89
106
|
if _IS_WINDOWS:
|
|
90
107
|
decrypted = _dpapi_decrypt(raw)
|
|
91
108
|
else:
|
|
92
|
-
decrypted = raw
|
|
109
|
+
decrypted = _xor_cipher(raw, _machine_key())
|
|
93
110
|
return decrypted.decode("utf-8")
|
|
94
111
|
|
|
95
112
|
|
|
@@ -606,6 +606,7 @@ class ProviderPalette:
|
|
|
606
606
|
record = ProviderRecord(
|
|
607
607
|
name=name, kind=kind, base_url=base_url,
|
|
608
608
|
api_key=api_key, default_model=model,
|
|
609
|
+
auth_method=auth,
|
|
609
610
|
)
|
|
610
611
|
ok, msg = validate_provider(record)
|
|
611
612
|
if not ok:
|
|
@@ -628,13 +629,12 @@ class ProviderPalette:
|
|
|
628
629
|
existing.default_model = model
|
|
629
630
|
existing.auth_method = auth
|
|
630
631
|
if api_key:
|
|
632
|
+
from reidx.provider_manager import keychain
|
|
631
633
|
existing.keys.append(StoredKey(
|
|
632
634
|
id=uuid.uuid4().hex[:12],
|
|
633
635
|
label="Default",
|
|
634
|
-
encrypted_key=
|
|
636
|
+
encrypted_key=keychain.encrypt(api_key),
|
|
635
637
|
))
|
|
636
|
-
from reidx.provider_manager import keychain
|
|
637
|
-
existing.keys[-1].encrypted_key = keychain.encrypt(api_key)
|
|
638
638
|
if existing.active_key_id is None:
|
|
639
639
|
existing.active_key_id = existing.keys[-1].id
|
|
640
640
|
self.db.save_provider(existing)
|
|
@@ -670,6 +670,7 @@ class ProviderPalette:
|
|
|
670
670
|
record = ProviderRecord(
|
|
671
671
|
name=name, kind=sp.kind, base_url=sp.base_url,
|
|
672
672
|
api_key=api_key, default_model=sp.default_model,
|
|
673
|
+
auth_method=sp.auth_method,
|
|
673
674
|
)
|
|
674
675
|
ok, msg = validate_provider(record)
|
|
675
676
|
if not ok:
|
|
@@ -708,6 +709,7 @@ class ProviderPalette:
|
|
|
708
709
|
record = ProviderRecord(
|
|
709
710
|
name=sp.name, kind=sp.kind, base_url=sp.base_url,
|
|
710
711
|
api_key=api_key, default_model=sp.default_model,
|
|
712
|
+
auth_method=sp.auth_method,
|
|
711
713
|
)
|
|
712
714
|
provider = build_provider(record)
|
|
713
715
|
if not sp.default_model:
|
|
Binary file
|
package/src/reidx/ui/commands.py
CHANGED
|
@@ -502,13 +502,14 @@ def _save_to_database(orchestrator: Orchestrator, record: ProviderRecord) -> Non
|
|
|
502
502
|
existing.active_key_id = k.id
|
|
503
503
|
existing.kind = record.kind
|
|
504
504
|
existing.base_url = record.base_url
|
|
505
|
+
existing.auth_method = record.auth_method
|
|
505
506
|
if record.default_model:
|
|
506
507
|
existing.default_model = record.default_model
|
|
507
508
|
db.save_provider(existing)
|
|
508
509
|
else:
|
|
509
510
|
sp = StoredProvider(
|
|
510
511
|
name=record.name, kind=record.kind, base_url=record.base_url,
|
|
511
|
-
default_model=record.default_model,
|
|
512
|
+
default_model=record.default_model, auth_method=record.auth_method,
|
|
512
513
|
)
|
|
513
514
|
if record.api_key:
|
|
514
515
|
k = StoredKey(
|