spawnllm 0.5.4__tar.gz → 0.5.5__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 (30) hide show
  1. spawnllm-0.5.5/PKG-INFO +129 -0
  2. spawnllm-0.5.5/README.md +82 -0
  3. {spawnllm-0.5.4 → spawnllm-0.5.5}/pyproject.toml +2 -2
  4. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/codex.py +4 -1
  5. spawnllm-0.5.4/PKG-INFO +0 -174
  6. spawnllm-0.5.4/README.md +0 -127
  7. {spawnllm-0.5.4 → spawnllm-0.5.5}/LICENSE +0 -0
  8. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/__init__.py +0 -0
  9. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/__main__.py +0 -0
  10. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/__init__.py +0 -0
  11. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/base.py +0 -0
  12. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/claude.py +0 -0
  13. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/gemini.py +0 -0
  14. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/mlx.py +0 -0
  15. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/backends/registry.py +0 -0
  16. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/call.py +0 -0
  17. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/cli.py +0 -0
  18. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/extract.py +0 -0
  19. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/mlx/__init__.py +0 -0
  20. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/mlx/codec.py +0 -0
  21. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/mlx/engine.py +0 -0
  22. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/mlx/fuse.py +0 -0
  23. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/mlx/patches.py +0 -0
  24. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/proc.py +0 -0
  25. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/py.typed +0 -0
  26. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/response.py +0 -0
  27. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/run.py +0 -0
  28. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/spec.py +0 -0
  29. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/structured.py +0 -0
  30. {spawnllm-0.5.4 → spawnllm-0.5.5}/spawnllm/types.py +0 -0
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: spawnllm
3
+ Version: 0.5.5
4
+ Summary: Delete your subprocess wrappers around claude, codex, and gemini.
5
+ Keywords:
6
+ Author: Yasyf Mohamedali
7
+ Author-email: Yasyf Mohamedali <yasyfm@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: click>=8
17
+ Requires-Dist: loguru>=0.7
18
+ Requires-Dist: pydantic>=2
19
+ Requires-Dist: openai>=2.43
20
+ Requires-Dist: anthropic>=0.111
21
+ Requires-Dist: zstandard>=0.25.0 ; extra == 'adapter'
22
+ Requires-Dist: numpy>=1.26 ; extra == 'adapter'
23
+ Requires-Dist: orjson>=3.10 ; extra == 'adapter'
24
+ Requires-Dist: anyio>=4 ; extra == 'dev'
25
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
26
+ Requires-Dist: ruff>=0.8 ; extra == 'dev'
27
+ Requires-Dist: ty>=0.0.44 ; extra == 'dev'
28
+ Requires-Dist: zstandard>=0.25.0 ; extra == 'dev'
29
+ Requires-Dist: numpy>=1.26 ; extra == 'dev'
30
+ Requires-Dist: orjson>=3.10 ; extra == 'dev'
31
+ Requires-Dist: zstandard>=0.25.0 ; extra == 'mlx'
32
+ Requires-Dist: numpy>=1.26 ; extra == 'mlx'
33
+ Requires-Dist: orjson>=3.10 ; extra == 'mlx'
34
+ Requires-Dist: anyio>=4.4 ; extra == 'mlx'
35
+ Requires-Dist: huggingface-hub>=0.25 ; extra == 'mlx'
36
+ Requires-Dist: mlx-lm>=0.31.3 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'mlx'
37
+ Requires-Python: >=3.13
38
+ Project-URL: Homepage, https://github.com/yasyf/spawnllm
39
+ Project-URL: Documentation, https://yasyf.github.io/spawnllm/
40
+ Project-URL: Repository, https://github.com/yasyf/spawnllm
41
+ Project-URL: Issues, https://github.com/yasyf/spawnllm/issues
42
+ Project-URL: Changelog, https://github.com/yasyf/spawnllm/blob/main/CHANGELOG.md
43
+ Provides-Extra: adapter
44
+ Provides-Extra: dev
45
+ Provides-Extra: mlx
46
+ Description-Content-Type: text/markdown
47
+
48
+ # ![spawnllm](https://github.com/yasyf/spawnllm/raw/main/docs/assets/readme-banner.webp)
49
+
50
+ **Delete your subprocess wrappers around claude, codex, and gemini.** spawnllm subshells all three CLIs plus local MLX and returns one Pydantic-validated Response, so the per-model plumbing you hand-rolled goes away.
51
+
52
+ [![CI](https://github.com/yasyf/spawnllm/actions/workflows/ci.yml/badge.svg)](https://github.com/yasyf/spawnllm/actions/workflows/ci.yml)
53
+ [![PyPI](https://img.shields.io/pypi/v/spawnllm)](https://pypi.org/project/spawnllm/)
54
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/yasyf/spawnllm/blob/main/LICENSE)
55
+
56
+ ## Get started
57
+
58
+ ```bash
59
+ uvx spawnllm status
60
+ ```
61
+
62
+ <img src="https://github.com/yasyf/spawnllm/raw/main/docs/assets/demo.png" alt="Terminal running 'uvx spawnllm status' — every backend reports ready and auto-selection picks claude" width="700">
63
+
64
+ Driving with an agent? Paste this:
65
+
66
+ ```text
67
+ Run `uv add spawnllm` in this project.
68
+ Replace our hand-rolled claude/codex subprocess code with spawnllm's `call_sync`,
69
+ or `extract_sync` with a Pydantic response model for structured output.
70
+ Verify available backends with `uvx spawnllm status`.
71
+ Docs: https://yasyf.github.io/spawnllm/
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Use cases
77
+
78
+ ### Delete your hand-rolled claude/codex subprocess plumbing
79
+
80
+ Every small tool grows its own `subprocess.run(["claude", "-p", ...])` — argv quirks, stdin piping, exit-code guesswork — and each copy drifts. One call replaces all of it:
81
+
82
+ ```python
83
+ from spawnllm import call_sync
84
+
85
+ print(call_sync("Reply with just the word: pong"))
86
+ ```
87
+
88
+ Prints `pong`. With no `backend=`, spawnllm auto-selects the first installed, authenticated CLI, pipes the prompt over stdin, and retries transient 529/overloaded/rate-limit failures with capped backoff.
89
+
90
+ ### Get a validated Pydantic object back, not a string to parse
91
+
92
+ Scraping JSON out of a model's stdout means regexes, code fences, and silent schema drift. `extract_sync` validates instead:
93
+
94
+ ```python
95
+ from pydantic import BaseModel
96
+
97
+ from spawnllm import extract_sync
98
+
99
+
100
+ class Capital(BaseModel):
101
+ country: str
102
+ capital: str
103
+
104
+
105
+ result = extract_sync("What is the capital of France?", Capital)
106
+ print(result.capital) # Paris
107
+ ```
108
+
109
+ The backend turns `Capital` into a JSON-schema constraint on the call itself, and a non-conforming reply raises `pydantic.ValidationError` instead of sneaking downstream.
110
+
111
+ ### Run Apple-Silicon MLX models with fused adapters and prompt-cache reuse
112
+
113
+ Shipping a LoRA-tuned local model means hand-rolling adapter fusion, model caching, and worker-thread lifecycle. The MLX extra owns all three:
114
+
115
+ ```bash
116
+ uv add "spawnllm[mlx]"
117
+ ```
118
+
119
+ `AdapterFuser.ensure_fused` fuses your compressed adapter into the base model once and caches the result in the Hugging Face hub layout; `MlxEngine` loads it on a dedicated worker thread, precomputes a prompt cache for your shared prefix messages, and batches generation. Wrap the engine in an `MlxBackend` and the same `run_sync` call works.
120
+
121
+ ## More in the docs
122
+
123
+ - **Spec-driven runs** — a literal model id, per-provider flag passthrough, and envelope-aware retry via `RunSpec` — [Running reference](https://yasyf.github.io/spawnllm/reference/#running)
124
+ - **Backend selection** — the priority chain, plus `specialty=` routing (`debugging` and `review` go to Codex, `general` to Claude) — [Backends reference](https://yasyf.github.io/spawnllm/reference/#backends)
125
+ - **Transport helpers** — `run_cli`, `collect_process`, and `map_concurrent`, the subprocess plumbing shared by every CLI backend — [Transport reference](https://yasyf.github.io/spawnllm/reference/#transport)
126
+ - **The CLI** — `spawnllm call`, `status`, and `backends` from any shell — [CLI reference](https://yasyf.github.io/spawnllm/reference/cli/)
127
+ - **MLX internals** — the adapter codec, fuser, and runtime patches behind the local engine — [MLX reference](https://yasyf.github.io/spawnllm/reference/#mlx)
128
+
129
+ Read the [docs](https://yasyf.github.io/spawnllm/) for the full guide and API reference. Licensed under [MIT](https://github.com/yasyf/spawnllm/blob/main/LICENSE).
@@ -0,0 +1,82 @@
1
+ # ![spawnllm](https://github.com/yasyf/spawnllm/raw/main/docs/assets/readme-banner.webp)
2
+
3
+ **Delete your subprocess wrappers around claude, codex, and gemini.** spawnllm subshells all three CLIs plus local MLX and returns one Pydantic-validated Response, so the per-model plumbing you hand-rolled goes away.
4
+
5
+ [![CI](https://github.com/yasyf/spawnllm/actions/workflows/ci.yml/badge.svg)](https://github.com/yasyf/spawnllm/actions/workflows/ci.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/spawnllm)](https://pypi.org/project/spawnllm/)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/yasyf/spawnllm/blob/main/LICENSE)
8
+
9
+ ## Get started
10
+
11
+ ```bash
12
+ uvx spawnllm status
13
+ ```
14
+
15
+ <img src="https://github.com/yasyf/spawnllm/raw/main/docs/assets/demo.png" alt="Terminal running 'uvx spawnllm status' — every backend reports ready and auto-selection picks claude" width="700">
16
+
17
+ Driving with an agent? Paste this:
18
+
19
+ ```text
20
+ Run `uv add spawnllm` in this project.
21
+ Replace our hand-rolled claude/codex subprocess code with spawnllm's `call_sync`,
22
+ or `extract_sync` with a Pydantic response model for structured output.
23
+ Verify available backends with `uvx spawnllm status`.
24
+ Docs: https://yasyf.github.io/spawnllm/
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Use cases
30
+
31
+ ### Delete your hand-rolled claude/codex subprocess plumbing
32
+
33
+ Every small tool grows its own `subprocess.run(["claude", "-p", ...])` — argv quirks, stdin piping, exit-code guesswork — and each copy drifts. One call replaces all of it:
34
+
35
+ ```python
36
+ from spawnllm import call_sync
37
+
38
+ print(call_sync("Reply with just the word: pong"))
39
+ ```
40
+
41
+ Prints `pong`. With no `backend=`, spawnllm auto-selects the first installed, authenticated CLI, pipes the prompt over stdin, and retries transient 529/overloaded/rate-limit failures with capped backoff.
42
+
43
+ ### Get a validated Pydantic object back, not a string to parse
44
+
45
+ Scraping JSON out of a model's stdout means regexes, code fences, and silent schema drift. `extract_sync` validates instead:
46
+
47
+ ```python
48
+ from pydantic import BaseModel
49
+
50
+ from spawnllm import extract_sync
51
+
52
+
53
+ class Capital(BaseModel):
54
+ country: str
55
+ capital: str
56
+
57
+
58
+ result = extract_sync("What is the capital of France?", Capital)
59
+ print(result.capital) # Paris
60
+ ```
61
+
62
+ The backend turns `Capital` into a JSON-schema constraint on the call itself, and a non-conforming reply raises `pydantic.ValidationError` instead of sneaking downstream.
63
+
64
+ ### Run Apple-Silicon MLX models with fused adapters and prompt-cache reuse
65
+
66
+ Shipping a LoRA-tuned local model means hand-rolling adapter fusion, model caching, and worker-thread lifecycle. The MLX extra owns all three:
67
+
68
+ ```bash
69
+ uv add "spawnllm[mlx]"
70
+ ```
71
+
72
+ `AdapterFuser.ensure_fused` fuses your compressed adapter into the base model once and caches the result in the Hugging Face hub layout; `MlxEngine` loads it on a dedicated worker thread, precomputes a prompt cache for your shared prefix messages, and batches generation. Wrap the engine in an `MlxBackend` and the same `run_sync` call works.
73
+
74
+ ## More in the docs
75
+
76
+ - **Spec-driven runs** — a literal model id, per-provider flag passthrough, and envelope-aware retry via `RunSpec` — [Running reference](https://yasyf.github.io/spawnllm/reference/#running)
77
+ - **Backend selection** — the priority chain, plus `specialty=` routing (`debugging` and `review` go to Codex, `general` to Claude) — [Backends reference](https://yasyf.github.io/spawnllm/reference/#backends)
78
+ - **Transport helpers** — `run_cli`, `collect_process`, and `map_concurrent`, the subprocess plumbing shared by every CLI backend — [Transport reference](https://yasyf.github.io/spawnllm/reference/#transport)
79
+ - **The CLI** — `spawnllm call`, `status`, and `backends` from any shell — [CLI reference](https://yasyf.github.io/spawnllm/reference/cli/)
80
+ - **MLX internals** — the adapter codec, fuser, and runtime patches behind the local engine — [MLX reference](https://yasyf.github.io/spawnllm/reference/#mlx)
81
+
82
+ Read the [docs](https://yasyf.github.io/spawnllm/) for the full guide and API reference. Licensed under [MIT](https://github.com/yasyf/spawnllm/blob/main/LICENSE).
@@ -1,8 +1,8 @@
1
1
  [project]
2
2
  name = "spawnllm"
3
3
  # Inert sentinel: the real version is set from the release tag (uv version --frozen).
4
- version = "0.5.4"
5
- description = "Subshell + MLX LLM-calling backends (Claude/Codex CLI, local MLX) shared across tools."
4
+ version = "0.5.5"
5
+ description = "Delete your subprocess wrappers around claude, codex, and gemini."
6
6
  readme = "README.md"
7
7
  license = "MIT"
8
8
  license-files = ["LICENSE"]
@@ -23,7 +23,9 @@ class CodexCliBackend(CliBackend):
23
23
 
24
24
  `build_command` translates a `RunSpec` into a `codex exec` argv that runs an
25
25
  ephemeral session in a read-only sandbox; `invocation` resolves the schema to
26
- a temp file and captures the final message to an `-o` file. An optional
26
+ a temp file and captures the final message to an `-o` file. The argv carries
27
+ `--skip-git-repo-check` so a run in an untrusted or non-git `cwd` is not refused —
28
+ the sandbox already confines it, so the trust gate adds nothing here. An optional
27
29
  `CodexConfig` overrides the sandbox and re-enables Codex hooks or MCP servers.
28
30
 
29
31
  Attributes:
@@ -70,6 +72,7 @@ class CodexCliBackend(CliBackend):
70
72
  "--ephemeral",
71
73
  "--sandbox",
72
74
  cfg.sandbox or "read-only",
75
+ "--skip-git-repo-check",
73
76
  "--model",
74
77
  model,
75
78
  *(["-c", f"model_reasoning_effort={effort}"] if effort else []),
spawnllm-0.5.4/PKG-INFO DELETED
@@ -1,174 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: spawnllm
3
- Version: 0.5.4
4
- Summary: Subshell + MLX LLM-calling backends (Claude/Codex CLI, local MLX) shared across tools.
5
- Keywords:
6
- Author: Yasyf Mohamedali
7
- Author-email: Yasyf Mohamedali <yasyfm@gmail.com>
8
- License-Expression: MIT
9
- License-File: LICENSE
10
- Classifier: Development Status :: 3 - Alpha
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3 :: Only
15
- Classifier: Typing :: Typed
16
- Requires-Dist: click>=8
17
- Requires-Dist: loguru>=0.7
18
- Requires-Dist: pydantic>=2
19
- Requires-Dist: openai>=2.43
20
- Requires-Dist: anthropic>=0.111
21
- Requires-Dist: zstandard>=0.25.0 ; extra == 'adapter'
22
- Requires-Dist: numpy>=1.26 ; extra == 'adapter'
23
- Requires-Dist: orjson>=3.10 ; extra == 'adapter'
24
- Requires-Dist: anyio>=4 ; extra == 'dev'
25
- Requires-Dist: pytest>=8.0 ; extra == 'dev'
26
- Requires-Dist: ruff>=0.8 ; extra == 'dev'
27
- Requires-Dist: ty>=0.0.44 ; extra == 'dev'
28
- Requires-Dist: zstandard>=0.25.0 ; extra == 'dev'
29
- Requires-Dist: numpy>=1.26 ; extra == 'dev'
30
- Requires-Dist: orjson>=3.10 ; extra == 'dev'
31
- Requires-Dist: zstandard>=0.25.0 ; extra == 'mlx'
32
- Requires-Dist: numpy>=1.26 ; extra == 'mlx'
33
- Requires-Dist: orjson>=3.10 ; extra == 'mlx'
34
- Requires-Dist: anyio>=4.4 ; extra == 'mlx'
35
- Requires-Dist: huggingface-hub>=0.25 ; extra == 'mlx'
36
- Requires-Dist: mlx-lm>=0.31.3 ; platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'mlx'
37
- Requires-Python: >=3.13
38
- Project-URL: Homepage, https://github.com/yasyf/spawnllm
39
- Project-URL: Documentation, https://yasyf.github.io/spawnllm/
40
- Project-URL: Repository, https://github.com/yasyf/spawnllm
41
- Project-URL: Issues, https://github.com/yasyf/spawnllm/issues
42
- Project-URL: Changelog, https://github.com/yasyf/spawnllm/blob/main/CHANGELOG.md
43
- Provides-Extra: adapter
44
- Provides-Extra: dev
45
- Provides-Extra: mlx
46
- Description-Content-Type: text/markdown
47
-
48
- # spawnllm
49
-
50
- ![spawnllm banner](https://github.com/yasyf/spawnllm/raw/main/docs/assets/readme-banner.webp)
51
-
52
- [![PyPI](https://img.shields.io/pypi/v/spawnllm.svg)](https://pypi.org/project/spawnllm/)
53
- [![Python](https://img.shields.io/pypi/pyversions/spawnllm.svg)](https://pypi.org/project/spawnllm/)
54
- [![Docs](https://img.shields.io/github/actions/workflow/status/yasyf/spawnllm/docs.yml?branch=main&label=docs)](https://yasyf.github.io/spawnllm/)
55
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/yasyf/spawnllm/blob/main/LICENSE)
56
-
57
- Subshell + MLX LLM-calling backends (Claude/Codex CLI, local MLX) shared across tools.
58
-
59
- spawnllm centralizes the LLM-calling plumbing that small tools keep re-inventing: driving the
60
- `claude` and `codex` CLIs as subshells — with structured Pydantic output, model tiers, and
61
- faithful error capture — and running local Apple-Silicon MLX models with adapter fusion,
62
- prompt-cache reuse, and batched generation. Depend on it once and each tool keeps only its
63
- domain logic instead of its own copy of the backends.
64
-
65
- ## Install
66
-
67
- Run the CLI with [uvx](https://docs.astral.sh/uv/):
68
-
69
- ```bash
70
- uvx spawnllm --help
71
- ```
72
-
73
- For the local MLX engine (Apple Silicon only), pull the extra: `uv add "spawnllm[mlx]"`.
74
-
75
- ## Quickstart
76
-
77
- See which backends are installed and authenticated, and which one auto-selection picks:
78
-
79
- ```bash
80
- uvx spawnllm status
81
- ```
82
-
83
- ```
84
- claude: ready
85
- codex: ready
86
- selected: claude
87
- ```
88
-
89
- Make a request by passing a prompt as the argument, or piping it over stdin:
90
-
91
- ```bash
92
- uvx spawnllm call --backend claude "What is 2+2? Reply with just the number."
93
- ```
94
-
95
- ```
96
- 4
97
- ```
98
-
99
- `--model small|medium|large` swaps the tier, which each backend maps to a concrete model — the
100
- `claude` backend resolves `small` to Haiku, `medium` to Sonnet, and `large` to Opus. Add
101
- `--agent` to let the call use tools. Run `uvx spawnllm --help` for the full flag list.
102
-
103
- ### From Python
104
-
105
- `call_sync` runs one request and returns the response. With no `backend`, it auto-selects
106
- the first installed, authenticated CLI (its async companion `call` mirrors the same
107
- signature):
108
-
109
- ```python
110
- from spawnllm import call_sync
111
-
112
- print(call_sync("Reply with just the word: pong"))
113
- # pong
114
- ```
115
-
116
- Pin a backend and tier explicitly, or pass a Pydantic model to get a validated object back
117
- instead of text:
118
-
119
- ```python
120
- from pydantic import BaseModel
121
-
122
- from spawnllm import call_sync, ClaudeCliBackend
123
-
124
-
125
- class Capital(BaseModel):
126
- country: str
127
- capital: str
128
-
129
-
130
- result = call_sync(
131
- "What is the capital of France?",
132
- backend=ClaudeCliBackend(),
133
- model="large",
134
- response_model=Capital,
135
- )
136
- print(result.capital) # Paris
137
- ```
138
-
139
- When you don't pin a backend, set `specialty=` to scope auto-selection by task. The
140
- `debugging` and `review` specialties route to Codex, and `general` routes to Claude.
141
-
142
- ### Spec-driven runs
143
-
144
- For full control, build a `RunSpec` and execute it with `run_sync` (or its async companion
145
- `run`). A `RunSpec` takes a literal provider model id — no tier mapping — and per-provider
146
- flag passthrough via `provider_configs`. The call returns a `RunResult` with raw stdout,
147
- stderr, and exit code, retrying transient `529`/overloaded/rate-limit failures with backoff:
148
-
149
- ```python
150
- from spawnllm import run_sync, RunSpec, ClaudeConfig, ClaudeCliBackend
151
-
152
- result = run_sync(
153
- RunSpec(
154
- prompt="What is 2+2? Reply with just the number.",
155
- model="opus",
156
- provider_configs={"claude": ClaudeConfig(permission_mode="bypassPermissions")},
157
- ),
158
- backend=ClaudeCliBackend(),
159
- )
160
- print(result.stdout) # 4
161
- ```
162
-
163
- ## How it works
164
-
165
- Each backend holds plumbing that consumers would otherwise rebuild: the CLI backends own argv
166
- construction, stdin/stdout piping, stderr teeing, and turning non-zero exits into useful errors,
167
- and they turn a Pydantic model into a JSON-schema constraint plus a parsed, validated result. The
168
- MLX engine wraps adapter fusion, prompt-cache reuse, worker-thread lifecycle, and batched
169
- single-token generation. Tools that share the layer stay byte-for-byte consistent instead of
170
- drifting across diverging copies.
171
-
172
- ## Docs
173
-
174
- [Read the docs](https://yasyf.github.io/spawnllm/) for the full guide and API reference.
spawnllm-0.5.4/README.md DELETED
@@ -1,127 +0,0 @@
1
- # spawnllm
2
-
3
- ![spawnllm banner](https://github.com/yasyf/spawnllm/raw/main/docs/assets/readme-banner.webp)
4
-
5
- [![PyPI](https://img.shields.io/pypi/v/spawnllm.svg)](https://pypi.org/project/spawnllm/)
6
- [![Python](https://img.shields.io/pypi/pyversions/spawnllm.svg)](https://pypi.org/project/spawnllm/)
7
- [![Docs](https://img.shields.io/github/actions/workflow/status/yasyf/spawnllm/docs.yml?branch=main&label=docs)](https://yasyf.github.io/spawnllm/)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/yasyf/spawnllm/blob/main/LICENSE)
9
-
10
- Subshell + MLX LLM-calling backends (Claude/Codex CLI, local MLX) shared across tools.
11
-
12
- spawnllm centralizes the LLM-calling plumbing that small tools keep re-inventing: driving the
13
- `claude` and `codex` CLIs as subshells — with structured Pydantic output, model tiers, and
14
- faithful error capture — and running local Apple-Silicon MLX models with adapter fusion,
15
- prompt-cache reuse, and batched generation. Depend on it once and each tool keeps only its
16
- domain logic instead of its own copy of the backends.
17
-
18
- ## Install
19
-
20
- Run the CLI with [uvx](https://docs.astral.sh/uv/):
21
-
22
- ```bash
23
- uvx spawnllm --help
24
- ```
25
-
26
- For the local MLX engine (Apple Silicon only), pull the extra: `uv add "spawnllm[mlx]"`.
27
-
28
- ## Quickstart
29
-
30
- See which backends are installed and authenticated, and which one auto-selection picks:
31
-
32
- ```bash
33
- uvx spawnllm status
34
- ```
35
-
36
- ```
37
- claude: ready
38
- codex: ready
39
- selected: claude
40
- ```
41
-
42
- Make a request by passing a prompt as the argument, or piping it over stdin:
43
-
44
- ```bash
45
- uvx spawnllm call --backend claude "What is 2+2? Reply with just the number."
46
- ```
47
-
48
- ```
49
- 4
50
- ```
51
-
52
- `--model small|medium|large` swaps the tier, which each backend maps to a concrete model — the
53
- `claude` backend resolves `small` to Haiku, `medium` to Sonnet, and `large` to Opus. Add
54
- `--agent` to let the call use tools. Run `uvx spawnllm --help` for the full flag list.
55
-
56
- ### From Python
57
-
58
- `call_sync` runs one request and returns the response. With no `backend`, it auto-selects
59
- the first installed, authenticated CLI (its async companion `call` mirrors the same
60
- signature):
61
-
62
- ```python
63
- from spawnllm import call_sync
64
-
65
- print(call_sync("Reply with just the word: pong"))
66
- # pong
67
- ```
68
-
69
- Pin a backend and tier explicitly, or pass a Pydantic model to get a validated object back
70
- instead of text:
71
-
72
- ```python
73
- from pydantic import BaseModel
74
-
75
- from spawnllm import call_sync, ClaudeCliBackend
76
-
77
-
78
- class Capital(BaseModel):
79
- country: str
80
- capital: str
81
-
82
-
83
- result = call_sync(
84
- "What is the capital of France?",
85
- backend=ClaudeCliBackend(),
86
- model="large",
87
- response_model=Capital,
88
- )
89
- print(result.capital) # Paris
90
- ```
91
-
92
- When you don't pin a backend, set `specialty=` to scope auto-selection by task. The
93
- `debugging` and `review` specialties route to Codex, and `general` routes to Claude.
94
-
95
- ### Spec-driven runs
96
-
97
- For full control, build a `RunSpec` and execute it with `run_sync` (or its async companion
98
- `run`). A `RunSpec` takes a literal provider model id — no tier mapping — and per-provider
99
- flag passthrough via `provider_configs`. The call returns a `RunResult` with raw stdout,
100
- stderr, and exit code, retrying transient `529`/overloaded/rate-limit failures with backoff:
101
-
102
- ```python
103
- from spawnllm import run_sync, RunSpec, ClaudeConfig, ClaudeCliBackend
104
-
105
- result = run_sync(
106
- RunSpec(
107
- prompt="What is 2+2? Reply with just the number.",
108
- model="opus",
109
- provider_configs={"claude": ClaudeConfig(permission_mode="bypassPermissions")},
110
- ),
111
- backend=ClaudeCliBackend(),
112
- )
113
- print(result.stdout) # 4
114
- ```
115
-
116
- ## How it works
117
-
118
- Each backend holds plumbing that consumers would otherwise rebuild: the CLI backends own argv
119
- construction, stdin/stdout piping, stderr teeing, and turning non-zero exits into useful errors,
120
- and they turn a Pydantic model into a JSON-schema constraint plus a parsed, validated result. The
121
- MLX engine wraps adapter fusion, prompt-cache reuse, worker-thread lifecycle, and batched
122
- single-token generation. Tools that share the layer stay byte-for-byte consistent instead of
123
- drifting across diverging copies.
124
-
125
- ## Docs
126
-
127
- [Read the docs](https://yasyf.github.io/spawnllm/) for the full guide and API reference.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes