trap-cli 0.0.4__tar.gz → 0.0.5__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.
- {trap_cli-0.0.4 → trap_cli-0.0.5}/PKG-INFO +1 -2
- {trap_cli-0.0.4 → trap_cli-0.0.5}/pyproject.toml +0 -1
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/_version.py +2 -2
- trap_cli-0.0.5/src/trap/cost/calculator.py +58 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/cost/providers.py +6 -1
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/cost/proxy.py +1 -1
- trap_cli-0.0.4/src/trap/cost/calculator.py +0 -41
- {trap_cli-0.0.4 → trap_cli-0.0.5}/.gitignore +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/LICENSE +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/README.md +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/examples/echo/task/README.md +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/auth/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/auth/client.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/auth/login.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/auth/oauth.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/auth/store.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/cli/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/cli/_auth.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/cli/_console.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/cost/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/display/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/display/progress.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/display/submit.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/environment/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/environment/detector.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/git_ops/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/git_ops/base.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/git_ops/local.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/git_ops/remote.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/git_ops/rev.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/git_ops/url.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/loader/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/loader/errors.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/loader/trap_yaml.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/loader/traptask_yaml.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/cost.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/environment.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/provenance.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/report.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/results.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/trap_yaml.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/models/traptask_yaml.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/report/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/report/base.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/report/handle.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/report/json.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/report/rich.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/__init__.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/capture.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/grader.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/judge.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/layout.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/proc.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/solution.py +0 -0
- {trap_cli-0.0.4 → trap_cli-0.0.5}/src/trap/runner/task.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trap-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: tp — non-invasive CLI testing framework for AI workflows. Submits results to trapstreet.run.
|
|
5
5
|
Project-URL: Homepage, https://trapstreet.run
|
|
6
6
|
Project-URL: Repository, https://github.com/trapstreet/trap
|
|
@@ -27,7 +27,6 @@ Requires-Dist: py-cpuinfo>=9.0.0
|
|
|
27
27
|
Requires-Dist: pydantic>=2.0
|
|
28
28
|
Requires-Dist: pyyaml>=6.0
|
|
29
29
|
Requires-Dist: rich>=13.0
|
|
30
|
-
Requires-Dist: tokencost>=0.1.26
|
|
31
30
|
Requires-Dist: typer>=0.12
|
|
32
31
|
Description-Content-Type: text/markdown
|
|
33
32
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 0,
|
|
21
|
+
__version__ = version = '0.0.5'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 0, 5)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Cost calculation for LLM token usage.
|
|
2
|
+
|
|
3
|
+
Isolated so this logic can be moved server-side without touching the proxy infrastructure.
|
|
4
|
+
The only public symbol is :func:`calculate_call_cost`.
|
|
5
|
+
|
|
6
|
+
Prices are maintained here rather than pulled from a third-party table: the previous
|
|
7
|
+
dependency (tokencost) lagged behind provider releases, silently pricing current models
|
|
8
|
+
at 0 or at a stale predecessor's rate. An explicit table with a NaN fallback keeps the
|
|
9
|
+
failure mode honest — an unknown cost is reported as unknown, never as a wrong number.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import math
|
|
15
|
+
|
|
16
|
+
# USD per million tokens (input, output), keyed by model-id prefix so one entry covers
|
|
17
|
+
# both a version alias ("claude-haiku-4-5") and the dated full id the API reports
|
|
18
|
+
# ("claude-haiku-4-5-20251001"). First match wins: keep more-specific prefixes
|
|
19
|
+
# ("gpt-5.5-pro") ahead of the prefixes they extend ("gpt-5.5").
|
|
20
|
+
_PRICES_PER_MTOK: list[tuple[str, float, float]] = [
|
|
21
|
+
# Anthropic
|
|
22
|
+
("claude-fable-5", 10.0, 50.0),
|
|
23
|
+
("claude-mythos-5", 10.0, 50.0),
|
|
24
|
+
# Introductory pricing through 2026-08-31; reverts to 3.0 / 15.0 after.
|
|
25
|
+
("claude-sonnet-5", 2.0, 10.0),
|
|
26
|
+
("claude-opus-4-8", 5.0, 25.0),
|
|
27
|
+
("claude-opus-4-7", 5.0, 25.0),
|
|
28
|
+
("claude-opus-4-6", 5.0, 25.0),
|
|
29
|
+
("claude-opus-4-5", 5.0, 25.0),
|
|
30
|
+
("claude-opus-4-1", 15.0, 75.0),
|
|
31
|
+
("claude-sonnet-4-6", 3.0, 15.0),
|
|
32
|
+
("claude-sonnet-4-5", 3.0, 15.0),
|
|
33
|
+
("claude-haiku-4-5", 1.0, 5.0),
|
|
34
|
+
# OpenAI
|
|
35
|
+
("gpt-5.6-sol", 5.0, 30.0),
|
|
36
|
+
("gpt-5.6-terra", 2.5, 15.0),
|
|
37
|
+
("gpt-5.6-luna", 1.0, 6.0),
|
|
38
|
+
("gpt-5.5-pro", 30.0, 180.0),
|
|
39
|
+
("gpt-5.5", 5.0, 30.0),
|
|
40
|
+
("gpt-5.4-pro", 30.0, 180.0),
|
|
41
|
+
("gpt-5.4-mini", 0.75, 4.5),
|
|
42
|
+
("gpt-5.4-nano", 0.2, 1.25),
|
|
43
|
+
("gpt-5.4", 2.5, 15.0),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def calculate_call_cost(prompt_tokens: int, completion_tokens: int, model: str | None) -> float:
|
|
48
|
+
"""Return the USD cost for one API call.
|
|
49
|
+
|
|
50
|
+
Returns NaN when the model is unknown or absent from the pricing table — an
|
|
51
|
+
unknown cost is not a zero cost (pydantic serialises the NaN as JSON null)."""
|
|
52
|
+
if model is None:
|
|
53
|
+
return math.nan
|
|
54
|
+
name = model.lower()
|
|
55
|
+
for prefix, input_per_mtok, output_per_mtok in _PRICES_PER_MTOK:
|
|
56
|
+
if name.startswith(prefix):
|
|
57
|
+
return (prompt_tokens * input_per_mtok + completion_tokens * output_per_mtok) / 1_000_000
|
|
58
|
+
return math.nan
|
|
@@ -23,7 +23,12 @@ class _ProtocolStyle(enum.StrEnum):
|
|
|
23
23
|
case (_ProtocolStyle.OPENAI_COMPATIBLE, True):
|
|
24
24
|
return self._parse_openai_style_sse(body.decode("utf-8", errors="replace"))
|
|
25
25
|
case (_ProtocolStyle.OPENAI_COMPATIBLE, False):
|
|
26
|
-
|
|
26
|
+
parsed = self._parse_json(body, "prompt_tokens", "completion_tokens")
|
|
27
|
+
if parsed[0] or parsed[1]:
|
|
28
|
+
return parsed
|
|
29
|
+
# The OpenAI Responses API names its usage fields input_/output_tokens
|
|
30
|
+
# (like Anthropic), not the Chat Completions prompt_/completion_tokens.
|
|
31
|
+
return self._parse_json(body, "input_tokens", "output_tokens")
|
|
27
32
|
case _: # pragma: no cover - exhaustive over the two styles above
|
|
28
33
|
raise ValueError(f"Unsupported protocol style: {self!r}")
|
|
29
34
|
|
|
@@ -65,7 +65,7 @@ class CostProxy:
|
|
|
65
65
|
) -> None:
|
|
66
66
|
if not (prompt_tokens or completion_tokens):
|
|
67
67
|
return
|
|
68
|
-
call_cost = _calc_cost(prompt_tokens, completion_tokens, model)
|
|
68
|
+
call_cost = _calc_cost(prompt_tokens, completion_tokens, model)
|
|
69
69
|
with self._lock:
|
|
70
70
|
key = (provider, model)
|
|
71
71
|
entry = self._cost_buckets.get(key)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""Cost calculation for LLM token usage.
|
|
2
|
-
|
|
3
|
-
Isolated so this logic can be moved server-side without touching the proxy infrastructure.
|
|
4
|
-
The only public symbol is :func:`calculate_call_cost`.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from tokencost import TOKEN_COSTS as _TOKEN_COSTS # type: ignore[import-untyped]
|
|
10
|
-
from tokencost import calculate_cost_by_tokens as _calc_cost # type: ignore[import-untyped]
|
|
11
|
-
from tokencost import register_model_pattern as _register_pattern # type: ignore[import-untyped]
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _register_anthropic_version_patterns() -> None:
|
|
15
|
-
# Anthropic now returns version-based names (e.g. "claude-sonnet-4-6") but tokencost only
|
|
16
|
-
# has date-based entries (e.g. "claude-sonnet-4-20250514"). Register wildcard patterns so
|
|
17
|
-
# calculate_cost_by_tokens can resolve them via _normalize_model_for_pricing.
|
|
18
|
-
for pattern, canonical in [
|
|
19
|
-
("claude-sonnet-4-*", "claude-sonnet-4-20250514"),
|
|
20
|
-
("claude-opus-4-*", "claude-opus-4-20250514"),
|
|
21
|
-
]:
|
|
22
|
-
entry = _TOKEN_COSTS.get(canonical)
|
|
23
|
-
if entry: # pragma: no branch - both canonicals ship in tokencost
|
|
24
|
-
_register_pattern(
|
|
25
|
-
pattern,
|
|
26
|
-
float(entry["input_cost_per_token"]) * 1000,
|
|
27
|
-
float(entry["output_cost_per_token"]) * 1000,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
_register_anthropic_version_patterns()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def calculate_call_cost(prompt_tokens: int, completion_tokens: int, model: str) -> float:
|
|
35
|
-
"""Return the USD cost for one API call. Returns 0.0 if the model is not in the pricing table."""
|
|
36
|
-
try:
|
|
37
|
-
return float(
|
|
38
|
-
_calc_cost(prompt_tokens, model, "input") + _calc_cost(completion_tokens, model, "output")
|
|
39
|
-
)
|
|
40
|
-
except KeyError:
|
|
41
|
-
return 0.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|