Initial commit: plan, spec, tickets and dashboard skill templates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
49f8f49b65
39 files changed
+10736
No files matched your search
@@ -0,0 +1,64 @@
|
||||
"""Confidence-band classification — single source of truth for the cut points.
|
||||
|
||||
The workbook colours and the review queue MUST agree about what counts as red.
|
||||
If each applied its own threshold they would drift the first time one is tuned,
|
||||
and a reviewer would see a red cell the queue never asked them to check.
|
||||
Everything that needs a band imports from here.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cut points
|
||||
# ---------------------------------------------------------------------------
|
||||
# Calibrated against the measured distribution over all 225 extracted rows of
|
||||
# the six sample documents:
|
||||
#
|
||||
# 1.00 69.8% crisp print
|
||||
# 0.90-0.99 18.2% crisp print
|
||||
# 0.80-0.89 11.0% almost entirely handwritten CPT and modifiers
|
||||
# 0.70-0.79 0.0% <- empty; the natural gap in this data
|
||||
# <0.70 1.1% handwritten modifiers
|
||||
#
|
||||
# 0.80 left only 1.1% of cells red so nearly everything went green and the
|
||||
# colour carried no information. 0.90 sits in the empty band and separates
|
||||
# the handwriting cluster from printed text.
|
||||
CONFIDENCE_THRESHOLD: float = 0.90
|
||||
|
||||
# Above CONFIDENCE_THRESHOLD the 0.90-0.99 band is still 18.2% of cells, and
|
||||
# routing treats it differently from a clean 1.00: reviewers sample it rather
|
||||
# than either trusting it outright or checking every one. A second cut at
|
||||
# 0.95 names that band so the sheet shows what the routing rule already assumes.
|
||||
#
|
||||
# GREEN >= 0.95 clean print, straight through
|
||||
# AMBER 0.90-0.95 slightly unclear, sampled
|
||||
# RED < 0.90 handwriting cluster, always reviewed
|
||||
GREEN_THRESHOLD: float = 0.95
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Band name constants
|
||||
# ---------------------------------------------------------------------------
|
||||
BAND_GREEN = "green"
|
||||
BAND_AMBER = "amber"
|
||||
BAND_RED = "red"
|
||||
|
||||
|
||||
def band_of(score: Optional[float]) -> Optional[str]:
|
||||
"""Return the confidence band name, or ``None`` when the value was never scored.
|
||||
|
||||
WHAT THE BAND CLAIMS: green means the source text this value came from was
|
||||
transcribed cleanly. It does NOT mean the value is correct. The score is
|
||||
the model's own legibility rating, so a confident misread comes back scored
|
||||
high. Verifying correctness needs a second, independent read. Red is the
|
||||
useful direction: it reliably surfaces handwriting.
|
||||
|
||||
Amber does not mean "probably fine". It means the reading was good enough to
|
||||
escape the handwriting cluster but not clean enough to trust unsampled, which
|
||||
is a routing decision, not a quality claim.
|
||||
"""
|
||||
if score is None:
|
||||
return None
|
||||
if score >= GREEN_THRESHOLD:
|
||||
return BAND_GREEN
|
||||
return BAND_AMBER if score >= CONFIDENCE_THRESHOLD else BAND_RED
|
||||
@@ -0,0 +1,60 @@
|
||||
INCLUDE_THINKING_IN_COST = True # thinking tokens bill at output rate
|
||||
|
||||
# ═════════════════════════════════════════════════════════════════════════════
|
||||
# GEMINI PRICING TABLE
|
||||
# ═════════════════════════════════════════════════════════════════════════════
|
||||
PRICING_SOURCE = "https://ai.google.dev/gemini-api/docs/pricing"
|
||||
PRICING_AS_OF = "2026-08-11"
|
||||
|
||||
GEMINI_PRICING = {
|
||||
# ---- Gemini 3.x family --------------------------------------------------
|
||||
"gemini-3.6-flash": {"input": 1.50, "output": 7.50, "free_tier": True},
|
||||
"gemini-3.5-flash": {"input": 1.50, "output": 9.00, "free_tier": True},
|
||||
"gemini-3.5-flash-lite": {"input": 0.30, "output": 2.50, "free_tier": True},
|
||||
"gemini-3-flash-preview": {"input": 0.50, "output": 3.00, "free_tier": True},
|
||||
"gemini-3.1-flash-lite": {"input": 0.25, "output": 1.50, "free_tier": True},
|
||||
"gemini-3.1-pro-preview": {
|
||||
"input": 2.00, "output": 12.00, "free_tier": False,
|
||||
"long_threshold": 200_000, "input_long": 4.00, "output_long": 18.00,
|
||||
},
|
||||
# ---- Gemini 2.5 family --------------------------------------------------
|
||||
"gemini-2.5-pro": {
|
||||
"input": 1.25, "output": 10.00, "free_tier": True,
|
||||
"long_threshold": 200_000, "input_long": 2.50, "output_long": 15.00,
|
||||
},
|
||||
"gemini-2.5-flash": {"input": 0.30, "output": 2.50, "free_tier": True},
|
||||
"gemini-2.5-flash-lite": {"input": 0.10, "output": 0.40, "free_tier": True},
|
||||
"gemini-2.5-flash-lite-preview-09-2025": {"input": 0.10, "output": 0.40, "free_tier": True},
|
||||
# ---- Gemini 2.0 family (DEPRECATED — shut down 2026-06-01) --------------
|
||||
"gemini-2.0-flash": {"input": 0.10, "output": 0.40, "free_tier": True, "deprecated": True},
|
||||
"gemini-2.0-flash-lite": {"input": 0.075, "output": 0.30, "free_tier": True, "deprecated": True},
|
||||
}
|
||||
|
||||
|
||||
def resolve_pricing(model: str, input_tokens: int):
|
||||
if model not in GEMINI_PRICING:
|
||||
raise KeyError(
|
||||
f"No pricing entry for model '{model}'. Add it to GEMINI_PRICING "
|
||||
f"from {PRICING_SOURCE}. Known models: {', '.join(GEMINI_PRICING)}"
|
||||
)
|
||||
p = GEMINI_PRICING[model]
|
||||
note = ""
|
||||
in_rate, out_rate = p["input"], p["output"]
|
||||
threshold = p.get("long_threshold")
|
||||
if threshold and input_tokens > threshold:
|
||||
in_rate = p.get("input_long", in_rate)
|
||||
out_rate = p.get("output_long", out_rate)
|
||||
note = f"long-context rate (>{threshold:,} input tokens)"
|
||||
return in_rate, out_rate, note
|
||||
|
||||
|
||||
def cost_for_call(model: str, usage: dict):
|
||||
in_rate, out_rate, note = resolve_pricing(model, usage["input"])
|
||||
billable_output = usage["output"]
|
||||
if INCLUDE_THINKING_IN_COST:
|
||||
billable_output += usage.get("thinking", 0)
|
||||
input_cost = usage["input"] / 1_000_000 * in_rate
|
||||
output_cost = billable_output / 1_000_000 * out_rate
|
||||
return input_cost + output_cost, input_cost, output_cost, note
|
||||
|
||||
|
||||
Reference in new issue
Block a user