deepparallel 0.5.0__tar.gz → 0.5.1__tar.gz

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 (76) hide show
  1. {deepparallel-0.5.0 → deepparallel-0.5.1}/PKG-INFO +1 -1
  2. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/__init__.py +1 -1
  3. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/backend.py +137 -37
  4. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/cli.py +7 -5
  5. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/config.py +28 -4
  6. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/serve.py +27 -5
  7. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel.egg-info/PKG-INFO +1 -1
  8. {deepparallel-0.5.0 → deepparallel-0.5.1}/pyproject.toml +1 -1
  9. {deepparallel-0.5.0 → deepparallel-0.5.1}/README.md +0 -0
  10. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/agent.py +0 -0
  11. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/branding.py +0 -0
  12. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/crowe_id.py +0 -0
  13. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/dsml.py +0 -0
  14. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/fusion.py +0 -0
  15. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/licensing.py +0 -0
  16. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/registry.json +0 -0
  17. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/renderer.py +0 -0
  18. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/research/__init__.py +0 -0
  19. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/research/conduit.py +0 -0
  20. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/research/provider.py +0 -0
  21. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/routing.example.json +0 -0
  22. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/routing.py +0 -0
  23. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/supply_chain.py +0 -0
  24. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/system_prompt.txt +0 -0
  25. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/__init__.py +0 -0
  26. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/codeast.py +0 -0
  27. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/edit.py +0 -0
  28. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/files.py +0 -0
  29. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/mcp.py +0 -0
  30. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/registry.py +0 -0
  31. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/sandbox.py +0 -0
  32. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/search.py +0 -0
  33. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/shell.py +0 -0
  34. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/vision.py +0 -0
  35. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/tools/web.py +0 -0
  36. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel/userinput.py +0 -0
  37. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel.egg-info/SOURCES.txt +0 -0
  38. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel.egg-info/dependency_links.txt +0 -0
  39. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel.egg-info/entry_points.txt +0 -0
  40. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel.egg-info/requires.txt +0 -0
  41. {deepparallel-0.5.0 → deepparallel-0.5.1}/deepparallel.egg-info/top_level.txt +0 -0
  42. {deepparallel-0.5.0 → deepparallel-0.5.1}/setup.cfg +0 -0
  43. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_agent.py +0 -0
  44. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_backend.py +0 -0
  45. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_backend_chat.py +0 -0
  46. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_backend_stream.py +0 -0
  47. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_branding.py +0 -0
  48. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_cli.py +0 -0
  49. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_config.py +0 -0
  50. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_crowe_backend.py +0 -0
  51. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_crowe_gateway_backend.py +0 -0
  52. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_crowe_id_auth.py +0 -0
  53. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_crowe_payment_required.py +0 -0
  54. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_dsml.py +0 -0
  55. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_fusion.py +0 -0
  56. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_issuer_signer.py +0 -0
  57. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_licensing.py +0 -0
  58. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_renderer.py +0 -0
  59. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_research.py +0 -0
  60. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_research_provider.py +0 -0
  61. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_routing.py +0 -0
  62. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_serve.py +0 -0
  63. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_spinner_color.py +0 -0
  64. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_supply_chain.py +0 -0
  65. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tool_registry.py +0 -0
  66. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_codeast.py +0 -0
  67. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_edit.py +0 -0
  68. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_files.py +0 -0
  69. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_mcp.py +0 -0
  70. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_sandbox.py +0 -0
  71. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_search.py +0 -0
  72. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_shell.py +0 -0
  73. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_vision.py +0 -0
  74. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_tools_web.py +0 -0
  75. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_userinput.py +0 -0
  76. {deepparallel-0.5.0 → deepparallel-0.5.1}/tests/test_userinput_paste.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepparallel
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
5
5
  Author-email: Michael Crowe <michael@crowelogic.com>
6
6
  License: Apache-2.0
@@ -1,3 +1,3 @@
1
1
  """DeepParallel CLI package."""
2
2
 
3
- __version__ = "0.5.0"
3
+ __version__ = "0.5.1"
@@ -11,6 +11,8 @@ stream_chat yields (channel, text) tuples where channel is "content" or
11
11
  from __future__ import annotations
12
12
 
13
13
  import json
14
+ import os
15
+ import sys
14
16
  from typing import Iterator, Protocol
15
17
  from urllib.parse import urlparse
16
18
 
@@ -142,6 +144,24 @@ class Backend(Protocol):
142
144
  ) -> Iterator[Chunk]: ...
143
145
 
144
146
 
147
+ def _should_failover(exc: Exception) -> bool:
148
+ """Fail over to direct Azure only on transport errors or upstream 5xx;
149
+ a 4xx means the request itself is bad, so retrying elsewhere is pointless."""
150
+ if isinstance(exc, httpx.TransportError):
151
+ return True
152
+ if isinstance(exc, httpx.HTTPStatusError):
153
+ return exc.response.status_code >= 500
154
+ return False
155
+
156
+
157
+ def _log_failover(label: str, exc: Exception) -> None:
158
+ sys.stderr.write(
159
+ f"[deepparallel] {label}: primary endpoint failed "
160
+ f"({exc.__class__.__name__}); failing over to direct Azure\n"
161
+ )
162
+ sys.stderr.flush()
163
+
164
+
145
165
  class AzureBackend:
146
166
  label = "Azure OpenAI"
147
167
 
@@ -151,13 +171,56 @@ class AzureBackend:
151
171
  self._deployment = deployment
152
172
  self._api_version = api_version
153
173
 
154
- @property
155
- def _url(self) -> str:
174
+ def _build_url(self, endpoint: str) -> str:
175
+ # Cloudflare AI Gateway azure-openai routes carry the resource in the
176
+ # path and drop the native "/openai/deployments" segment; native Azure
177
+ # endpoints keep it.
178
+ endpoint = endpoint.rstrip("/")
179
+ if "/azure-openai/" in endpoint:
180
+ return (
181
+ f"{endpoint}/{self._deployment}"
182
+ f"/chat/completions?api-version={self._api_version}"
183
+ )
156
184
  return (
157
- f"{self._endpoint}/openai/deployments/{self._deployment}"
185
+ f"{endpoint}/openai/deployments/{self._deployment}"
158
186
  f"/chat/completions?api-version={self._api_version}"
159
187
  )
160
188
 
189
+ def _endpoints(self) -> list[str]:
190
+ # Primary is whatever is configured (typically the Cloudflare AI Gateway
191
+ # route). When that primary is a gateway route, derive the direct Azure
192
+ # endpoint from its resource segment and append it as automatic failover:
193
+ # fail-open, so a gateway outage degrades to direct Azure instead of
194
+ # taking down every CroweLM request. The failover request is not logged
195
+ # by the gateway -- the acceptable cost of staying available.
196
+ eps = [self._endpoint]
197
+ marker = "/azure-openai/"
198
+ if marker in self._endpoint:
199
+ resource = self._endpoint.split(marker, 1)[1].split("/", 1)[0]
200
+ if resource:
201
+ eps.append(f"https://{resource}.cognitiveservices.azure.com")
202
+ return eps
203
+
204
+ @property
205
+ def _url(self) -> str:
206
+ return self._build_url(self._endpoint)
207
+
208
+ @property
209
+ def _headers(self) -> dict:
210
+ # cf-aig-* headers configure AI Gateway per-request (no management API
211
+ # needed). Cache TTL is operationally tunable via DEEPPARALLEL_CACHE_TTL
212
+ # (seconds; "0" or empty disables caching for this agentic workload).
213
+ # Ignored by direct Azure on the failover path.
214
+ headers = {
215
+ "api-key": self._api_key,
216
+ "content-type": "application/json",
217
+ }
218
+ ttl = os.getenv("DEEPPARALLEL_CACHE_TTL", "300").strip()
219
+ if ttl and ttl != "0":
220
+ headers["cf-aig-cache-ttl"] = ttl
221
+ headers["cf-aig-metadata"] = '{"via":"deepparallel-gateway"}'
222
+ return headers
223
+
161
224
  def check(self) -> tuple[bool, str]:
162
225
  if not self._endpoint or not self._api_key:
163
226
  return False, "Azure endpoint or API key not configured."
@@ -167,49 +230,86 @@ class AzureBackend:
167
230
  return False, f"Azure endpoint unreachable ({e.__class__.__name__})"
168
231
  return True, f"Azure @ {_host(self._endpoint)}"
169
232
 
233
+ def _payload(self, messages, stream, temperature, max_tokens) -> dict:
234
+ # GPT-5 family deployments require `max_completion_tokens` and reject a
235
+ # custom `temperature` (only the default is accepted) -> they 400 on the
236
+ # legacy `max_tokens`/`temperature` shape. Everything else uses the
237
+ # classic params.
238
+ payload = {"messages": messages, "stream": stream}
239
+ dep = self._deployment.lower()
240
+ if dep.startswith("gpt-5") or dep.startswith("gpt-chat"):
241
+ # GPT-5 family + gpt-chat-latest require max_completion_tokens and
242
+ # reject a custom temperature (only the default is accepted).
243
+ payload["max_completion_tokens"] = max_tokens
244
+ else:
245
+ payload["temperature"] = temperature
246
+ payload["max_tokens"] = max_tokens
247
+ return payload
248
+
170
249
  def stream_chat(self, messages, temperature, max_tokens):
171
- payload = {
172
- "messages": messages,
173
- "stream": True,
174
- "temperature": temperature,
175
- "max_tokens": max_tokens,
176
- }
177
- headers = {"api-key": self._api_key, "content-type": "application/json"}
178
- with httpx.stream(
179
- "POST", self._url, json=payload, headers=headers, timeout=_STREAM_TIMEOUT
180
- ) as r:
181
- r.raise_for_status()
182
- yield from parse_sse_lines(r.iter_lines())
250
+ payload = self._payload(messages, True, temperature, max_tokens)
251
+ urls = [self._build_url(e) for e in self._endpoints()]
252
+ for i, url in enumerate(urls):
253
+ last = i == len(urls) - 1
254
+ started = False
255
+ try:
256
+ with httpx.stream(
257
+ "POST", url, json=payload, headers=self._headers, timeout=_STREAM_TIMEOUT
258
+ ) as r:
259
+ r.raise_for_status()
260
+ for chunk in parse_sse_lines(r.iter_lines()):
261
+ started = True
262
+ yield chunk
263
+ return
264
+ except (httpx.TransportError, httpx.HTTPStatusError) as e:
265
+ if last or started or not _should_failover(e):
266
+ raise
267
+ _log_failover(self.label, e)
268
+ continue
183
269
 
184
270
  def chat(self, messages, tools, temperature, max_tokens) -> dict:
185
- payload = {
186
- "messages": messages,
187
- "stream": False,
188
- "temperature": temperature,
189
- "max_tokens": max_tokens,
190
- }
271
+ payload = self._payload(messages, False, temperature, max_tokens)
191
272
  if tools:
192
273
  payload["tools"] = tools
193
- headers = {"api-key": self._api_key, "content-type": "application/json"}
194
- r = httpx.post(self._url, json=payload, headers=headers, timeout=_STREAM_TIMEOUT)
195
- r.raise_for_status()
196
- return _message_from_choice(r.json()["choices"][0])
274
+ urls = [self._build_url(e) for e in self._endpoints()]
275
+ for i, url in enumerate(urls):
276
+ last = i == len(urls) - 1
277
+ try:
278
+ r = httpx.post(url, json=payload, headers=self._headers, timeout=_STREAM_TIMEOUT)
279
+ r.raise_for_status()
280
+ return _message_from_choice(r.json()["choices"][0])
281
+ except (httpx.TransportError, httpx.HTTPStatusError) as e:
282
+ if last or not _should_failover(e):
283
+ raise
284
+ _log_failover(self.label, e)
285
+ continue
197
286
 
198
287
  def stream_chat_tools(self, messages, tools, temperature, max_tokens):
199
- payload = {
200
- "messages": messages,
201
- "stream": True,
202
- "temperature": temperature,
203
- "max_tokens": max_tokens,
204
- }
288
+ payload = self._payload(messages, True, temperature, max_tokens)
205
289
  if tools:
206
290
  payload["tools"] = tools
207
- headers = {"api-key": self._api_key, "content-type": "application/json"}
208
- with httpx.stream(
209
- "POST", self._url, json=payload, headers=headers, timeout=_STREAM_TIMEOUT
210
- ) as r:
211
- r.raise_for_status()
212
- return (yield from parse_sse_stream(r.iter_lines()))
291
+ urls = [self._build_url(e) for e in self._endpoints()]
292
+ for i, url in enumerate(urls):
293
+ last = i == len(urls) - 1
294
+ started = False
295
+ try:
296
+ with httpx.stream(
297
+ "POST", url, json=payload, headers=self._headers, timeout=_STREAM_TIMEOUT
298
+ ) as r:
299
+ r.raise_for_status()
300
+ gen = parse_sse_stream(r.iter_lines())
301
+ while True:
302
+ try:
303
+ chunk = next(gen)
304
+ except StopIteration as stop:
305
+ return stop.value
306
+ started = True
307
+ yield chunk
308
+ except (httpx.TransportError, httpx.HTTPStatusError) as e:
309
+ if last or started or not _should_failover(e):
310
+ raise
311
+ _log_failover(self.label, e)
312
+ continue
213
313
 
214
314
 
215
315
  class FoundryBackend:
@@ -495,13 +495,15 @@ def review(ctx: click.Context, as_diff: bool, path: str | None) -> None:
495
495
 
496
496
  Reviews a file (PATH) or a unified diff (--diff, from stdin) with a second
497
497
  model and prints a verdict. Exit code: 0 safe, 1 risky, 2 bug - so it can
498
- gate a commit or PR. Paid (Pro+).
498
+ gate a commit or PR. Free with your own key (DEEPPARALLEL_BACKEND=openai or
499
+ ollama); Pro unlocks the hosted Crowe Logic model stack.
499
500
  """
500
501
  settings: Settings = ctx.obj["settings"]
501
- ok, msg = licensing.check_feature("review")
502
- if not ok:
503
- branding.error(msg)
504
- sys.exit(3)
502
+ if not settings.byok:
503
+ ok, msg = licensing.check_feature("review")
504
+ if not ok:
505
+ branding.error(msg)
506
+ sys.exit(3)
505
507
  if as_diff:
506
508
  content = sys.stdin.read()
507
509
  elif path:
@@ -45,6 +45,10 @@ class Settings:
45
45
  mycelium_key: str | None = None
46
46
  mycelium_secret: str | None = None
47
47
  mycelium_model: str = "Mcrowe1210/gemma-4-mycelium-e4b"
48
+ # True when the user brings their own key/model (openai/ollama/foundry):
49
+ # the FREE tier. review + the agent run unlicensed; only the Crowe-hosted
50
+ # stack (azure/crowe) is gated to Pro.
51
+ byok: bool = False
48
52
  # Crowe ID agent identity (backend="crowe"): route through the Foundry gateway
49
53
  # authenticated by a client_credentials token instead of a raw provider key.
50
54
  gateway_url: str | None = None
@@ -102,17 +106,36 @@ def _int_env(name: str, default: int) -> int:
102
106
 
103
107
  def resolve_settings() -> Settings:
104
108
  backend = os.environ.get("DEEPPARALLEL_BACKEND", "azure").strip().lower()
105
- if backend not in {"azure", "foundry", "crowe"}:
109
+ if backend not in {"azure", "foundry", "crowe", "openai", "ollama"}:
106
110
  backend = "azure"
111
+
112
+ # BYOK (bring-your-own-key) backends are the FREE tier. "openai" and "ollama"
113
+ # are friendly aliases onto the OpenAI-compatible foundry transport with
114
+ # sensible defaults, so `dp` works with no Crowe account and no license.
115
+ foundry_base_url = os.environ.get("FOUNDRY_BASE_URL")
116
+ foundry_api_key = os.environ.get("FOUNDRY_API_KEY")
117
+ foundry_model = os.environ.get("DEEPPARALLEL_FOUNDRY_MODEL", "DeepSeek-V3-1")
118
+ if backend == "openai":
119
+ foundry_base_url = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com")
120
+ foundry_api_key = os.environ.get("OPENAI_API_KEY") or foundry_api_key
121
+ foundry_model = os.environ.get("DEEPPARALLEL_MODEL", "gpt-4o-mini")
122
+ backend = "foundry"
123
+ elif backend == "ollama":
124
+ foundry_base_url = os.environ.get("OLLAMA_HOST", "http://localhost:11434")
125
+ foundry_api_key = foundry_api_key or "ollama"
126
+ foundry_model = os.environ.get("DEEPPARALLEL_MODEL", "llama3.1")
127
+ backend = "foundry"
128
+ byok = backend == "foundry"
129
+
107
130
  return Settings(
108
131
  backend=backend,
109
132
  azure_endpoint=os.environ.get("AZURE_CORE_ENDPOINT"),
110
133
  azure_api_key=os.environ.get("AZURE_CORE_API_KEY"),
111
134
  deployment=os.environ.get("DEEPPARALLEL_DEPLOYMENT", "DeepSeek-V4-Pro"),
112
135
  api_version=os.environ.get("DEEPPARALLEL_API_VERSION", "2024-08-01-preview"),
113
- foundry_base_url=os.environ.get("FOUNDRY_BASE_URL"),
114
- foundry_api_key=os.environ.get("FOUNDRY_API_KEY"),
115
- foundry_model=os.environ.get("DEEPPARALLEL_FOUNDRY_MODEL", "DeepSeek-V3-1"),
136
+ foundry_base_url=foundry_base_url,
137
+ foundry_api_key=foundry_api_key,
138
+ foundry_model=foundry_model,
116
139
  temperature=_float_env("DEEPPARALLEL_TEMPERATURE", 0.4),
117
140
  max_tokens=_int_env("DEEPPARALLEL_MAX_TOKENS", 8192),
118
141
  show_thinking=_bool_env("DEEPPARALLEL_THINK", False),
@@ -149,6 +172,7 @@ def resolve_settings() -> Settings:
149
172
  crowe_id_client_id=os.environ.get("CROWE_ID_CLIENT_ID"),
150
173
  crowe_id_client_secret=os.environ.get("CROWE_ID_CLIENT_SECRET"),
151
174
  crowe_id_audience=os.environ.get("CROWE_ID_AUDIENCE"),
175
+ byok=byok,
152
176
  )
153
177
 
154
178
 
@@ -51,16 +51,35 @@ _SCRUB: list[tuple[re.Pattern[str], str]] = [
51
51
  # The listing must never leak raw deployment names; chat accepts either the
52
52
  # alias or the raw name, so existing callers keep working.
53
53
  _MODEL_ALIASES: dict[str, str] = {
54
+ # DeepSeek family
54
55
  "crowelm-apex": "DeepSeek-V4-Pro",
55
56
  "crowelm-reason": "DeepSeek-R1-0528",
56
57
  "crowelm-flash": "DeepSeek-V4-Flash",
57
- "crowelm-vector": "DeepSeek-V3.1",
58
+ "crowelm-vector": "DeepSeek-V3-1",
59
+ # GPT family (Azure frontier)
60
+ "crowelm-zenith": "gpt-5.5",
61
+ # --- gpt-5.4 family back-burnered (de-prioritized) — uncomment to re-enable ---
62
+ # "crowelm-prime": "gpt-5.4",
63
+ # "crowelm-prime-mini": "gpt-5.4-mini",
64
+ # "crowelm-prime-nano": "gpt-5.4-nano",
65
+ "crowelm-chat": "gpt-chat-latest",
66
+ "crowelm-swift": "gpt-4o",
67
+ # Grok family
58
68
  "crowelm-quasar": "grok-4-3",
59
69
  "crowelm-quasar-fast": "grok-4-1-fast-reasoning",
60
- "crowelm-herald": "cohere-command-a",
70
+ "crowelm-quasar-lite": "grok-4-1-fast-non-r",
71
+ "crowelm-quasar-max": "grok-4-20-reasoning",
72
+ # Kimi family
61
73
  "crowelm-eclipse": "Kimi-K2-6",
74
+ "crowelm-eclipse-lite": "Kimi-K2.5",
75
+ # Llama family
62
76
  "crowelm-titan": "Llama-3-3-70B",
63
- "crowelm-swift": "gpt-4o",
77
+ "crowelm-maverick": "Llama-4-Maverick",
78
+ "crowelm-scout": "Llama-4-Scout",
79
+ # Specialist tiers
80
+ "crowelm-herald": "Cohere-Command-A",
81
+ "crowelm-forge": "Codestral-2501",
82
+ "crowelm-router": "model-router",
64
83
  # Free base tier: the sovereign Gemma 4 Mycelium model on Modal. Only listed
65
84
  # in /v1/models when MODAL_MYCELIUM_ENDPOINT is configured (see below).
66
85
  "crowelm-mycelium": "Mcrowe1210/gemma-4-mycelium-e4b",
@@ -174,8 +193,11 @@ def _models_payload(settings) -> dict:
174
193
  # this gateway process. Never list raw deployment names; chat still accepts
175
194
  # them for existing callers, but discovery should not advertise unknown
176
195
  # deployments.
177
- configured = _configured_deployments(settings)
178
- ids = [alias for alias, deployment in _MODEL_ALIASES.items() if deployment in configured]
196
+ ids = [
197
+ alias
198
+ for alias, deployment in _MODEL_ALIASES.items()
199
+ if _deployment_available(deployment, settings)
200
+ ]
179
201
  created = int(time.time())
180
202
  return {
181
203
  "object": "list",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepparallel
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
5
5
  Author-email: Michael Crowe <michael@crowelogic.com>
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepparallel"
7
- version = "0.5.0"
7
+ version = "0.5.1"
8
8
  description = "DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
File without changes
File without changes