usagebassoon 0.1.0a1__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.
- usagebassoon-0.1.0a1/.github/CODEOWNERS +4 -0
- usagebassoon-0.1.0a1/.github/dependabot.yaml +12 -0
- usagebassoon-0.1.0a1/.github/rulesets/apply-rulesets.sh +32 -0
- usagebassoon-0.1.0a1/.github/rulesets/protect-dev.json +44 -0
- usagebassoon-0.1.0a1/.github/rulesets/protect-main.json +44 -0
- usagebassoon-0.1.0a1/.github/rulesets/protect-release-tags.json +27 -0
- usagebassoon-0.1.0a1/.gitignore +218 -0
- usagebassoon-0.1.0a1/.pre-commit-config.yaml +36 -0
- usagebassoon-0.1.0a1/.python-version +1 -0
- usagebassoon-0.1.0a1/.vscode/extensions.json +19 -0
- usagebassoon-0.1.0a1/.vscode/settings.json +16 -0
- usagebassoon-0.1.0a1/AGENTS.md +73 -0
- usagebassoon-0.1.0a1/CLA.md +84 -0
- usagebassoon-0.1.0a1/LICENSE +661 -0
- usagebassoon-0.1.0a1/LICENSES/AGPL-3.0-only.txt +235 -0
- usagebassoon-0.1.0a1/PKG-INFO +28 -0
- usagebassoon-0.1.0a1/README.md +11 -0
- usagebassoon-0.1.0a1/REUSE.toml +10 -0
- usagebassoon-0.1.0a1/justfile +122 -0
- usagebassoon-0.1.0a1/pyproject.toml +76 -0
- usagebassoon-0.1.0a1/src/usagebassoon/__init__.py +6 -0
- usagebassoon-0.1.0a1/src/usagebassoon/backends/base.py +30 -0
- usagebassoon-0.1.0a1/src/usagebassoon/backends/duckdb_local.py +51 -0
- usagebassoon-0.1.0a1/src/usagebassoon/backends/motherduck.py +65 -0
- usagebassoon-0.1.0a1/src/usagebassoon/merge.py +461 -0
- usagebassoon-0.1.0a1/src/usagebassoon/parsers/graph.py +129 -0
- usagebassoon-0.1.0a1/src/usagebassoon/parsers/models.py +111 -0
- usagebassoon-0.1.0a1/src/usagebassoon/parsers/pricing.py +68 -0
- usagebassoon-0.1.0a1/src/usagebassoon/parsers/report.py +126 -0
- usagebassoon-0.1.0a1/src/usagebassoon/reconcile.py +234 -0
- usagebassoon-0.1.0a1/src/usagebassoon/sql/ddl.sql +162 -0
- usagebassoon-0.1.0a1/tests/__init__.py +2 -0
- usagebassoon-0.1.0a1/tests/conftest.py +154 -0
- usagebassoon-0.1.0a1/tests/fixtures/golden-2026-09-10.graph.json +711 -0
- usagebassoon-0.1.0a1/tests/fixtures/golden-2026-09-10.models.json +1860 -0
- usagebassoon-0.1.0a1/tests/fixtures/golden-2026-09-10.pricing.json +20 -0
- usagebassoon-0.1.0a1/tests/fixtures/golden-2026-09-10.report.json +1952 -0
- usagebassoon-0.1.0a1/tests/test_backends.py +75 -0
- usagebassoon-0.1.0a1/tests/test_merge.py +148 -0
- usagebassoon-0.1.0a1/tests/test_parsers.py +118 -0
- usagebassoon-0.1.0a1/tests/test_reconcile.py +77 -0
- usagebassoon-0.1.0a1/tokledger-proposal.md +535 -0
- usagebassoon-0.1.0a1/uv.lock +1417 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: 2026 Israel Flores-Arbolay
|
|
4
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
5
|
+
|
|
6
|
+
# apply-rulesets.sh — create or update repository rulesets from
|
|
7
|
+
# .github/rulesets/*.json. Idempotent: matches existing rulesets
|
|
8
|
+
# by name and updates them in place, creating only what's missing.
|
|
9
|
+
#
|
|
10
|
+
# Requires: gh (authenticated as a repo admin) and jq.
|
|
11
|
+
# Usage: ./apply-rulesets.sh
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
shopt -s nullglob
|
|
14
|
+
|
|
15
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
repo="$(gh repo view --json nameWithOwner --jq .nameWithOwner)"
|
|
17
|
+
[[ "$repo" == "israelflores8789/usagebassoon" ]] || { echo "unexpected repo: $repo" >&2; exit 1; }
|
|
18
|
+
|
|
19
|
+
for file in "$script_dir"/*.json; do
|
|
20
|
+
name="$(jq --raw-output .name "$file")"
|
|
21
|
+
id="$(gh api "repos/$repo/rulesets?per_page=100" --paginate \
|
|
22
|
+
| jq -r --arg name "$name" '[.[] | select(.name == $name) | .id] | .[0] // empty')"
|
|
23
|
+
|
|
24
|
+
body="$(jq 'del(.source_type)' "$file")" # drop if API 422s without this
|
|
25
|
+
if [[ -n "$id" ]]; then
|
|
26
|
+
printf '%s' "$body" | gh api --method PUT "repos/$repo/rulesets/$id" --input - >/dev/null
|
|
27
|
+
echo "updated ruleset: $name (id $id)"
|
|
28
|
+
else
|
|
29
|
+
printf '%s' "$body" | gh api --method POST "repos/$repo/rulesets" --input - >/dev/null
|
|
30
|
+
echo "created ruleset: $name"
|
|
31
|
+
fi
|
|
32
|
+
done
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "protect-dev",
|
|
3
|
+
"target": "branch",
|
|
4
|
+
"source_type": "Repository",
|
|
5
|
+
"enforcement": "active",
|
|
6
|
+
"conditions": {
|
|
7
|
+
"ref_name": {
|
|
8
|
+
"include": ["refs/heads/dev"],
|
|
9
|
+
"exclude": []
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"bypass_actors": [
|
|
13
|
+
{
|
|
14
|
+
"actor_id": 5,
|
|
15
|
+
"actor_type": "RepositoryRole",
|
|
16
|
+
"bypass_mode": "always"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"rules": [
|
|
20
|
+
{ "type": "deletion" },
|
|
21
|
+
{ "type": "non_fast_forward" },
|
|
22
|
+
{
|
|
23
|
+
"type": "pull_request",
|
|
24
|
+
"parameters": {
|
|
25
|
+
"required_approving_review_count": 0,
|
|
26
|
+
"dismiss_stale_reviews_on_push": false,
|
|
27
|
+
"require_code_owner_review": true,
|
|
28
|
+
"require_last_push_approval": false,
|
|
29
|
+
"required_review_thread_resolution": false,
|
|
30
|
+
"allowed_merge_methods": ["merge", "squash", "rebase"]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "required_status_checks",
|
|
35
|
+
"parameters": {
|
|
36
|
+
"do_not_enforce_on_create": true,
|
|
37
|
+
"required_status_checks": [
|
|
38
|
+
{ "context": "license/cla" }
|
|
39
|
+
],
|
|
40
|
+
"strict_required_status_checks_policy": false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "protect-main",
|
|
3
|
+
"target": "branch",
|
|
4
|
+
"source_type": "Repository",
|
|
5
|
+
"enforcement": "active",
|
|
6
|
+
"conditions": {
|
|
7
|
+
"ref_name": {
|
|
8
|
+
"include": ["~DEFAULT_BRANCH"],
|
|
9
|
+
"exclude": []
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"bypass_actors": [
|
|
13
|
+
{
|
|
14
|
+
"actor_id": 5,
|
|
15
|
+
"actor_type": "RepositoryRole",
|
|
16
|
+
"bypass_mode": "always"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"rules": [
|
|
20
|
+
{ "type": "deletion" },
|
|
21
|
+
{ "type": "non_fast_forward" },
|
|
22
|
+
{
|
|
23
|
+
"type": "pull_request",
|
|
24
|
+
"parameters": {
|
|
25
|
+
"required_approving_review_count": 0,
|
|
26
|
+
"dismiss_stale_reviews_on_push": false,
|
|
27
|
+
"require_code_owner_review": true,
|
|
28
|
+
"require_last_push_approval": false,
|
|
29
|
+
"required_review_thread_resolution": false,
|
|
30
|
+
"allowed_merge_methods": ["merge", "squash", "rebase"]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "required_status_checks",
|
|
35
|
+
"parameters": {
|
|
36
|
+
"do_not_enforce_on_create": true,
|
|
37
|
+
"required_status_checks": [
|
|
38
|
+
{ "context": "license/cla" }
|
|
39
|
+
],
|
|
40
|
+
"strict_required_status_checks_policy": false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "protect-release-tags",
|
|
3
|
+
"target": "tag",
|
|
4
|
+
"source_type": "Repository",
|
|
5
|
+
"enforcement": "active",
|
|
6
|
+
"conditions": {
|
|
7
|
+
"ref_name": {
|
|
8
|
+
"include": ["refs/tags/v*"],
|
|
9
|
+
"exclude": []
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"bypass_actors": [
|
|
13
|
+
{
|
|
14
|
+
"actor_id": 5,
|
|
15
|
+
"actor_type": "RepositoryRole",
|
|
16
|
+
"bypass_mode": "always"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"rules": [
|
|
20
|
+
{
|
|
21
|
+
"type": "update"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "deletion"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Israel Flores-Arbolay
|
|
2
|
+
# SPDX-License-Identifier: AGPL-3.0-only
|
|
3
|
+
|
|
4
|
+
exclude: '^tests/fixtures/.*\.json$'
|
|
5
|
+
|
|
6
|
+
repos:
|
|
7
|
+
- repo: https://github.com/crate-ci/typos
|
|
8
|
+
rev: v1.50.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: typos
|
|
11
|
+
|
|
12
|
+
- repo: https://github.com/google/yamlfmt
|
|
13
|
+
rev: v0.21.0
|
|
14
|
+
hooks:
|
|
15
|
+
- id: yamlfmt
|
|
16
|
+
args:
|
|
17
|
+
- -formatter
|
|
18
|
+
- indent=2,indentless_arrays=true,retain_line_breaks=true
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
21
|
+
rev: v6.0.0
|
|
22
|
+
hooks:
|
|
23
|
+
- id: trailing-whitespace
|
|
24
|
+
- id: end-of-file-fixer
|
|
25
|
+
|
|
26
|
+
- repo: https://github.com/rhysd/actionlint
|
|
27
|
+
rev: v1.7.12
|
|
28
|
+
hooks:
|
|
29
|
+
- id: actionlint
|
|
30
|
+
|
|
31
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
32
|
+
rev: v6.0.0
|
|
33
|
+
hooks:
|
|
34
|
+
- id: check-yaml
|
|
35
|
+
args: [--unsafe, --allow-multiple-documents]
|
|
36
|
+
- id: check-added-large-files
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recommendations": [
|
|
3
|
+
"ms-python.python",
|
|
4
|
+
"ms-python.debugpy",
|
|
5
|
+
"meta.pyrefly",
|
|
6
|
+
"charliermarsh.ruff",
|
|
7
|
+
"nefrob.vscode-just-syntax",
|
|
8
|
+
"tamasfe.even-better-toml",
|
|
9
|
+
"tekumara.typos-vscode"
|
|
10
|
+
],
|
|
11
|
+
"unwantedRecommendations": [
|
|
12
|
+
"ms-python.vscode-pylance",
|
|
13
|
+
"ms-python.pyright",
|
|
14
|
+
"detachhead.basedpyright",
|
|
15
|
+
"ms-python.flake8",
|
|
16
|
+
"ms-python.black-formatter",
|
|
17
|
+
"dbaeumer.vscode-eslint"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"python.languageServer": "None",
|
|
3
|
+
"[python]": {
|
|
4
|
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
|
5
|
+
"editor.formatOnSave": true,
|
|
6
|
+
"editor.codeActionsOnSave": {
|
|
7
|
+
"source.organizeImports.ruff": "always",
|
|
8
|
+
"source.fixAll.ruff": "always"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"ruff.enable": true,
|
|
12
|
+
"ruff.organizeImports": true,
|
|
13
|
+
"evenBetterToml.schema.associations": {
|
|
14
|
+
"pyproject.toml": "https://json.schemastore.org/pyproject.json"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Israel Flores-Arbolay
|
|
3
|
+
SPDX-License-Identifier: AGPL-3.0-only
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# UsageBassoon — Agent Context
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
A Python CLI and library (pipx-installable, import usagebassoon) that persists `tokscale` JSON token usage statistics to a persistent data store into DuckDB or MotherDuck. This allows users to aggregate token usage across agentic environments.
|
|
11
|
+
|
|
12
|
+
## Repository Structure
|
|
13
|
+
```
|
|
14
|
+
usagebassoon/
|
|
15
|
+
├── pyproject.toml # hatchling; pipx-installable; Python >= 3.11
|
|
16
|
+
├── src/usagebassoon/
|
|
17
|
+
│ ├── cli.py # typer app
|
|
18
|
+
│ ├── collector.py # tokscale subprocess + retry
|
|
19
|
+
│ ├── parsers/ # one module per payload kind
|
|
20
|
+
│ │ ├── models.py # per session×model rows → session_model_stats
|
|
21
|
+
│ │ ├── report.py # session metadata → sessions (no LLM summary fields)
|
|
22
|
+
│ │ ├── graph.py # daily contributions → daily_stats/daily_activity/run_metrics
|
|
23
|
+
│ │ └── pricing.py # rates + resolution → pricing_snapshots + row stamps
|
|
24
|
+
│ ├── backends/
|
|
25
|
+
│ │ ├── base.py # StorageBackend protocol
|
|
26
|
+
│ │ ├── duckdb_local.py
|
|
27
|
+
│ │ └── motherduck.py
|
|
28
|
+
│ ├── merge.py # staging + transactional merge + rebuild + session_label
|
|
29
|
+
│ ├── reconcile.py # cross-payload consistency checks
|
|
30
|
+
│ ├── curation.py # tags + notes
|
|
31
|
+
│ ├── sanitize.py # export-time pseudonymization (--sanitize)
|
|
32
|
+
│ ├── report_term.py # rich tables + plotext charts (+ --save/--sanitize)
|
|
33
|
+
│ ├── api.py # usagebassoon.query/connect (pandas default, polars opt-in)
|
|
34
|
+
│ └── sql/ # ddl + views, loaded as package data
|
|
35
|
+
├── tests/
|
|
36
|
+
│ ├── fixtures/ # sanitized golden captures: models, report, graph, pricing
|
|
37
|
+
│ └── test_*.py # incl. fixture-derived invariants
|
|
38
|
+
└── .github/workflows/ # ci (ruff, pyrefly, pytest), release to PyPI
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Architecture
|
|
42
|
+
|
|
43
|
+
- **Dependencies**: `duckdb`, `pandas`, `pydantic`, `typer`, `rich`, `plotext`, `polars` (optional).
|
|
44
|
+
- **Dev Environment**: `uv`, `hatchling`, `twine`, `pyrefly`, `ruff`, `pytest`, `just`, `pre-commit`.
|
|
45
|
+
|
|
46
|
+
## Commands
|
|
47
|
+
|
|
48
|
+
Run all project tasks via `just` from the repository root. Use `just --list` to inspect available recipes.
|
|
49
|
+
|
|
50
|
+
- USE `just install-dev` to synchronize all uv dependency groups.
|
|
51
|
+
- USE `just test` to run the test suite; pass pytest arguments with `just test <args>`.
|
|
52
|
+
- USE `just coverage` to run test suite with terminal coverage reporting.
|
|
53
|
+
- USE `just lint` to perform Ruff linting and formatting checks.
|
|
54
|
+
- USE `just typecheck` to perform Pyrefly type checks.
|
|
55
|
+
- USE `just spell-diff` to run typos spelling checker.
|
|
56
|
+
- PREFER `just ci` for combined test, lint, and typecheck recipes.
|
|
57
|
+
- USE `just build` to clean `dist/` and build an sdist and wheel with Hatchling.
|
|
58
|
+
- USE `just check-dist` to clean, build, and validate artifacts with Twine.
|
|
59
|
+
- USE `just clean` to remove build artifacts and local caches.
|
|
60
|
+
|
|
61
|
+
## Rules
|
|
62
|
+
|
|
63
|
+
- USE Google-style docstrings for **all** source code.
|
|
64
|
+
- KEEP comments concise yet clear. Do NOT use numbered headers (e.g. "1." or "(1)" etc).
|
|
65
|
+
- NO version string is ever hard-coded in source; `hatch-vcs` manages version numbering from git tags (`v0.1.0` → `0.1.0`).
|
|
66
|
+
|
|
67
|
+
### Prohibitions
|
|
68
|
+
The following actions are **prohibited** and are reserved exclusively for the user. When encountering a task that involves a prohibited action, you MUST **stop** and **report** to the user the conflict:
|
|
69
|
+
|
|
70
|
+
- NEVER modify the golden JSON fixtures in `tests/fixtures/`.
|
|
71
|
+
- NEVER attempt to publish to PyPI or invoke any publishing command; consequently, NEVER use `just publish` or `just release`.
|
|
72
|
+
- NEVER attempt to perform a release to GitHub or invoke any release command.
|
|
73
|
+
- NEVER attempt to push git changes to GitHub.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Contributor License Agreement
|
|
2
|
+
|
|
3
|
+
**UsageBassoon Contributor License Agreement v1.0**
|
|
4
|
+
|
|
5
|
+
*Based on the Project Harmony Individual Contributor License Agreement (HA-CLA-I v1.0), with modifications for dual licensing.*
|
|
6
|
+
|
|
7
|
+
Thank you for your interest in contributing to UsageBassoon (the "Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, the Project maintainer, Israel Flores-Arbolay ("We" or "Us"), must have a Contributor License Agreement ("Agreement") on file that has been accepted by each Contributor, indicating agreement to the license terms below. This license is for Your protection as a Contributor as well as the protection of Us and the users of the Project; it does not change Your rights to use Your own Contributions for any other purpose.
|
|
8
|
+
|
|
9
|
+
By clicking the acceptance checkbox, signing electronically via the CLA Assistant tool, or otherwise submitting a Contribution with an indication of Your acceptance of this Agreement, You accept and agree to be bound by the following terms and conditions for Your present and future Contributions submitted to the Project. Except for the license granted herein to Us and recipients of software distributed by Us, You reserve all right, title, and interest in and to Your Contributions.
|
|
10
|
+
|
|
11
|
+
## 1. Definitions
|
|
12
|
+
|
|
13
|
+
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is entering into this Agreement with Us. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
|
14
|
+
|
|
15
|
+
"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to Us for inclusion in, or documentation of, the Project. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Us or Our representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us for the purpose of discussing and improving the Project, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
|
16
|
+
|
|
17
|
+
## 2. Grant of Rights
|
|
18
|
+
|
|
19
|
+
### 2.1 Copyright License
|
|
20
|
+
|
|
21
|
+
Subject to the terms of this Agreement, You hereby grant to Us and to recipients of software distributed by Us a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
|
|
22
|
+
|
|
23
|
+
### 2.2 Patent License
|
|
24
|
+
|
|
25
|
+
Subject to the terms of this Agreement, You hereby grant to Us and to recipients of software distributed by Us a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer Your Contributions, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Project to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that Your Contribution, or the Project to which You have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or that work shall terminate as of the date such litigation is filed.
|
|
26
|
+
|
|
27
|
+
### 2.3 Outbound License
|
|
28
|
+
|
|
29
|
+
As a condition on the grant of rights in Sections 2.1 and 2.2, We agree to license Your Contribution only under the terms of the license or licenses which We are using on the Submission Date for the Project (the "Current License"). As of the date of this Agreement, the Current License is the GNU Affero General Public License v3.0 only (AGPL-3.0-only).
|
|
30
|
+
|
|
31
|
+
**In addition**, You grant to Us the right to license Your Contributions, and derivative works thereof, under **any other license terms of Our choosing, including proprietary and commercial license terms**, at Our sole discretion. This additional grant exists to enable the Project's dual-licensing model: the Project is offered to the community under the Current License and may simultaneously be offered to commercial licensees under separate terms, revenue from which may support continued open-source development of the Project.
|
|
32
|
+
|
|
33
|
+
### 2.4 Moral Rights
|
|
34
|
+
|
|
35
|
+
To the fullest extent permitted under applicable law, You waive, and agree not to assert, any moral rights You may have in Your Contributions against Us and recipients of software distributed by Us. If moral rights cannot be waived under applicable law, You agree not to enforce them against such parties to the fullest extent permitted.
|
|
36
|
+
|
|
37
|
+
### 2.5 Your Rights
|
|
38
|
+
|
|
39
|
+
You retain ownership of the copyright in Your Contribution and have the same rights to use or license the Contribution which You would have had without entering into this Agreement.
|
|
40
|
+
|
|
41
|
+
## 3. Representations
|
|
42
|
+
|
|
43
|
+
You represent that:
|
|
44
|
+
|
|
45
|
+
(a) each of Your Contributions is Your original work of authorship, or You have secured all rights necessary to submit it under this Agreement;
|
|
46
|
+
|
|
47
|
+
(b) You are legally entitled to grant the license set forth in Section 2;
|
|
48
|
+
|
|
49
|
+
(c) if Your employer(s) or other third party has rights to intellectual property that You create that includes Your Contributions, You represent that You have received permission to make Contributions on behalf of that employer or third party, that Your employer or such third party has waived such rights for Your Contributions to the Project, or that Your employer or such third party has executed a separate entity contributor license agreement with Us;
|
|
50
|
+
|
|
51
|
+
(d) Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which You are personally aware and which are associated with any part of Your Contributions; and
|
|
52
|
+
|
|
53
|
+
(e) You have disclosed all Third-Party Materials. "Third-Party Materials" means any material that is not Your original work of authorship contained within Your Contribution, along with the licenses and any restrictions applicable to such materials.
|
|
54
|
+
|
|
55
|
+
## 4. Disclaimer
|
|
56
|
+
|
|
57
|
+
EXCEPT FOR THE EXPRESS REPRESENTATIONS IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS IS" TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. ALL OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, ARE DISCLAIMED BY BOTH YOU AND US.
|
|
58
|
+
|
|
59
|
+
## 5. No Obligation
|
|
60
|
+
|
|
61
|
+
You acknowledge that We are under no obligation to use, include, or continue to distribute Your Contribution at all, and that the decision to do so is made at Our sole discretion.
|
|
62
|
+
|
|
63
|
+
## 6. Consequential Damage Waiver
|
|
64
|
+
|
|
65
|
+
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU OR WE BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT, REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT, OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
|
|
66
|
+
|
|
67
|
+
## 7. Term and Termination
|
|
68
|
+
|
|
69
|
+
This Agreement comes into effect when You accept its terms, and remains in effect unless and until terminated in writing by either party. Termination of this Agreement does not affect the rights previously granted under Section 2 for Contributions submitted prior to termination; those licenses are perpetual and irrevocable.
|
|
70
|
+
|
|
71
|
+
## 8. Miscellaneous
|
|
72
|
+
|
|
73
|
+
This Agreement is the entire agreement between You and Us with respect to its subject matter and supersedes all prior or contemporaneous agreements, understandings, and communications, whether written or oral, between You and Us with respect to such subject matter. This Agreement is governed by the laws of the State of Delaware, excluding its conflict-of-laws provisions. If any provision of this Agreement is found void or unenforceable, such provision will be reformed to the extent necessary to make it enforceable, and the remainder of this Agreement will remain in full force and effect. Any notices under this Agreement should be directed to israelflores8789@gmail.com.
|
|
74
|
+
|
|
75
|
+
We may assign or transfer this Agreement, and all rights granted hereunder, to any successor in interest, including without limitation any corporate entity We form or control, by merger, sale of assets, operation of law, or otherwise, without Your consent, provided the assignee agrees in writing to be bound by the terms of this Agreement.
|
|
76
|
+
|
|
77
|
+
## 9. Acceptance
|
|
78
|
+
|
|
79
|
+
We may publish revised versions of this Agreement from time to time. By clicking the acceptance control presented by CLA Assistant on a pull request to the Project (or otherwise indicating acceptance in writing), You agree to be bound by the terms of this Agreement for all present and future Contributions You submit to the Project, unless and until this Agreement is superseded by a subsequent version that You separately accept.
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
*UsageBassoon CLA v1.0. Structure adapted from the Project Harmony Individual Contributor License Agreement (harmonyagreements.org, HA-CLA-I v1.0), with the addition of the proprietary relicensing grant in Section 2.3 and other modifications.*
|