leanswarm 0.1.0__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.
- leanswarm-0.1.0/LICENSE +22 -0
- leanswarm-0.1.0/PKG-INFO +181 -0
- leanswarm-0.1.0/README.md +145 -0
- leanswarm-0.1.0/pyproject.toml +77 -0
- leanswarm-0.1.0/setup.cfg +4 -0
- leanswarm-0.1.0/src/lean_swarm/__init__.py +6 -0
- leanswarm-0.1.0/src/lean_swarm/api/__init__.py +4 -0
- leanswarm-0.1.0/src/lean_swarm/api/app.py +31 -0
- leanswarm-0.1.0/src/lean_swarm/cli.py +130 -0
- leanswarm-0.1.0/src/lean_swarm/engine/__init__.py +6 -0
- leanswarm-0.1.0/src/lean_swarm/engine/config.py +54 -0
- leanswarm-0.1.0/src/lean_swarm/engine/llm.py +615 -0
- leanswarm-0.1.0/src/lean_swarm/engine/logging.py +22 -0
- leanswarm-0.1.0/src/lean_swarm/engine/memory.py +103 -0
- leanswarm-0.1.0/src/lean_swarm/engine/models.py +223 -0
- leanswarm-0.1.0/src/lean_swarm/engine/population.py +620 -0
- leanswarm-0.1.0/src/lean_swarm/engine/simulator.py +544 -0
- leanswarm-0.1.0/src/lean_swarm/engine/world.py +1008 -0
- leanswarm-0.1.0/src/lean_swarm/tools/__init__.py +4 -0
- leanswarm-0.1.0/src/lean_swarm/tools/benchmark.py +483 -0
- leanswarm-0.1.0/src/leanswarm.egg-info/PKG-INFO +181 -0
- leanswarm-0.1.0/src/leanswarm.egg-info/SOURCES.txt +30 -0
- leanswarm-0.1.0/src/leanswarm.egg-info/dependency_links.txt +1 -0
- leanswarm-0.1.0/src/leanswarm.egg-info/entry_points.txt +2 -0
- leanswarm-0.1.0/src/leanswarm.egg-info/requires.txt +19 -0
- leanswarm-0.1.0/src/leanswarm.egg-info/top_level.txt +1 -0
- leanswarm-0.1.0/tests/test_api.py +27 -0
- leanswarm-0.1.0/tests/test_benchmark.py +85 -0
- leanswarm-0.1.0/tests/test_cli.py +67 -0
- leanswarm-0.1.0/tests/test_engine.py +22 -0
- leanswarm-0.1.0/tests/test_llm.py +28 -0
- leanswarm-0.1.0/tests/test_phase2_integration.py +231 -0
leanswarm-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohith Das
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
leanswarm-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: leanswarm
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lean Swarm multi-agent prediction and simulation engine
|
|
5
|
+
Author: Mohith Das
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mohith-das/leanswarm
|
|
8
|
+
Project-URL: Repository, https://github.com/mohith-das/leanswarm.git
|
|
9
|
+
Keywords: agents,forecasting,simulation,llm,pydantic
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Framework :: FastAPI
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: diskcache<6.0,>=5.6
|
|
20
|
+
Requires-Dist: fastapi<1.0,>=0.115
|
|
21
|
+
Requires-Dist: litellm<2.0,>=1.72
|
|
22
|
+
Requires-Dist: networkx<4.0,>=3.3
|
|
23
|
+
Requires-Dist: pydantic<3.0,>=2.8
|
|
24
|
+
Requires-Dist: sentence-transformers<4.0,>=3.0
|
|
25
|
+
Requires-Dist: sqlite-vss>=0.1.2; platform_system != "Windows"
|
|
26
|
+
Requires-Dist: uvicorn<1.0,>=0.30
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
29
|
+
Requires-Dist: httpx>=0.27; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit>=3.8; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.1; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# Lean Swarm
|
|
38
|
+
|
|
39
|
+
Lean Swarm is a cost-focused multi-agent prediction and simulation engine designed to approximate
|
|
40
|
+
MiroFish-class narrative forecasting with aggressive batching, sparse activation, hybrid state, and
|
|
41
|
+
strict LLM routing guardrails.
|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
Given a seed document and a prediction question, Lean Swarm builds a simulated world of agents,
|
|
46
|
+
runs a bounded number of interaction ticks, and returns:
|
|
47
|
+
|
|
48
|
+
- a structured prediction report
|
|
49
|
+
- a post-simulation world snapshot with agent states and relationship edges
|
|
50
|
+
|
|
51
|
+
The project is structured for open-source collaboration, MIT licensing, and PyPI publishing from
|
|
52
|
+
day one.
|
|
53
|
+
|
|
54
|
+
## Quickstart
|
|
55
|
+
|
|
56
|
+
### Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install -e .[dev]
|
|
60
|
+
cp .env.example .env
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Optional with `uv`:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uv venv
|
|
67
|
+
uv pip install -e .[dev]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Run
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
lean-swarm smoke
|
|
74
|
+
lean-swarm simulate --seed examples/seed.txt --question "Will public trust rise this quarter?"
|
|
75
|
+
lean-swarm api
|
|
76
|
+
lean-swarm bench
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Architecture
|
|
80
|
+
|
|
81
|
+
### Core rules
|
|
82
|
+
|
|
83
|
+
- All model traffic is routed through `engine/llm.py`.
|
|
84
|
+
- Every LLM route and simulation tick is logged.
|
|
85
|
+
- The engine uses Pydantic schemas at every boundary.
|
|
86
|
+
- LLM calls are retried and concurrency-limited with semaphores.
|
|
87
|
+
|
|
88
|
+
### Phase 1 engine shape
|
|
89
|
+
|
|
90
|
+
- Tiered model routing with `FLAGSHIP`, `STANDARD`, and `CHEAP` tiers.
|
|
91
|
+
- Batched group actions for active agents.
|
|
92
|
+
- Archetype pooling with 96 archetypes and up to 50 named agents.
|
|
93
|
+
- Hybrid numeric state for mood, energy, attention, and relationships.
|
|
94
|
+
- Event-driven activation with a bounded active fraction per tick.
|
|
95
|
+
- Hierarchical memory slices for working, episodic, and semantic references.
|
|
96
|
+
- Disk-backed action caching via `diskcache`.
|
|
97
|
+
- Early convergence detection on low-delta ticks.
|
|
98
|
+
|
|
99
|
+
See [docs/architecture.md](docs/architecture.md) for more detail.
|
|
100
|
+
|
|
101
|
+
## CLI Usage
|
|
102
|
+
|
|
103
|
+
### Smoke test
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
lean-swarm smoke
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Simulate a scenario
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
lean-swarm simulate \
|
|
113
|
+
--seed examples/seed.txt \
|
|
114
|
+
--question "Will the policy announcement improve sentiment?" \
|
|
115
|
+
--activation-mode lean \
|
|
116
|
+
--active-agent-fraction 0.25
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Run the API
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
lean-swarm api --host 127.0.0.1 --port 8000
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Run the benchmark harness
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
lean-swarm bench
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## API Usage
|
|
132
|
+
|
|
133
|
+
### Start server
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
lean-swarm api
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Example request
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
curl -X POST http://127.0.0.1:8000/simulate \
|
|
143
|
+
-H "Content-Type: application/json" \
|
|
144
|
+
-d '{
|
|
145
|
+
"seed_document": "A national survey shows mixed views on new policy proposals.",
|
|
146
|
+
"question": "Will approval improve over the next month?",
|
|
147
|
+
"rounds": 4
|
|
148
|
+
}'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Benchmarks
|
|
152
|
+
|
|
153
|
+
`lean-swarm bench` runs the same benchmark cases in both `lean` and `naive` activation modes and
|
|
154
|
+
returns a comparison payload with:
|
|
155
|
+
|
|
156
|
+
- top-level deltas: `cost_ratio_naive_to_lean`, `quality_delta_lean_vs_naive`,
|
|
157
|
+
`runtime_ratio_naive_to_lean`
|
|
158
|
+
- per-mode outputs under `modes.lean` and `modes.naive` (quality proxy, runtime, cache stats, token
|
|
159
|
+
and estimated cost totals)
|
|
160
|
+
- `plot_points`: per-case points with `mode`, `score`, `cost_usd`, `runtime_seconds`,
|
|
161
|
+
`token_total`, and related fields for quality-vs-cost plotting
|
|
162
|
+
|
|
163
|
+
This lets you compare lean efficiency against naive full activation and plot quality-vs-cost points
|
|
164
|
+
directly from benchmark output without extra transforms.
|
|
165
|
+
|
|
166
|
+
## Limitations
|
|
167
|
+
|
|
168
|
+
- The Phase 1 engine uses deterministic mock responses unless live credentials are configured.
|
|
169
|
+
- Semantic memory is scaffolded but not yet backed by a production embedding and retrieval flow.
|
|
170
|
+
- The web client is intentionally minimal at this stage.
|
|
171
|
+
|
|
172
|
+
## Roadmap
|
|
173
|
+
|
|
174
|
+
See [ROADMAP.md](ROADMAP.md) for V2 priorities including multi-world simulations, Monte Carlo
|
|
175
|
+
runs, intervention testing, replay UX, plugin architecture, large-scale agent support, and
|
|
176
|
+
hybrid local+cloud routing.
|
|
177
|
+
|
|
178
|
+
## Contributing
|
|
179
|
+
|
|
180
|
+
Contributions are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md), pick an issue template
|
|
181
|
+
that matches your contribution track, and open a focused PR.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Lean Swarm
|
|
2
|
+
|
|
3
|
+
Lean Swarm is a cost-focused multi-agent prediction and simulation engine designed to approximate
|
|
4
|
+
MiroFish-class narrative forecasting with aggressive batching, sparse activation, hybrid state, and
|
|
5
|
+
strict LLM routing guardrails.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Given a seed document and a prediction question, Lean Swarm builds a simulated world of agents,
|
|
10
|
+
runs a bounded number of interaction ticks, and returns:
|
|
11
|
+
|
|
12
|
+
- a structured prediction report
|
|
13
|
+
- a post-simulation world snapshot with agent states and relationship edges
|
|
14
|
+
|
|
15
|
+
The project is structured for open-source collaboration, MIT licensing, and PyPI publishing from
|
|
16
|
+
day one.
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
### Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install -e .[dev]
|
|
24
|
+
cp .env.example .env
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Optional with `uv`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv venv
|
|
31
|
+
uv pip install -e .[dev]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Run
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
lean-swarm smoke
|
|
38
|
+
lean-swarm simulate --seed examples/seed.txt --question "Will public trust rise this quarter?"
|
|
39
|
+
lean-swarm api
|
|
40
|
+
lean-swarm bench
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
### Core rules
|
|
46
|
+
|
|
47
|
+
- All model traffic is routed through `engine/llm.py`.
|
|
48
|
+
- Every LLM route and simulation tick is logged.
|
|
49
|
+
- The engine uses Pydantic schemas at every boundary.
|
|
50
|
+
- LLM calls are retried and concurrency-limited with semaphores.
|
|
51
|
+
|
|
52
|
+
### Phase 1 engine shape
|
|
53
|
+
|
|
54
|
+
- Tiered model routing with `FLAGSHIP`, `STANDARD`, and `CHEAP` tiers.
|
|
55
|
+
- Batched group actions for active agents.
|
|
56
|
+
- Archetype pooling with 96 archetypes and up to 50 named agents.
|
|
57
|
+
- Hybrid numeric state for mood, energy, attention, and relationships.
|
|
58
|
+
- Event-driven activation with a bounded active fraction per tick.
|
|
59
|
+
- Hierarchical memory slices for working, episodic, and semantic references.
|
|
60
|
+
- Disk-backed action caching via `diskcache`.
|
|
61
|
+
- Early convergence detection on low-delta ticks.
|
|
62
|
+
|
|
63
|
+
See [docs/architecture.md](docs/architecture.md) for more detail.
|
|
64
|
+
|
|
65
|
+
## CLI Usage
|
|
66
|
+
|
|
67
|
+
### Smoke test
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
lean-swarm smoke
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Simulate a scenario
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
lean-swarm simulate \
|
|
77
|
+
--seed examples/seed.txt \
|
|
78
|
+
--question "Will the policy announcement improve sentiment?" \
|
|
79
|
+
--activation-mode lean \
|
|
80
|
+
--active-agent-fraction 0.25
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Run the API
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
lean-swarm api --host 127.0.0.1 --port 8000
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Run the benchmark harness
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
lean-swarm bench
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## API Usage
|
|
96
|
+
|
|
97
|
+
### Start server
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
lean-swarm api
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Example request
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
curl -X POST http://127.0.0.1:8000/simulate \
|
|
107
|
+
-H "Content-Type: application/json" \
|
|
108
|
+
-d '{
|
|
109
|
+
"seed_document": "A national survey shows mixed views on new policy proposals.",
|
|
110
|
+
"question": "Will approval improve over the next month?",
|
|
111
|
+
"rounds": 4
|
|
112
|
+
}'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Benchmarks
|
|
116
|
+
|
|
117
|
+
`lean-swarm bench` runs the same benchmark cases in both `lean` and `naive` activation modes and
|
|
118
|
+
returns a comparison payload with:
|
|
119
|
+
|
|
120
|
+
- top-level deltas: `cost_ratio_naive_to_lean`, `quality_delta_lean_vs_naive`,
|
|
121
|
+
`runtime_ratio_naive_to_lean`
|
|
122
|
+
- per-mode outputs under `modes.lean` and `modes.naive` (quality proxy, runtime, cache stats, token
|
|
123
|
+
and estimated cost totals)
|
|
124
|
+
- `plot_points`: per-case points with `mode`, `score`, `cost_usd`, `runtime_seconds`,
|
|
125
|
+
`token_total`, and related fields for quality-vs-cost plotting
|
|
126
|
+
|
|
127
|
+
This lets you compare lean efficiency against naive full activation and plot quality-vs-cost points
|
|
128
|
+
directly from benchmark output without extra transforms.
|
|
129
|
+
|
|
130
|
+
## Limitations
|
|
131
|
+
|
|
132
|
+
- The Phase 1 engine uses deterministic mock responses unless live credentials are configured.
|
|
133
|
+
- Semantic memory is scaffolded but not yet backed by a production embedding and retrieval flow.
|
|
134
|
+
- The web client is intentionally minimal at this stage.
|
|
135
|
+
|
|
136
|
+
## Roadmap
|
|
137
|
+
|
|
138
|
+
See [ROADMAP.md](ROADMAP.md) for V2 priorities including multi-world simulations, Monte Carlo
|
|
139
|
+
runs, intervention testing, replay UX, plugin architecture, large-scale agent support, and
|
|
140
|
+
hybrid local+cloud routing.
|
|
141
|
+
|
|
142
|
+
## Contributing
|
|
143
|
+
|
|
144
|
+
Contributions are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md), pick an issue template
|
|
145
|
+
that matches your contribution track, and open a focused PR.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "leanswarm"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Lean Swarm multi-agent prediction and simulation engine"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Mohith Das" }]
|
|
13
|
+
keywords = ["agents", "forecasting", "simulation", "llm", "pydantic"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Framework :: FastAPI",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"diskcache>=5.6,<6.0",
|
|
24
|
+
"fastapi>=0.115,<1.0",
|
|
25
|
+
"litellm>=1.72,<2.0",
|
|
26
|
+
"networkx>=3.3,<4.0",
|
|
27
|
+
"pydantic>=2.8,<3.0",
|
|
28
|
+
"sentence-transformers>=3.0,<4.0",
|
|
29
|
+
"sqlite-vss>=0.1.2; platform_system != 'Windows'",
|
|
30
|
+
"uvicorn>=0.30,<1.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"build>=1.2",
|
|
36
|
+
"httpx>=0.27",
|
|
37
|
+
"mypy>=1.11",
|
|
38
|
+
"pre-commit>=3.8",
|
|
39
|
+
"pytest>=8.3",
|
|
40
|
+
"ruff>=0.6",
|
|
41
|
+
"twine>=5.1",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
lean-swarm = "lean_swarm.cli:main"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/mohith-das/leanswarm"
|
|
49
|
+
Repository = "https://github.com/mohith-das/leanswarm.git"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
package-dir = { "" = "src" }
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.packages.find]
|
|
55
|
+
where = ["src"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
testpaths = ["tests"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
target-version = "py311"
|
|
62
|
+
line-length = 100
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "I", "B", "UP", "ASYNC"]
|
|
66
|
+
|
|
67
|
+
[tool.mypy]
|
|
68
|
+
python_version = "3.11"
|
|
69
|
+
warn_unused_configs = true
|
|
70
|
+
warn_redundant_casts = true
|
|
71
|
+
warn_unused_ignores = true
|
|
72
|
+
strict_optional = true
|
|
73
|
+
check_untyped_defs = true
|
|
74
|
+
disallow_incomplete_defs = true
|
|
75
|
+
disallow_untyped_defs = true
|
|
76
|
+
ignore_missing_imports = true
|
|
77
|
+
packages = ["lean_swarm"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
|
|
5
|
+
from lean_swarm import __version__
|
|
6
|
+
from lean_swarm.engine.models import SimulationRequest, SimulationResult
|
|
7
|
+
from lean_swarm.engine.simulator import LeanSwarmEngine
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_app() -> FastAPI:
|
|
11
|
+
app = FastAPI(
|
|
12
|
+
title="Lean Swarm API",
|
|
13
|
+
version=__version__,
|
|
14
|
+
description="HTTP surface for the Lean Swarm simulation engine.",
|
|
15
|
+
)
|
|
16
|
+
engine = LeanSwarmEngine()
|
|
17
|
+
|
|
18
|
+
@app.get("/healthz")
|
|
19
|
+
async def healthz() -> dict[str, str]:
|
|
20
|
+
return {"status": "ok"}
|
|
21
|
+
|
|
22
|
+
@app.get("/smoke", response_model=SimulationResult)
|
|
23
|
+
async def smoke() -> SimulationResult:
|
|
24
|
+
return await engine.smoke_test()
|
|
25
|
+
|
|
26
|
+
@app.post("/simulate", response_model=SimulationResult)
|
|
27
|
+
async def simulate(request: SimulationRequest) -> SimulationResult:
|
|
28
|
+
return await engine.simulate(request)
|
|
29
|
+
|
|
30
|
+
return app
|
|
31
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import asyncio
|
|
5
|
+
import json
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import uvicorn
|
|
10
|
+
|
|
11
|
+
from lean_swarm.api.app import create_app
|
|
12
|
+
from lean_swarm.engine.config import RuntimeSettings
|
|
13
|
+
from lean_swarm.engine.models import ActivationMode, SimulationRequest
|
|
14
|
+
from lean_swarm.engine.simulator import LeanSwarmEngine
|
|
15
|
+
from lean_swarm.tools.benchmark import run_benchmark
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
19
|
+
parser = argparse.ArgumentParser(prog="lean-swarm", description="Lean Swarm CLI")
|
|
20
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
21
|
+
|
|
22
|
+
smoke_parser = subparsers.add_parser("smoke", help="Run a deterministic smoke simulation")
|
|
23
|
+
smoke_parser.set_defaults(handler=handle_smoke)
|
|
24
|
+
|
|
25
|
+
simulate_parser = subparsers.add_parser(
|
|
26
|
+
"simulate", help="Run a simulation from a seed document and prediction question"
|
|
27
|
+
)
|
|
28
|
+
simulate_parser.add_argument("--seed", required=True, help="Path to a seed document")
|
|
29
|
+
simulate_parser.add_argument("--question", required=True, help="Prediction question")
|
|
30
|
+
simulate_parser.add_argument("--rounds", type=int, default=6, help="Maximum tick count")
|
|
31
|
+
simulate_parser.add_argument(
|
|
32
|
+
"--max-agents", type=int, default=24, help="Maximum number of simulated agents"
|
|
33
|
+
)
|
|
34
|
+
simulate_parser.add_argument(
|
|
35
|
+
"--active-agent-fraction",
|
|
36
|
+
type=float,
|
|
37
|
+
default=0.2,
|
|
38
|
+
help="Fraction of agents active on each tick",
|
|
39
|
+
)
|
|
40
|
+
simulate_parser.add_argument(
|
|
41
|
+
"--activation-mode",
|
|
42
|
+
choices=[mode.value for mode in ActivationMode],
|
|
43
|
+
default=ActivationMode.LEAN.value,
|
|
44
|
+
help="Activation strategy for selecting active agents",
|
|
45
|
+
)
|
|
46
|
+
simulate_parser.add_argument(
|
|
47
|
+
"--group-size", type=int, default=5, help="Number of active agents processed per batch"
|
|
48
|
+
)
|
|
49
|
+
simulate_parser.add_argument(
|
|
50
|
+
"--convergence-threshold",
|
|
51
|
+
type=int,
|
|
52
|
+
default=2,
|
|
53
|
+
help="Stable tick streak before ending simulation early",
|
|
54
|
+
)
|
|
55
|
+
simulate_parser.add_argument(
|
|
56
|
+
"--random-seed", type=int, default=7, help="Random seed for deterministic behavior"
|
|
57
|
+
)
|
|
58
|
+
simulate_parser.add_argument(
|
|
59
|
+
"--use-llm",
|
|
60
|
+
action="store_true",
|
|
61
|
+
dest="use_llm",
|
|
62
|
+
default=True,
|
|
63
|
+
help="Enable live LLM calls when credentials are available",
|
|
64
|
+
)
|
|
65
|
+
simulate_parser.add_argument(
|
|
66
|
+
"--no-use-llm",
|
|
67
|
+
action="store_false",
|
|
68
|
+
dest="use_llm",
|
|
69
|
+
help="Disable live LLM calls and force mock execution",
|
|
70
|
+
)
|
|
71
|
+
simulate_parser.set_defaults(handler=handle_simulate)
|
|
72
|
+
|
|
73
|
+
api_parser = subparsers.add_parser("api", help="Run the FastAPI server")
|
|
74
|
+
api_parser.add_argument("--host", default=None, help="API host override")
|
|
75
|
+
api_parser.add_argument("--port", type=int, default=None, help="API port override")
|
|
76
|
+
api_parser.set_defaults(handler=handle_api)
|
|
77
|
+
|
|
78
|
+
bench_parser = subparsers.add_parser("bench", help="Run the benchmark harness")
|
|
79
|
+
bench_parser.set_defaults(handler=handle_bench)
|
|
80
|
+
|
|
81
|
+
return parser
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
85
|
+
parser = build_parser()
|
|
86
|
+
args = parser.parse_args(argv)
|
|
87
|
+
return int(args.handler(args))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def handle_smoke(_: argparse.Namespace) -> int:
|
|
91
|
+
result = asyncio.run(LeanSwarmEngine().smoke_test())
|
|
92
|
+
print(result.model_dump_json(indent=2))
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def handle_simulate(args: argparse.Namespace) -> int:
|
|
97
|
+
seed_path = Path(args.seed)
|
|
98
|
+
request = SimulationRequest(
|
|
99
|
+
seed_document=seed_path.read_text(encoding="utf-8"),
|
|
100
|
+
question=args.question,
|
|
101
|
+
rounds=args.rounds,
|
|
102
|
+
max_agents=args.max_agents,
|
|
103
|
+
active_agent_fraction=args.active_agent_fraction,
|
|
104
|
+
activation_mode=args.activation_mode,
|
|
105
|
+
group_size=args.group_size,
|
|
106
|
+
convergence_threshold=args.convergence_threshold,
|
|
107
|
+
random_seed=args.random_seed,
|
|
108
|
+
use_llm=args.use_llm,
|
|
109
|
+
)
|
|
110
|
+
result = asyncio.run(LeanSwarmEngine().simulate(request))
|
|
111
|
+
print(result.model_dump_json(indent=2))
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def handle_api(args: argparse.Namespace) -> int:
|
|
116
|
+
settings = RuntimeSettings.from_env()
|
|
117
|
+
host = args.host or settings.api_host
|
|
118
|
+
port = args.port or settings.api_port
|
|
119
|
+
uvicorn.run(create_app(), host=host, port=port)
|
|
120
|
+
return 0
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def handle_bench(_: argparse.Namespace) -> int:
|
|
124
|
+
result = asyncio.run(run_benchmark())
|
|
125
|
+
print(json.dumps(result, indent=2))
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _env_flag(name: str, default: bool) -> bool:
|
|
10
|
+
value = os.getenv(name)
|
|
11
|
+
if value is None:
|
|
12
|
+
return default
|
|
13
|
+
return value.strip().lower() in {"1", "true", "yes", "on"}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RuntimeSettings(BaseModel):
|
|
17
|
+
cache_dir: Path = Field(default=Path(".leanswarm/cache"))
|
|
18
|
+
log_dir: Path = Field(default=Path(".leanswarm/logs"))
|
|
19
|
+
dry_run: bool = True
|
|
20
|
+
max_concurrency: int = 4
|
|
21
|
+
retry_attempts: int = 3
|
|
22
|
+
api_host: str = "127.0.0.1"
|
|
23
|
+
api_port: int = 8000
|
|
24
|
+
flagship_model: str = "gpt-4.1"
|
|
25
|
+
standard_model: str = "gpt-4.1-mini"
|
|
26
|
+
cheap_model: str = "gpt-4.1-nano"
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_env(cls) -> "RuntimeSettings":
|
|
30
|
+
return cls(
|
|
31
|
+
cache_dir=Path(os.getenv("LEANSWARM_CACHE_DIR", ".leanswarm/cache")),
|
|
32
|
+
log_dir=Path(os.getenv("LEANSWARM_LOG_DIR", ".leanswarm/logs")),
|
|
33
|
+
dry_run=_env_flag("LEANSWARM_DRY_RUN", True),
|
|
34
|
+
max_concurrency=int(os.getenv("LEANSWARM_MAX_CONCURRENCY", "4")),
|
|
35
|
+
retry_attempts=int(os.getenv("LEANSWARM_RETRY_ATTEMPTS", "3")),
|
|
36
|
+
api_host=os.getenv("LEANSWARM_API_HOST", "127.0.0.1"),
|
|
37
|
+
api_port=int(os.getenv("LEANSWARM_API_PORT", "8000")),
|
|
38
|
+
flagship_model=os.getenv("LEANSWARM_FLAGSHIP_MODEL", "gpt-4.1"),
|
|
39
|
+
standard_model=os.getenv("LEANSWARM_STANDARD_MODEL", "gpt-4.1-mini"),
|
|
40
|
+
cheap_model=os.getenv("LEANSWARM_CHEAP_MODEL", "gpt-4.1-nano"),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def ensure_dirs(self) -> None:
|
|
44
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
self.log_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def llm_log_path(self) -> Path:
|
|
49
|
+
return self.log_dir / "llm_calls.jsonl"
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def tick_log_path(self) -> Path:
|
|
53
|
+
return self.log_dir / "ticks.jsonl"
|
|
54
|
+
|