specsmith 0.2.2.dev50__tar.gz → 0.2.3.dev1__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.
- {specsmith-0.2.2.dev50/src/specsmith.egg-info → specsmith-0.2.3.dev1}/PKG-INFO +37 -4
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/README.md +36 -3
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/pyproject.toml +1 -1
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/__init__.py +1 -1
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/cli.py +162 -0
- specsmith-0.2.3.dev1/src/specsmith/rate_limits.py +784 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/governance/rules.md.j2 +12 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/validator.py +93 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1/src/specsmith.egg-info}/PKG-INFO +37 -4
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith.egg-info/SOURCES.txt +2 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_cli.py +38 -0
- specsmith-0.2.3.dev1/tests/test_rate_limits.py +276 -0
- specsmith-0.2.3.dev1/tests/test_validator.py +142 -0
- specsmith-0.2.2.dev50/tests/test_validator.py +0 -62
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/LICENSE +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/setup.cfg +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/__main__.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/architect.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/auditor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/commands/__init__.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/compressor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/config.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/credit_analyzer.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/credits.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/differ.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/doctor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/executor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/exporter.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/importer.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/__init__.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/aider.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/base.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/claude_code.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/copilot.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/cursor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/gemini.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/warp.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/integrations/windsurf.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/ledger.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/plugins.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/releaser.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/requirements.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/scaffolder.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/session.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/agents.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/bug_report.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/code_of_conduct.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/contributing.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/feature_request.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/license-Apache-2.0.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/license-MIT.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/pull_request_template.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/community/security.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/docs/architecture.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/docs/mkdocs.yml.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/docs/readthedocs.yaml.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/docs/requirements.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/docs/test-spec.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/docs/workflow.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/editorconfig.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/gitattributes.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/gitignore.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/go/go.mod.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/go/main.go.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/governance/context-budget.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/governance/drift-metrics.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/governance/roles.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/governance/verification.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/governance/workflow.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/js/package.json.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/ledger.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/python/cli.py.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/python/init.py.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/python/pyproject.toml.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/readme.md.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/rust/Cargo.toml.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/rust/main.rs.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/scripts/exec.cmd.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/scripts/exec.sh.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/scripts/run.cmd.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/scripts/run.sh.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/scripts/setup.cmd.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/scripts/setup.sh.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/templates/workflows/release.yml.j2 +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/tools.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/updater.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/upgrader.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/vcs/__init__.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/vcs/base.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/vcs/bitbucket.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/vcs/github.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/vcs/gitlab.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith/vcs_commands.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith.egg-info/dependency_links.txt +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith.egg-info/entry_points.txt +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith.egg-info/requires.txt +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/src/specsmith.egg-info/top_level.txt +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_auditor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_compressor.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_importer.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_integrations.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_scaffolder.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_smoke.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_tools.py +0 -0
- {specsmith-0.2.2.dev50 → specsmith-0.2.3.dev1}/tests/test_vcs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specsmith
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3.dev1
|
|
4
4
|
Summary: Forge governed project scaffolds from the Agentic AI Development Workflow Specification.
|
|
5
5
|
Author: BitConcepts
|
|
6
6
|
License: MIT
|
|
@@ -129,7 +129,7 @@ Each type gets: tool-aware CI (correct lint/test/security/build tools), domain-s
|
|
|
129
129
|
| `import` | Adopt an existing project (merge mode) |
|
|
130
130
|
| `audit` | Drift detection and health checks (`--fix` to auto-repair) |
|
|
131
131
|
| `architect` | Interactive architecture generation |
|
|
132
|
-
| `validate` | Governance
|
|
132
|
+
| `validate` | Governance consistency + H11 blocking-loop detection |
|
|
133
133
|
| `compress` | Archive old ledger entries |
|
|
134
134
|
| `upgrade` | Update governance to new spec version |
|
|
135
135
|
| `status` | CI/PR/alert status from VCS platform |
|
|
@@ -137,7 +137,13 @@ Each type gets: tool-aware CI (correct lint/test/security/build tools), domain-s
|
|
|
137
137
|
| `export` | Compliance report with REQ↔TEST coverage |
|
|
138
138
|
| `doctor` | Check if verification tools are installed |
|
|
139
139
|
| `self-update` | Update specsmith (channel-aware) |
|
|
140
|
-
| `credits` | AI credit tracking, analysis, and
|
|
140
|
+
| `credits` | AI credit tracking, analysis, budgets, and rate-limit pacing |
|
|
141
|
+
| `exec` / `ps` / `abort` | Tracked process execution with PID tracking and timeout |
|
|
142
|
+
| `commit` / `push` / `sync` | Governance-aware VCS operations |
|
|
143
|
+
| `branch` / `pr` | Strategy-aware branching and PR creation |
|
|
144
|
+
| `ledger` | Structured ledger add/list/stats |
|
|
145
|
+
| `req` | Requirements list/add/trace/gaps/orphans |
|
|
146
|
+
| `session-end` | End-of-session checklist |
|
|
141
147
|
|
|
142
148
|
## 7 Agent Integrations
|
|
143
149
|
|
|
@@ -147,9 +153,36 @@ AGENTS.md (cross-platform standard), Warp/Oz, Claude Code, GitHub Copilot, Curso
|
|
|
147
153
|
|
|
148
154
|
GitHub Actions, GitLab CI, Bitbucket Pipelines — all with tool-aware CI generated from the verification tool registry. Dependabot/Renovate configured per language ecosystem.
|
|
149
155
|
|
|
156
|
+
## Governance Rules (H1–H12)
|
|
157
|
+
|
|
158
|
+
specsmith-governed projects enforce 12 hard rules. Two were added in v0.2.3 for agentic workflows:
|
|
159
|
+
|
|
160
|
+
- **H11** — Every loop or blocking wait in agent-written scripts must have a deadline, a fallback exit, and a diagnostic message on timeout. `specsmith validate` enforces this automatically.
|
|
161
|
+
- **H12** — On Windows, multi-step automation goes into a `.cmd` file, not inline shell invocations or `.ps1` files.
|
|
162
|
+
|
|
163
|
+
See [Governance Model](https://specsmith.readthedocs.io/en/stable/governance/) for the full rule set.
|
|
164
|
+
|
|
165
|
+
## Proactive Rate Limit Pacing
|
|
166
|
+
|
|
167
|
+
specsmith ships a rolling-window scheduler that paces AI provider requests before dispatch:
|
|
168
|
+
|
|
169
|
+
- Built-in RPM/TPM profiles for OpenAI, Anthropic, and Google models (including wildcard fallbacks)
|
|
170
|
+
- Pre-dispatch budget check: sleeps until the 60-second window refills instead of overshooting
|
|
171
|
+
- Parses OpenAI-style `"Please try again in 10.793s"` messages and obeys them
|
|
172
|
+
- Adaptive concurrency: halved after a 429, gradually restored after consecutive successes
|
|
173
|
+
- Local overrides always take precedence over built-in defaults
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
specsmith credits limits defaults # list built-in profiles
|
|
177
|
+
specsmith credits limits defaults --install # merge into project config
|
|
178
|
+
specsmith credits limits status --provider openai --model gpt-5.4
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
See [Rate Limit Pacing](https://specsmith.readthedocs.io/en/stable/rate-limits/) for full details.
|
|
182
|
+
|
|
150
183
|
## Documentation
|
|
151
184
|
|
|
152
|
-
**[specsmith.readthedocs.io](https://specsmith.readthedocs.io)** — Full user manual with tutorials, command reference, project type details, tool registry, governance model, troubleshooting.
|
|
185
|
+
**[specsmith.readthedocs.io](https://specsmith.readthedocs.io)** — Full user manual with tutorials, command reference, project type details, tool registry, governance model, rate-limit pacing, troubleshooting.
|
|
153
186
|
|
|
154
187
|
## Links
|
|
155
188
|
|
|
@@ -86,7 +86,7 @@ Each type gets: tool-aware CI (correct lint/test/security/build tools), domain-s
|
|
|
86
86
|
| `import` | Adopt an existing project (merge mode) |
|
|
87
87
|
| `audit` | Drift detection and health checks (`--fix` to auto-repair) |
|
|
88
88
|
| `architect` | Interactive architecture generation |
|
|
89
|
-
| `validate` | Governance
|
|
89
|
+
| `validate` | Governance consistency + H11 blocking-loop detection |
|
|
90
90
|
| `compress` | Archive old ledger entries |
|
|
91
91
|
| `upgrade` | Update governance to new spec version |
|
|
92
92
|
| `status` | CI/PR/alert status from VCS platform |
|
|
@@ -94,7 +94,13 @@ Each type gets: tool-aware CI (correct lint/test/security/build tools), domain-s
|
|
|
94
94
|
| `export` | Compliance report with REQ↔TEST coverage |
|
|
95
95
|
| `doctor` | Check if verification tools are installed |
|
|
96
96
|
| `self-update` | Update specsmith (channel-aware) |
|
|
97
|
-
| `credits` | AI credit tracking, analysis, and
|
|
97
|
+
| `credits` | AI credit tracking, analysis, budgets, and rate-limit pacing |
|
|
98
|
+
| `exec` / `ps` / `abort` | Tracked process execution with PID tracking and timeout |
|
|
99
|
+
| `commit` / `push` / `sync` | Governance-aware VCS operations |
|
|
100
|
+
| `branch` / `pr` | Strategy-aware branching and PR creation |
|
|
101
|
+
| `ledger` | Structured ledger add/list/stats |
|
|
102
|
+
| `req` | Requirements list/add/trace/gaps/orphans |
|
|
103
|
+
| `session-end` | End-of-session checklist |
|
|
98
104
|
|
|
99
105
|
## 7 Agent Integrations
|
|
100
106
|
|
|
@@ -104,9 +110,36 @@ AGENTS.md (cross-platform standard), Warp/Oz, Claude Code, GitHub Copilot, Curso
|
|
|
104
110
|
|
|
105
111
|
GitHub Actions, GitLab CI, Bitbucket Pipelines — all with tool-aware CI generated from the verification tool registry. Dependabot/Renovate configured per language ecosystem.
|
|
106
112
|
|
|
113
|
+
## Governance Rules (H1–H12)
|
|
114
|
+
|
|
115
|
+
specsmith-governed projects enforce 12 hard rules. Two were added in v0.2.3 for agentic workflows:
|
|
116
|
+
|
|
117
|
+
- **H11** — Every loop or blocking wait in agent-written scripts must have a deadline, a fallback exit, and a diagnostic message on timeout. `specsmith validate` enforces this automatically.
|
|
118
|
+
- **H12** — On Windows, multi-step automation goes into a `.cmd` file, not inline shell invocations or `.ps1` files.
|
|
119
|
+
|
|
120
|
+
See [Governance Model](https://specsmith.readthedocs.io/en/stable/governance/) for the full rule set.
|
|
121
|
+
|
|
122
|
+
## Proactive Rate Limit Pacing
|
|
123
|
+
|
|
124
|
+
specsmith ships a rolling-window scheduler that paces AI provider requests before dispatch:
|
|
125
|
+
|
|
126
|
+
- Built-in RPM/TPM profiles for OpenAI, Anthropic, and Google models (including wildcard fallbacks)
|
|
127
|
+
- Pre-dispatch budget check: sleeps until the 60-second window refills instead of overshooting
|
|
128
|
+
- Parses OpenAI-style `"Please try again in 10.793s"` messages and obeys them
|
|
129
|
+
- Adaptive concurrency: halved after a 429, gradually restored after consecutive successes
|
|
130
|
+
- Local overrides always take precedence over built-in defaults
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
specsmith credits limits defaults # list built-in profiles
|
|
134
|
+
specsmith credits limits defaults --install # merge into project config
|
|
135
|
+
specsmith credits limits status --provider openai --model gpt-5.4
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See [Rate Limit Pacing](https://specsmith.readthedocs.io/en/stable/rate-limits/) for full details.
|
|
139
|
+
|
|
107
140
|
## Documentation
|
|
108
141
|
|
|
109
|
-
**[specsmith.readthedocs.io](https://specsmith.readthedocs.io)** — Full user manual with tutorials, command reference, project type details, tool registry, governance model, troubleshooting.
|
|
142
|
+
**[specsmith.readthedocs.io](https://specsmith.readthedocs.io)** — Full user manual with tutorials, command reference, project type details, tool registry, governance model, rate-limit pacing, troubleshooting.
|
|
110
143
|
|
|
111
144
|
## Links
|
|
112
145
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specsmith"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3.dev1"
|
|
8
8
|
description = "Forge governed project scaffolds from the Agentic AI Development Workflow Specification."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -1501,6 +1501,168 @@ def credits_budget(
|
|
|
1501
1501
|
console.print(f" Enabled: {budget.enabled}")
|
|
1502
1502
|
|
|
1503
1503
|
|
|
1504
|
+
@credits.group(name="limits")
|
|
1505
|
+
def credits_limits() -> None:
|
|
1506
|
+
"""Manage persisted per-model RPM/TPM profiles."""
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
@credits_limits.command(name="list")
|
|
1510
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
1511
|
+
def credits_limits_list(project_dir: str) -> None:
|
|
1512
|
+
"""List configured local model rate-limit profiles."""
|
|
1513
|
+
from specsmith.rate_limits import load_rate_limit_profiles
|
|
1514
|
+
|
|
1515
|
+
root = Path(project_dir).resolve()
|
|
1516
|
+
profiles = sorted(
|
|
1517
|
+
load_rate_limit_profiles(root),
|
|
1518
|
+
key=lambda profile: (profile.provider, profile.model),
|
|
1519
|
+
)
|
|
1520
|
+
if not profiles:
|
|
1521
|
+
console.print("[yellow]No model rate-limit profiles configured.[/yellow]")
|
|
1522
|
+
return
|
|
1523
|
+
|
|
1524
|
+
for profile in profiles:
|
|
1525
|
+
console.print(
|
|
1526
|
+
" "
|
|
1527
|
+
f"{profile.provider}/{profile.model} "
|
|
1528
|
+
f"RPM={profile.rpm_limit} TPM={profile.tpm_limit} "
|
|
1529
|
+
f"target={profile.utilization_target:.2f} "
|
|
1530
|
+
f"concurrency={profile.concurrency_cap}"
|
|
1531
|
+
)
|
|
1532
|
+
|
|
1533
|
+
|
|
1534
|
+
@credits_limits.command(name="set")
|
|
1535
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
1536
|
+
@click.option("--provider", required=True, help="Provider key, such as openai or anthropic.")
|
|
1537
|
+
@click.option("--model", required=True, help="Model key, such as gpt-5.4.")
|
|
1538
|
+
@click.option("--rpm", "rpm_limit", type=int, required=True, help="Requests per minute limit.")
|
|
1539
|
+
@click.option("--tpm", "tpm_limit", type=int, required=True, help="Tokens per minute limit.")
|
|
1540
|
+
@click.option("--target", "utilization_target", type=float, default=0.7, show_default=True)
|
|
1541
|
+
@click.option("--concurrency", "concurrency_cap", type=int, default=1, show_default=True)
|
|
1542
|
+
def credits_limits_set(
|
|
1543
|
+
project_dir: str,
|
|
1544
|
+
provider: str,
|
|
1545
|
+
model: str,
|
|
1546
|
+
rpm_limit: int,
|
|
1547
|
+
tpm_limit: int,
|
|
1548
|
+
utilization_target: float,
|
|
1549
|
+
concurrency_cap: int,
|
|
1550
|
+
) -> None:
|
|
1551
|
+
"""Create or replace a local model rate-limit profile."""
|
|
1552
|
+
from specsmith.rate_limits import (
|
|
1553
|
+
ModelRateLimitProfile,
|
|
1554
|
+
load_rate_limit_profiles,
|
|
1555
|
+
save_rate_limit_profiles,
|
|
1556
|
+
)
|
|
1557
|
+
|
|
1558
|
+
root = Path(project_dir).resolve()
|
|
1559
|
+
updated_profile = ModelRateLimitProfile(
|
|
1560
|
+
provider=provider,
|
|
1561
|
+
model=model,
|
|
1562
|
+
rpm_limit=rpm_limit,
|
|
1563
|
+
tpm_limit=tpm_limit,
|
|
1564
|
+
utilization_target=utilization_target,
|
|
1565
|
+
concurrency_cap=concurrency_cap,
|
|
1566
|
+
source="override",
|
|
1567
|
+
)
|
|
1568
|
+
profiles = {profile.key: profile for profile in load_rate_limit_profiles(root)}
|
|
1569
|
+
profiles[updated_profile.key] = updated_profile
|
|
1570
|
+
save_rate_limit_profiles(root, list(profiles.values()))
|
|
1571
|
+
console.print(
|
|
1572
|
+
"[green]✓[/green] "
|
|
1573
|
+
f"Saved {updated_profile.provider}/{updated_profile.model} "
|
|
1574
|
+
f"(RPM={updated_profile.rpm_limit}, TPM={updated_profile.tpm_limit}, "
|
|
1575
|
+
f"target={updated_profile.utilization_target:.2f}, "
|
|
1576
|
+
f"concurrency={updated_profile.concurrency_cap})"
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
|
|
1580
|
+
@credits_limits.command(name="status")
|
|
1581
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
1582
|
+
@click.option("--provider", required=True, help="Provider key, such as openai or anthropic.")
|
|
1583
|
+
@click.option("--model", required=True, help="Model key, such as gpt-5.4.")
|
|
1584
|
+
def credits_limits_status(project_dir: str, provider: str, model: str) -> None:
|
|
1585
|
+
"""Show rolling-window snapshot for a model (RPM, TPM, concurrency, moving averages)."""
|
|
1586
|
+
from specsmith.rate_limits import (
|
|
1587
|
+
BUILTIN_PROFILES,
|
|
1588
|
+
load_rate_limit_profiles,
|
|
1589
|
+
load_rate_limit_scheduler,
|
|
1590
|
+
)
|
|
1591
|
+
|
|
1592
|
+
root = Path(project_dir).resolve()
|
|
1593
|
+
profiles = load_rate_limit_profiles(root, defaults=BUILTIN_PROFILES)
|
|
1594
|
+
scheduler = load_rate_limit_scheduler(root, profiles)
|
|
1595
|
+
|
|
1596
|
+
try:
|
|
1597
|
+
snap = scheduler.snapshot(provider, model)
|
|
1598
|
+
except KeyError:
|
|
1599
|
+
console.print(
|
|
1600
|
+
f"[red]No profile found for {provider}/{model}.[/red] "
|
|
1601
|
+
"Use 'specsmith credits limits set' to configure one."
|
|
1602
|
+
)
|
|
1603
|
+
raise SystemExit(1) from None
|
|
1604
|
+
|
|
1605
|
+
console.print(f"[bold]{snap.provider}/{snap.model}[/bold]")
|
|
1606
|
+
console.print(
|
|
1607
|
+
f" RPM: {snap.rolling_request_count} / {snap.effective_rpm_limit} "
|
|
1608
|
+
f"(limit {snap.rpm_limit}, target {snap.effective_rpm_limit})"
|
|
1609
|
+
)
|
|
1610
|
+
console.print(
|
|
1611
|
+
f" TPM: {snap.rolling_token_count:,} / {snap.effective_tpm_limit:,} "
|
|
1612
|
+
f"(limit {snap.tpm_limit:,})"
|
|
1613
|
+
)
|
|
1614
|
+
console.print(
|
|
1615
|
+
f" Utilization: RPM {snap.request_utilization:.1%} TPM {snap.token_utilization:.1%}"
|
|
1616
|
+
)
|
|
1617
|
+
console.print(
|
|
1618
|
+
f" Concurrency: {snap.in_flight} in-flight / {snap.current_concurrency_cap} cap "
|
|
1619
|
+
f"(base {snap.base_concurrency_cap})"
|
|
1620
|
+
)
|
|
1621
|
+
console.print(
|
|
1622
|
+
f" Moving avg: {snap.moving_average_requests:.1f} req/window "
|
|
1623
|
+
f"{snap.moving_average_tokens:,.0f} tok/window"
|
|
1624
|
+
)
|
|
1625
|
+
|
|
1626
|
+
|
|
1627
|
+
@credits_limits.command(name="defaults")
|
|
1628
|
+
@click.option("--project-dir", type=click.Path(exists=True), default=".")
|
|
1629
|
+
@click.option(
|
|
1630
|
+
"--install",
|
|
1631
|
+
is_flag=True,
|
|
1632
|
+
default=False,
|
|
1633
|
+
help="Merge built-in defaults into the local project config (existing overrides preserved).",
|
|
1634
|
+
)
|
|
1635
|
+
def credits_limits_defaults(project_dir: str, install: bool) -> None:
|
|
1636
|
+
"""List (or install) built-in RPM/TPM profiles for common provider/model paths."""
|
|
1637
|
+
from specsmith.rate_limits import (
|
|
1638
|
+
BUILTIN_PROFILES,
|
|
1639
|
+
load_rate_limit_profiles,
|
|
1640
|
+
save_rate_limit_profiles,
|
|
1641
|
+
)
|
|
1642
|
+
|
|
1643
|
+
console.print("[bold]Built-in model rate-limit profiles[/bold]")
|
|
1644
|
+
console.print("[dim](conservative defaults — local overrides take precedence)[/dim]\n")
|
|
1645
|
+
for profile in BUILTIN_PROFILES:
|
|
1646
|
+
console.print(
|
|
1647
|
+
f" {profile.provider}/{profile.model:25s} "
|
|
1648
|
+
f"RPM={profile.rpm_limit:<6} TPM={profile.tpm_limit:>12,} "
|
|
1649
|
+
f"target={profile.utilization_target:.2f}"
|
|
1650
|
+
)
|
|
1651
|
+
|
|
1652
|
+
if install:
|
|
1653
|
+
root = Path(project_dir).resolve()
|
|
1654
|
+
# Load existing local profiles; they take precedence over built-ins
|
|
1655
|
+
existing = {p.key: p for p in load_rate_limit_profiles(root)}
|
|
1656
|
+
merged = {p.key: p for p in BUILTIN_PROFILES}
|
|
1657
|
+
merged.update(existing) # local overrides win
|
|
1658
|
+
save_rate_limit_profiles(root, list(merged.values()))
|
|
1659
|
+
added = len(merged) - len(existing)
|
|
1660
|
+
console.print(
|
|
1661
|
+
f"\n[green]\u2713[/green] Installed {added} new default(s) to "
|
|
1662
|
+
".specsmith/model-rate-limits.json (existing profiles preserved)."
|
|
1663
|
+
)
|
|
1664
|
+
|
|
1665
|
+
|
|
1504
1666
|
main.add_command(credits)
|
|
1505
1667
|
|
|
1506
1668
|
|