@agxnte/reidx 2.0.2
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/README.md +724 -0
- package/bin/reidx.js +33 -0
- package/package.json +40 -0
- package/pyproject.toml +45 -0
- package/scripts/find-python.js +24 -0
- package/scripts/postinstall.js +35 -0
- package/src/reidx/__init__.py +3 -0
- package/src/reidx/__main__.py +9 -0
- package/src/reidx/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/app/__init__.py +3 -0
- package/src/reidx/app/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/app/__pycache__/commands.cpython-312.pyc +0 -0
- package/src/reidx/app/commands.py +221 -0
- package/src/reidx/automation/__init__.py +3 -0
- package/src/reidx/automation/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/automation/__pycache__/exec.cpython-312.pyc +0 -0
- package/src/reidx/automation/exec.py +35 -0
- package/src/reidx/config/__init__.py +10 -0
- package/src/reidx/config/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/config/__pycache__/loader.cpython-312.pyc +0 -0
- 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/loader.py +104 -0
- package/src/reidx/config/models.py +49 -0
- package/src/reidx/config/settings.py +127 -0
- package/src/reidx/deepreid/__init__.py +8 -0
- package/src/reidx/deepreid/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/deepreid/__pycache__/pipeline.cpython-312.pyc +0 -0
- package/src/reidx/deepreid/pipeline.py +221 -0
- package/src/reidx/diagnostics/__init__.py +0 -0
- package/src/reidx/diagnostics/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/diagnostics/__pycache__/logger.cpython-312.pyc +0 -0
- package/src/reidx/diagnostics/logger.py +56 -0
- package/src/reidx/goals/__init__.py +21 -0
- package/src/reidx/goals/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/goals/__pycache__/models.cpython-312.pyc +0 -0
- package/src/reidx/goals/__pycache__/store.cpython-312.pyc +0 -0
- package/src/reidx/goals/models.py +101 -0
- package/src/reidx/goals/store.py +233 -0
- package/src/reidx/integrations/__init__.py +3 -0
- package/src/reidx/integrations/mcp.py +70 -0
- package/src/reidx/nyx/__init__.py +3 -0
- package/src/reidx/nyx/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/nyx/__pycache__/persona.cpython-312.pyc +0 -0
- package/src/reidx/nyx/persona.py +35 -0
- package/src/reidx/policy/__init__.py +15 -0
- package/src/reidx/policy/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/policy/__pycache__/engine.cpython-312.pyc +0 -0
- package/src/reidx/policy/__pycache__/models.cpython-312.pyc +0 -0
- package/src/reidx/policy/engine.py +101 -0
- package/src/reidx/policy/models.py +35 -0
- package/src/reidx/provider/__init__.py +16 -0
- package/src/reidx/provider/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/_http.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/anthropic.cpython-312.pyc +0 -0
- package/src/reidx/provider/__pycache__/base.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/__pycache__/stub.cpython-312.pyc +0 -0
- package/src/reidx/provider/_http.py +29 -0
- package/src/reidx/provider/anthropic.py +176 -0
- package/src/reidx/provider/base.py +50 -0
- package/src/reidx/provider/ollama.py +109 -0
- package/src/reidx/provider/openai.py +144 -0
- package/src/reidx/provider/registry.py +88 -0
- package/src/reidx/provider/store.py +141 -0
- package/src/reidx/provider/stub.py +60 -0
- package/src/reidx/provider_manager/__init__.py +40 -0
- package/src/reidx/provider_manager/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/provider_manager/__pycache__/catalog.cpython-312.pyc +0 -0
- package/src/reidx/provider_manager/__pycache__/database.cpython-312.pyc +0 -0
- package/src/reidx/provider_manager/__pycache__/keychain.cpython-312.pyc +0 -0
- package/src/reidx/provider_manager/__pycache__/palette.cpython-312.pyc +0 -0
- package/src/reidx/provider_manager/catalog.py +330 -0
- package/src/reidx/provider_manager/database.py +234 -0
- package/src/reidx/provider_manager/keychain.py +102 -0
- package/src/reidx/provider_manager/palette.py +831 -0
- package/src/reidx/runtime/__init__.py +5 -0
- package/src/reidx/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/runtime/__pycache__/agent.cpython-312.pyc +0 -0
- package/src/reidx/runtime/__pycache__/orchestrator.cpython-312.pyc +0 -0
- package/src/reidx/runtime/__pycache__/reasoning.cpython-312.pyc +0 -0
- package/src/reidx/runtime/__pycache__/state.cpython-312.pyc +0 -0
- package/src/reidx/runtime/__pycache__/subagent.cpython-312.pyc +0 -0
- package/src/reidx/runtime/agent.py +198 -0
- package/src/reidx/runtime/orchestrator.py +244 -0
- package/src/reidx/runtime/reasoning.py +77 -0
- package/src/reidx/runtime/state.py +32 -0
- package/src/reidx/runtime/subagent.py +174 -0
- package/src/reidx/session/__init__.py +4 -0
- package/src/reidx/session/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/session/__pycache__/models.cpython-312.pyc +0 -0
- package/src/reidx/session/__pycache__/store.cpython-312.pyc +0 -0
- package/src/reidx/session/models.py +43 -0
- package/src/reidx/session/store.py +101 -0
- package/src/reidx/tasks/__init__.py +4 -0
- package/src/reidx/tasks/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/tasks/__pycache__/models.cpython-312.pyc +0 -0
- package/src/reidx/tasks/__pycache__/store.cpython-312.pyc +0 -0
- package/src/reidx/tasks/models.py +44 -0
- package/src/reidx/tasks/store.py +86 -0
- package/src/reidx/tools/__init__.py +44 -0
- package/src/reidx/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/tools/__pycache__/base.cpython-312.pyc +0 -0
- package/src/reidx/tools/__pycache__/file_tools.cpython-312.pyc +0 -0
- package/src/reidx/tools/__pycache__/registry.cpython-312.pyc +0 -0
- package/src/reidx/tools/__pycache__/shell_tool.cpython-312.pyc +0 -0
- package/src/reidx/tools/__pycache__/spawn_agent.cpython-312.pyc +0 -0
- package/src/reidx/tools/__pycache__/web_tools.cpython-312.pyc +0 -0
- package/src/reidx/tools/base.py +84 -0
- package/src/reidx/tools/file_tools.py +222 -0
- package/src/reidx/tools/registry.py +45 -0
- package/src/reidx/tools/shell_tool.py +75 -0
- package/src/reidx/tools/spawn_agent.py +185 -0
- package/src/reidx/tools/web_tools.py +216 -0
- package/src/reidx/ui/__init__.py +41 -0
- package/src/reidx/ui/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/ui/__pycache__/app.cpython-312.pyc +0 -0
- package/src/reidx/ui/__pycache__/commands.cpython-312.pyc +0 -0
- package/src/reidx/ui/__pycache__/render.cpython-312.pyc +0 -0
- package/src/reidx/ui/__pycache__/repl.cpython-312.pyc +0 -0
- package/src/reidx/ui/__pycache__/theme.cpython-312.pyc +0 -0
- package/src/reidx/ui/app.py +1343 -0
- package/src/reidx/ui/commands.py +663 -0
- package/src/reidx/ui/render.py +517 -0
- package/src/reidx/ui/repl.py +14 -0
- package/src/reidx/ui/theme.py +126 -0
- package/src/reidx/workflows/__init__.py +4 -0
- package/src/reidx/workflows/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/reidx/workflows/__pycache__/models.cpython-312.pyc +0 -0
- package/src/reidx/workflows/__pycache__/store.cpython-312.pyc +0 -0
- package/src/reidx/workflows/models.py +19 -0
- package/src/reidx/workflows/store.py +56 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""On-disk persistence for user-added providers (`/connect`).
|
|
2
|
+
|
|
3
|
+
File: `<storage_root>/providers.json`, chmod 600 on POSIX so API keys aren't
|
|
4
|
+
world-readable. Format:
|
|
5
|
+
|
|
6
|
+
{"providers": [
|
|
7
|
+
{"name": "local-llama", "kind": "openai-compatible",
|
|
8
|
+
"base_url": "http://localhost:8080", "api_key": "", "default_model": "..."},
|
|
9
|
+
...
|
|
10
|
+
]}
|
|
11
|
+
|
|
12
|
+
The registry rebuilds each provider on load via `build_provider(kind, ...)`.
|
|
13
|
+
Nothing in here auto-changes `config.default_provider` — stub stays default;
|
|
14
|
+
switching is explicit via `/use`.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
from dataclasses import asdict, dataclass
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from reidx.diagnostics.logger import get_logger
|
|
24
|
+
from reidx.provider.anthropic import AnthropicProvider
|
|
25
|
+
from reidx.provider.base import BaseProvider
|
|
26
|
+
from reidx.provider.ollama import OllamaProvider
|
|
27
|
+
from reidx.provider.openai import OpenAICompatibleProvider, OpenAIProvider
|
|
28
|
+
from reidx.provider.registry import ProviderRegistry
|
|
29
|
+
|
|
30
|
+
log = get_logger("reidx.provider.store")
|
|
31
|
+
|
|
32
|
+
SUPPORTED_KINDS = ("anthropic", "openai", "openai-compatible", "ollama")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class ProviderRecord:
|
|
37
|
+
name: str
|
|
38
|
+
kind: str
|
|
39
|
+
base_url: str = ""
|
|
40
|
+
api_key: str = ""
|
|
41
|
+
default_model: str = ""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_provider(record: ProviderRecord) -> BaseProvider:
|
|
45
|
+
kind = record.kind
|
|
46
|
+
if kind == "anthropic":
|
|
47
|
+
return AnthropicProvider(
|
|
48
|
+
api_key=record.api_key,
|
|
49
|
+
base_url=record.base_url or "https://api.anthropic.com",
|
|
50
|
+
default_model=record.default_model,
|
|
51
|
+
)
|
|
52
|
+
if kind == "openai":
|
|
53
|
+
return OpenAIProvider(
|
|
54
|
+
api_key=record.api_key,
|
|
55
|
+
base_url=record.base_url,
|
|
56
|
+
default_model=record.default_model,
|
|
57
|
+
)
|
|
58
|
+
if kind == "openai-compatible":
|
|
59
|
+
return OpenAICompatibleProvider(
|
|
60
|
+
api_key=record.api_key,
|
|
61
|
+
base_url=record.base_url,
|
|
62
|
+
default_model=record.default_model,
|
|
63
|
+
)
|
|
64
|
+
if kind == "ollama":
|
|
65
|
+
return OllamaProvider(
|
|
66
|
+
base_url=record.base_url,
|
|
67
|
+
default_model=record.default_model,
|
|
68
|
+
api_key=record.api_key,
|
|
69
|
+
)
|
|
70
|
+
raise ValueError(f"unsupported provider kind: {kind}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ProviderStore:
|
|
74
|
+
def __init__(self, storage_root: Path) -> None:
|
|
75
|
+
self.path = Path(storage_root) / "providers.json"
|
|
76
|
+
|
|
77
|
+
def _read(self) -> list[ProviderRecord]:
|
|
78
|
+
if not self.path.exists():
|
|
79
|
+
return []
|
|
80
|
+
try:
|
|
81
|
+
data = json.loads(self.path.read_text(encoding="utf-8"))
|
|
82
|
+
except (OSError, ValueError):
|
|
83
|
+
log.exception("failed to read providers.json; treating as empty")
|
|
84
|
+
return []
|
|
85
|
+
out: list[ProviderRecord] = []
|
|
86
|
+
for entry in data.get("providers", []):
|
|
87
|
+
try:
|
|
88
|
+
out.append(ProviderRecord(**entry))
|
|
89
|
+
except TypeError:
|
|
90
|
+
log.warning("skipping malformed provider entry: %s", entry)
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
def _write(self, records: list[ProviderRecord]) -> None:
|
|
94
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
self.path.write_text(
|
|
96
|
+
json.dumps({"providers": [asdict(r) for r in records]}, indent=2),
|
|
97
|
+
encoding="utf-8",
|
|
98
|
+
)
|
|
99
|
+
# Best-effort key protection on POSIX; a no-op on Windows.
|
|
100
|
+
try:
|
|
101
|
+
os.chmod(self.path, 0o600)
|
|
102
|
+
except OSError:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
def list(self) -> list[ProviderRecord]:
|
|
106
|
+
return self._read()
|
|
107
|
+
|
|
108
|
+
def get(self, name: str) -> ProviderRecord | None:
|
|
109
|
+
for r in self._read():
|
|
110
|
+
if r.name == name:
|
|
111
|
+
return r
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
def save(self, record: ProviderRecord) -> None:
|
|
115
|
+
records = [r for r in self._read() if r.name != record.name]
|
|
116
|
+
records.append(record)
|
|
117
|
+
self._write(records)
|
|
118
|
+
|
|
119
|
+
def delete(self, name: str) -> bool:
|
|
120
|
+
records = self._read()
|
|
121
|
+
remaining = [r for r in records if r.name != name]
|
|
122
|
+
if len(remaining) == len(records):
|
|
123
|
+
return False
|
|
124
|
+
self._write(remaining)
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def load_into(registry: ProviderRegistry, storage_root: Path) -> list[str]:
|
|
129
|
+
"""Register every persisted provider into `registry`. Returns the list of
|
|
130
|
+
names successfully registered. Skips (with a warning) any record whose
|
|
131
|
+
kind is unknown or fails to construct — the app must not crash on a
|
|
132
|
+
single bad entry."""
|
|
133
|
+
added: list[str] = []
|
|
134
|
+
store = ProviderStore(storage_root)
|
|
135
|
+
for record in store.list():
|
|
136
|
+
try:
|
|
137
|
+
registry.register(record.name, build_provider(record))
|
|
138
|
+
added.append(record.name)
|
|
139
|
+
except (ValueError, TypeError):
|
|
140
|
+
log.exception("skipping provider %s (kind=%s): failed to build", record.name, record.kind)
|
|
141
|
+
return added
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""StubProvider: offline provider so the runtime loop is real and testable.
|
|
2
|
+
|
|
3
|
+
Returns a canned assistant turn. If the user message asks to "list" files or "read"
|
|
4
|
+
a file, it emits a matching tool call so the agent loop executes a real tool
|
|
5
|
+
end-to-end. This is honest scaffolding — replace by registering a real provider.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from reidx.provider.base import BaseProvider, Message, ProviderResponse, ToolCall, Usage
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StubProvider(BaseProvider):
|
|
16
|
+
name = "stub"
|
|
17
|
+
|
|
18
|
+
def chat(
|
|
19
|
+
self,
|
|
20
|
+
messages: list[Message],
|
|
21
|
+
tools: list[dict[str, Any]] | None = None,
|
|
22
|
+
model: str | None = None,
|
|
23
|
+
) -> ProviderResponse:
|
|
24
|
+
last = next((m for m in reversed(messages) if m.role == "user"), None)
|
|
25
|
+
text = (last.content if last else "").lower()
|
|
26
|
+
tool_names = {t.get("function", {}).get("name") for t in tools or []}
|
|
27
|
+
|
|
28
|
+
# After a tool result comes back, finalize the turn instead of looping.
|
|
29
|
+
last_msg = messages[-1] if messages else None
|
|
30
|
+
if last_msg is not None and last_msg.role == "tool":
|
|
31
|
+
return ProviderResponse(
|
|
32
|
+
text=f"[stub] Tool returned: {last_msg.content[:200]}",
|
|
33
|
+
usage=Usage(prompt_tokens=12, completion_tokens=8),
|
|
34
|
+
stop_reason="stop",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if ("list" in text or "dir" in text) and "list_dir" in tool_names:
|
|
38
|
+
return ProviderResponse(
|
|
39
|
+
text="I'll list the current directory first.",
|
|
40
|
+
tool_calls=[ToolCall(id=uuid.uuid4().hex[:8], name="list_dir", arguments={})],
|
|
41
|
+
usage=Usage(prompt_tokens=8, completion_tokens=6),
|
|
42
|
+
stop_reason="tool_use",
|
|
43
|
+
)
|
|
44
|
+
if "read" in text and "read_file" in tool_names:
|
|
45
|
+
target = "README.md"
|
|
46
|
+
return ProviderResponse(
|
|
47
|
+
text=f"Reading {target}.",
|
|
48
|
+
tool_calls=[
|
|
49
|
+
ToolCall(id=uuid.uuid4().hex[:8], name="read_file", arguments={"path": target})
|
|
50
|
+
],
|
|
51
|
+
usage=Usage(prompt_tokens=10, completion_tokens=4),
|
|
52
|
+
stop_reason="tool_use",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
echo = f"[stub] Acknowledged: {last.content if last else ''}"
|
|
56
|
+
return ProviderResponse(
|
|
57
|
+
text=echo,
|
|
58
|
+
usage=Usage(prompt_tokens=len(echo), completion_tokens=len(echo)),
|
|
59
|
+
stop_reason="stop",
|
|
60
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from reidx.provider_manager.catalog import (
|
|
4
|
+
ProviderDefinition,
|
|
5
|
+
all_providers,
|
|
6
|
+
by_id,
|
|
7
|
+
popular_providers,
|
|
8
|
+
search,
|
|
9
|
+
)
|
|
10
|
+
from reidx.provider_manager.database import (
|
|
11
|
+
ProviderDatabase,
|
|
12
|
+
StoredKey,
|
|
13
|
+
StoredProvider,
|
|
14
|
+
)
|
|
15
|
+
from reidx.provider_manager.keychain import decrypt, encrypt
|
|
16
|
+
from reidx.provider_manager.palette import (
|
|
17
|
+
ACCENT,
|
|
18
|
+
BG,
|
|
19
|
+
BG_ALT,
|
|
20
|
+
BORDER,
|
|
21
|
+
ProviderPalette,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"ACCENT",
|
|
26
|
+
"BG",
|
|
27
|
+
"BG_ALT",
|
|
28
|
+
"BORDER",
|
|
29
|
+
"ProviderDatabase",
|
|
30
|
+
"ProviderDefinition",
|
|
31
|
+
"ProviderPalette",
|
|
32
|
+
"StoredKey",
|
|
33
|
+
"StoredProvider",
|
|
34
|
+
"all_providers",
|
|
35
|
+
"by_id",
|
|
36
|
+
"decrypt",
|
|
37
|
+
"encrypt",
|
|
38
|
+
"popular_providers",
|
|
39
|
+
"search",
|
|
40
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class ProviderDefinition:
|
|
8
|
+
id: str
|
|
9
|
+
name: str
|
|
10
|
+
description: str
|
|
11
|
+
kind: str
|
|
12
|
+
base_url: str
|
|
13
|
+
default_model: str
|
|
14
|
+
aliases: list[str] = field(default_factory=list)
|
|
15
|
+
auth_method: str = "bearer"
|
|
16
|
+
extra_headers: dict[str, str] = field(default_factory=dict)
|
|
17
|
+
popular: bool = False
|
|
18
|
+
icon: str = ""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_BUILTIN: list[ProviderDefinition] = [
|
|
22
|
+
ProviderDefinition(
|
|
23
|
+
id="openai", name="OpenAI", description="GPT-4o, o-series, and DALL-E models",
|
|
24
|
+
kind="openai", base_url="https://api.openai.com", default_model="gpt-4o-mini",
|
|
25
|
+
aliases=["gpt", "chatgpt"], popular=True, icon="🟢",
|
|
26
|
+
),
|
|
27
|
+
ProviderDefinition(
|
|
28
|
+
id="anthropic", name="Anthropic", description="Claude Sonnet, Opus, and Haiku models",
|
|
29
|
+
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="🟠",
|
|
31
|
+
),
|
|
32
|
+
ProviderDefinition(
|
|
33
|
+
id="google-ai", name="Google AI", description="Gemini Pro, Flash, and Flash-Lite models",
|
|
34
|
+
kind="openai-compatible", base_url="https://generativelanguage.googleapis.com/v1beta/openai",
|
|
35
|
+
default_model="gemini-2.0-flash", aliases=["gemini", "google", "google-gemini"],
|
|
36
|
+
popular=True, icon="🔵",
|
|
37
|
+
),
|
|
38
|
+
ProviderDefinition(
|
|
39
|
+
id="openrouter", name="OpenRouter", description="Unified API for 300+ models from all providers",
|
|
40
|
+
kind="openai-compatible", base_url="https://openrouter.ai/api/v1",
|
|
41
|
+
default_model="auto", aliases=["or"], popular=True, icon="🔀",
|
|
42
|
+
),
|
|
43
|
+
ProviderDefinition(
|
|
44
|
+
id="zai", name="Z.ai", description="GLM-4 and GLM-4.5 series models",
|
|
45
|
+
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="🟣",
|
|
47
|
+
),
|
|
48
|
+
ProviderDefinition(
|
|
49
|
+
id="agentrouter", name="AgentRouter", description="Multi-model routing for AI agents",
|
|
50
|
+
kind="openai-compatible", base_url="https://api.agentrouter.io/v1",
|
|
51
|
+
default_model="auto", aliases=["agent-router"], icon="🤖",
|
|
52
|
+
),
|
|
53
|
+
ProviderDefinition(
|
|
54
|
+
id="together", name="Together AI", description="Open-source Llama, Mixtral, and Qwen models",
|
|
55
|
+
kind="openai-compatible", base_url="https://api.together.xyz/v1",
|
|
56
|
+
default_model="meta-llama/Llama-3.3-70B-Instruct-Turbo", aliases=["together-ai"],
|
|
57
|
+
popular=True, icon="🤝",
|
|
58
|
+
),
|
|
59
|
+
ProviderDefinition(
|
|
60
|
+
id="fireworks", name="Fireworks AI", description="Fast inference for open-source models",
|
|
61
|
+
kind="openai-compatible", base_url="https://api.fireworks.ai/inference/v1",
|
|
62
|
+
default_model="accounts/fireworks/models/llama-v3p1-70b-instruct",
|
|
63
|
+
aliases=["fireworks-ai"], popular=True, icon="🎆",
|
|
64
|
+
),
|
|
65
|
+
ProviderDefinition(
|
|
66
|
+
id="groq", name="Groq", description="Ultra-fast LPU inference for Llama and Mixtral",
|
|
67
|
+
kind="openai-compatible", base_url="https://api.groq.com/openai/v1",
|
|
68
|
+
default_model="llama-3.3-70b-versatile", aliases=[], popular=True, icon="⚡",
|
|
69
|
+
),
|
|
70
|
+
ProviderDefinition(
|
|
71
|
+
id="deepinfra", name="DeepInfra", description="Cost-effective inference for open models",
|
|
72
|
+
kind="openai-compatible", base_url="https://api.deepinfra.com/v1/openai",
|
|
73
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="🔧",
|
|
74
|
+
),
|
|
75
|
+
ProviderDefinition(
|
|
76
|
+
id="cerebras", name="Cerebras", description="Fastest AI inference with Wafer-Scale Engines",
|
|
77
|
+
kind="openai-compatible", base_url="https://api.cerebras.ai/v1",
|
|
78
|
+
default_model="llama3.1-70b", aliases=[], icon="🧠",
|
|
79
|
+
),
|
|
80
|
+
ProviderDefinition(
|
|
81
|
+
id="cohere", name="Cohere", description="Command R+ and Aya models for enterprise",
|
|
82
|
+
kind="openai-compatible", base_url="https://api.cohere.ai/v1",
|
|
83
|
+
default_model="command-r-plus-08-2024", aliases=[], icon="🔗",
|
|
84
|
+
),
|
|
85
|
+
ProviderDefinition(
|
|
86
|
+
id="xai", name="xAI", description="Grok models from xAI",
|
|
87
|
+
kind="openai-compatible", base_url="https://api.x.ai/v1",
|
|
88
|
+
default_model="grok-2-latest", aliases=["grok", "x-ai"], popular=True, icon="✖️",
|
|
89
|
+
),
|
|
90
|
+
ProviderDefinition(
|
|
91
|
+
id="mistral", name="Mistral", description="Mistral Large, Codestral, and Pixtral models",
|
|
92
|
+
kind="openai-compatible", base_url="https://api.mistral.ai/v1",
|
|
93
|
+
default_model="mistral-large-latest", aliases=["mistral-ai"], popular=True, icon="🌬️",
|
|
94
|
+
),
|
|
95
|
+
ProviderDefinition(
|
|
96
|
+
id="perplexity", name="Perplexity", description="Sonar models with built-in web search",
|
|
97
|
+
kind="openai-compatible", base_url="https://api.perplexity.ai",
|
|
98
|
+
default_model="llama-3.1-sonar-large-128k-online", aliases=[], icon="🔮",
|
|
99
|
+
),
|
|
100
|
+
ProviderDefinition(
|
|
101
|
+
id="ollama", name="Ollama", description="Run models locally — Llama, Qwen, Phi, and more",
|
|
102
|
+
kind="ollama", base_url="http://localhost:11434",
|
|
103
|
+
default_model="llama3.2", aliases=[], auth_method="none", popular=True, icon="🐪",
|
|
104
|
+
),
|
|
105
|
+
ProviderDefinition(
|
|
106
|
+
id="lm-studio", name="LM Studio", description="Local model server with OpenAI-compatible API",
|
|
107
|
+
kind="openai-compatible", base_url="http://localhost:1234/v1",
|
|
108
|
+
default_model="local", aliases=["lmstudio"], auth_method="none", icon="🏠",
|
|
109
|
+
),
|
|
110
|
+
ProviderDefinition(
|
|
111
|
+
id="vllm", name="vLLM", description="High-throughput local inference server",
|
|
112
|
+
kind="openai-compatible", base_url="http://localhost:8000/v1",
|
|
113
|
+
default_model="auto", aliases=[], auth_method="none", icon="🚀",
|
|
114
|
+
),
|
|
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",
|
|
118
|
+
aliases=["azure"], icon="☁️",
|
|
119
|
+
),
|
|
120
|
+
ProviderDefinition(
|
|
121
|
+
id="huggingface", name="HuggingFace", description="Inference API for 100k+ models",
|
|
122
|
+
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="🤗",
|
|
124
|
+
),
|
|
125
|
+
ProviderDefinition(
|
|
126
|
+
id="sambanova", name="SambaNova", description="Fast RDU-based inference for open models",
|
|
127
|
+
kind="openai-compatible", base_url="https://api.sambanova.ai/v1",
|
|
128
|
+
default_model="Meta-Llama-3.1-70B-Instruct", aliases=[], icon="🔴",
|
|
129
|
+
),
|
|
130
|
+
ProviderDefinition(
|
|
131
|
+
id="novita", name="Novita", description="Affordable API for 100+ open models",
|
|
132
|
+
kind="openai-compatible", base_url="https://api.novita.ai/v3/openai",
|
|
133
|
+
default_model="meta-llama/llama-3.1-70b-instruct", aliases=[], icon="💎",
|
|
134
|
+
),
|
|
135
|
+
ProviderDefinition(
|
|
136
|
+
id="featherless", name="Featherless", description="Serverless inference for open models",
|
|
137
|
+
kind="openai-compatible", base_url="https://api.featherless.ai/v1/openai",
|
|
138
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="🪶",
|
|
139
|
+
),
|
|
140
|
+
ProviderDefinition(
|
|
141
|
+
id="chutes", name="Chutes", description="GPU-accelerated inference for AI workloads",
|
|
142
|
+
kind="openai-compatible", base_url="https://api.chutes.ai/v1",
|
|
143
|
+
default_model="deepseek-ai/DeepSeek-V3", aliases=[], icon="🎯",
|
|
144
|
+
),
|
|
145
|
+
ProviderDefinition(
|
|
146
|
+
id="aimlapi", name="AI/ML API", description="Unified API for 200+ AI models",
|
|
147
|
+
kind="openai-compatible", base_url="https://api.aimlapi.com/v1",
|
|
148
|
+
default_model="gpt-4o", aliases=["ai-ml-api"], icon="📊",
|
|
149
|
+
),
|
|
150
|
+
ProviderDefinition(
|
|
151
|
+
id="hyperbolic", name="Hyperbolic", description="GPU marketplace for AI inference",
|
|
152
|
+
kind="openai-compatible", base_url="https://api.hyperbolic.xyz/v1",
|
|
153
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="📈",
|
|
154
|
+
),
|
|
155
|
+
ProviderDefinition(
|
|
156
|
+
id="deepseek", name="DeepSeek", description="DeepSeek-V3 and DeepSeek-R1 reasoning models",
|
|
157
|
+
kind="openai-compatible", base_url="https://api.deepseek.com/v1",
|
|
158
|
+
default_model="deepseek-chat", aliases=[], popular=True, icon="🐳",
|
|
159
|
+
),
|
|
160
|
+
ProviderDefinition(
|
|
161
|
+
id="moonshot", name="Moonshot", description="Kimi K1.5 and Moonshot models",
|
|
162
|
+
kind="openai-compatible", base_url="https://api.moonshot.cn/v1",
|
|
163
|
+
default_model="moonshot-v1-8k", aliases=["kimi"], icon="🌙",
|
|
164
|
+
),
|
|
165
|
+
ProviderDefinition(
|
|
166
|
+
id="voyage-ai", name="Voyage AI", description="Embeddings and reranking models",
|
|
167
|
+
kind="openai-compatible", base_url="https://api.voyageai.com/v1",
|
|
168
|
+
default_model="voyage-large-2", aliases=[], icon="🧭",
|
|
169
|
+
),
|
|
170
|
+
ProviderDefinition(
|
|
171
|
+
id="nvidia-nim", name="NVIDIA NIM", description="NVIDIA inference microservices for open models",
|
|
172
|
+
kind="openai-compatible", base_url="https://integrate.api.nvidia.com/v1",
|
|
173
|
+
default_model="meta/llama-3.1-70b-instruct", aliases=["nvidia"], icon="💚",
|
|
174
|
+
),
|
|
175
|
+
ProviderDefinition(
|
|
176
|
+
id="ai21", name="AI21 Labs", description="Jamba models with long context windows",
|
|
177
|
+
kind="openai-compatible", base_url="https://api.ai21.com/v1",
|
|
178
|
+
default_model="jamba-1.5-large", aliases=[], icon="🃏",
|
|
179
|
+
),
|
|
180
|
+
ProviderDefinition(
|
|
181
|
+
id="databricks", name="Databricks", description="Serving endpoints for fine-tuned models",
|
|
182
|
+
kind="openai-compatible", base_url="", default_model="databricks-dbrx-instruct",
|
|
183
|
+
aliases=[], icon="🧱",
|
|
184
|
+
),
|
|
185
|
+
ProviderDefinition(
|
|
186
|
+
id="replicate", name="Replicate", description="Run open-source models in the cloud",
|
|
187
|
+
kind="openai-compatible", base_url="https://api.replicate.com/v1",
|
|
188
|
+
default_model="meta/llama-2-70b-chat", aliases=[], icon="🔁",
|
|
189
|
+
),
|
|
190
|
+
ProviderDefinition(
|
|
191
|
+
id="siliconflow", name="SiliconFlow", description="Cost-effective inference for 200+ models",
|
|
192
|
+
kind="openai-compatible", base_url="https://api.siliconflow.cn/v1",
|
|
193
|
+
default_model="deepseek-ai/DeepSeek-V3", aliases=[], icon="🌊",
|
|
194
|
+
),
|
|
195
|
+
ProviderDefinition(
|
|
196
|
+
id="nebius", name="Nebius", description="Cloud GPU platform for AI inference",
|
|
197
|
+
kind="openai-compatible", base_url="https://api.studio.nebius.ai/v1",
|
|
198
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="☁️",
|
|
199
|
+
),
|
|
200
|
+
ProviderDefinition(
|
|
201
|
+
id="lepton", name="Lepton AI", description="Serverless inference with auto-scaling",
|
|
202
|
+
kind="openai-compatible", base_url="https://api.lepton.ai/v1",
|
|
203
|
+
default_model="meta-llama/Llama-3.2-70B-Instruct", aliases=[], icon="⚛️",
|
|
204
|
+
),
|
|
205
|
+
ProviderDefinition(
|
|
206
|
+
id="lambda", name="Lambda Labs", description="GPU cloud for AI training and inference",
|
|
207
|
+
kind="openai-compatible", base_url="https://api.lambdalabs.com/v1",
|
|
208
|
+
default_model="", aliases=["lambda-labs"], icon="λ",
|
|
209
|
+
),
|
|
210
|
+
ProviderDefinition(
|
|
211
|
+
id="kluster", name="Kluster AI", description="Distributed GPU network for inference",
|
|
212
|
+
kind="openai-compatible", base_url="https://api.kluster.ai/v1",
|
|
213
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="🔗",
|
|
214
|
+
),
|
|
215
|
+
ProviderDefinition(
|
|
216
|
+
id="upstage", name="Upstage", description="Solar Mini and Pro models for Korean and English",
|
|
217
|
+
kind="openai-compatible", base_url="https://api.upstage.ai/v1",
|
|
218
|
+
default_model="solar-pro", aliases=[], icon="☀️",
|
|
219
|
+
),
|
|
220
|
+
ProviderDefinition(
|
|
221
|
+
id="yi", name="Yi (01.AI)", description="Yi-Large and Yi-34B models",
|
|
222
|
+
kind="openai-compatible", base_url="https://api.01.ai/v1",
|
|
223
|
+
default_model="yi-large", aliases=["01-ai", "yi-ai"], icon="🎯",
|
|
224
|
+
),
|
|
225
|
+
ProviderDefinition(
|
|
226
|
+
id="volcengine", name="Volcengine", description="ByteDance's Ark platform for Doubao models",
|
|
227
|
+
kind="openai-compatible", base_url="https://ark.cn-beijing.volces.com/api/v3",
|
|
228
|
+
default_model="doubao-pro-32k", aliases=["ark", "bytedance"], icon="🌋",
|
|
229
|
+
),
|
|
230
|
+
ProviderDefinition(
|
|
231
|
+
id="hunyuan", name="Tencent Hunyuan", description="Tencent's Hunyuan large language models",
|
|
232
|
+
kind="openai-compatible", base_url="https://api.hunyuan.cloud.tencent.com/v1",
|
|
233
|
+
default_model="hunyuan-turbos-latest", aliases=["tencent"], icon="🐧",
|
|
234
|
+
),
|
|
235
|
+
ProviderDefinition(
|
|
236
|
+
id="cloudflare-ai", name="Cloudflare Workers AI", description="Serverless AI inference at the edge",
|
|
237
|
+
kind="openai-compatible", base_url="", default_model="@cf/meta/llama-3.1-70b-instruct",
|
|
238
|
+
aliases=["cf-ai"], icon="orange",
|
|
239
|
+
),
|
|
240
|
+
ProviderDefinition(
|
|
241
|
+
id="monsterapi", name="Monster API", description="Affordable GPU inference for open models",
|
|
242
|
+
kind="openai-compatible", base_url="https://api.monsterapi.ai/v1",
|
|
243
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="👾",
|
|
244
|
+
),
|
|
245
|
+
ProviderDefinition(
|
|
246
|
+
id="baseten", name="Baseten", description="Model deployment and serving platform",
|
|
247
|
+
kind="openai-compatible", base_url="https://inference.baseten.co/v1",
|
|
248
|
+
default_model="", aliases=[], icon="🏗️",
|
|
249
|
+
),
|
|
250
|
+
ProviderDefinition(
|
|
251
|
+
id="anyscale", name="Anyscale", description="Ray-based scalable model serving",
|
|
252
|
+
kind="openai-compatible", base_url="https://api.endpoints.anyscale.com/v1",
|
|
253
|
+
default_model="meta-llama/Llama-3.1-70B-Instruct", aliases=[], icon="📐",
|
|
254
|
+
),
|
|
255
|
+
ProviderDefinition(
|
|
256
|
+
id="jan", name="Jan", description="Local AI model server with web UI",
|
|
257
|
+
kind="openai-compatible", base_url="http://localhost:1337/v1",
|
|
258
|
+
default_model="local", aliases=[], auth_method="none", icon="🖥️",
|
|
259
|
+
),
|
|
260
|
+
ProviderDefinition(
|
|
261
|
+
id="llamacpp", name="Llama.cpp Server", description="Local C++ inference server",
|
|
262
|
+
kind="openai-compatible", base_url="http://localhost:8080",
|
|
263
|
+
default_model="local", aliases=["llama-cpp"], auth_method="none", icon="🦙",
|
|
264
|
+
),
|
|
265
|
+
ProviderDefinition(
|
|
266
|
+
id="watsonx", name="IBM Watsonx", description="Enterprise AI platform with Granite models",
|
|
267
|
+
kind="openai-compatible", base_url="", default_model="ibm/granite-3-8b-instruct",
|
|
268
|
+
aliases=["ibm"], icon="🔷",
|
|
269
|
+
),
|
|
270
|
+
ProviderDefinition(
|
|
271
|
+
id="aleph-alpha", name="Aleph Alpha", description="European sovereign AI — Luminous models",
|
|
272
|
+
kind="openai-compatible", base_url="https://api.aleph-alpha.eu/v1",
|
|
273
|
+
default_model="luminous-supreme-control", aliases=[], icon="α",
|
|
274
|
+
),
|
|
275
|
+
ProviderDefinition(
|
|
276
|
+
id="predibase", name="Predibase", description="Fine-tune and serve open-source LLMs",
|
|
277
|
+
kind="openai-compatible", base_url="https://serving.app.predibase.com/v1",
|
|
278
|
+
default_model="meta-llama/Meta-Llama-3.1-8B-Instruct", aliases=[], icon="🎯",
|
|
279
|
+
),
|
|
280
|
+
ProviderDefinition(
|
|
281
|
+
id="gravity", name="Gravity API", description="Multi-model API gateway",
|
|
282
|
+
kind="openai-compatible", base_url="https://api.gravityapi.com/v1",
|
|
283
|
+
default_model="auto", aliases=[], icon="🌌",
|
|
284
|
+
),
|
|
285
|
+
ProviderDefinition(
|
|
286
|
+
id="infermatic", name="Infermatic", description="Fast inference for popular open models",
|
|
287
|
+
kind="openai-compatible", base_url="https://api.infermatic.ai/v1",
|
|
288
|
+
default_model="meta-llama/Meta-Llama-3.1-70B-Instruct", aliases=[], icon="💡",
|
|
289
|
+
),
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
_BY_ID: dict[str, ProviderDefinition] = {p.id: p for p in _BUILTIN}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def all_providers() -> list[ProviderDefinition]:
|
|
296
|
+
return list(_BUILTIN)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def popular_providers() -> list[ProviderDefinition]:
|
|
300
|
+
return [p for p in _BUILTIN if p.popular]
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def by_id(provider_id: str) -> ProviderDefinition | None:
|
|
304
|
+
return _BY_ID.get(provider_id)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def search(query: str) -> list[ProviderDefinition]:
|
|
308
|
+
q = query.strip().lower()
|
|
309
|
+
if not q:
|
|
310
|
+
return list(_BUILTIN)
|
|
311
|
+
results: list[tuple[int, ProviderDefinition]] = []
|
|
312
|
+
for p in _BUILTIN:
|
|
313
|
+
score = 0
|
|
314
|
+
name_lower = p.name.lower()
|
|
315
|
+
if name_lower == q:
|
|
316
|
+
score = 100
|
|
317
|
+
elif name_lower.startswith(q):
|
|
318
|
+
score = 80
|
|
319
|
+
elif q in name_lower:
|
|
320
|
+
score = 60
|
|
321
|
+
elif any(q in a.lower() for a in p.aliases):
|
|
322
|
+
score = 70
|
|
323
|
+
elif q in p.description.lower():
|
|
324
|
+
score = 30
|
|
325
|
+
elif q in p.id.lower():
|
|
326
|
+
score = 50
|
|
327
|
+
if score > 0:
|
|
328
|
+
results.append((score, p))
|
|
329
|
+
results.sort(key=lambda x: (-x[0], x[1].name))
|
|
330
|
+
return [p for _, p in results]
|