Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

## Unreleased

## 0.10.0

### Added

- A `subq` backend for SubQ's decision model: `createTypeSafe({ backend: "subq" })` sends judgments to `api.subq.ai` under `/v1/systemone` with the key from `SUBQ_API_KEY`. The default model is `subq-snap-preview`, priced at $0.05 per million input tokens. Model ids go to SubQ unchanged. Its model list, under `/v1/models`, is public, so `listModels()` there does not verify the key.
- Per-backend request limits. `BackendLimits` names the questions in one request, the options in one Choice, and the levels in one Score; `DEFAULT_BACKEND_LIMITS` holds the schema's bounds, which apply to a backend that declares none. A registry entry or a `BackendEndpoint` declares its own in `limits`, and no limit can be above the schema's bound. `subq` takes at most 16 questions, a Choice of at least 2 options, and a Score of at most 10 levels. `ResolvedBackend.limits` and `TypeSafe.limits` give the active backend's limits without a request.
- `chunkEvaluationRequest` takes a `backend` option and chunks to that backend's question limit.

### Changed

- `evaluate`, `evaluateMany`, and `evaluateAll` refuse a request over the backend's limits before they send it, with a `validation` error. On `typesafe`, `openrouter`, `commandcode`, and `liquid` the limits are the schema's own, so nothing new is refused there.
- `evaluateAll` chunks to the client's question limit, and `chunkEvaluationRequest` to the `backend` option's: 16 on `subq`, 32 elsewhere. A `maxQuestions` above the limit is now capped at it; before, `chunkEvaluationRequest` made chunks that larger, which the schema then refused.
- `prepareEvaluationRequest` takes an optional `limits`.
- A backend that names `modelsField` without `modelsPath` now has its model list renamed to the field the SDK reads; before, the field was ignored unless `modelsPath` was set too. No existing registry backend is affected.
- `TypeSafeBackend` now includes `"subq"`; a consumer with an exhaustive `switch` over it sees a new member.

## 0.9.1

### Fixed
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ An enabled extension with no usable key looks exactly like a working one — one

The environment may lower an explicit cap but never raise it. A reached cap raises a `budget` error naming the cap, the amount used, and the day.

Need more than one request? `evaluateAll(request)` asks any number of questions about one state (over 32 are chunked and fanned out), and `evaluateMany(requests)` runs several requests at once. Both preserve order, bound concurrency, never throw, and stop submitting once the budget is gone. [`pi-typesafe/calibrate`](docs/api.md#calibration-pi-typesafecalibrate) turns labelled cases into thresholds with AUC, a sweep, and a replay runner.
Need more than one request? `evaluateAll(request)` asks any number of questions about one state (over the backend's limit, 32 or 16 on SubQ, are chunked and fanned out), and `evaluateMany(requests)` runs several requests at once. Both preserve order, bound concurrency, never throw, and stop submitting once the budget is gone. [`pi-typesafe/calibrate`](docs/api.md#calibration-pi-typesafecalibrate) turns labelled cases into thresholds with AUC, a sweep, and a replay runner.

## Commands

Expand Down Expand Up @@ -117,7 +117,7 @@ The main model can answer any of these questions in prose. It is slower, costs m
Only the state and questions you (or the agent, once enabled) submit, to `https://api.typesafe.ai` only. No files, conversation history, or telemetry. Error messages never include upstream response bodies, headers, keys, or your submitted state.

**Limits?**
Per request: 32 questions and 64 KiB of JSON. Per session: 20 attempts, 15-second timeout, no automatic retries. Per day: no cap unless you set one. Session limits reset when a session starts or reloads; daily counters live in `~/.pi/agent/pi-typesafe/usage.json` and roll over at local midnight. The SDK's `TYPESAFE_BASE_URL` and `TYPESAFE_LOG_LEVEL` overrides are ignored.
Per request: 32 questions (16 on SubQ) and 64 KiB of JSON. Per session: 20 attempts, 15-second timeout, no automatic retries. Per day: no cap unless you set one. Session limits reset when a session starts or reloads; daily counters live in `~/.pi/agent/pi-typesafe/usage.json` and roll over at local midnight. The SDK's `TYPESAFE_BASE_URL` and `TYPESAFE_LOG_LEVEL` overrides are ignored.

## For extension authors

Expand All @@ -142,7 +142,7 @@ if (!answer.ok) return { skipped: answer.errorCode === "budget" }; // never thr

Your extension owns its own user consent and budget; `/typesafe enable` applies only to this package's tool. Check `authState()` rather than your own consent flag before you report that judgments are on.

Judgments can also go through OpenRouter: `createTypeSafe({ backend: "openrouter" })` sends them to `openrouter.ai` with the key from `OPENROUTER_API_KEY`. Command Code serves the same protocol: `createTypeSafe({ backend: "commandcode" })` sends them to `api.commandcode.ai` with the key from `COMMANDCODE_API_KEY` and the model `typesafe/jev`. So does Liquid AI: `createTypeSafe({ backend: "liquid" })` sends them to `api.liquid.ai` with the key from `LIQUID_API_KEY` and the model `d1:free`, which is free; pass `model: "d1"` for the paid model, priced at $0.040 per million input tokens. Liquid counts the state once per question, where TypeSafe counts it once per request, so a request with N questions costs about N times the state on Liquid. Speed, measured on 2026-10-03 with one request at a time: `d1:free` took a median of about 14 seconds and sometimes failed with HTTP 504 after about 46 seconds, while `d1` took about 0.5 seconds. With `d1:free`, raise `timeoutMs` above its 15-second default, or pass `model: "d1"`. Or pass a `BackendEndpoint` object as `backend` to name any host that serves the protocol: it brings its own key variable, never reads `TYPESAFE_API_KEY`, and `backendHost()` reports its destination host for your consent text. These backends have no login store, so `/typesafe login` does not apply to them. Pass the same `backend` to `authState`, `keySituation`, and `ensureApiKey`, or the status you report describes the TypeSafe key while the requests use another one. The `/typesafe` commands and the `typesafe_evaluate` tool always use the TypeSafe backend. Every export — the client, `ask`, batching, the usage ledger, auth state, and the `pi-typesafe/calibrate` and `pi-typesafe/ui` entry points — is in [docs/api.md](docs/api.md).
Judgments can also go through OpenRouter: `createTypeSafe({ backend: "openrouter" })` sends them to `openrouter.ai` with the key from `OPENROUTER_API_KEY`. Command Code serves the same protocol: `createTypeSafe({ backend: "commandcode" })` sends them to `api.commandcode.ai` with the key from `COMMANDCODE_API_KEY` and the model `typesafe/jev`. So does Liquid AI: `createTypeSafe({ backend: "liquid" })` sends them to `api.liquid.ai` with the key from `LIQUID_API_KEY` and the model `d1:free`, which is free; pass `model: "d1"` for the paid model, priced at $0.040 per million input tokens. Liquid counts the state once per question, where TypeSafe counts it once per request, so a request with N questions costs about N times the state on Liquid. Speed, measured on 2026-10-03 with one request at a time: `d1:free` took a median of about 14 seconds and sometimes failed with HTTP 504 after about 46 seconds, while `d1` took about 0.5 seconds. With `d1:free`, raise `timeoutMs` above its 15-second default, or pass `model: "d1"`. SubQ too: `createTypeSafe({ backend: "subq" })` sends them to `api.subq.ai` with the key from `SUBQ_API_KEY` and the model `subq-snap-preview`, priced at $0.05 per million input tokens. SubQ takes at most 16 questions in one request, a Choice of at least 2 options, and a Score of at most 10 levels: `evaluate` refuses a larger request before it sends it, `evaluateAll` splits to 16, and `client.limits` tells you the limit without a request. Or pass a `BackendEndpoint` object as `backend` to name any host that serves the protocol: it brings its own key variable, never reads `TYPESAFE_API_KEY`, and `backendHost()` reports its destination host for your consent text. These backends have no login store, so `/typesafe login` does not apply to them. Pass the same `backend` to `authState`, `keySituation`, and `ensureApiKey`, or the status you report describes the TypeSafe key while the requests use another one. The `/typesafe` commands and the `typesafe_evaluate` tool always use the TypeSafe backend. Every export — the client, `ask`, batching, the usage ledger, auth state, and the `pi-typesafe/calibrate` and `pi-typesafe/ui` entry points — is in [docs/api.md](docs/api.md).

## Development

Expand Down
Loading
Loading