greenlit-ai 0.3.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.
- greenlit_ai-0.3.0/PKG-INFO +95 -0
- greenlit_ai-0.3.0/README.md +141 -0
- greenlit_ai-0.3.0/docs/grlt.md +54 -0
- greenlit_ai-0.3.0/fsm_agent/__init__.py +7 -0
- greenlit_ai-0.3.0/fsm_agent/__main__.py +3 -0
- greenlit_ai-0.3.0/fsm_agent/agent.py +451 -0
- greenlit_ai-0.3.0/fsm_agent/api.py +99 -0
- greenlit_ai-0.3.0/fsm_agent/app.html +776 -0
- greenlit_ai-0.3.0/fsm_agent/assess.py +141 -0
- greenlit_ai-0.3.0/fsm_agent/bench_pairs.py +112 -0
- greenlit_ai-0.3.0/fsm_agent/billing.py +614 -0
- greenlit_ai-0.3.0/fsm_agent/board.html +525 -0
- greenlit_ai-0.3.0/fsm_agent/board_state.py +709 -0
- greenlit_ai-0.3.0/fsm_agent/bookings/__init__.py +0 -0
- greenlit_ai-0.3.0/fsm_agent/bookings/env.py +220 -0
- greenlit_ai-0.3.0/fsm_agent/bookings/scripted.py +78 -0
- greenlit_ai-0.3.0/fsm_agent/bookings/tasks.py +289 -0
- greenlit_ai-0.3.0/fsm_agent/bookings/workflow.py +148 -0
- greenlit_ai-0.3.0/fsm_agent/bookings/world.py +235 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/__init__.py +0 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/boltons.json +609 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/env.py +225 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/publish.py +303 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/scripted.py +72 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/tasks.py +267 -0
- greenlit_ai-0.3.0/fsm_agent/bugs/workflow.py +143 -0
- greenlit_ai-0.3.0/fsm_agent/cachesim.py +60 -0
- greenlit_ai-0.3.0/fsm_agent/claims.py +546 -0
- greenlit_ai-0.3.0/fsm_agent/cli.py +474 -0
- greenlit_ai-0.3.0/fsm_agent/cloud.py +346 -0
- greenlit_ai-0.3.0/fsm_agent/compare.py +311 -0
- greenlit_ai-0.3.0/fsm_agent/contracts.py +1122 -0
- greenlit_ai-0.3.0/fsm_agent/controller.py +546 -0
- greenlit_ai-0.3.0/fsm_agent/cost.py +203 -0
- greenlit_ai-0.3.0/fsm_agent/crossings.py +1173 -0
- greenlit_ai-0.3.0/fsm_agent/dashboard.py +172 -0
- greenlit_ai-0.3.0/fsm_agent/dashboard_template.html +866 -0
- greenlit_ai-0.3.0/fsm_agent/decide.py +253 -0
- greenlit_ai-0.3.0/fsm_agent/demo_data/shop_run3.jsonl +86 -0
- greenlit_ai-0.3.0/fsm_agent/demo_data/shop_seed.sh +85 -0
- greenlit_ai-0.3.0/fsm_agent/ensemble.py +143 -0
- greenlit_ai-0.3.0/fsm_agent/evalset.py +73 -0
- greenlit_ai-0.3.0/fsm_agent/evaluate.py +75 -0
- greenlit_ai-0.3.0/fsm_agent/experiment.py +338 -0
- greenlit_ai-0.3.0/fsm_agent/explain.py +364 -0
- greenlit_ai-0.3.0/fsm_agent/folders.py +66 -0
- greenlit_ai-0.3.0/fsm_agent/footprint.py +45 -0
- greenlit_ai-0.3.0/fsm_agent/forecast.py +530 -0
- greenlit_ai-0.3.0/fsm_agent/forecast_report.py +397 -0
- greenlit_ai-0.3.0/fsm_agent/forecaster.py +427 -0
- greenlit_ai-0.3.0/fsm_agent/gate.py +40 -0
- greenlit_ai-0.3.0/fsm_agent/gbm.py +85 -0
- greenlit_ai-0.3.0/fsm_agent/graph.py +334 -0
- greenlit_ai-0.3.0/fsm_agent/greenlit_cli.py +68 -0
- greenlit_ai-0.3.0/fsm_agent/greenlit_policy.py +23 -0
- greenlit_ai-0.3.0/fsm_agent/grlt.py +625 -0
- greenlit_ai-0.3.0/fsm_agent/holds.py +103 -0
- greenlit_ai-0.3.0/fsm_agent/hub.py +565 -0
- greenlit_ai-0.3.0/fsm_agent/hub_tool.py +64 -0
- greenlit_ai-0.3.0/fsm_agent/importers.py +397 -0
- greenlit_ai-0.3.0/fsm_agent/judge.py +91 -0
- greenlit_ai-0.3.0/fsm_agent/keys.py +151 -0
- greenlit_ai-0.3.0/fsm_agent/labeler.py +146 -0
- greenlit_ai-0.3.0/fsm_agent/logview.py +203 -0
- greenlit_ai-0.3.0/fsm_agent/mcp.py +304 -0
- greenlit_ai-0.3.0/fsm_agent/merge_driver.py +241 -0
- greenlit_ai-0.3.0/fsm_agent/models/__init__.py +0 -0
- greenlit_ai-0.3.0/fsm_agent/models/_fallback.py +57 -0
- greenlit_ai-0.3.0/fsm_agent/models/anthropic_messages.py +158 -0
- greenlit_ai-0.3.0/fsm_agent/models/openai_chat.py +138 -0
- greenlit_ai-0.3.0/fsm_agent/modes.py +190 -0
- greenlit_ai-0.3.0/fsm_agent/overlap.py +417 -0
- greenlit_ai-0.3.0/fsm_agent/paths.py +480 -0
- greenlit_ai-0.3.0/fsm_agent/plans.py +106 -0
- greenlit_ai-0.3.0/fsm_agent/policy.py +183 -0
- greenlit_ai-0.3.0/fsm_agent/policy_v0.json +6 -0
- greenlit_ai-0.3.0/fsm_agent/precision.py +113 -0
- greenlit_ai-0.3.0/fsm_agent/prices.json +75 -0
- greenlit_ai-0.3.0/fsm_agent/prior.py +131 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/README.md +13 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/growth.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/manifest.json +59 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/n_q10.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/n_q30.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/n_q50.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/n_q70.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/n_q90.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/prior_model/output.txt +3002 -0
- greenlit_ai-0.3.0/fsm_agent/providers.py +681 -0
- greenlit_ai-0.3.0/fsm_agent/proxy.py +760 -0
- greenlit_ai-0.3.0/fsm_agent/public_traces.py +78 -0
- greenlit_ai-0.3.0/fsm_agent/report/__init__.py +6 -0
- greenlit_ai-0.3.0/fsm_agent/report/agent.py +273 -0
- greenlit_ai-0.3.0/fsm_agent/report/series.py +165 -0
- greenlit_ai-0.3.0/fsm_agent/results.py +194 -0
- greenlit_ai-0.3.0/fsm_agent/runner.py +798 -0
- greenlit_ai-0.3.0/fsm_agent/schema.py +171 -0
- greenlit_ai-0.3.0/fsm_agent/skill/SKILL.md +39 -0
- greenlit_ai-0.3.0/fsm_agent/spec.py +100 -0
- greenlit_ai-0.3.0/fsm_agent/state.py +313 -0
- greenlit_ai-0.3.0/fsm_agent/tasksets.py +81 -0
- greenlit_ai-0.3.0/fsm_agent/team.py +80 -0
- greenlit_ai-0.3.0/fsm_agent/team_report.py +179 -0
- greenlit_ai-0.3.0/fsm_agent/traces.py +176 -0
- greenlit_ai-0.3.0/fsm_agent/track.py +1088 -0
- greenlit_ai-0.3.0/fsm_agent/types.py +82 -0
- greenlit_ai-0.3.0/fsm_agent/ui/__init__.py +4 -0
- greenlit_ai-0.3.0/fsm_agent/ui/page.html +54 -0
- greenlit_ai-0.3.0/fsm_agent/ui/server.py +252 -0
- greenlit_ai-0.3.0/fsm_agent/watch.py +181 -0
- greenlit_ai-0.3.0/greenlit_ai.egg-info/PKG-INFO +95 -0
- greenlit_ai-0.3.0/greenlit_ai.egg-info/SOURCES.txt +180 -0
- greenlit_ai-0.3.0/greenlit_ai.egg-info/dependency_links.txt +1 -0
- greenlit_ai-0.3.0/greenlit_ai.egg-info/entry_points.txt +3 -0
- greenlit_ai-0.3.0/greenlit_ai.egg-info/requires.txt +41 -0
- greenlit_ai-0.3.0/greenlit_ai.egg-info/top_level.txt +1 -0
- greenlit_ai-0.3.0/pyproject.toml +41 -0
- greenlit_ai-0.3.0/setup.cfg +4 -0
- greenlit_ai-0.3.0/tests/test_agent.py +165 -0
- greenlit_ai-0.3.0/tests/test_app.py +42 -0
- greenlit_ai-0.3.0/tests/test_billing.py +308 -0
- greenlit_ai-0.3.0/tests/test_board.py +371 -0
- greenlit_ai-0.3.0/tests/test_bookings.py +320 -0
- greenlit_ai-0.3.0/tests/test_bugs.py +193 -0
- greenlit_ai-0.3.0/tests/test_claims.py +464 -0
- greenlit_ai-0.3.0/tests/test_claims_live.py +91 -0
- greenlit_ai-0.3.0/tests/test_contracts.py +404 -0
- greenlit_ai-0.3.0/tests/test_core.py +148 -0
- greenlit_ai-0.3.0/tests/test_crossings.py +552 -0
- greenlit_ai-0.3.0/tests/test_decide.py +207 -0
- greenlit_ai-0.3.0/tests/test_delivery.py +220 -0
- greenlit_ai-0.3.0/tests/test_eval_arms.py +187 -0
- greenlit_ai-0.3.0/tests/test_eval_score.py +120 -0
- greenlit_ai-0.3.0/tests/test_evalset.py +131 -0
- greenlit_ai-0.3.0/tests/test_experiment.py +163 -0
- greenlit_ai-0.3.0/tests/test_explain.py +121 -0
- greenlit_ai-0.3.0/tests/test_forecast.py +179 -0
- greenlit_ai-0.3.0/tests/test_forecaster.py +362 -0
- greenlit_ai-0.3.0/tests/test_import.py +283 -0
- greenlit_ai-0.3.0/tests/test_keys.py +88 -0
- greenlit_ai-0.3.0/tests/test_labeler.py +163 -0
- greenlit_ai-0.3.0/tests/test_launch.py +319 -0
- greenlit_ai-0.3.0/tests/test_live.py +257 -0
- greenlit_ai-0.3.0/tests/test_live_run.py +103 -0
- greenlit_ai-0.3.0/tests/test_mcp.py +129 -0
- greenlit_ai-0.3.0/tests/test_modes.py +250 -0
- greenlit_ai-0.3.0/tests/test_note.py +34 -0
- greenlit_ai-0.3.0/tests/test_nov5_records.py +32 -0
- greenlit_ai-0.3.0/tests/test_overlap.py +279 -0
- greenlit_ai-0.3.0/tests/test_paths.py +288 -0
- greenlit_ai-0.3.0/tests/test_prediction.py +188 -0
- greenlit_ai-0.3.0/tests/test_prices.py +73 -0
- greenlit_ai-0.3.0/tests/test_proxy.py +230 -0
- greenlit_ai-0.3.0/tests/test_publish.py +157 -0
- greenlit_ai-0.3.0/tests/test_repeat.py +51 -0
- greenlit_ai-0.3.0/tests/test_report.py +120 -0
- greenlit_ai-0.3.0/tests/test_runner.py +506 -0
- greenlit_ai-0.3.0/tests/test_sdk_compat.py +77 -0
- greenlit_ai-0.3.0/tests/test_steering.py +376 -0
- greenlit_ai-0.3.0/tests/test_success.py +175 -0
- greenlit_ai-0.3.0/tests/test_track.py +270 -0
- greenlit_ai-0.3.0/tests/test_train_data.py +118 -0
- greenlit_ai-0.3.0/tests/test_ui_snippet.py +401 -0
- greenlit_ai-0.3.0/ui/app.css +186 -0
- greenlit_ai-0.3.0/ui/calls.js +802 -0
- greenlit_ai-0.3.0/ui/compare.js +354 -0
- greenlit_ai-0.3.0/ui/cone.js +940 -0
- greenlit_ai-0.3.0/ui/connect.js +1282 -0
- greenlit_ai-0.3.0/ui/console.html +367 -0
- greenlit_ai-0.3.0/ui/devserve.py +128 -0
- greenlit_ai-0.3.0/ui/fixtures/state.cold.json +131 -0
- greenlit_ai-0.3.0/ui/fixtures/state.empty.json +36 -0
- greenlit_ai-0.3.0/ui/fixtures/state.live.json +614 -0
- greenlit_ai-0.3.0/ui/fixtures/state.paired.json +566 -0
- greenlit_ai-0.3.0/ui/fixtures/state.paths.json +3454 -0
- greenlit_ai-0.3.0/ui/history.js +1403 -0
- greenlit_ai-0.3.0/ui/modes.js +293 -0
- greenlit_ai-0.3.0/ui/paths.js +323 -0
- greenlit_ai-0.3.0/ui/state.js +725 -0
- greenlit_ai-0.3.0/ui/steering.js +193 -0
- greenlit_ai-0.3.0/ui/summary.js +1074 -0
- greenlit_ai-0.3.0/ui/svg.js +108 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: greenlit-ai
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: greenlit: coding agents hear about each other's plans before they edit, and ask a person only when your rules say to
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Project-URL: Homepage, https://neelsck.github.io/greenlit/
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: numpy>=1.24
|
|
10
|
+
Requires-Dist: httpx>=0.24
|
|
11
|
+
Provides-Extra: claude
|
|
12
|
+
Requires-Dist: anthropic; extra == "claude"
|
|
13
|
+
Provides-Extra: openai
|
|
14
|
+
Requires-Dist: openai; extra == "openai"
|
|
15
|
+
Provides-Extra: proxy
|
|
16
|
+
Requires-Dist: httpx; extra == "proxy"
|
|
17
|
+
Provides-Extra: prior
|
|
18
|
+
Requires-Dist: lightgbm>=4; extra == "prior"
|
|
19
|
+
Provides-Extra: all
|
|
20
|
+
Requires-Dist: anthropic; extra == "all"
|
|
21
|
+
Requires-Dist: openai; extra == "all"
|
|
22
|
+
Requires-Dist: httpx; extra == "all"
|
|
23
|
+
Requires-Dist: lightgbm>=4; extra == "all"
|
|
24
|
+
Requires-Dist: duckdb>=1.0; extra == "all"
|
|
25
|
+
Provides-Extra: report
|
|
26
|
+
Requires-Dist: duckdb>=1.0; extra == "report"
|
|
27
|
+
Requires-Dist: anthropic; extra == "report"
|
|
28
|
+
Requires-Dist: httpx; extra == "report"
|
|
29
|
+
Provides-Extra: team
|
|
30
|
+
Requires-Dist: httpx; extra == "team"
|
|
31
|
+
Provides-Extra: bugs
|
|
32
|
+
Requires-Dist: pytest>=7; extra == "bugs"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
35
|
+
Requires-Dist: anthropic; extra == "dev"
|
|
36
|
+
Requires-Dist: openai; extra == "dev"
|
|
37
|
+
Requires-Dist: httpx; extra == "dev"
|
|
38
|
+
Requires-Dist: lightgbm>=4; extra == "dev"
|
|
39
|
+
Requires-Dist: pyarrow; extra == "dev"
|
|
40
|
+
Requires-Dist: duckdb>=1.0; extra == "dev"
|
|
41
|
+
|
|
42
|
+
# greenlit (`pip install greenlit-ai`, command `grlt`)
|
|
43
|
+
|
|
44
|
+
greenlit for teams running coding agents. When two agents are about to change the same thing in different ways,
|
|
45
|
+
greenlit tells both of them, at plan time, before either edits. It asks a person only when your repo's rules say to,
|
|
46
|
+
or when the agents still collide after being told.
|
|
47
|
+
|
|
48
|
+
## Quickstart
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install greenlit-ai
|
|
52
|
+
cd your-repo
|
|
53
|
+
grlt up # starts a team on the hosted hub, wires Claude Code in this checkout, prints an invite link
|
|
54
|
+
claude # as usual
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
A teammate runs `grlt join '<invite link>'` in their own checkout. On your own, run a second agent in another worktree:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git worktree add ../your-repo-2 -b second && cd ../your-repo-2 && grlt join '<invite link>'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`grlt up` writes `goal.md` if the repo has none: what you're shipping, the decisions agents must hold to, and a
|
|
64
|
+
**Don't change without asking** list. A commit that still changes something on that list after its agent was told is
|
|
65
|
+
held until someone approves, denies, or tells the agents to settle it, in the web app's **Needs you**.
|
|
66
|
+
|
|
67
|
+
## Inside Claude Code
|
|
68
|
+
|
|
69
|
+
- Your agent gets greenlit's notes in its context, as facts about teammates' work.
|
|
70
|
+
- You see each note too: `greenlit told your agent: ...` appears in the session.
|
|
71
|
+
- The status line reads `greenlit · 2 agents · 1 note · nothing needs you`.
|
|
72
|
+
|
|
73
|
+
## The web app
|
|
74
|
+
|
|
75
|
+
The invite link is also the web app: **Needs you** (held commits, both sides, the rule from goal.md, approve / deny /
|
|
76
|
+
settle), **Map** (only the files in someone's plan, each agent's planned path, forecast crossings before any edit) and
|
|
77
|
+
**Threads** (one per clash: forecast, notes, holds, decision, outcome, a rating on each note).
|
|
78
|
+
|
|
79
|
+
## What leaves your laptop
|
|
80
|
+
|
|
81
|
+
The agent's plan text, file paths, changed lines and the functions around them, and the task text go to the hub.
|
|
82
|
+
Never your prompts, your code base or your API keys: model traffic goes from your machine to Anthropic through a local
|
|
83
|
+
proxy, and passes through untouched if the hub is down or slow.
|
|
84
|
+
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
| | |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `grlt up` | start a team for this repo, wire Claude Code here |
|
|
90
|
+
| `grlt join <link>` | join a team from your own checkout |
|
|
91
|
+
| `grlt demo` | watch the shop demo's catch in the web app (a replay; no agents, no cost) |
|
|
92
|
+
| `grlt demo --seed DIR` | make the shop repo with two checkouts, to run it with real Claude Code |
|
|
93
|
+
| `grlt goal` | send an edited goal.md to the team |
|
|
94
|
+
| `grlt stats` | what greenlit did for this team |
|
|
95
|
+
| `grlt down` | unwire this checkout: Claude Code's settings go back exactly as they were |
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# fsm-agent
|
|
2
|
+
|
|
3
|
+
fsm-agent forecasts what an LLM agent will spend, in tokens and dollars, before each task and before each model call, learning from the agent's own history, and controls that spend: budgets that stop a run before it goes over, phase budgets for an agent you already have, and a phase controller for an agent you build on it. For a task the agent repeats, it also keeps the routes: the tree of tool calls earlier runs made, the cheapest one that succeeded, and a note to the agent at the start, at each stage and when it strays ([docs/paths.md](docs/paths.md)). It works with any agent, in any language, on Anthropic's or OpenAI's API or any OpenAI-compatible server, and it runs on your machine: your keys and your prompts never leave it.
|
|
4
|
+
|
|
5
|
+
It also contains the paired experiment the controller was developed with: the same tasks with and without it, each arm billed to its own API key and checked against Anthropic's usage records.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Python 3.9 or later.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone https://github.com/neelsck/fsm-agent.git && cd fsm-agent
|
|
13
|
+
python3 -m venv .venv && source .venv/bin/activate # once per clone; activate again in a new terminal
|
|
14
|
+
python3 -m pip install -e ".[dev]" # numpy, the anthropic and openai SDKs, httpx, pytest
|
|
15
|
+
python3 -m pytest -q # about a minute and a half; nothing reaches a real API
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Use it with your agent
|
|
19
|
+
|
|
20
|
+
**New here? [docs/quickstart.md](docs/quickstart.md) gets your agent's runs forecast in about 15 minutes, step by step.**
|
|
21
|
+
|
|
22
|
+
How the forecasts are made, and how accurate they have been: [docs/forecasting.md](docs/forecasting.md).
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python3 -m fsm_agent import calls.jsonl --agent support-triage # your history, from logs it already writes
|
|
26
|
+
python3 -m fsm_agent proxy --agent support-triage # from now on: point the agent's base URL here
|
|
27
|
+
python3 -m fsm_agent report fsm_runs/support-triage # forecast against actual, the step graph, a dashboard
|
|
28
|
+
python3 -m fsm_agent ui fsm_runs/support-triage # a local page following its runs as they happen
|
|
29
|
+
python3 -m fsm_agent compare --agent support-triage --task "..." -- ./my-agent "{task}" # one task, as it is and steered
|
|
30
|
+
python3 -m fsm_agent mode --agent support-triage controller # steer every run (compare: none)
|
|
31
|
+
python3 -m fsm_agent paths fsm_runs/support-triage # what its repeated tasks did, and the cheapest successful route
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Way in | For |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `import` | past runs, from logged request/response bodies (Anthropic, OpenAI), usage rows (CSV or JSON lines) or OpenTelemetry traces |
|
|
37
|
+
| `proxy` | any agent in any language: set `ANTHROPIC_BASE_URL` or `OPENAI_BASE_URL` (or any OpenAI-compatible base URL) to it; runs by header or by conversation; budgets that stop a call before it is sent |
|
|
38
|
+
| `track.wrap(client)` | a Python agent: wrap its `anthropic` or `openai` client |
|
|
39
|
+
| `mcp` | Claude Code, Codex, Cursor or any MCP client: forecast a task, ask for the best known path through it, check a run's spend, report how it went |
|
|
40
|
+
| a `policy.json` | phase budgets for an agent you already have, enforced by the proxy or the wrapper |
|
|
41
|
+
| `fsm_agent.agent` | the phase controller for your own tools: phases, a check after each change, undo and retry, on Claude or any OpenAI-compatible model |
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from fsm_agent import agent
|
|
45
|
+
|
|
46
|
+
flow = agent.Workflow(name="refunds", system="You handle refund requests.",
|
|
47
|
+
phases=[agent.Phase("research", tools=[search_orders], max_calls=6),
|
|
48
|
+
agent.Phase("refund", tools=[issue_refund], max_calls=2, commits=True)],
|
|
49
|
+
check=refund_is_right)
|
|
50
|
+
result = agent.run(flow, task="Customer 4411 says they paid twice for order 991.", model="claude-sonnet-5-5")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Every forecast is written to disk before the run or call it is about, so the report is an honest test of what you would have known at the time. [docs/onboarding.md](docs/onboarding.md) is the guide for all of this; [docs/traces.md](docs/traces.md) describes the files.
|
|
54
|
+
|
|
55
|
+
## The experiment
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python3 -m fsm_agent experiment --dry-run --n 20 # no key, no cost: a scripted stand-in plays the agent
|
|
59
|
+
python3 -m fsm_agent experiment --n 3 # a trial with a real model: 3 runs in each arm
|
|
60
|
+
python3 -m fsm_agent experiment --n 50 # the experiment
|
|
61
|
+
python3 -m fsm_agent experiment --tasks bugs --n 12 # the bug-fix task set
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**The arms.** Both arms are the same agent with the same tools on the same tasks, and both finish the same number of runs.
|
|
65
|
+
|
|
66
|
+
- `baseline`: no phases. The harness runs the check after every change the agent makes (the test suite, or the booking check), as the controller does, so the comparison is about the phases and nothing else. `--plain-baseline` instead leaves checking to the agent.
|
|
67
|
+
- `fsm`: the phase controller. The task is split into phases (for a bug: reproduce, locate, read, edit, verify), each with its own tools and a call budget; after an edit the harness runs the check; a failed attempt is cut from the context and the change undone, up to three attempts.
|
|
68
|
+
|
|
69
|
+
**The task sets** (`--tasks`):
|
|
70
|
+
|
|
71
|
+
- `bookings` (the default): meeting requests on a sandbox company calendar (14 people in five time zones, rooms, two weeks of meetings), graded against a hidden specification. About a cent a run.
|
|
72
|
+
- `bugs`: 36 bugs seeded into [boltons](https://github.com/mahmoud/boltons) (BSD-licensed, pinned at commit `4e5faa3d`), 12 easy, 13 medium and 11 hard, each with a bug report written the way a user would. A run passes when the library's own tests for that module pass. Tens of cents a run. The first run fetches boltons with git into `~/.cache/fsm-agent/` (or `$FSM_AGENT_CACHE`). `python3 -m fsm_agent tasks --check` proves every bug is real: its tests fail with the bug and pass without it.
|
|
73
|
+
|
|
74
|
+
**API keys.** A real run asks for two API keys, one per arm, each from its own workspace in the Claude Console, so each arm's spend shows up separately in your organization's usage. It also asks, optionally, for an Admin key, used only for read-only requests to fetch Anthropic's usage report for the audit. Keys are pasted at hidden prompts (or set as `ANTHROPIC_API_KEY_BASELINE`, `ANTHROPIC_API_KEY_FSM` and `ANTHROPIC_ADMIN_KEY`) and kept in memory: files and output identify a key only the way the Console does, by its first and last few characters. The plan and the spend limits are shown before anything starts, and nothing is spent until you answer `y`.
|
|
75
|
+
|
|
76
|
+
**What it writes**, in a new folder under `experiments/`:
|
|
77
|
+
|
|
78
|
+
| file | what |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `baseline.jsonl`, `fsm.jsonl` | every step of every run ([docs/traces.md](docs/traces.md)) |
|
|
81
|
+
| `forecasts.jsonl` | each run's cost forecast, written before the run started |
|
|
82
|
+
| `forecast.md`, `dashboard.html`, `forecast.json` | the report: forecast against actual per run, per call and during a run; the step graph; the dashboard |
|
|
83
|
+
| `audit.md`, `usage_ledger.jsonl` | what each key sent, against what Anthropic recorded for it, and where to see the same in the Console |
|
|
84
|
+
| `patches/` | bugs: each run's change as a diff |
|
|
85
|
+
| `tasks.json`, `experiment.json` | the tasks with their hidden answers, and the settings |
|
|
86
|
+
|
|
87
|
+
**Forecasts** learn from the runs of the same arm that finished earlier, and start from earlier experiments with the same task set and model (`--learn-from FOLDER ...` to choose, `--fresh` for none).
|
|
88
|
+
|
|
89
|
+
**Publishing the bug fixes** (`--publish OWNER/REPO`, bugs only). Every run is pushed to that repository as a branch, `runs/<experiment>/<arm>/<task>-r<n>`, on top of a branch holding the task's starting point, `tasks/<task>`, with a pull request whose description has the bug report, the agent's steps and the cost against its forecast. An index of every run is committed to `main` under `experiments/`. Use a repository of its own for this, not this one. It needs push access; the pull requests are opened with the GitHub CLI if you are logged in (`gh auth login`), else with `GITHUB_TOKEN`, and otherwise the run's link to open one is printed.
|
|
90
|
+
|
|
91
|
+
## How the forecast works
|
|
92
|
+
|
|
93
|
+
- **Before a call.** Most of a request repeats an earlier one, block for block; the model's reply and the tool's result are what was added. So the input is forecast from the earlier request's actual input, plus the reply carried over and what was added, and the cache split follows from the same match. The reply's length is learned per kind of step.
|
|
94
|
+
- **Before a run.** Two methods. The *step graph*: an absorbing Markov chain over the kinds of step the agent takes (the tool each call asked for, or a plain reply), with each step's cost, which gives an expected cost and a range by simulation. And a *regression on the request's features*: whatever is known about a task before it starts (for bookings, the number of attendees and whether a room is needed; for bugs, the size of the module and of the bug report; for your agent, the `features` you name). The regression is used once there are enough runs to fit it, because it is what tells cheap requests from expensive ones in advance.
|
|
95
|
+
- **During a run.** The projected total is what has been spent plus what is still to come, combining the step graph's view from the current step with the run's forecast.
|
|
96
|
+
|
|
97
|
+
Costs are in input-token equivalents (ITE): one uncached input token is 1, and cache reads, cache writes and output tokens are weighted by the model's prices (on Claude Sonnet 5.5: 0.05, 1.25 and 5). An agent on several models counts every call in units of its first model, converted at list prices. Dollar figures come from the price table in `fsm_agent/prices.json` (Anthropic, OpenAI and Google, with sources), to which you can add your own models. Output tokens cost five times as much as input or more, so most of a call's forecast error is the length of the reply.
|
|
98
|
+
|
|
99
|
+
## Commands
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
python3 -m fsm_agent proxy --agent NAME [--port 8788] [--upstream NAME=URL] [--budget ITE --on-budget stop] [--policy FILE]
|
|
103
|
+
python3 -m fsm_agent import LOGS --agent NAME [--features a,b] logs: messages, usage rows (CSV/JSONL) or OpenTelemetry
|
|
104
|
+
python3 -m fsm_agent report [FOLDER] [--full] [--replay] an agent's or an experiment's folder; default: the latest
|
|
105
|
+
python3 -m fsm_agent mcp [--runs DIR] [--proxy URL] the MCP server (stdio)
|
|
106
|
+
python3 -m fsm_agent skill [--to DIR] the Skill for the MCP tools
|
|
107
|
+
python3 -m fsm_agent experiment [--tasks bookings|bugs] [--n 10] [--dry-run] [--publish OWNER/REPO]
|
|
108
|
+
python3 -m fsm_agent audit [FOLDER] an experiment's bill against Anthropic's usage report
|
|
109
|
+
python3 -m fsm_agent show TRACE.jsonl [--run ID] one run, step by step
|
|
110
|
+
python3 -m fsm_agent ui [FOLDER] [--page FILE] a local page following an agent's runs live
|
|
111
|
+
python3 -m fsm_agent evaluate [FOLDER] [--skip 10] how good its forecasts are, replayed stage by stage
|
|
112
|
+
python3 -m fsm_agent compare --agent NAME --task T [--isolate] [--check CMD] [--split-keys VAR] -- CMD "{task}"
|
|
113
|
+
python3 -m fsm_agent serve --agent NAME [--port 8788] [--ui-port 8765] the proxy, the runner and the console in one process (docs/runner.md)
|
|
114
|
+
python3 -m fsm_agent mode --agent NAME [compare|controller] whether fsm-agent steers the agent's runs
|
|
115
|
+
python3 -m fsm_agent paths [FOLDER] [--family F] [--from a,b] the tree of tool calls per repeated task, the cheapest successful path, advice from a point
|
|
116
|
+
python3 -m fsm_agent tasks [--tasks bugs] [--n 10] [--check] the tasks an experiment would run
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`python3 -m fsm_agent <command> --help` lists every option.
|
|
120
|
+
|
|
121
|
+
## The code
|
|
122
|
+
|
|
123
|
+
| | |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `fsm_agent/proxy.py` | the local proxy |
|
|
126
|
+
| `fsm_agent/track.py` | the wrapper, and the bookkeeping the proxy and `import` share with it |
|
|
127
|
+
| `fsm_agent/providers.py` | the API shapes: Anthropic's Messages, OpenAI's Chat Completions and Responses |
|
|
128
|
+
| `fsm_agent/cost.py`, `prices.json` | prices for any model, and costs in a common unit |
|
|
129
|
+
| `fsm_agent/importers.py` | history from logs |
|
|
130
|
+
| `fsm_agent/policy.py` | guardrails for an agent you already have |
|
|
131
|
+
| `fsm_agent/paths.py` | per repeated task: the tree of tool calls, the cheapest successful path, the advice |
|
|
132
|
+
| `fsm_agent/agent.py` | the phase controller as a library |
|
|
133
|
+
| `fsm_agent/mcp.py`, `skill/SKILL.md` | the MCP server and its Skill |
|
|
134
|
+
python3 -m fsm_agent ui fsm_runs/support-triage # a local page following its runs as they happen
|
|
135
|
+
| `fsm_agent/forecast.py`, `graph.py`, `forecast_report.py` | the forecaster, the step graph, the report |
|
|
136
|
+
| `fsm_agent/controller.py`, `spec.py` | the phase controller and the workflow definitions |
|
|
137
|
+
| `fsm_agent/models/` | the controller's model adapters: Anthropic's Messages, OpenAI's Chat Completions |
|
|
138
|
+
| `fsm_agent/experiment.py`, `billing.py` | the paired experiment and the billing audit |
|
|
139
|
+
| `fsm_agent/bookings/`, `fsm_agent/bugs/` | the two task sets (each: workflow, environment, tasks, a scripted stand-in); `bugs/publish.py` publishes runs to GitHub |
|
|
140
|
+
| `fsm_agent/dashboard.py`, `dashboard_template.html` | the dashboard |
|
|
141
|
+
| `tests/` | `stub_api.py` and `stub_openai.py` are local stand-ins for Anthropic's and OpenAI's APIs that enforce their documented rules, so tests drive the real SDKs over HTTP without a key |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# greenlit (`pip install greenlit-ai`, command `grlt`)
|
|
2
|
+
|
|
3
|
+
greenlit for teams running coding agents. When two agents are about to change the same thing in different ways,
|
|
4
|
+
greenlit tells both of them, at plan time, before either edits. It asks a person only when your repo's rules say to,
|
|
5
|
+
or when the agents still collide after being told.
|
|
6
|
+
|
|
7
|
+
## Quickstart
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install greenlit-ai
|
|
11
|
+
cd your-repo
|
|
12
|
+
grlt up # starts a team on the hosted hub, wires Claude Code in this checkout, prints an invite link
|
|
13
|
+
claude # as usual
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A teammate runs `grlt join '<invite link>'` in their own checkout. On your own, run a second agent in another worktree:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git worktree add ../your-repo-2 -b second && cd ../your-repo-2 && grlt join '<invite link>'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`grlt up` writes `goal.md` if the repo has none: what you're shipping, the decisions agents must hold to, and a
|
|
23
|
+
**Don't change without asking** list. A commit that still changes something on that list after its agent was told is
|
|
24
|
+
held until someone approves, denies, or tells the agents to settle it, in the web app's **Needs you**.
|
|
25
|
+
|
|
26
|
+
## Inside Claude Code
|
|
27
|
+
|
|
28
|
+
- Your agent gets greenlit's notes in its context, as facts about teammates' work.
|
|
29
|
+
- You see each note too: `greenlit told your agent: ...` appears in the session.
|
|
30
|
+
- The status line reads `greenlit · 2 agents · 1 note · nothing needs you`.
|
|
31
|
+
|
|
32
|
+
## The web app
|
|
33
|
+
|
|
34
|
+
The invite link is also the web app: **Needs you** (held commits, both sides, the rule from goal.md, approve / deny /
|
|
35
|
+
settle), **Map** (only the files in someone's plan, each agent's planned path, forecast crossings before any edit) and
|
|
36
|
+
**Threads** (one per clash: forecast, notes, holds, decision, outcome, a rating on each note).
|
|
37
|
+
|
|
38
|
+
## What leaves your laptop
|
|
39
|
+
|
|
40
|
+
The agent's plan text, file paths, changed lines and the functions around them, and the task text go to the hub.
|
|
41
|
+
Never your prompts, your code base or your API keys: model traffic goes from your machine to Anthropic through a local
|
|
42
|
+
proxy, and passes through untouched if the hub is down or slow.
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
| | |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `grlt up` | start a team for this repo, wire Claude Code here |
|
|
49
|
+
| `grlt join <link>` | join a team from your own checkout |
|
|
50
|
+
| `grlt demo` | watch the shop demo's catch in the web app (a replay; no agents, no cost) |
|
|
51
|
+
| `grlt demo --seed DIR` | make the shop repo with two checkouts, to run it with real Claude Code |
|
|
52
|
+
| `grlt goal` | send an edited goal.md to the team |
|
|
53
|
+
| `grlt stats` | what greenlit did for this team |
|
|
54
|
+
| `grlt down` | unwire this checkout: Claude Code's settings go back exactly as they were |
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""fsm-agent: forecast the token spend of LLM agents, before every run and every call.
|
|
2
|
+
|
|
3
|
+
fsm_agent.track wrap the Claude client your agent already uses; every run and call is forecast and recorded
|
|
4
|
+
fsm_agent.importers bring an agent's history in from its logs (Messages API logs, OpenTelemetry)
|
|
5
|
+
fsm_agent.experiment the paired experiment: the same tasks without and with the phase controller
|
|
6
|
+
"""
|
|
7
|
+
__version__ = "0.2.0"
|