|
oc_chat
|
Classes | |
| class | TypeSafeError |
| class | MissingKeyError |
| class | Leg |
Functions | |
| Path | bench_dir () |
| str | api_key (dict[str, str]|None environ=None) |
| str | default_instructions (int index, str command, str suffix) |
| dict[str, Any] | build_request (Sequence[str] commands, str|None policy, dict[str, str]|None criteria=None, str model=MODEL_ID, str sandbox_root=SANDBOX_ROOT, Callable[[int, str, str], str]|None instructions=None) |
| bytes | encode_request (dict[str, Any] body) |
| dict[str, Any] | post (dict[str, Any] body, str key, float timeout=DEFAULT_TIMEOUT, int retries=DEFAULT_RETRIES, Callable[..., Any]|None opener=None, Callable[[float], None] sleep=time.sleep, Callable[[int, int, float], None]|None on_retry=None) |
| str | _error_body (urllib.error.HTTPError error) |
| tuple[str|None, dict[str, float], float|None] | read_choice (Any answer, Sequence[str] labels=LABELS) |
| dict[str, Any] | _row_from_answer (dict[str, Any] case, Any answer, float ms) |
| list[dict[str, Any]] | evaluate (list[dict[str, Any]] cases, str|None policy_text, int batch=DEFAULT_BATCH, str|None key=None, dict[str, Any]|None usage=None, float timeout=DEFAULT_TIMEOUT, int retries=DEFAULT_RETRIES, Callable[..., Any]|None opener=None, Callable[[float], None] sleep=time.sleep, Callable[[str], None]|None progress=None, dict[str, str]|None criteria=None) |
| float | cost_usd (dict[str, Any] usage) |
| list[dict[str, Any]] | read_cases (Path path) |
| str|None | read_policy (Leg leg, Path|None root=None) |
| dict[str, Any] | run_leg (Leg leg, list[dict[str, Any]] cases, str|None policy_text, int batch=DEFAULT_BATCH, str|None key=None, float timeout=DEFAULT_TIMEOUT, int retries=DEFAULT_RETRIES, Callable[..., Any]|None opener=None, Callable[[float], None] sleep=time.sleep, Callable[[str], None]|None progress=None) |
| Path | write_result (dict[str, Any] payload, Path out_path) |
| float|None | _top_probability (dict[str, Any] row) |
| dict[str, Any] | compare_routes (dict[str, Any] direct, dict[str, Any] gateway, float threshold=DEFAULT_THRESHOLD) |
| dict[str, Any] | confidence_escalation (dict[str, Any] payload, float threshold=DEFAULT_THRESHOLD) |
| str | render_agreement_table (Iterable[tuple[str, str, dict[str, Any]]] pairs) |
| str | render_confidence_table (Iterable[tuple[str, dict[str, Any]]] rows) |
| int | _analyse (Path results_dir, float threshold) |
| argparse.ArgumentParser | _build_parser () |
| int | main (list[str]|None argv=None) |
Variables | |
| tuple | LABELS = ("allow", "confirm", "deny") |
| str | ENDPOINT = "https://api.typesafe.ai/v1/systemone" |
| str | MODEL_ID = "jev-latest" |
| str | KEY_ENV = "TYPESAFE_API" |
| str | ROUTE = "typesafe-direct" |
| str | SANDBOX_ROOT = "G:/PythonApps/option_c/oc_chat" |
| dict | CRITERIA |
| int | DEFAULT_BATCH = 8 |
| float | DEFAULT_TIMEOUT = 60.0 |
| int | DEFAULT_RETRIES = 4 |
| float | BACKOFF_FIRST_SECONDS = 1.0 |
| float | BACKOFF_FACTOR = 2.0 |
| tuple | RETRYABLE_STATUS = (429, 529) |
| tuple | FATAL_STATUS = (401, 422) |
| float | PRICE_USD_PER_INPUT_TOKEN = 0.042 / 1e6 |
| float | DEFAULT_THRESHOLD = 0.9 |
| float | PROB_MOVE_THRESHOLD = 0.1 |
Jev through TypeSafe's own API, so the route is the only variable.
Purpose
-------
The bench's Jev reference rows (``results/ref_jev_*.json``) came through the
Vercel AI Gateway's evaluation route. That route returns a choice and per-option
probabilities but strips the ``confidence`` field TypeSafe's API documents on a
choice answer, and it adds a hop to every latency figure. This module calls
``api.typesafe.ai`` directly with the SAME request body the gateway run built,
so three questions the gateway rows cannot answer become measurable: whether the
two routes give the same probabilities on the same commands, whether the
separate confidence is a better escalation signal than the top probability, and
what a decision costs in time without the intermediary.
The request body is reused verbatim from the 2026-09-17 session's runner
(``jev_backup/cloud_models.py::run_jev``): the policy text as ``state.policy``,
the sandbox root as ``state.sandbox_root``, the batch as ``state.commands`` with
0-based ids, and one choice question per command under a ``q<n>`` id carrying
the same instructions and the same three criteria strings. The only additions
are the ``model`` key, which the direct API takes in the body where the gateway
took it in a header, and the ``confidence`` the direct route actually returns.
That verbatim reuse is the whole point, and :mod:`test_293_jev_direct` pins the
body byte for byte in both conditions so a reworded criterion cannot silently
turn a route comparison into a prompt comparison.
Run it under the PROJECT venv from the subproject root, in a shell where the
operator has set ``TYPESAFE_API``::
python bench/argv_policy/baselines/jev_direct.py --all
python bench/argv_policy/baselines/jev_direct.py --analyse
The key is read from the process environment and from nowhere else. This module
never opens a key file, never logs a key, never writes one into a result file
and never prints one; a result file carries the NAME of the variable as
provenance and nothing more.
Responsibilities
----------------
- :func:`build_request` / :func:`encode_request` -- the pinned request body.
- :func:`default_instructions` -- the pinned per-question instruction text.
- :func:`read_choice` -- one TypeSafe choice answer to a choice, a distribution
over a named option set, and a confidence.
- :func:`api_key` -- the key contract: the environment, or a clear refusal.
- :func:`post` -- one request, with backoff on 429 and 529 and a hard stop on
401 and 422.
- :func:`evaluate` -- a case set to result rows in the plan-288 schema plus
``confidence`` and ``route``.
- :func:`run_leg` / :func:`write_result` -- one leg to one scorable file.
- :func:`compare_routes` / :func:`confidence_escalation` -- the two analyses
the report and the findings document quote.
- :func:`main` -- the CLI.
Diagnostics
-----------
Domain: OC_CHAT.MAIN
Levels:
L2 - run lifecycle (leg started, requests sent, files written)
L3 - per-batch and per-leg agreement, retries taken
L4 - module load
Contracts
---------
- Standard library only, exactly as the rest of this bench is: no dependency
enters any ``pyproject.toml`` for it.
- Latency per decision is the request wall time divided by the batch size, as
the gateway rows did, so the two latency columns compare.
- A question the API does not answer produces a row with ``pred`` of ``None``
and no probabilities rather than an invented verdict.
- Console output is ASCII only; commands are printed through :func:`ascii`.
- The only network host this module contacts is ``api.typesafe.ai``.
Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
|
protected |
Print P3's and P4's tables from the result files already on disk.
Definition at line 759 of file jev_direct.py.
|
protected |
Construct the CLI argument parser.
Definition at line 800 of file jev_direct.py.
|
protected |
The response body of an error, or an empty string when it has none.
Definition at line 374 of file jev_direct.py.
|
protected |
One result row from one choice answer. A question the API did not answer, or answered with something that is not a choice, produces ``pred`` of ``None`` and an empty forecast. ``score.py`` counts such a row towards agreement and latency and leaves it out of the Brier score, which is the honest treatment; filling it with a verdict would be an invention, and filling it with a uniform forecast would be a claim about the model that nothing measured.
Definition at line 416 of file jev_direct.py.
|
protected |
The row's top forecast probability, or ``None`` when it has none. The same rule ``score.py`` applies, restated here rather than imported so this module stays runnable from the baselines directory on its own.
Definition at line 603 of file jev_direct.py.
| str jev_direct.api_key | ( | dict[str, str] | None | environ = None | ) |
The operator's key, from the process environment and nowhere else. The message on failure names the variable and quotes nothing from the environment: a runner that printed what it *did* find would turn a missing key into a disclosure of whatever else the shell was carrying.
Definition at line 237 of file jev_direct.py.
| Path jev_direct.bench_dir | ( | ) |
``oc_chat/bench/argv_policy`` -- the directory holding the case files.
Definition at line 231 of file jev_direct.py.
| dict[str, Any] jev_direct.build_request | ( | Sequence[str] | commands, |
| str | None | policy, | ||
| dict[str, str] | None | criteria = None, | ||
| str | model = MODEL_ID, | ||
| str | sandbox_root = SANDBOX_ROOT, | ||
| Callable[[int, str, str], str] | None | instructions = None ) |
One request body for a batch of *commands*, in the gateway's shape. With *policy* supplied the state carries the policy text and the sandbox root and the instructions say "under the policy"; with *policy* ``None`` the state is the command list alone. Question ids are ``q<n>`` over the batch, matching ``state.commands[n].id``, and they are for this code only: TypeSafe does not send an id to the model, so the whole meaning of a question lives in its instructions. *instructions* writes one question's text from its index, its command and the policy suffix; it defaults to :func:`default_instructions`, which is the plan-293 wording pinned byte for byte by the witnesses. Plan 296's walk passes its own so a node can ask about structure or program class rather than about the verdict, over the same body, batching and ids.
Definition at line 270 of file jev_direct.py.
| dict[str, Any] jev_direct.compare_routes | ( | dict[str, Any] | direct, |
| dict[str, Any] | gateway, | ||
| float | threshold = DEFAULT_THRESHOLD ) |
P3: how far the direct route and the gateway route disagree. The two files must cover the same commands in the same order; anything else is a table that looks fine and compares different things, so it raises.
Definition at line 617 of file jev_direct.py.
| dict[str, Any] jev_direct.confidence_escalation | ( | dict[str, Any] | payload, |
| float | threshold = DEFAULT_THRESHOLD ) |
P4: the escalation analysis over TypeSafe's ``confidence`` field. When no row carries the field the counts are ``None`` rather than zero: "the API documented a field and did not send it" and "the field was sent and never fell below the threshold" are different findings and must not share a number.
Definition at line 682 of file jev_direct.py.
| float jev_direct.cost_usd | ( | dict[str, Any] | usage | ) |
What one leg cost: input tokens at the published rate, output free.
Definition at line 510 of file jev_direct.py.
| str jev_direct.default_instructions | ( | int | index, |
| str | command, | ||
| str | suffix ) |
The pinned instruction text for one question of the flat three-way choice. Extracted from :func:`build_request` by plan 296 so the walk's per-node questions can supply their own wording through the same builder instead of copying the request, the batching and the id convention into a second module. The DEFAULT path through :func:`build_request` still produces this string, which is why ``test_293_jev_direct``'s byte-for-byte fixtures are untouched by the extraction.
Definition at line 256 of file jev_direct.py.
| bytes jev_direct.encode_request | ( | dict[str, Any] | body | ) |
The request body as the bytes actually sent. Insertion order is preserved rather than sorted, because the pinned fixture is the gateway's byte sequence and key order is part of it.
Definition at line 313 of file jev_direct.py.
| list[dict[str, Any]] jev_direct.evaluate | ( | list[dict[str, Any]] | cases, |
| str | None | policy_text, | ||
| int | batch = DEFAULT_BATCH, | ||
| str | None | key = None, | ||
| dict[str, Any] | None | usage = None, | ||
| float | timeout = DEFAULT_TIMEOUT, | ||
| int | retries = DEFAULT_RETRIES, | ||
| Callable[..., Any] | None | opener = None, | ||
| Callable[[float], None] | sleep = time.sleep, | ||
| Callable[[str], None] | None | progress = None, | ||
| dict[str, str] | None | criteria = None ) |
Classify every case, returning result rows in the bench schema. The rows are plan 288's schema plus two keys: ``confidence``, TypeSafe's own field or ``None`` when the response omits it, and ``route``. Latency is the request wall time divided by the batch size, exactly as the gateway rows computed it, so the two median columns measure the same thing. *usage* is updated in place when given, with the summed token counts, the request count, the retry count and the wall clock, which is what the cost and the provenance block are built from. *criteria* is the plan-296 seam: it reaches :func:`build_request` unchanged and defaults to :data:`CRITERIA`, so every plan-293 leg sends the same bytes it always did. Plan 296's label-permutation legs pass the same three descriptions in a different insertion order through it, which is the only thing that differs between those runs and this one.
Definition at line 441 of file jev_direct.py.
| int jev_direct.main | ( | list[str] | None | argv = None | ) |
CLI entry point: run the pinned legs, or analyse what is on disk.
Definition at line 830 of file jev_direct.py.
| dict[str, Any] jev_direct.post | ( | dict[str, Any] | body, |
| str | key, | ||
| float | timeout = DEFAULT_TIMEOUT, | ||
| int | retries = DEFAULT_RETRIES, | ||
| Callable[..., Any] | None | opener = None, | ||
| Callable[[float], None] | sleep = time.sleep, | ||
| Callable[[int, int, float], None] | None | on_retry = None ) |
Send one request and return the decoded response. 429 and 529 are retried with the documented exponential backoff. 401 and 422 raise at once: neither is transient, and retrying a rejected request is how a bench run earns a rate limit it then blames on the API. Every other status raises unretried as well, because nothing else is documented as transient and inventing a retry policy would hide a real fault. *opener* and *sleep* are injected so the witnesses can replay a status sequence without a socket.
Definition at line 323 of file jev_direct.py.
| list[dict[str, Any]] jev_direct.read_cases | ( | Path | path | ) |
One case file from disk.
Definition at line 516 of file jev_direct.py.
| tuple[str | None, dict[str, float], float | None] jev_direct.read_choice | ( | Any | answer, |
| Sequence[str] | labels = LABELS ) |
One TypeSafe choice answer as a choice, a distribution and a confidence. Extracted from :func:`_row_from_answer` by plan 296, with the option set as a parameter: the flat choice's options are the three verdicts, and the walk's nodes have their own. The three refusals to invent are unchanged -- a non-dict answer is no choice at all, a missing ``probabilities`` block is an EMPTY distribution rather than a uniform one, and an option the response omits is given zero rather than a share.
Definition at line 386 of file jev_direct.py.
| str | None jev_direct.read_policy | ( | Leg | leg, |
| Path | None | root = None ) |
The policy text this leg sends, or ``None`` when it withholds one.
Definition at line 522 of file jev_direct.py.
| str jev_direct.render_agreement_table | ( | Iterable[tuple[str, str, dict[str, Any]]] | pairs | ) |
P3's table as markdown, one line per compared pair.
Definition at line 717 of file jev_direct.py.
| str jev_direct.render_confidence_table | ( | Iterable[tuple[str, dict[str, Any]]] | rows | ) |
P4's table as markdown, one line per direct result file.
Definition at line 738 of file jev_direct.py.
| dict[str, Any] jev_direct.run_leg | ( | Leg | leg, |
| list[dict[str, Any]] | cases, | ||
| str | None | policy_text, | ||
| int | batch = DEFAULT_BATCH, | ||
| str | None | key = None, | ||
| float | timeout = DEFAULT_TIMEOUT, | ||
| int | retries = DEFAULT_RETRIES, | ||
| Callable[..., Any] | None | opener = None, | ||
| Callable[[float], None] | sleep = time.sleep, | ||
| Callable[[str], None] | None | progress = None ) |
Run one leg and build the result payload the scorer reads. The payload carries the same meta keys the carried gateway files do -- ``route``, ``batch``, ``usage``, ``cost_usd`` -- so the two sit beside each other in the scorer's table without special-casing.
Definition at line 531 of file jev_direct.py.
| Path jev_direct.write_result | ( | dict[str, Any] | payload, |
| Path | out_path ) |
Write one result payload where :mod:`score` can find it.
Definition at line 591 of file jev_direct.py.
| float jev_direct.BACKOFF_FACTOR = 2.0 |
Definition at line 148 of file jev_direct.py.
| float jev_direct.BACKOFF_FIRST_SECONDS = 1.0 |
Definition at line 147 of file jev_direct.py.
| dict jev_direct.CRITERIA |
Definition at line 126 of file jev_direct.py.
| int jev_direct.DEFAULT_BATCH = 8 |
Definition at line 136 of file jev_direct.py.
| int jev_direct.DEFAULT_RETRIES = 4 |
Definition at line 142 of file jev_direct.py.
| float jev_direct.DEFAULT_THRESHOLD = 0.9 |
Definition at line 162 of file jev_direct.py.
| float jev_direct.DEFAULT_TIMEOUT = 60.0 |
Definition at line 139 of file jev_direct.py.
| str jev_direct.ENDPOINT = "https://api.typesafe.ai/v1/systemone" |
Definition at line 104 of file jev_direct.py.
| tuple jev_direct.FATAL_STATUS = (401, 422) |
Definition at line 155 of file jev_direct.py.
| str jev_direct.KEY_ENV = "TYPESAFE_API" |
Definition at line 113 of file jev_direct.py.
| tuple jev_direct.LABELS = ("allow", "confirm", "deny") |
Definition at line 101 of file jev_direct.py.
| str jev_direct.MODEL_ID = "jev-latest" |
Definition at line 109 of file jev_direct.py.
| float jev_direct.PRICE_USD_PER_INPUT_TOKEN = 0.042 / 1e6 |
Definition at line 159 of file jev_direct.py.
| float jev_direct.PROB_MOVE_THRESHOLD = 0.1 |
Definition at line 166 of file jev_direct.py.
| tuple jev_direct.RETRYABLE_STATUS = (429, 529) |
Definition at line 151 of file jev_direct.py.
| str jev_direct.ROUTE = "typesafe-direct" |
Definition at line 116 of file jev_direct.py.
| str jev_direct.SANDBOX_ROOT = "G:/PythonApps/option_c/oc_chat" |
Definition at line 121 of file jev_direct.py.