@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.
Files changed (135) hide show
  1. package/README.md +724 -0
  2. package/bin/reidx.js +33 -0
  3. package/package.json +40 -0
  4. package/pyproject.toml +45 -0
  5. package/scripts/find-python.js +24 -0
  6. package/scripts/postinstall.js +35 -0
  7. package/src/reidx/__init__.py +3 -0
  8. package/src/reidx/__main__.py +9 -0
  9. package/src/reidx/__pycache__/__init__.cpython-312.pyc +0 -0
  10. package/src/reidx/app/__init__.py +3 -0
  11. package/src/reidx/app/__pycache__/__init__.cpython-312.pyc +0 -0
  12. package/src/reidx/app/__pycache__/commands.cpython-312.pyc +0 -0
  13. package/src/reidx/app/commands.py +221 -0
  14. package/src/reidx/automation/__init__.py +3 -0
  15. package/src/reidx/automation/__pycache__/__init__.cpython-312.pyc +0 -0
  16. package/src/reidx/automation/__pycache__/exec.cpython-312.pyc +0 -0
  17. package/src/reidx/automation/exec.py +35 -0
  18. package/src/reidx/config/__init__.py +10 -0
  19. package/src/reidx/config/__pycache__/__init__.cpython-312.pyc +0 -0
  20. package/src/reidx/config/__pycache__/loader.cpython-312.pyc +0 -0
  21. package/src/reidx/config/__pycache__/models.cpython-312.pyc +0 -0
  22. package/src/reidx/config/__pycache__/settings.cpython-312.pyc +0 -0
  23. package/src/reidx/config/loader.py +104 -0
  24. package/src/reidx/config/models.py +49 -0
  25. package/src/reidx/config/settings.py +127 -0
  26. package/src/reidx/deepreid/__init__.py +8 -0
  27. package/src/reidx/deepreid/__pycache__/__init__.cpython-312.pyc +0 -0
  28. package/src/reidx/deepreid/__pycache__/pipeline.cpython-312.pyc +0 -0
  29. package/src/reidx/deepreid/pipeline.py +221 -0
  30. package/src/reidx/diagnostics/__init__.py +0 -0
  31. package/src/reidx/diagnostics/__pycache__/__init__.cpython-312.pyc +0 -0
  32. package/src/reidx/diagnostics/__pycache__/logger.cpython-312.pyc +0 -0
  33. package/src/reidx/diagnostics/logger.py +56 -0
  34. package/src/reidx/goals/__init__.py +21 -0
  35. package/src/reidx/goals/__pycache__/__init__.cpython-312.pyc +0 -0
  36. package/src/reidx/goals/__pycache__/models.cpython-312.pyc +0 -0
  37. package/src/reidx/goals/__pycache__/store.cpython-312.pyc +0 -0
  38. package/src/reidx/goals/models.py +101 -0
  39. package/src/reidx/goals/store.py +233 -0
  40. package/src/reidx/integrations/__init__.py +3 -0
  41. package/src/reidx/integrations/mcp.py +70 -0
  42. package/src/reidx/nyx/__init__.py +3 -0
  43. package/src/reidx/nyx/__pycache__/__init__.cpython-312.pyc +0 -0
  44. package/src/reidx/nyx/__pycache__/persona.cpython-312.pyc +0 -0
  45. package/src/reidx/nyx/persona.py +35 -0
  46. package/src/reidx/policy/__init__.py +15 -0
  47. package/src/reidx/policy/__pycache__/__init__.cpython-312.pyc +0 -0
  48. package/src/reidx/policy/__pycache__/engine.cpython-312.pyc +0 -0
  49. package/src/reidx/policy/__pycache__/models.cpython-312.pyc +0 -0
  50. package/src/reidx/policy/engine.py +101 -0
  51. package/src/reidx/policy/models.py +35 -0
  52. package/src/reidx/provider/__init__.py +16 -0
  53. package/src/reidx/provider/__pycache__/__init__.cpython-312.pyc +0 -0
  54. package/src/reidx/provider/__pycache__/_http.cpython-312.pyc +0 -0
  55. package/src/reidx/provider/__pycache__/anthropic.cpython-312.pyc +0 -0
  56. package/src/reidx/provider/__pycache__/base.cpython-312.pyc +0 -0
  57. package/src/reidx/provider/__pycache__/ollama.cpython-312.pyc +0 -0
  58. package/src/reidx/provider/__pycache__/openai.cpython-312.pyc +0 -0
  59. package/src/reidx/provider/__pycache__/registry.cpython-312.pyc +0 -0
  60. package/src/reidx/provider/__pycache__/store.cpython-312.pyc +0 -0
  61. package/src/reidx/provider/__pycache__/stub.cpython-312.pyc +0 -0
  62. package/src/reidx/provider/_http.py +29 -0
  63. package/src/reidx/provider/anthropic.py +176 -0
  64. package/src/reidx/provider/base.py +50 -0
  65. package/src/reidx/provider/ollama.py +109 -0
  66. package/src/reidx/provider/openai.py +144 -0
  67. package/src/reidx/provider/registry.py +88 -0
  68. package/src/reidx/provider/store.py +141 -0
  69. package/src/reidx/provider/stub.py +60 -0
  70. package/src/reidx/provider_manager/__init__.py +40 -0
  71. package/src/reidx/provider_manager/__pycache__/__init__.cpython-312.pyc +0 -0
  72. package/src/reidx/provider_manager/__pycache__/catalog.cpython-312.pyc +0 -0
  73. package/src/reidx/provider_manager/__pycache__/database.cpython-312.pyc +0 -0
  74. package/src/reidx/provider_manager/__pycache__/keychain.cpython-312.pyc +0 -0
  75. package/src/reidx/provider_manager/__pycache__/palette.cpython-312.pyc +0 -0
  76. package/src/reidx/provider_manager/catalog.py +330 -0
  77. package/src/reidx/provider_manager/database.py +234 -0
  78. package/src/reidx/provider_manager/keychain.py +102 -0
  79. package/src/reidx/provider_manager/palette.py +831 -0
  80. package/src/reidx/runtime/__init__.py +5 -0
  81. package/src/reidx/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  82. package/src/reidx/runtime/__pycache__/agent.cpython-312.pyc +0 -0
  83. package/src/reidx/runtime/__pycache__/orchestrator.cpython-312.pyc +0 -0
  84. package/src/reidx/runtime/__pycache__/reasoning.cpython-312.pyc +0 -0
  85. package/src/reidx/runtime/__pycache__/state.cpython-312.pyc +0 -0
  86. package/src/reidx/runtime/__pycache__/subagent.cpython-312.pyc +0 -0
  87. package/src/reidx/runtime/agent.py +198 -0
  88. package/src/reidx/runtime/orchestrator.py +244 -0
  89. package/src/reidx/runtime/reasoning.py +77 -0
  90. package/src/reidx/runtime/state.py +32 -0
  91. package/src/reidx/runtime/subagent.py +174 -0
  92. package/src/reidx/session/__init__.py +4 -0
  93. package/src/reidx/session/__pycache__/__init__.cpython-312.pyc +0 -0
  94. package/src/reidx/session/__pycache__/models.cpython-312.pyc +0 -0
  95. package/src/reidx/session/__pycache__/store.cpython-312.pyc +0 -0
  96. package/src/reidx/session/models.py +43 -0
  97. package/src/reidx/session/store.py +101 -0
  98. package/src/reidx/tasks/__init__.py +4 -0
  99. package/src/reidx/tasks/__pycache__/__init__.cpython-312.pyc +0 -0
  100. package/src/reidx/tasks/__pycache__/models.cpython-312.pyc +0 -0
  101. package/src/reidx/tasks/__pycache__/store.cpython-312.pyc +0 -0
  102. package/src/reidx/tasks/models.py +44 -0
  103. package/src/reidx/tasks/store.py +86 -0
  104. package/src/reidx/tools/__init__.py +44 -0
  105. package/src/reidx/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  106. package/src/reidx/tools/__pycache__/base.cpython-312.pyc +0 -0
  107. package/src/reidx/tools/__pycache__/file_tools.cpython-312.pyc +0 -0
  108. package/src/reidx/tools/__pycache__/registry.cpython-312.pyc +0 -0
  109. package/src/reidx/tools/__pycache__/shell_tool.cpython-312.pyc +0 -0
  110. package/src/reidx/tools/__pycache__/spawn_agent.cpython-312.pyc +0 -0
  111. package/src/reidx/tools/__pycache__/web_tools.cpython-312.pyc +0 -0
  112. package/src/reidx/tools/base.py +84 -0
  113. package/src/reidx/tools/file_tools.py +222 -0
  114. package/src/reidx/tools/registry.py +45 -0
  115. package/src/reidx/tools/shell_tool.py +75 -0
  116. package/src/reidx/tools/spawn_agent.py +185 -0
  117. package/src/reidx/tools/web_tools.py +216 -0
  118. package/src/reidx/ui/__init__.py +41 -0
  119. package/src/reidx/ui/__pycache__/__init__.cpython-312.pyc +0 -0
  120. package/src/reidx/ui/__pycache__/app.cpython-312.pyc +0 -0
  121. package/src/reidx/ui/__pycache__/commands.cpython-312.pyc +0 -0
  122. package/src/reidx/ui/__pycache__/render.cpython-312.pyc +0 -0
  123. package/src/reidx/ui/__pycache__/repl.cpython-312.pyc +0 -0
  124. package/src/reidx/ui/__pycache__/theme.cpython-312.pyc +0 -0
  125. package/src/reidx/ui/app.py +1343 -0
  126. package/src/reidx/ui/commands.py +663 -0
  127. package/src/reidx/ui/render.py +517 -0
  128. package/src/reidx/ui/repl.py +14 -0
  129. package/src/reidx/ui/theme.py +126 -0
  130. package/src/reidx/workflows/__init__.py +4 -0
  131. package/src/reidx/workflows/__pycache__/__init__.cpython-312.pyc +0 -0
  132. package/src/reidx/workflows/__pycache__/models.cpython-312.pyc +0 -0
  133. package/src/reidx/workflows/__pycache__/store.cpython-312.pyc +0 -0
  134. package/src/reidx/workflows/models.py +19 -0
  135. 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
+ ]
@@ -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]