mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-17 20:19:29 +00:00
feat(learn): metered embeddings and the cache (decision-engine 6.2)
plane/models/openrouter-embedding.yaml: openai/text-embedding-3-small via OpenRouter (embeddings only), input_per_m 0.02, cache/output 0, egress openrouter.ai; `openrouter` joins the provider extensions. plane/budget.py MeteredEmbeddingClient: prompt_tokens priced from the Model manifest, usd/calls ceilings stop the next call, a priced call that reports no tokens raises UnmeteredUsage (a silent zero is an instrument failure). OpenRouterEmbeddingClient keeps one usage row per call (parse_embedding_payload); its api key is out of repr. learn/embeddings.py + `ousast learn memory embed`: excerpts embedded in batches into embeddings/<model-slug>/<excerpt_sha>.json (float32 base64), cached by content hash so a rerun makes no call; the declared dimension is checked; --dry-run estimates tokens and cost without a call; a paid run needs --budget-usd and exits 3 (unfinished) at its ceiling. Not run against the provider: the store embedding waits for its budget go-ahead. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7b81c9ac4e
commit
98fa4ec8cd
@@ -26,15 +26,63 @@ def add_commands(learn_sub: Any) -> None:
|
||||
build.add_argument("--memory", help="the store (default OUSAST_MEMORY, then results_root()/memory)")
|
||||
build.add_argument("--profile", choices=("static", "plane"), default="static")
|
||||
build.add_argument("--dry-run", action="store_true", help="count, write nothing")
|
||||
embed = memory_sub.add_parser("embed", help="embed the stored excerpts (OpenRouter, metered; cached by excerpt sha)")
|
||||
embed.add_argument("--memory", help="the store (default OUSAST_MEMORY, then results_root()/memory)")
|
||||
embed.add_argument("--model", default="openai/text-embedding-3-small")
|
||||
embed.add_argument("--manifest", type=Path, default=Path("plane/models/openrouter-embedding.yaml"), help="the Model with the prices")
|
||||
embed.add_argument("--budget-usd", type=float, help="the ceiling; required unless --dry-run")
|
||||
embed.add_argument("--budget-calls", type=int)
|
||||
embed.add_argument("--batch", type=int, default=32)
|
||||
embed.add_argument("--dry-run", action="store_true", help="count and estimate the tokens; no call")
|
||||
|
||||
|
||||
def run(args: argparse.Namespace) -> int:
|
||||
if args.learn_command == "memory" and args.memory_command == "build":
|
||||
return _memory_build(args)
|
||||
if args.learn_command == "memory" and args.memory_command == "embed":
|
||||
return _memory_embed(args)
|
||||
print(f"learn: unknown command {args.learn_command}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
|
||||
def _model_parameters(path: Path) -> dict[str, Any]:
|
||||
from ..plane.manifests import load_manifests
|
||||
|
||||
models = load_manifests([path]).models
|
||||
if len(models) != 1:
|
||||
raise SystemExit(f"{path}: expected one Model, found {sorted(models)}")
|
||||
return dict(next(iter(models.values())).parameters)
|
||||
|
||||
|
||||
def _memory_embed(args: argparse.Namespace) -> int:
|
||||
"""Exit 2 without a ceiling, 3 when the ceiling stops the run (``unfinished``; a rerun resumes from the cache)."""
|
||||
from ..plane.budget import BudgetExhausted, MeteredEmbeddingClient
|
||||
from ..plane.memory import open_store
|
||||
from .embeddings import embed_excerpts
|
||||
from .examples import EXAMPLE_KIND
|
||||
|
||||
store = open_store(args.memory)
|
||||
shas = sorted({str(r.row["excerpt_sha"]) for r in store.rows(kind=EXAMPLE_KIND)})
|
||||
client = None
|
||||
if not args.dry_run:
|
||||
if args.budget_usd is None:
|
||||
print("learn memory embed: --budget-usd is required (a paid run starts only under a ceiling)", file=sys.stderr)
|
||||
return 2
|
||||
from ..provider.openrouter import OpenRouterEmbeddingClient
|
||||
|
||||
client = MeteredEmbeddingClient(
|
||||
OpenRouterEmbeddingClient.from_env(), prices=_model_parameters(args.manifest), budget_usd=args.budget_usd,
|
||||
budget_calls=args.budget_calls,
|
||||
) # fmt: skip
|
||||
try:
|
||||
report = embed_excerpts(store, shas, client, model=args.model, batch=args.batch)
|
||||
except BudgetExhausted as exc:
|
||||
print(f"learn memory embed: unfinished -- {exc}", file=sys.stderr)
|
||||
return 3
|
||||
print(json.dumps(report.as_dict(), indent=2, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
def _memory_build(args: argparse.Namespace) -> int:
|
||||
"""Exit 2 on a refused label (``origin: user``, a source outside the list) or a source with >10% unread excerpts."""
|
||||
from ..plane.memory import open_store
|
||||
|
||||
Reference in New Issue
Block a user