deepparallel 0.2.0__py3-none-any.whl
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.
- deepparallel/__init__.py +3 -0
- deepparallel/agent.py +286 -0
- deepparallel/backend.py +302 -0
- deepparallel/branding.py +211 -0
- deepparallel/cli.py +569 -0
- deepparallel/config.py +158 -0
- deepparallel/fusion.py +225 -0
- deepparallel/licensing.py +108 -0
- deepparallel/registry.json +13 -0
- deepparallel/renderer.py +222 -0
- deepparallel/system_prompt.txt +4 -0
- deepparallel/tools/__init__.py +27 -0
- deepparallel/tools/codeast.py +171 -0
- deepparallel/tools/edit.py +29 -0
- deepparallel/tools/files.py +74 -0
- deepparallel/tools/registry.py +149 -0
- deepparallel/tools/sandbox.py +110 -0
- deepparallel/tools/search.py +38 -0
- deepparallel/tools/shell.py +38 -0
- deepparallel/tools/vision.py +54 -0
- deepparallel/tools/web.py +76 -0
- deepparallel-0.2.0.dist-info/METADATA +128 -0
- deepparallel-0.2.0.dist-info/RECORD +26 -0
- deepparallel-0.2.0.dist-info/WHEEL +5 -0
- deepparallel-0.2.0.dist-info/entry_points.txt +3 -0
- deepparallel-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Sandboxed code execution.
|
|
2
|
+
|
|
3
|
+
Runs a snippet in an ephemeral Docker container when the daemon is reachable
|
|
4
|
+
(real isolation: no network, memory cap), else falls back to a timeboxed local
|
|
5
|
+
subprocess. Force a mode with DEEPPARALLEL_SANDBOX=docker|subprocess (default auto).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
import tempfile
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from deepparallel.tools import tool
|
|
18
|
+
|
|
19
|
+
_LANGS = {
|
|
20
|
+
"python": {
|
|
21
|
+
"image": "python:3.12-slim",
|
|
22
|
+
"argv": ["python"],
|
|
23
|
+
"local": [sys.executable],
|
|
24
|
+
"ext": ".py",
|
|
25
|
+
},
|
|
26
|
+
"javascript": {"image": "node:20-slim", "argv": ["node"], "local": ["node"], "ext": ".js"},
|
|
27
|
+
"bash": {"image": "bash:5", "argv": ["bash"], "local": ["bash"], "ext": ".sh"},
|
|
28
|
+
}
|
|
29
|
+
_ALIASES = {"py": "python", "js": "javascript", "node": "javascript", "sh": "bash", "shell": "bash"}
|
|
30
|
+
|
|
31
|
+
_MAX_OUT = 50_000
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _docker_available() -> bool:
|
|
35
|
+
try:
|
|
36
|
+
proc = subprocess.run(["docker", "version"], capture_output=True, text=True, timeout=4)
|
|
37
|
+
return proc.returncode == 0
|
|
38
|
+
except Exception: # noqa: BLE001 - any failure means no usable daemon
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _run(cmd: list[str], cwd: str | None, timeout: int):
|
|
43
|
+
proc = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout)
|
|
44
|
+
return proc.stdout or "", proc.stderr or "", proc.returncode
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _use_docker() -> bool:
|
|
48
|
+
mode = os.environ.get("DEEPPARALLEL_SANDBOX", "auto").strip().lower()
|
|
49
|
+
if mode == "docker":
|
|
50
|
+
return True
|
|
51
|
+
if mode == "subprocess":
|
|
52
|
+
return False
|
|
53
|
+
return _docker_available()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@tool(dangerous=True)
|
|
57
|
+
def run_code(language: str, code: str, timeout_seconds: int = 30) -> str:
|
|
58
|
+
"""Execute a code snippet in a sandbox and capture its output.
|
|
59
|
+
|
|
60
|
+
:param language: One of python, javascript, bash.
|
|
61
|
+
:param code: Source code to run.
|
|
62
|
+
:param timeout_seconds: Maximum execution time in seconds.
|
|
63
|
+
"""
|
|
64
|
+
lang = _ALIASES.get(language.strip().lower(), language.strip().lower())
|
|
65
|
+
spec = _LANGS.get(lang)
|
|
66
|
+
if spec is None:
|
|
67
|
+
return json.dumps({"error": f"unsupported language: {language}"})
|
|
68
|
+
timeout = min(int(timeout_seconds), 300)
|
|
69
|
+
|
|
70
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
71
|
+
src = Path(tmp) / f"snippet{spec['ext']}"
|
|
72
|
+
src.write_text(code, encoding="utf-8")
|
|
73
|
+
if _use_docker():
|
|
74
|
+
sandbox = "docker"
|
|
75
|
+
cmd = [
|
|
76
|
+
"docker",
|
|
77
|
+
"run",
|
|
78
|
+
"--rm",
|
|
79
|
+
"--network",
|
|
80
|
+
"none",
|
|
81
|
+
"--memory",
|
|
82
|
+
"512m",
|
|
83
|
+
"-v",
|
|
84
|
+
f"{tmp}:/work",
|
|
85
|
+
"-w",
|
|
86
|
+
"/work",
|
|
87
|
+
spec["image"],
|
|
88
|
+
*spec["argv"],
|
|
89
|
+
src.name,
|
|
90
|
+
]
|
|
91
|
+
cwd = None
|
|
92
|
+
else:
|
|
93
|
+
sandbox = "subprocess"
|
|
94
|
+
cmd = [*spec["local"], str(src)]
|
|
95
|
+
cwd = tmp
|
|
96
|
+
try:
|
|
97
|
+
stdout, stderr, rc = _run(cmd, cwd, timeout)
|
|
98
|
+
except subprocess.TimeoutExpired:
|
|
99
|
+
return json.dumps({"error": f"code timed out after {timeout}s", "sandbox": sandbox})
|
|
100
|
+
except FileNotFoundError as e:
|
|
101
|
+
return json.dumps({"error": f"runtime not available: {e}", "sandbox": sandbox})
|
|
102
|
+
|
|
103
|
+
return json.dumps(
|
|
104
|
+
{
|
|
105
|
+
"stdout": stdout[:_MAX_OUT],
|
|
106
|
+
"stderr": stderr[:10_000],
|
|
107
|
+
"exit_code": rc,
|
|
108
|
+
"sandbox": sandbox,
|
|
109
|
+
}
|
|
110
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Content-search tool (grep)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from deepparallel.tools import tool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@tool(dangerous=False)
|
|
13
|
+
def grep(dir_path: str, pattern: str, include: str = "**/*") -> str:
|
|
14
|
+
"""Search file contents for a regular-expression pattern.
|
|
15
|
+
|
|
16
|
+
:param dir_path: Root directory to search.
|
|
17
|
+
:param pattern: Python regular expression.
|
|
18
|
+
:param include: Glob restricting which files are searched.
|
|
19
|
+
"""
|
|
20
|
+
root = Path(dir_path).expanduser().resolve()
|
|
21
|
+
try:
|
|
22
|
+
rx = re.compile(pattern)
|
|
23
|
+
except re.error as e:
|
|
24
|
+
return json.dumps({"error": f"bad pattern: {e}"})
|
|
25
|
+
matches = []
|
|
26
|
+
for p in sorted(root.glob(include)):
|
|
27
|
+
if not p.is_file():
|
|
28
|
+
continue
|
|
29
|
+
try:
|
|
30
|
+
text = p.read_text(encoding="utf-8", errors="replace")
|
|
31
|
+
except OSError:
|
|
32
|
+
continue
|
|
33
|
+
for i, line in enumerate(text.splitlines(), 1):
|
|
34
|
+
if rx.search(line):
|
|
35
|
+
matches.append({"path": str(p), "line_number": i, "line": line[:400]})
|
|
36
|
+
if len(matches) >= 500:
|
|
37
|
+
return json.dumps({"matches": matches, "truncated": True})
|
|
38
|
+
return json.dumps({"matches": matches})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Shell execution tool."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import subprocess
|
|
7
|
+
|
|
8
|
+
from deepparallel.tools import tool
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@tool(dangerous=True)
|
|
12
|
+
def run_shell(command: str, working_directory: str = "", timeout_seconds: int = 120) -> str:
|
|
13
|
+
"""Run a shell command and capture its output.
|
|
14
|
+
|
|
15
|
+
:param command: Command to execute via the shell.
|
|
16
|
+
:param working_directory: Directory to run in (default: current).
|
|
17
|
+
:param timeout_seconds: Maximum execution time in seconds.
|
|
18
|
+
"""
|
|
19
|
+
timeout_seconds = min(int(timeout_seconds), 600)
|
|
20
|
+
try:
|
|
21
|
+
r = subprocess.run(
|
|
22
|
+
command,
|
|
23
|
+
shell=True,
|
|
24
|
+
capture_output=True,
|
|
25
|
+
text=True,
|
|
26
|
+
timeout=timeout_seconds,
|
|
27
|
+
cwd=working_directory or None,
|
|
28
|
+
)
|
|
29
|
+
except subprocess.TimeoutExpired:
|
|
30
|
+
return json.dumps(
|
|
31
|
+
{"error": f"Command timed out after {timeout_seconds}s", "return_code": -1}
|
|
32
|
+
)
|
|
33
|
+
stdout = r.stdout or ""
|
|
34
|
+
if len(stdout) > 50000:
|
|
35
|
+
stdout = stdout[:50000] + "\n... (output truncated at 50KB)"
|
|
36
|
+
return json.dumps(
|
|
37
|
+
{"stdout": stdout, "stderr": (r.stderr or "")[:10000], "return_code": r.returncode}
|
|
38
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Vision tool: analyze a local image with a multimodal deployment.
|
|
2
|
+
|
|
3
|
+
The text backend (DeepParallel) is not multimodal, so this routes to a separate
|
|
4
|
+
vision deployment named by DEEPPARALLEL_VISION_DEPLOYMENT, using the same transport.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import base64
|
|
10
|
+
import json
|
|
11
|
+
import mimetypes
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from deepparallel.tools import tool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@tool(dangerous=False)
|
|
19
|
+
def analyze_image(image_path: str, prompt: str = "Describe this image in detail.") -> str:
|
|
20
|
+
"""Analyze a local image with a vision model and return a description.
|
|
21
|
+
|
|
22
|
+
Requires DEEPPARALLEL_VISION_DEPLOYMENT (a multimodal deployment).
|
|
23
|
+
|
|
24
|
+
:param image_path: Path to a local image file.
|
|
25
|
+
:param prompt: What to ask about the image.
|
|
26
|
+
"""
|
|
27
|
+
path = Path(image_path).expanduser().resolve()
|
|
28
|
+
if not path.is_file():
|
|
29
|
+
return json.dumps({"error": f"image not found: {image_path}"})
|
|
30
|
+
# Defaults to a multimodal deployment so vision works out of the box;
|
|
31
|
+
# override with DEEPPARALLEL_VISION_DEPLOYMENT.
|
|
32
|
+
deployment = (os.environ.get("DEEPPARALLEL_VISION_DEPLOYMENT") or "Llama-4-Scout").strip()
|
|
33
|
+
|
|
34
|
+
from deepparallel import backend as backend_mod
|
|
35
|
+
from deepparallel.config import resolve_settings
|
|
36
|
+
|
|
37
|
+
settings = resolve_settings()
|
|
38
|
+
data = base64.b64encode(path.read_bytes()).decode()
|
|
39
|
+
mime = mimetypes.guess_type(str(path))[0] or "image/png"
|
|
40
|
+
messages = [
|
|
41
|
+
{
|
|
42
|
+
"role": "user",
|
|
43
|
+
"content": [
|
|
44
|
+
{"type": "text", "text": prompt},
|
|
45
|
+
{"type": "image_url", "image_url": {"url": f"data:{mime};base64,{data}"}},
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
vision = backend_mod.backend_for_deployment(settings, deployment)
|
|
50
|
+
try:
|
|
51
|
+
msg = vision.chat(messages, [], settings.temperature, settings.max_tokens)
|
|
52
|
+
except Exception as e: # noqa: BLE001 - surface vision failure to the model
|
|
53
|
+
return json.dumps({"error": f"vision call failed: {type(e).__name__}: {e}"})
|
|
54
|
+
return json.dumps({"description": (msg.get("content") or "").strip()})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Web tools: fetch a page's text, and search (key-gated)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
from deepparallel.tools import tool
|
|
12
|
+
|
|
13
|
+
_SCRIPT_STYLE = re.compile(r"<(script|style)\b[^>]*>.*?</\1>", re.IGNORECASE | re.DOTALL)
|
|
14
|
+
_TAG = re.compile(r"<[^>]+>")
|
|
15
|
+
_TITLE = re.compile(r"<title[^>]*>(.*?)</title>", re.IGNORECASE | re.DOTALL)
|
|
16
|
+
_WS = re.compile(r"\s+")
|
|
17
|
+
_TIMEOUT = 15.0
|
|
18
|
+
_UA = "DeepParallel/0.1"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@tool(dangerous=False)
|
|
22
|
+
def web_fetch(url: str, max_chars: int = 8000) -> str:
|
|
23
|
+
"""Fetch a web page and return its readable text (HTML stripped).
|
|
24
|
+
|
|
25
|
+
:param url: The URL to fetch.
|
|
26
|
+
:param max_chars: Maximum characters of text to return.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
r = httpx.get(url, timeout=_TIMEOUT, follow_redirects=True, headers={"user-agent": _UA})
|
|
30
|
+
r.raise_for_status()
|
|
31
|
+
except Exception as e: # noqa: BLE001 - surface fetch failure to the model
|
|
32
|
+
return json.dumps({"error": f"fetch failed: {type(e).__name__}: {e}"})
|
|
33
|
+
html = r.text or ""
|
|
34
|
+
title_m = _TITLE.search(html)
|
|
35
|
+
title = _WS.sub(" ", _TAG.sub("", title_m.group(1))).strip() if title_m else ""
|
|
36
|
+
text = _WS.sub(" ", _TAG.sub(" ", _SCRIPT_STYLE.sub(" ", html))).strip()
|
|
37
|
+
return json.dumps({"url": url, "title": title, "text": text[:max_chars]})
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@tool(dangerous=False)
|
|
41
|
+
def web_search(query: str, count: int = 5) -> str:
|
|
42
|
+
"""Search the web and return result titles, URLs, and snippets.
|
|
43
|
+
|
|
44
|
+
Requires DEEPPARALLEL_SEARCH_API_KEY (Brave Search API by default).
|
|
45
|
+
|
|
46
|
+
:param query: The search query.
|
|
47
|
+
:param count: Maximum number of results.
|
|
48
|
+
"""
|
|
49
|
+
key = os.environ.get("DEEPPARALLEL_SEARCH_API_KEY")
|
|
50
|
+
if not key:
|
|
51
|
+
return json.dumps(
|
|
52
|
+
{"error": "search not configured: set DEEPPARALLEL_SEARCH_API_KEY (Brave Search API)"}
|
|
53
|
+
)
|
|
54
|
+
url = os.environ.get(
|
|
55
|
+
"DEEPPARALLEL_SEARCH_URL", "https://api.search.brave.com/res/v1/web/search"
|
|
56
|
+
)
|
|
57
|
+
try:
|
|
58
|
+
r = httpx.get(
|
|
59
|
+
url,
|
|
60
|
+
params={"q": query, "count": count},
|
|
61
|
+
headers={"X-Subscription-Token": key, "accept": "application/json"},
|
|
62
|
+
timeout=_TIMEOUT,
|
|
63
|
+
)
|
|
64
|
+
r.raise_for_status()
|
|
65
|
+
data = r.json()
|
|
66
|
+
except Exception as e: # noqa: BLE001 - surface search failure to the model
|
|
67
|
+
return json.dumps({"error": f"search failed: {type(e).__name__}: {e}"})
|
|
68
|
+
results = [
|
|
69
|
+
{
|
|
70
|
+
"title": item.get("title", ""),
|
|
71
|
+
"url": item.get("url", ""),
|
|
72
|
+
"snippet": item.get("description", ""),
|
|
73
|
+
}
|
|
74
|
+
for item in (data.get("web", {}).get("results") or [])[:count]
|
|
75
|
+
]
|
|
76
|
+
return json.dumps({"results": results})
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deepparallel
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: DeepParallel - a multi-model agentic coding CLI with cross-model Guardian review, served via Crowe Logic.
|
|
5
|
+
Author-email: Michael Crowe <michael@crowelogic.com>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://crowelogic.com
|
|
8
|
+
Keywords: deepparallel,agent,coding-agent,cli,llm,code-review,crowe-logic
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: click>=8.1.0
|
|
12
|
+
Requires-Dist: rich>=14.0.0
|
|
13
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
14
|
+
Requires-Dist: httpx>=0.28.0
|
|
15
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
16
|
+
Requires-Dist: tree-sitter>=0.25.0
|
|
17
|
+
Requires-Dist: tree-sitter-language-pack>=1.8.0
|
|
18
|
+
Requires-Dist: cryptography>=42.0.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
22
|
+
|
|
23
|
+
# DeepParallel
|
|
24
|
+
|
|
25
|
+
A focused command-line interface for the DeepParallel model, served via Crowe Logic.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
uv venv
|
|
30
|
+
uv pip install -p .venv -e .
|
|
31
|
+
|
|
32
|
+
## Configure
|
|
33
|
+
|
|
34
|
+
Set the backend env vars (a `.env` file in the working directory is loaded automatically):
|
|
35
|
+
|
|
36
|
+
# default backend: azure
|
|
37
|
+
AZURE_CORE_ENDPOINT=https://<resource>.openai.azure.com
|
|
38
|
+
AZURE_CORE_API_KEY=<key>
|
|
39
|
+
# optional overrides
|
|
40
|
+
DEEPPARALLEL_API_VERSION=2024-08-01-preview
|
|
41
|
+
|
|
42
|
+
# or route through the Crowe Logic Foundry control plane:
|
|
43
|
+
DEEPPARALLEL_BACKEND=foundry
|
|
44
|
+
FOUNDRY_BASE_URL=https://<control-plane>
|
|
45
|
+
FOUNDRY_API_KEY=<token>
|
|
46
|
+
|
|
47
|
+
## Use
|
|
48
|
+
|
|
49
|
+
deepparallel # interactive agent chat
|
|
50
|
+
deepparallel run "question" # one-shot, pipe-friendly (answer on stdout)
|
|
51
|
+
deepparallel tools # list the agent's tools
|
|
52
|
+
deepparallel info # model + backend status
|
|
53
|
+
deepparallel doctor # diagnose config + reachability
|
|
54
|
+
deepparallel run --no-tools "..." # plain chat, no tools
|
|
55
|
+
deepparallel run --yes "..." # auto-approve tool actions
|
|
56
|
+
|
|
57
|
+
## Fusion (stacking models for stronger output)
|
|
58
|
+
|
|
59
|
+
`deepparallel` can stack the answerer with a separate reasoning model. All modes
|
|
60
|
+
compose hosted backends (no GPU/weight-merging), so they are API-call stacking.
|
|
61
|
+
|
|
62
|
+
deepparallel run --fuse reason "hard question" # reasoner thinks, answerer answers
|
|
63
|
+
deepparallel run --fuse escalate "question" # answer first; escalate to reasoner only if unsure
|
|
64
|
+
deepparallel run --deep "question" # heavy: many models in parallel + a judge (slow)
|
|
65
|
+
deepparallel run --dual "A,B" "question" # compare two deployments side by side
|
|
66
|
+
deepparallel run --dual "A,B" --synth "question" # ...and synthesize a merged answer
|
|
67
|
+
|
|
68
|
+
`--fuse` also works in interactive chat. Set a default with `DEEPPARALLEL_FUSION=reason`.
|
|
69
|
+
|
|
70
|
+
## Fusion-native UX (what single-model agents cannot do)
|
|
71
|
+
|
|
72
|
+
- **Guardian review** - before an edit (`write_file` / `edit_file` / `ast_replace_symbol`)
|
|
73
|
+
is applied, a second model reviews the diff and its verdict
|
|
74
|
+
(`safe` / `risky: ...` / `bug: ...`) is shown in the confirm card. Advisory:
|
|
75
|
+
you still approve. Toggle with `DEEPPARALLEL_GUARDIAN`; pick the reviewer with
|
|
76
|
+
`DEEPPARALLEL_GUARDIAN_DEPLOYMENT`.
|
|
77
|
+
- **Consensus + divergence** - `run --deep` prints a `consensus:` chip from
|
|
78
|
+
cross-model agreement (agreement, not correctness), and on low agreement
|
|
79
|
+
lists the dissenting candidates.
|
|
80
|
+
- **Live dial** - in interactive chat, `/fast`, `/fuse`, `/escalate` switch the
|
|
81
|
+
fusion mode mid-session (shown in the prompt); `/deep` runs the next prompt as
|
|
82
|
+
a multi-model query.
|
|
83
|
+
|
|
84
|
+
## Agent and tools
|
|
85
|
+
|
|
86
|
+
`deepparallel` is an agent: it can call tools to inspect and change your project.
|
|
87
|
+
|
|
88
|
+
- Read-only (run automatically): `read_file`, `list_dir`, `glob`, `grep`,
|
|
89
|
+
`ast_symbols`, `ast_show_symbol`, `web_fetch`, `web_search`, `analyze_image`.
|
|
90
|
+
- Mutating / executing (require confirmation): `write_file`, `edit_file`,
|
|
91
|
+
`ast_replace_symbol`, `run_shell`, `run_code`.
|
|
92
|
+
|
|
93
|
+
`web_search` needs `DEEPPARALLEL_SEARCH_API_KEY`; `analyze_image` works out of the
|
|
94
|
+
box on a multimodal deployment (override with `DEEPPARALLEL_VISION_DEPLOYMENT`).
|
|
95
|
+
|
|
96
|
+
In interactive chat, mutating actions prompt for y/n. In `run` (non-interactive)
|
|
97
|
+
they are denied unless you pass `--yes`. `ast_*` tools are multi-language via
|
|
98
|
+
tree-sitter; `run_code` executes in a Docker sandbox when available, else a
|
|
99
|
+
timeboxed local subprocess.
|
|
100
|
+
|
|
101
|
+
## Configuration reference
|
|
102
|
+
|
|
103
|
+
| Variable | Purpose | Default |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `DEEPPARALLEL_BACKEND` | `azure` or `foundry` | `azure` |
|
|
106
|
+
| `AZURE_CORE_ENDPOINT` / `AZURE_CORE_API_KEY` | Azure transport | (required for azure) |
|
|
107
|
+
| `DEEPPARALLEL_API_VERSION` | Azure API version | `2024-08-01-preview` |
|
|
108
|
+
| `FOUNDRY_BASE_URL` / `FOUNDRY_API_KEY` | control-plane transport | (required for foundry) |
|
|
109
|
+
| `DEEPPARALLEL_TEMPERATURE` | default sampling temperature | `0.4` |
|
|
110
|
+
| `DEEPPARALLEL_MAX_TOKENS` | response cap | `2048` |
|
|
111
|
+
| `DEEPPARALLEL_THINK` | surface reasoning stream | `0` (answer-only) |
|
|
112
|
+
| `DEEPPARALLEL_TOOLS` | enable agent tools | `1` (on) |
|
|
113
|
+
| `DEEPPARALLEL_AUTO_APPROVE` | auto-approve mutating tools | `0` (off) |
|
|
114
|
+
| `DEEPPARALLEL_MAX_STEPS` | max agent tool-call rounds | `12` |
|
|
115
|
+
| `DEEPPARALLEL_SHELL_TIMEOUT` | run_shell timeout (s) | `120` |
|
|
116
|
+
| `DEEPPARALLEL_SANDBOX` | `auto` / `docker` / `subprocess` for run_code | `auto` |
|
|
117
|
+
| `DEEPPARALLEL_PLAIN` | force plain (non-rich) output | `0` |
|
|
118
|
+
| `DEEPPARALLEL_FUSION` | default fusion: `off` / `reason` / `escalate` | `off` |
|
|
119
|
+
| `DEEPPARALLEL_REASONER_DEPLOYMENT` | reasoner model for fusion | `DeepSeek-R1-0528` |
|
|
120
|
+
| `DEEPPARALLEL_PARALLEL_MODELS` | comma-separated chains for `--deep` | (three defaults) |
|
|
121
|
+
| `DEEPPARALLEL_JUDGE_DEPLOYMENT` | judge/synthesizer model | the primary deployment |
|
|
122
|
+
| `DEEPPARALLEL_SEARCH_API_KEY` | enables web_search (Brave Search API) | (unset) |
|
|
123
|
+
| `DEEPPARALLEL_SEARCH_URL` | search API endpoint | Brave web search |
|
|
124
|
+
| `DEEPPARALLEL_VISION_DEPLOYMENT` | multimodal model for analyze_image | `Llama-4-Scout` |
|
|
125
|
+
| `DEEPPARALLEL_GUARDIAN` | second-model review of edits before apply | `1` (on) |
|
|
126
|
+
| `DEEPPARALLEL_GUARDIAN_DEPLOYMENT` | the reviewer model | the reasoner |
|
|
127
|
+
|
|
128
|
+
DeepParallel is served via Crowe Logic infrastructure. https://crowelogic.com
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
deepparallel/__init__.py,sha256=dcRd-DGbeCsxhE2nBkRv6XCRb0Gsg3zBKc7vcf-4h8o,55
|
|
2
|
+
deepparallel/agent.py,sha256=HH8IntUvRF6sbOKXOP3RYstVGV6XllNhRY0vv2p3EII,10598
|
|
3
|
+
deepparallel/backend.py,sha256=rKlpilOb3Wj8PkjK5OlYZg7p-0_UxY3pe6A_lMFY85Q,10257
|
|
4
|
+
deepparallel/branding.py,sha256=40FmA2syEK6gjzD886HeL5obaKMWROTxc_IveiTuntA,7208
|
|
5
|
+
deepparallel/cli.py,sha256=fVjwiXeeN7aHGF9fKa0e4J6PJ0FKd262ocFqLCaBH-Q,20217
|
|
6
|
+
deepparallel/config.py,sha256=G6xt7oyV89h8veJEZiA0KM6U-KozUHGjJhdbsIF6gss,5255
|
|
7
|
+
deepparallel/fusion.py,sha256=GoMJMmg5TkZDFpjNqo1dAEo7-Xuc0C_kyd7MHtyzR04,8265
|
|
8
|
+
deepparallel/licensing.py,sha256=1vG_VBuovNIjIy674Ej1w55wRSU8owc3XNSnwm4WrS8,3558
|
|
9
|
+
deepparallel/registry.json,sha256=HHKm4tIOHFGO9KR0Z2dEQMIo4qTUlAswE_IzNv187Q0,298
|
|
10
|
+
deepparallel/renderer.py,sha256=pW0bA1-1WN2DgawbZKAwT_vtaRVTUFLztdClLKfFhdI,7759
|
|
11
|
+
deepparallel/system_prompt.txt,sha256=WBWszuTW9B25DK0I584QVbcoo-fGCDV8hFs1qBv5H8g,667
|
|
12
|
+
deepparallel/tools/__init__.py,sha256=vgZQtfEIxH39Qho6hhpUo6rsYtZu4moEfcx7KINpQBk,611
|
|
13
|
+
deepparallel/tools/codeast.py,sha256=6wlH6XsBx39Jkizj7P0Wkb1_PyWxfZt1EA0SzXF-hWY,5860
|
|
14
|
+
deepparallel/tools/edit.py,sha256=puT4x6FTG54MqiE3ur1EL6YVQwg66f5rmT7AE5CUgAY,1060
|
|
15
|
+
deepparallel/tools/files.py,sha256=aydsTgovEsoCWNwp2y4V3afDa8p8oIBeZA5tXXw175g,2667
|
|
16
|
+
deepparallel/tools/registry.py,sha256=Eqv_VcWToZbPuOCvNul6_jx1ItAxEt7OLMP4eCr_XQw,4612
|
|
17
|
+
deepparallel/tools/sandbox.py,sha256=WZhsZnkVUnC1ZTDAgyoIUO4C8sUvZHab6Ji3ftfSu_o,3459
|
|
18
|
+
deepparallel/tools/search.py,sha256=jsX0ieouuUZzkWsgDrtwzVF-aV36NetarFqmQy4mT-4,1222
|
|
19
|
+
deepparallel/tools/shell.py,sha256=DjUGBT5DKYPNplEgMIpCy6z5ss7agbzlV2cby9LjGZo,1186
|
|
20
|
+
deepparallel/tools/vision.py,sha256=Nm8uIDllyaa6ucfgmzdM_272H1EVW27fO2ALybw6n0w,2044
|
|
21
|
+
deepparallel/tools/web.py,sha256=Q3HZ0uAOyHWt4EV9q-gNLdcp1zOOB0QDXhjgRX70YeY,2647
|
|
22
|
+
deepparallel-0.2.0.dist-info/METADATA,sha256=HY1ZGm6Rcht5yNrFGgYXnTfXeFeR056B80aniTLfAsQ,6110
|
|
23
|
+
deepparallel-0.2.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
24
|
+
deepparallel-0.2.0.dist-info/entry_points.txt,sha256=1WPP0AaAhVqet5SvmVlFj-SdkY4cYWGVXy12mAHTTXs,82
|
|
25
|
+
deepparallel-0.2.0.dist-info/top_level.txt,sha256=KpyrGzNnWKi_qNljdM2Z5dyfMAff_-6_lpDZv07NcEc,13
|
|
26
|
+
deepparallel-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
deepparallel
|