forecost 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.
- forecost-0.1.0/.forecost.toml.example +5 -0
- forecost-0.1.0/.github/FUNDING.yml +1 -0
- forecost-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +46 -0
- forecost-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- forecost-0.1.0/.github/workflows/ci.yml +52 -0
- forecost-0.1.0/.github/workflows/release.yml +51 -0
- forecost-0.1.0/.gitignore +55 -0
- forecost-0.1.0/CHANGELOG.md +33 -0
- forecost-0.1.0/CLAUDE.md +46 -0
- forecost-0.1.0/CONTRIBUTING.md +39 -0
- forecost-0.1.0/LICENSE +21 -0
- forecost-0.1.0/PKG-INFO +249 -0
- forecost-0.1.0/README.md +205 -0
- forecost-0.1.0/demo.tape +24 -0
- forecost-0.1.0/forecost/__init__.py +68 -0
- forecost-0.1.0/forecost/cli.py +32 -0
- forecost-0.1.0/forecost/commands/__init__.py +0 -0
- forecost-0.1.0/forecost/commands/demo_cmd.py +117 -0
- forecost-0.1.0/forecost/commands/export_cmd.py +50 -0
- forecost-0.1.0/forecost/commands/forecast_cmd.py +273 -0
- forecost-0.1.0/forecost/commands/init_cmd.py +120 -0
- forecost-0.1.0/forecost/commands/optimize_cmd.py +107 -0
- forecost-0.1.0/forecost/commands/reset_cmd.py +47 -0
- forecost-0.1.0/forecost/commands/serve_cmd.py +114 -0
- forecost-0.1.0/forecost/commands/status_cmd.py +53 -0
- forecost-0.1.0/forecost/commands/track_cmd.py +61 -0
- forecost-0.1.0/forecost/commands/watch_cmd.py +86 -0
- forecost-0.1.0/forecost/db.py +339 -0
- forecost-0.1.0/forecost/forecaster.py +311 -0
- forecost-0.1.0/forecost/interceptor.py +231 -0
- forecost-0.1.0/forecost/pricing.py +166 -0
- forecost-0.1.0/forecost/py.typed +0 -0
- forecost-0.1.0/forecost/scope.py +231 -0
- forecost-0.1.0/forecost/tracker.py +226 -0
- forecost-0.1.0/forecost/tui.py +149 -0
- forecost-0.1.0/pyproject.toml +57 -0
- forecost-0.1.0/scripts/test_real_sdk.py +108 -0
- forecost-0.1.0/tests/__init__.py +0 -0
- forecost-0.1.0/tests/benchmarks/__init__.py +0 -0
- forecost-0.1.0/tests/benchmarks/test_forecast_accuracy.py +220 -0
- forecost-0.1.0/tests/conftest.py +10 -0
- forecost-0.1.0/tests/test_cli.py +36 -0
- forecost-0.1.0/tests/test_commands.py +218 -0
- forecost-0.1.0/tests/test_db.py +121 -0
- forecost-0.1.0/tests/test_forecaster.py +216 -0
- forecost-0.1.0/tests/test_interceptor.py +39 -0
- forecost-0.1.0/tests/test_pricing.py +47 -0
- forecost-0.1.0/tests/test_production.py +217 -0
- forecost-0.1.0/tests/test_sdk_compat.py +177 -0
- forecost-0.1.0/tests/test_tracker.py +35 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: ArivunidhiA
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug in forecost
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: input
|
|
6
|
+
id: version
|
|
7
|
+
attributes:
|
|
8
|
+
label: forecost version
|
|
9
|
+
placeholder: "0.1.0"
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: input
|
|
13
|
+
id: python-version
|
|
14
|
+
attributes:
|
|
15
|
+
label: Python version
|
|
16
|
+
placeholder: "3.12"
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: input
|
|
20
|
+
id: os
|
|
21
|
+
attributes:
|
|
22
|
+
label: Operating System
|
|
23
|
+
placeholder: "macOS 15, Ubuntu 24.04, Windows 11"
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: textarea
|
|
27
|
+
id: description
|
|
28
|
+
attributes:
|
|
29
|
+
label: What happened?
|
|
30
|
+
description: Describe the bug clearly.
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: steps
|
|
35
|
+
attributes:
|
|
36
|
+
label: Steps to reproduce
|
|
37
|
+
description: How can we reproduce the issue?
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
- type: textarea
|
|
41
|
+
id: expected
|
|
42
|
+
attributes:
|
|
43
|
+
label: Expected behavior
|
|
44
|
+
description: What did you expect to happen?
|
|
45
|
+
validations:
|
|
46
|
+
required: true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature for forecost
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: Description
|
|
9
|
+
description: What feature would you like?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: use-case
|
|
14
|
+
attributes:
|
|
15
|
+
label: Use case
|
|
16
|
+
description: Why do you need this feature?
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: solution
|
|
21
|
+
attributes:
|
|
22
|
+
label: Proposed solution
|
|
23
|
+
description: How would you implement this?
|
|
24
|
+
validations:
|
|
25
|
+
required: false
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
cache: pip
|
|
21
|
+
- run: pip install -e ".[dev]"
|
|
22
|
+
- run: ruff check forecost/ tests/
|
|
23
|
+
- run: ruff format --check forecost/ tests/
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
runs-on: ${{ matrix.os }}
|
|
27
|
+
strategy:
|
|
28
|
+
fail-fast: false
|
|
29
|
+
matrix:
|
|
30
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
31
|
+
os: [ubuntu-latest, macos-latest]
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
cache: pip
|
|
38
|
+
- run: pip install -e ".[dev,forecast]"
|
|
39
|
+
- run: pytest tests/ -v --tb=short -x
|
|
40
|
+
|
|
41
|
+
smoke:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
needs: [lint, test]
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
- uses: actions/setup-python@v5
|
|
47
|
+
with:
|
|
48
|
+
python-version: "3.12"
|
|
49
|
+
- run: pip install .
|
|
50
|
+
- run: forecost --version
|
|
51
|
+
- run: forecost --help
|
|
52
|
+
- run: forecost demo
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
build:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
with:
|
|
12
|
+
fetch-depth: 0
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: pip install build
|
|
17
|
+
- run: python -m build
|
|
18
|
+
- uses: actions/upload-artifact@v4
|
|
19
|
+
with:
|
|
20
|
+
name: dist
|
|
21
|
+
path: dist/
|
|
22
|
+
|
|
23
|
+
test-install:
|
|
24
|
+
needs: build
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/download-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: dist
|
|
30
|
+
path: dist/
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- run: pip install dist/*.whl
|
|
35
|
+
- run: forecost --version
|
|
36
|
+
- run: forecost demo
|
|
37
|
+
|
|
38
|
+
publish:
|
|
39
|
+
needs: [build, test-install]
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/p/forecost
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg-info/
|
|
7
|
+
*.egg
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
venv/
|
|
14
|
+
.venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Environment variables
|
|
18
|
+
.env
|
|
19
|
+
*.env.local
|
|
20
|
+
|
|
21
|
+
# Databases
|
|
22
|
+
*.db
|
|
23
|
+
*.sqlite3
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
*.swp
|
|
29
|
+
*.swo
|
|
30
|
+
*~
|
|
31
|
+
|
|
32
|
+
# OS
|
|
33
|
+
.DS_Store
|
|
34
|
+
Thumbs.db
|
|
35
|
+
|
|
36
|
+
# Node.js / Frontend
|
|
37
|
+
node_modules/
|
|
38
|
+
.next/
|
|
39
|
+
out/
|
|
40
|
+
|
|
41
|
+
# Test
|
|
42
|
+
.pytest_cache/
|
|
43
|
+
htmlcov/
|
|
44
|
+
.coverage
|
|
45
|
+
coverage.xml
|
|
46
|
+
|
|
47
|
+
# Logs
|
|
48
|
+
*.log
|
|
49
|
+
|
|
50
|
+
# Cursor
|
|
51
|
+
.cursor/
|
|
52
|
+
|
|
53
|
+
# forecost
|
|
54
|
+
.forecost.toml
|
|
55
|
+
.forecost/
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] - 2026-03-12
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **Branding:** PyPI package, CLI, and Python import are **`forecost`**. Project config is **`.forecost.toml`**; local data lives under **`~/.forecost/`**. Disable tracking with **`FORECOST_DISABLED=1`** (replaces older env var names).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `forecost init` -- Initialize project with heuristic or LLM-powered scope analysis
|
|
14
|
+
- `forecost forecast` -- Adaptive exponential smoothing cost forecast with Rich output
|
|
15
|
+
- `forecost status` -- One-line project status
|
|
16
|
+
- `forecost track` -- View recent tracked LLM calls
|
|
17
|
+
- `forecost serve` -- Local HTTP API server
|
|
18
|
+
- `forecost demo` -- See forecost in action with sample data
|
|
19
|
+
- `forecost watch` -- Live cost dashboard in terminal
|
|
20
|
+
- `forecost optimize` -- Model optimization suggestions
|
|
21
|
+
- `forecost reset` -- Reset project baseline or full data
|
|
22
|
+
- `auto_track()` -- Zero-code-change cost tracking via httpx interception
|
|
23
|
+
- `log_call()` -- Manual cost logging
|
|
24
|
+
- `log_stream_usage()` -- Streaming response cost logging
|
|
25
|
+
- `@track_cost` decorator for function-level tracking
|
|
26
|
+
- `FORECOST_DISABLED` environment variable to disable tracking
|
|
27
|
+
- `forecost.disable()` function
|
|
28
|
+
- Support for 90+ models across OpenAI, Anthropic, Google, Mistral, DeepSeek, xAI, Meta, Cohere
|
|
29
|
+
- Self-correcting pricing via ratio-based forecasting
|
|
30
|
+
- Forecast stability metric (replaces misleading MAPE)
|
|
31
|
+
- Budget alerts and CI exit codes (`--exit-code`)
|
|
32
|
+
- Optional Textual TUI dashboard (`pip install forecost[tui]`)
|
|
33
|
+
- PEP 561 py.typed marker for type checking support
|
forecost-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# forecost Development Guide
|
|
2
|
+
|
|
3
|
+
## What This Project Is
|
|
4
|
+
forecost is a local-first Python CLI tool that forecasts LLM API costs.
|
|
5
|
+
It uses adaptive exponential smoothing (upgrading to a 3-model ensemble)
|
|
6
|
+
on daily spend data to predict total project cost.
|
|
7
|
+
|
|
8
|
+
## Architecture Rules
|
|
9
|
+
- All source code lives in forecost/ (flat layout, not src/)
|
|
10
|
+
- CLI commands are in forecost/commands/ — each is a thin wrapper calling core logic
|
|
11
|
+
- Core modules: db.py (SQLite), pricing.py (static prices), forecaster.py (ensemble),
|
|
12
|
+
interceptor.py (httpx patching), tracker.py (public API), scope.py (heuristic analyzer)
|
|
13
|
+
- Tests live in tests/ and tests/benchmarks/
|
|
14
|
+
- pyproject.toml uses hatchling build backend
|
|
15
|
+
|
|
16
|
+
## Iron Rules (Never Violate These)
|
|
17
|
+
1. The interceptor must NEVER break the host application's HTTP requests.
|
|
18
|
+
All tracking logic is wrapped in try/except. Real errors propagate, tracking errors are swallowed.
|
|
19
|
+
2. calculate_forecast(save=False) is the default. Only the explicit `forecast` command saves.
|
|
20
|
+
Status, serve, and JSON output do NOT save forecast rows.
|
|
21
|
+
3. No network calls for pricing data. All prices are hardcoded in FALLBACK_PRICING dict.
|
|
22
|
+
4. The WriteQueue worker thread has its OWN SQLite connection. Never share connections across threads.
|
|
23
|
+
5. .forecost.toml uses relative paths (path = "."). Never write absolute paths to config files.
|
|
24
|
+
|
|
25
|
+
## Testing Rules
|
|
26
|
+
- Run pytest tests/ -v after every change
|
|
27
|
+
- The forecaster is the most important module — test accuracy with synthetic data
|
|
28
|
+
- The interceptor is the most dangerous module — test that it never breaks httpx
|
|
29
|
+
- Mock all HTTP calls in tests. Never make real API calls.
|
|
30
|
+
- Benchmarks in tests/benchmarks/ can be slower but must pass
|
|
31
|
+
|
|
32
|
+
## What NOT To Do
|
|
33
|
+
- Don't add a web dashboard or frontend
|
|
34
|
+
- Don't add database migrations (schema-less metadata JSON column handles extensibility)
|
|
35
|
+
- Don't add authentication or multi-user support
|
|
36
|
+
- Don't add real-time pricing fetches from the internet
|
|
37
|
+
- Don't add heavy ML dependencies (statsmodels is the ceiling)
|
|
38
|
+
- Don't use except Exception: pass — always log errors to ~/.forecost/error.log
|
|
39
|
+
|
|
40
|
+
## Key Commands
|
|
41
|
+
pip install -e ".[dev,forecast]" # Install for development
|
|
42
|
+
pytest tests/ -v # Run all tests
|
|
43
|
+
pytest tests/benchmarks/ -v # Run accuracy benchmarks
|
|
44
|
+
ruff check forecost/ tests/ # Lint
|
|
45
|
+
forecost demo # See it working with sample data
|
|
46
|
+
python -m build # Build for PyPI
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributing to forecost
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing to forecost.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/ArivunidhiA/forecost.git
|
|
9
|
+
cd forecost
|
|
10
|
+
python -m venv .venv
|
|
11
|
+
source .venv/bin/activate
|
|
12
|
+
pip install -e ".[dev]"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Running Tests
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pytest tests/ -v
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Code Style
|
|
22
|
+
|
|
23
|
+
We use ruff for linting and formatting:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ruff check forecost/ tests/
|
|
27
|
+
ruff format forecost/ tests/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Pull Request Process
|
|
31
|
+
|
|
32
|
+
1. Fork the repo and create a feature branch
|
|
33
|
+
2. Make your changes with tests
|
|
34
|
+
3. Ensure `ruff check` and `pytest` pass
|
|
35
|
+
4. Submit a PR against `main`
|
|
36
|
+
|
|
37
|
+
## Reporting Issues
|
|
38
|
+
|
|
39
|
+
Use the GitHub issue templates for bugs and feature requests.
|
forecost-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arivunidhi A
|
|
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.
|
forecost-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forecost
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Know exactly what your AI project will cost. Local-first LLM cost forecasting that learns from your usage.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ArivunidhiA/forecost
|
|
6
|
+
Project-URL: Issues, https://github.com/ArivunidhiA/forecost/issues
|
|
7
|
+
Author: Forecost contributors
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: anthropic,cli,cost,forecast,llm,openai
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: click>=8.0
|
|
19
|
+
Requires-Dist: httpx>=0.25.0
|
|
20
|
+
Requires-Dist: rich>=13.0
|
|
21
|
+
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
22
|
+
Provides-Extra: all
|
|
23
|
+
Requires-Dist: litellm>=1.0; extra == 'all'
|
|
24
|
+
Requires-Dist: numpy>=1.24; extra == 'all'
|
|
25
|
+
Requires-Dist: plotext>=5.0; extra == 'all'
|
|
26
|
+
Requires-Dist: statsmodels>=0.14; extra == 'all'
|
|
27
|
+
Requires-Dist: textual>=0.50; extra == 'all'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
30
|
+
Requires-Dist: numpy>=1.24; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
34
|
+
Requires-Dist: statsmodels>=0.14; extra == 'dev'
|
|
35
|
+
Provides-Extra: forecast
|
|
36
|
+
Requires-Dist: numpy>=1.24; extra == 'forecast'
|
|
37
|
+
Requires-Dist: statsmodels>=0.14; extra == 'forecast'
|
|
38
|
+
Provides-Extra: llm
|
|
39
|
+
Requires-Dist: litellm>=1.0; extra == 'llm'
|
|
40
|
+
Provides-Extra: tui
|
|
41
|
+
Requires-Dist: plotext>=5.0; extra == 'tui'
|
|
42
|
+
Requires-Dist: textual>=0.50; extra == 'tui'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# forecost
|
|
46
|
+
|
|
47
|
+
**Know what your AI project will cost. Before you build it.**
|
|
48
|
+
|
|
49
|
+
[](https://pypi.org/project/forecost/)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](https://opensource.org/licenses/MIT)
|
|
52
|
+
|
|
53
|
+
Python 3.10+ required. forecost is in Alpha: APIs may change and some features are experimental.
|
|
54
|
+
|
|
55
|
+
See `forecost demo` for a live preview.
|
|
56
|
+
|
|
57
|
+
## The Problem
|
|
58
|
+
|
|
59
|
+
LLM API costs are unpredictable. You prototype with GPT-4, ship to production, and the first month's bill arrives as a surprise. Most teams have no way to forecast spend until it's too late. forecost fixes this by learning from your actual usage and giving you accurate cost projections before you scale.
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
Full walkthrough from install to forecast:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install forecost
|
|
67
|
+
cd your-project
|
|
68
|
+
forecost init
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Add to your app's entry point (before any LLM calls):
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import forecost
|
|
75
|
+
forecost.auto_track()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Call `auto_track()` early, before any httpx usage. If your app imports httpx before forecost, the interceptor may not attach correctly.
|
|
79
|
+
|
|
80
|
+
Run your app as usual. After building usage for a few days:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
forecost forecast
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## See It in Action
|
|
87
|
+
|
|
88
|
+
`forecost demo` runs a forecast with sample data and no setup. Use it to see the full output before tracking your own project.
|
|
89
|
+
|
|
90
|
+
## Auto-Tracking
|
|
91
|
+
|
|
92
|
+
Non-streaming calls are tracked automatically. No decorators, no manual logging.
|
|
93
|
+
|
|
94
|
+
**Streaming limitation:** forecost cannot intercept streaming responses automatically. You must call `log_stream_usage` after consuming the stream. Pass the accumulated response dict containing a `usage` key (and optionally `model` for identification):
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import forecost
|
|
98
|
+
forecost.auto_track()
|
|
99
|
+
|
|
100
|
+
# Example: OpenAI streaming
|
|
101
|
+
response = client.chat.completions.create(model="gpt-4", messages=[...], stream=True)
|
|
102
|
+
accumulated = {"usage": {"prompt_tokens": 0, "completion_tokens": 0}, "model": "gpt-4"}
|
|
103
|
+
for chunk in response:
|
|
104
|
+
if chunk.usage:
|
|
105
|
+
accumulated["usage"] = {"prompt_tokens": chunk.usage.prompt_tokens,
|
|
106
|
+
"completion_tokens": chunk.usage.completion_tokens}
|
|
107
|
+
if chunk.model:
|
|
108
|
+
accumulated["model"] = chunk.model
|
|
109
|
+
forecost.log_stream_usage(accumulated)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For Anthropic, use `input_tokens` and `output_tokens` instead of `prompt_tokens` and `completion_tokens`.
|
|
113
|
+
|
|
114
|
+
## Manual Tracking
|
|
115
|
+
|
|
116
|
+
For fine-grained control, use the `@track_cost` decorator or `log_call`:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import forecost
|
|
120
|
+
|
|
121
|
+
@forecost.track_cost(provider="openai")
|
|
122
|
+
def call_gpt(prompt: str):
|
|
123
|
+
return openai.chat.completions.create(model="gpt-4", messages=[{"role": "user", "content": prompt}])
|
|
124
|
+
|
|
125
|
+
# Or log calls manually
|
|
126
|
+
forecost.log_call(model="gpt-4", tokens_in=500, tokens_out=200, provider="openai")
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Commands
|
|
130
|
+
|
|
131
|
+
| Command | Description |
|
|
132
|
+
|---------|-------------|
|
|
133
|
+
| `forecost init` | Initialize project and create `.forecost.toml` config |
|
|
134
|
+
| `forecost init --budget X` | Set a budget cap in USD |
|
|
135
|
+
| `forecost forecast` | Show cost forecast in terminal |
|
|
136
|
+
| `forecost forecast --output markdown` | Output forecast as Markdown |
|
|
137
|
+
| `forecost forecast --output csv` | Output forecast as CSV |
|
|
138
|
+
| `forecost forecast --tui` | Interactive TUI dashboard (requires `pip install forecost[tui]`) |
|
|
139
|
+
| `forecost forecast --json` | JSON output for CI/scripts |
|
|
140
|
+
| `forecost forecast --brief` | One-line summary (same format as `status`) |
|
|
141
|
+
| `forecost forecast --exit-code` | Exit 1 if projected over budget, 2 if actual over budget (for CI) |
|
|
142
|
+
| `forecost status` | One-line summary: spend, projected total, day count, drift status |
|
|
143
|
+
| `forecost track` | View recent tracked LLM calls |
|
|
144
|
+
| `forecost watch` | Live cost dashboard; updates as your app makes calls |
|
|
145
|
+
| `forecost export --format csv` | Export usage data as CSV |
|
|
146
|
+
| `forecost export --format json` | Export usage data as JSON |
|
|
147
|
+
| `forecost demo` | Run forecast with sample data, no setup needed |
|
|
148
|
+
| `forecost optimize` | Suggest cost optimizations based on usage |
|
|
149
|
+
| `forecost reset` | Reset the current project (optionally keep usage logs) |
|
|
150
|
+
| `forecost serve` | Run local API server for programmatic access |
|
|
151
|
+
|
|
152
|
+
`status` and `forecast --brief` both show the same one-line summary. Use `status` when you only need a quick check; use `forecast --brief` when you want that format in a script or CI pipeline.
|
|
153
|
+
|
|
154
|
+
## Budget Enforcement
|
|
155
|
+
|
|
156
|
+
Set a budget at init with `--budget`:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
forecost init --budget 100
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Use `--exit-code` on forecast to fail CI when over budget:
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
- name: Check LLM Budget
|
|
166
|
+
run: |
|
|
167
|
+
pip install forecost
|
|
168
|
+
forecost forecast --exit-code
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Exit codes: 0 = on track, 1 = projected over budget, 2 = actual spend over budget.
|
|
172
|
+
|
|
173
|
+
## Disabling in Tests
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
FORECOST_DISABLED=1 pytest
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Or in code:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
forecost.disable()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Forecasting Accuracy
|
|
186
|
+
|
|
187
|
+
forecost uses an ensemble of three statistical forecasting methods (Simple Exponential Smoothing, Damped Trend, and Linear Regression) inspired by the M4 Forecasting Competition, where simple combinations beat complex ML models across 100,000 time series.
|
|
188
|
+
|
|
189
|
+
| Metric | What it means | Typical result |
|
|
190
|
+
|--------|---------------|----------------|
|
|
191
|
+
| MASE | Are we beating a naive guess? | < 1.0 after 5 days |
|
|
192
|
+
| MAE | How many dollars could we be off? | Decreases as data grows |
|
|
193
|
+
| 80% interval | Will the real cost land here? | ~80% of the time |
|
|
194
|
+
| 95% interval | Conservative budget range | ~95% of the time |
|
|
195
|
+
|
|
196
|
+
Install the ensemble engine for best results: `pip install forecost[forecast]`
|
|
197
|
+
|
|
198
|
+
The base install uses a simpler exponential moving average that works without additional dependencies.
|
|
199
|
+
|
|
200
|
+
## Why forecost?
|
|
201
|
+
|
|
202
|
+
| Feature | forecost | LiteLLM | Helicone | LangSmith |
|
|
203
|
+
|---------|--------|---------|----------|-----------|
|
|
204
|
+
| Cost tracking | Yes | Yes | Yes | Yes |
|
|
205
|
+
| Cost forecasting | Yes | No | No | No |
|
|
206
|
+
| Prediction intervals | Yes | No | No | No |
|
|
207
|
+
| Zero infrastructure | Yes | No (proxy) | No (cloud) | No (cloud) |
|
|
208
|
+
| Zero overhead on requests | Yes (post-response) | No (proxy latency) | No (proxy latency) | No (SDK wrapper) |
|
|
209
|
+
| Local-only / private | Yes | Partial | No | No |
|
|
210
|
+
| pip install, 2 lines | Yes | SDK wrapper | Proxy setup | SDK setup |
|
|
211
|
+
| Free forever | Yes | Freemium | Freemium | $39/seat/mo |
|
|
212
|
+
|
|
213
|
+
Minimal footprint: 3 runtime dependencies (click, rich, httpx), under 3MB.
|
|
214
|
+
|
|
215
|
+
## Data Storage
|
|
216
|
+
|
|
217
|
+
- **Usage and forecasts:** `~/.forecost/costs.db` (SQLite). All projects share this database.
|
|
218
|
+
- **Project config:** `.forecost.toml` in your project root. Contains project name, baseline days, and optional budget.
|
|
219
|
+
|
|
220
|
+
## Glossary
|
|
221
|
+
|
|
222
|
+
| Term | Meaning |
|
|
223
|
+
|------|---------|
|
|
224
|
+
| **Confidence levels** | How reliable the forecast is based on data volume: low (0 days), medium-low (1-3), medium (4-7), high (8-14), very-high (15+). More usage data yields higher confidence. |
|
|
225
|
+
| **Drift status** | Whether spend is trending above or below the baseline: `on_track`, `over_budget`, or `under_budget`. Based on recent daily burn ratios. |
|
|
226
|
+
| **MASE** | Mean Absolute Scaled Error. Compares forecast accuracy to a naive "yesterday = tomorrow" guess. MASE < 1.0 means the forecast beats the naive baseline. |
|
|
227
|
+
| **Stability** | How much the forecast changes between runs: `converged` (< 5% change), `stabilizing` (5-15%), or `adjusting` (> 15%). |
|
|
228
|
+
| **Prediction intervals** | 80% and 95% ranges around the projected total. The real cost will fall within the 80% interval about 80% of the time. |
|
|
229
|
+
|
|
230
|
+
## Local API Server
|
|
231
|
+
|
|
232
|
+
`forecost serve` starts a local HTTP server (default port 8787) for programmatic access:
|
|
233
|
+
|
|
234
|
+
| Endpoint | Description |
|
|
235
|
+
|----------|-------------|
|
|
236
|
+
| `GET /api/health` | Health check. Returns `{"status": "ok"}`. |
|
|
237
|
+
| `GET /api/forecast` | Full forecast result (same as `forecost forecast --json`). |
|
|
238
|
+
| `GET /api/status` | Project status: active days, actual spend, baseline info. |
|
|
239
|
+
| `GET /api/costs` | Recent usage logs. |
|
|
240
|
+
|
|
241
|
+
Run from your project directory so forecost can find `.forecost.toml`.
|
|
242
|
+
|
|
243
|
+
## Contributing
|
|
244
|
+
|
|
245
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT
|