@jaypie/mcp 0.8.161 → 0.8.163

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.
package/dist/assets.js CHANGED
@@ -55,11 +55,11 @@ function getMcpAssetPaths() {
55
55
  */
56
56
  function getMcpBuildInfo() {
57
57
  return {
58
- commit: "89205145"
58
+ commit: "d5f7052e"
59
59
  ,
60
- version: "0.8.161"
60
+ version: "0.8.163"
61
61
  ,
62
- versionString: "@jaypie/mcp@0.8.161#89205145"
62
+ versionString: "@jaypie/mcp@0.8.163#d5f7052e"
63
63
  ,
64
64
  };
65
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.8.161",
3
+ "version": "0.8.163",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,12 @@
1
+ ---
2
+ version: 1.2.102
3
+ date: 2026-09-18
4
+ summary: Pulls @jaypie/llm 1.3.43
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`@jaypie/llm` 1.3.43**: `Llm.question` answers typed questions in
10
+ TypeSafe's Jev shape, served natively by the new `typesafe` provider or
11
+ emulated on any provider that returns structured JSON. A per-call `model` no
12
+ longer follows `operate()` onto a fallback provider.
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 1.2.103
3
+ date: 2026-09-19
4
+ summary: Pulls @jaypie/llm 1.3.44
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`@jaypie/llm` 1.3.44**: `Llm.ocr` extracts documents to per-page markdown
10
+ over Mistral OCR or the new `llamacloud` provider (LlamaParse), behind one
11
+ request and response shape with fallback across engines.
@@ -0,0 +1,94 @@
1
+ ---
2
+ version: 1.3.43
3
+ date: 2026-09-18
4
+ summary: "Llm.question answers typed questions in TypeSafe's Jev shape, natively or emulated on any provider"
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ ### New Features
10
+
11
+ - `Llm.question(state, { questions })` is a fourth entry point, in TypeSafe's
12
+ (Jev's) shape: `noul`, `choice`, and `score` questions asked of a state,
13
+ answered one apiece with probabilities. Static and instance forms mirror
14
+ `operate`, including `model` array sugar, `fallback`, `retry`, `signal`, and
15
+ `user`.
16
+ - New provider `typesafe` serves TypeSafe's System One models over
17
+ `POST /v1/systemone`, with match words `jev` and `typesafe` and
18
+ `TYPESAFE_API_KEY`. It implements `question` only: a System One model
19
+ generates no text, so `send` throws `NotImplementedError` and the provider
20
+ offers no `operate` or `stream`. The client is `fetch` against the two
21
+ documented routes; no SDK dependency is taken.
22
+ - `MODEL.JEV` is `jev-1.13.0`, the versioned id the API echoes on every
23
+ response. The `jev-latest` and `jev-preview` aliases still route to the
24
+ provider through the match words. `COST["jev-1.13.0"]` is $0.042 input and
25
+ $0 output, which TypeSafe bills for answers.
26
+ - Every other provider answers the same call through an emulator: one
27
+ structured `operate()` call at `temperature: 0` and `turns: 1`, asking for
28
+ the full distribution, from which choice (argmax), score
29
+ (probability-weighted level index), and confidence are derived. A chain can
30
+ mix the two, so a rate-limited or unreachable Jev falls through to a
31
+ traditional model without the caller's request or response shape changing.
32
+ - The response carries `emulated`. When it is `true`, `probabilities` are
33
+ model-reported (normalized in code) and `confidence` is Jaypie's normalized
34
+ peak probability, `(max(p) - 1/n) / (1 - 1/n)`, exported as
35
+ `peakConfidence` alongside `normalizeDistribution`. TypeSafe has not
36
+ published its own formula; normalized peak reproduces both documented
37
+ examples and a live Jev call within rounding, and is replaced by TypeSafe's
38
+ when it publishes.
39
+ - TypeSafe's question limits (1 to 255 choice options, 2 to 10 ordered score
40
+ levels, non-empty instructions, `noul` criteria restricted to `true` and
41
+ `false`) are validated at the entry point for **every** provider, so a
42
+ question that validates is answerable anywhere in a chain. A violation
43
+ throws `BadRequestError` naming the question id.
44
+
45
+ ### Mistral: dated ids, OCR 4.1, and the CI shard restored
46
+
47
+ - `MODEL.MISTRAL.LARGE` and `MODEL.MISTRAL.SMALL` name the dated ids
48
+ `mistral-large-2512` and `mistral-small-2603` in place of the `-latest`
49
+ aliases they resolved to. Prices are unchanged ($0.5/$1.5 and $0.15/$0.6,
50
+ cached input at 10%), and `COST[MODEL.MISTRAL.LARGE]` now hits rather than
51
+ missing by design: the catalog no longer names an alias.
52
+ - `MODEL.MISTRAL.OCR` moves to `mistral-ocr-4-1`. It stays unpriced in `COST`
53
+ because it bills per page ($4 per 1000 pages, $0.4 cached), which
54
+ `LlmModelCost` cannot express. Verified live against `POST /v1/ocr`.
55
+ - Mistral is a CI capability-matrix shard again, and `mistral-large-2512`
56
+ rejoins the live matrix. Both were dropped while the account's tier answered
57
+ every capability with "This model is not available in your subscription
58
+ tier"; on the upgraded tier both cataloged models pass all seven cells
59
+ (verified 2026-09-19).
60
+
61
+ ### Bug Fixes
62
+
63
+ - The Anthropic hot spec streamed with `max_tokens: 64`. The 5-family models
64
+ open a response with a thinking block, so the budget was spent before any
65
+ text delta arrived and `streams text deltas` failed for Fable 5.1, Opus 5,
66
+ and Sonnet 5. The budget now accommodates the thinking block and the test
67
+ stops reading at the first text delta.
68
+ - A per-call `model` no longer follows `operate()` onto a fallback provider.
69
+ `operate(input, { model: ["gpt-5.6-luna", "claude-haiku-4-5"] })` sent the
70
+ primary model id to the fallback, asking Anthropic for an OpenAI model. Each
71
+ fallback now runs the model its own chain entry names.
72
+
73
+ ### Internal
74
+
75
+ - The primary-then-chain loop moves to `src/util/runWithFallback.ts` and
76
+ serves both `operate` and `question`. Exchange settlement stays in
77
+ `operate`; `question` emits no exchange in this release.
78
+ - `MODEL.JEV` is excluded from the live capability matrix: a System One model
79
+ implements neither `operate()` nor `send()`, so every cell would fail by
80
+ construction.
81
+
82
+ ## Testing
83
+
84
+ - Live, 2026-09-18 (`npm run test:llm:question`): all three question types
85
+ against `jev-1.13.0` natively and against `claude-haiku-4-5` and
86
+ `gpt-5.6-luna` by emulation, plus a chain falling from an unknown Jev model
87
+ to emulation. 4 of 4 passed, with the three models agreeing on every answer
88
+ (choice `billing`; noul 0.97, 0.92, 0.98; score 1.86, 1.60, 1.83 on a 0 to 2
89
+ scale).
90
+ - New unit coverage for validation, the emulator (prompt, schema,
91
+ normalization, argmax, weighted score, confidence against TypeSafe's
92
+ documented examples), `Llm.question` fallback behavior, the TypeSafe client's
93
+ status-to-error mapping, and provider routing. A hot spec exercises both
94
+ live routes when `TYPESAFE_API_KEY` is set.
@@ -0,0 +1,120 @@
1
+ ---
2
+ version: 1.3.44
3
+ date: 2026-09-19
4
+ summary: "Llm.ocr extracts documents to per-page markdown over Mistral OCR or LlamaParse behind one shape"
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ ### New Features
10
+
11
+ - `Llm.ocr(document, options?)` is a fifth entry point: a document in,
12
+ per-page markdown out. Static and instance forms mirror `question`,
13
+ including `model` array sugar, `fallback`, `retry`, and `signal`. The
14
+ document (an `https://` URL, a `data:` URI, an S3 key, a local path, or the
15
+ `{ file }` / `{ image }` objects `operate()` accepts) is resolved once before
16
+ the fallback chain runs.
17
+ - New provider `llamacloud` serves LlamaParse over the LlamaCloud Parse API
18
+ v2 with `LLAMA_CLOUD_API_KEY` and match words `llamacloud`, `llamaindex`,
19
+ and `llamaparse`. A parse is a job: submitted by `source_url` or multipart
20
+ upload, polled from one second backing off to five, then fetched. It
21
+ implements `ocr` only; `send` throws `NotImplementedError`. The client is
22
+ `fetch` against four routes; no SDK dependency is taken.
23
+ - The LlamaParse **tier is the model id**: `MODEL.LLAMAPARSE.FAST`,
24
+ `COST_EFFECTIVE`, `AGENTIC` (the provider default), and `AGENTIC_PLUS`.
25
+ A chain such as `[MODEL.MISTRAL.OCR, MODEL.LLAMAPARSE.COST_EFFECTIVE]`
26
+ therefore reads as a plain model list and routes through
27
+ `determineModelProvider`.
28
+ - The API version is pinned per tier in `PROVIDER.LLAMACLOUD.VERSION`; each
29
+ tier accepts its own dated versions and rejects the others (verified live
30
+ 2026-09-19). `providerOptions.version` overrides it per call, and the
31
+ response `model` is `<tier id>@<version>`.
32
+ - Common shape: `markdown`, 1-indexed `pages[]` with `header`, `footer`,
33
+ `images`, `success`/`error`, and the vendor page at `raw`; `images[]`;
34
+ `usage` with `pages`, LlamaParse `credits`, and `cost`; `responses[]`; and
35
+ the `provider`/`model`/`fallbackAttempts`/`fallbackUsed` fields `operate`
36
+ carries. Options: `pages` (`[1, 3]` or `"1,3,5-10"`), `tables`
37
+ (`"markdown"` or `"html"`), `images` (download extracted images as `data:`
38
+ URIs), `timeout` (asynchronous job ceiling, default ten minutes), and
39
+ `providerOptions` spread last onto the vendor request.
40
+ - `PAGE_COST` prices document-extraction engines in USD per 1,000 pages,
41
+ keyed by literal id: `mistral-ocr-4-1` at $4 and the four LlamaParse tiers
42
+ at $1.25, $3.75, $12.50, and $56.25 (credits at
43
+ `PROVIDER.LLAMACLOUD.CREDIT_COST`, $1.25 per 1,000). A LlamaParse job's
44
+ recorded credits win over the per-page estimate.
45
+ - `DEFAULT.OCR` names the engine `Llm.ocr` uses when neither a provider nor a
46
+ model is given: Mistral OCR, one synchronous call and the cheapest markdown
47
+ tier on offer.
48
+ - **OCR emulation on chat models**, as `question` emulates: a provider with
49
+ `operate` but no `ocr` transcribes through `src/ocr/OcrEmulator.ts`, so a
50
+ chain such as `[MODEL.MISTRAL.OCR, MODEL.HAIKU]` falls from a native engine
51
+ to any model that accepts files. Each page rides alone in one structured
52
+ `operate()` call (a PDF is trimmed to one page per request, so the model
53
+ never counts or numbers pages), under a fixed `{ confidence, markdown,
54
+ notes }` schema and a verbatim-transcription prompt with `[UNCLEAR: …]` and
55
+ `[ELEMENT: …]` markers. A page scored under 0.4 is transcribed once more
56
+ and the higher score wins. `concurrency` (default 5) bounds pages in
57
+ flight; the first failure aborts the rest and rethrows so the next engine
58
+ starts clean, and unreadable content throws `InternalError` to the same
59
+ end. The response carries `emulated: true`, `pages[].confidence`,
60
+ `usage.tokens`, and `usage.cost` priced from `COST` through the new
61
+ `tokenCost` helper. `user` forwards to the calls. A provider with neither
62
+ (`typesafe`) fails its attempt and the chain moves on.
63
+ - `LlmOcrResponse.emulated` is present on every response (`false` from a
64
+ native engine), matching `LlmQuestionResponse`.
65
+
66
+ ### Changed
67
+
68
+ - `MistralProvider.ocr()` takes the common `(document, options)` signature
69
+ and returns the common shape. The provider-specific `{ markdown, model,
70
+ pages, raw }` return shipped in 1.3.43 and is gone: `raw` is
71
+ `responses[0]`, and the 0-indexed `pages[].index` is the 1-indexed
72
+ `pages[].page` with the vendor page at `pages[].raw`. `MistralOcrPage` and
73
+ `MistralOcrResponse` are no longer exported. `OcrRequest` widens to the full
74
+ Mistral `OCRRequest` field set.
75
+ - Client failures on Mistral OCR classify through the chat adapter into the
76
+ typed `LlmError` family, and both engines share one retry loop
77
+ (`src/ocr/runOcrAttempts.ts`), so `retry` and `signal` mean the same thing
78
+ on each. A LlamaParse job that reports `FAILED` throws
79
+ `LlmUnrecoverableError` with the API's message, `CANCELLED` throws
80
+ `LlmAbortError`, and a job that outlives `timeout` throws
81
+ `LlmTransientError` so a chain can try the next engine.
82
+ - `loadFileData` and `getExtensionForMimeType` are exported from the upload
83
+ helpers for reuse; `LlmProvider` gains an optional `ocr`.
84
+ - `extractPdfPages` throws `BadRequestError` on an out-of-range page where it
85
+ threw a vanilla `Error`.
86
+
87
+ ### Internal
88
+
89
+ - `MODEL.LLAMAPARSE.*` join `MODEL.MISTRAL.OCR` in `MATRIX_EXCLUDE`: a
90
+ document-extraction engine implements neither `operate()` nor `send()`, so
91
+ every capability cell would fail by construction. They are exercised by
92
+ `tsx test/ocr.ts` (`npm run test:llm:ocr`) and the provider hot specs.
93
+ - Both CI workflows pass `LLAMA_CLOUD_API_KEY` from the
94
+ `CICD_LLAMA_CLOUD_API_KEY` org secret; the LlamaCloud hot spec skips until
95
+ the secret exists.
96
+
97
+ ## Testing
98
+
99
+ - Live, 2026-09-19 (`npm run test:llm:ocr`): `page.pdf` and `page.png`
100
+ through Mistral OCR and all four LlamaParse tiers, every run reading the
101
+ fixture text, plus a chain falling from an unknown tier to Mistral. 11 of 11
102
+ passed. Mistral answered in about one second at $0.004 per page; LlamaParse
103
+ completed in 3 to 5 polls (6 to 15 seconds) at 1, 3, 10, and 45 credits per
104
+ page by tier.
105
+ - Live, 2026-09-19, with emulation: the same fixtures through Haiku and
106
+ Luna, plus a chain falling from an unknown tier into Haiku. 15 of 15
107
+ passed. Haiku transcribed a page in 1.5 to 2.5 seconds for $0.0015 to
108
+ $0.0026 (1,200 to 2,200 tokens) at 0.95 confidence; Luna in 1 to 3 seconds
109
+ for $0.0002 to $0.0003 at 0.98 to 0.99. Both marked the fixture's rule as
110
+ `---` or `[ELEMENT: decorative horizontal line]`; Luna read the PDF's
111
+ heading last, where every other engine read it first.
112
+ - New unit coverage for page-selection parsing, document resolution (URL,
113
+ data URI, S3, disk, inline data, PDF trimming), the shared retry loop, the
114
+ LlamaCloud client's routes and status-to-error mapping, the provider's
115
+ submit/poll/fetch cycle (including fast-tier text, failed pages, image
116
+ download, per-tier version, timeout, and abort), the reworked Mistral OCR
117
+ mapping, `Llm.ocr` routing and fallback (native to emulated and back),
118
+ the emulator (page splitting, selection, image and URL input, low-confidence
119
+ re-transcription, concurrency cap, first-failure abort, caller abort, and
120
+ fetch classification), `tokenCost`, and provider routing for every new id.
@@ -0,0 +1,15 @@
1
+ ---
2
+ version: 0.8.162
3
+ date: 2026-09-18
4
+ summary: Document Llm.question and the TypeSafe provider in the llm skill
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ ### Documentation
10
+
11
+ - `skills/llm.md` gains a "Questions (Jev Shape)" section: the three question
12
+ types and their answers, the response fields, what `emulated` means for
13
+ `confidence`, and how chains mix native and emulated providers.
14
+ - The providers table carries a `typesafe` row, and the environment block
15
+ carries `TYPESAFE_API_KEY`.
@@ -0,0 +1,17 @@
1
+ ---
2
+ version: 0.8.163
3
+ date: 2026-09-19
4
+ summary: Document Llm.ocr and the LlamaCloud provider in the llm skill
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ ### Documentation
10
+
11
+ - `skills/llm.md` gains an "OCR" section: the document forms `Llm.ocr`
12
+ accepts, the page and usage shape, `pages`/`tables`/`images`/`timeout`/
13
+ `providerOptions`, per-tier LlamaParse versions, and fallback across
14
+ engines.
15
+ - The providers table carries a `llamacloud` row, the model constants name
16
+ `MODEL.LLAMAPARSE.*` and `PAGE_COST`, and the environment block carries
17
+ `LLAMA_CLOUD_API_KEY`.
@@ -0,0 +1,12 @@
1
+ ---
2
+ version: 1.2.75
3
+ date: 2026-09-18
4
+ summary: Mock Llm.question and the TypeSafe provider
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - `Llm.question` (static, instance, and on the inner provider) resolves a
10
+ mock question response with one `choice` answer and `emulated: false`.
11
+ - New mocks: `TypeSafeProvider`, `TypeSafeClient`, `LlmQuestionType`,
12
+ `normalizeDistribution`, and `peakConfidence`.
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 1.2.76
3
+ date: 2026-09-19
4
+ summary: Mock Llm.ocr and the LlamaCloud provider
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - `Llm.ocr` (static, instance, and on the inner provider) resolves a mock OCR
10
+ response with one page of `_MOCK_OCR_MARKDOWN`.
11
+ - New mocks: `LlamaCloudProvider` and `LlamaCloudClient`.
package/skills/llm.md CHANGED
@@ -41,6 +41,8 @@ the alias rather than the id it currently resolves to. Read the id off
41
41
  | xAI | "xai", "grok" | `MODEL.GROK` |
42
42
  | Meta | "muse" (plus the exact name `meta` and a `meta:` prefix) | `MODEL.MUSE_SPARK` |
43
43
  | Bedrock | "amazon.nova", "anthropic.claude", "meta.llama", "deepseek.", "google.gemma", "moonshotai.", "openai.gpt-oss", … | `MODEL.NOVA_PRO` |
44
+ | TypeSafe | "jev", "typesafe" | `MODEL.JEV` |
45
+ | LlamaCloud | "llamacloud", "llamaindex", "llamaparse" | `MODEL.LLAMAPARSE.AGENTIC` |
44
46
 
45
47
  The provider name for Gemini models is `"google"` — `"gemini"` is accepted as a deprecated alias.
46
48
 
@@ -48,10 +50,14 @@ Mistral's family names mostly do not contain the substring "mistral" (`ministral
48
50
 
49
51
  "meta" is deliberately not a Meta match word: `meta.llama-*` is a Bedrock id and `meta-llama/*` an OpenRouter route. Muse Spark ids resolve on "muse"; use the `meta:` prefix to force the Meta Model API for any other id.
50
52
 
53
+ TypeSafe serves System One models (Jev), which answer typed questions and generate no text. The provider implements `question()` only: `operate()`, `send()`, and `stream()` throw. See [Questions](#questions-jev-shape).
54
+
55
+ LlamaCloud serves LlamaParse, which extracts documents and generates no text. The provider implements `ocr()` only. The parse tier is the model id (`MODEL.LLAMAPARSE.FAST`, `COST_EFFECTIVE`, `AGENTIC`, `AGENTIC_PLUS`). See [OCR](#ocr).
56
+
51
57
  ### Model Constants
52
58
 
53
59
  - **`PROVIDER.<name>.DEFAULT`** — the single default model per provider (above), used when no `model` is given.
54
- - **`LLM.MODEL.*`** — the named model catalog (e.g. `MODEL.SONNET`, `MODEL.ASTRA`, `MODEL.SOL`, `MODEL.GEMINI_FLASH`, `MODEL.GROK`, `MODEL.MUSE_SPARK`, `MODEL.MUSE_SPARK_CONTRIBUTOR`, `MODEL.NOVA_PRO`, `MODEL.NOVA_LITE`), plus three nested subtrees: `MODEL.FIREWORKS.*` for Fireworks serverless models (`DEEPSEEK`, `GLM`, `GPT_OSS`, `INKLING`, `KIMI`, `MINIMAX`, `NEMOTRON`, `QWEN`), `MODEL.MISTRAL.*` (`LARGE`, `OCR`, `SMALL`), and `MODEL.OPENROUTER.*` for provider-prefixed routes (`GLM`, `LUNA`, `SONNET`). Pick specific models from here. `MODEL.MISTRAL.OCR` serves `POST /v1/ocr` rather than chat completions: it is priced per page, carries no `COST` entry, and is reached through `MistralProvider.ocr()`. Amazon's Nova models are first-class ids served over Bedrock; Bedrock's third-party routes are not catalogued — pass the literal id (e.g. `us.anthropic.claude-sonnet-4-6`) and `determineModelProvider` resolves it to `bedrock`.
60
+ - **`LLM.MODEL.*`** — the named model catalog (e.g. `MODEL.SONNET`, `MODEL.ASTRA`, `MODEL.SOL`, `MODEL.GEMINI_FLASH`, `MODEL.GROK`, `MODEL.MUSE_SPARK`, `MODEL.MUSE_SPARK_CONTRIBUTOR`, `MODEL.NOVA_PRO`, `MODEL.NOVA_LITE`), plus three nested subtrees: `MODEL.FIREWORKS.*` for Fireworks serverless models (`DEEPSEEK`, `GLM`, `GPT_OSS`, `INKLING`, `KIMI`, `MINIMAX`, `NEMOTRON`, `QWEN`), `MODEL.MISTRAL.*` (`LARGE`, `OCR`, `SMALL`), `MODEL.LLAMAPARSE.*` for LlamaParse tiers (`FAST`, `COST_EFFECTIVE`, `AGENTIC`, `AGENTIC_PLUS`), and `MODEL.OPENROUTER.*` for provider-prefixed routes (`GLM`, `LUNA`, `SONNET`). Pick specific models from here. `MODEL.MISTRAL.OCR` and `MODEL.LLAMAPARSE.*` are document-extraction engines reached through `Llm.ocr`, not chat completions: they are priced per page in `LLM.PAGE_COST` (USD per 1,000 pages, keyed by literal id) and carry no `COST` entry. Amazon's Nova models are first-class ids served over Bedrock; Bedrock's third-party routes are not catalogued — pass the literal id (e.g. `us.anthropic.claude-sonnet-4-6`) and `determineModelProvider` resolves it to `bedrock`.
55
61
  - `MODEL.MUSE_SPARK` and `MODEL.MUSE_SPARK_CONTRIBUTOR` are the same Meta model at two tiers. The contributor tier is roughly a tenth of the price because prompts and completions may train Meta models; it is limited to 100 RPM and does not accept `reasoning.effort: "max"`. It is an explicit opt-in: `PROVIDER.META.DEFAULT` is `MODEL.MUSE_SPARK`.
56
62
  - The catalog is the **single source of truth for CI coverage**: `packages/llm/test/models.ts` derives the live capability matrix from `MODEL.*` plus each `PROVIDER.*.DEFAULT`, and the workflow shards it by provider. Adding a model to `MODEL.*` puts it under test; no id list exists anywhere else.
57
63
  - **Deprecated:** the size-tier map `PROVIDER.<name>.MODEL.{DEFAULT,LARGE,SMALL,TINY}`, the `DEFAULT.MODEL` bundle, and `ALL` are `@deprecated` and retired in 2.0 — use `PROVIDER.*.DEFAULT` for defaults and `MODEL.*` for named models.
@@ -401,6 +407,151 @@ jsonSchemaToNaturalSchema({
401
407
 
402
408
  `naturalSchemaToJsonSchema` is lossless — Natural Schema is a strict subset of JSON Schema. `jsonSchemaToNaturalSchema` is lossy: JSON Schema constraints, descriptions, defaults, unions, and optionality have no Natural Schema equivalent and are dropped. It never throws; every dropped keyword is logged at `log.debug` with the keyword name and JSON path.
403
409
 
410
+ ## Questions (Jev Shape)
411
+
412
+ `Llm.question(state, { questions })` asks typed questions about a body of
413
+ text and returns one typed answer each, with probabilities. The request and
414
+ response shapes are TypeSafe's, so a question written once is answered the
415
+ same way by every provider: TypeSafe's System One models (Jev) answer
416
+ natively, and every other provider answers through an emulator that rigs up
417
+ one structured `operate()` call. Traditional models fit the question shape;
418
+ the question shape does not bend to fit them.
419
+
420
+ ```typescript
421
+ import { Llm, LLM } from "@jaypie/llm";
422
+
423
+ const { answers, emulated } = await Llm.question(ticket, {
424
+ model: [LLM.MODEL.JEV, LLM.MODEL.HAIKU], // native first, emulated if Jev is down
425
+ questions: {
426
+ department: {
427
+ type: "choice",
428
+ instructions: "Which team should handle this?",
429
+ criteria: {
430
+ billing: "Charges, invoices, refunds",
431
+ sales: null, // the option name speaks for itself
432
+ technical: "Bugs, outages",
433
+ },
434
+ },
435
+ is_urgent: { type: "noul", instructions: "Does this convey urgency?" },
436
+ frustration: {
437
+ type: "score",
438
+ instructions: "How frustrated is the customer?",
439
+ criteria: ["Calm", "Frustrated", "Very angry"], // 2 to 10, lowest first
440
+ },
441
+ },
442
+ });
443
+
444
+ answers.department; // { type: "choice", choice: "billing", confidence: 0.98, probabilities: {...} }
445
+ answers.is_urgent; // { type: "noul", noul: 0.94 }
446
+ answers.frustration; // { type: "score", score: 1.7, confidence: 0.56, legend: {...}, probabilities: {...} }
447
+ ```
448
+
449
+ ### Question Types
450
+
451
+ | Type | Criteria | Answer |
452
+ |------|----------|--------|
453
+ | `noul` | optional `{ true, false }` descriptions | `{ type, noul }` — probability 0 to 1, no confidence: the number is the degree of belief |
454
+ | `choice` | `Record<option, description \| null>`, 1 to 255 options | `{ type, choice, confidence, probabilities }` — `choice` is the argmax, `probabilities` keyed by option name |
455
+ | `score` | ordered `string[]`, 2 to 10 levels lowest first | `{ type, score, confidence, legend, probabilities }` — `score` is the probability-weighted level index, `legend` and `probabilities` keyed by level index as a string |
456
+
457
+ `instructions` and every criteria value accept a string, an object, or an
458
+ array. `state` accepts the same three and is text only: no files, no images,
459
+ no conversation. Question ids are the caller's and never reach the model.
460
+ TypeSafe's limits are enforced on every provider at the entry point, so a
461
+ question that validates works anywhere in a chain; a violation throws
462
+ `BadRequestError` naming the question id.
463
+
464
+ ### Response
465
+
466
+ ```typescript
467
+ {
468
+ answers, // Record<question id, answer>
469
+ emulated, // false when a System One model answered natively
470
+ fallbackAttempts, // providers tried (1 = primary only)
471
+ fallbackUsed,
472
+ model, // the versioned id that served the request
473
+ provider,
474
+ responses, // raw provider payloads
475
+ usage, // LlmUsage, same shape operate() reports
476
+ }
477
+ ```
478
+
479
+ `emulated: true` marks an answer a traditional model produced. Its
480
+ `probabilities` are model-reported (normalized in code) and its `confidence`
481
+ is a Jaypie statistic over them — normalized peak probability,
482
+ `(max(p) - 1/n) / (1 - 1/n)`, exported as `peakConfidence` — not a calibrated
483
+ System One output. Threshold on it knowing that.
484
+
485
+ Chains work exactly as they do for `operate()`: `model` accepts a
486
+ preference-ordered array, `fallback` accepts explicit entries or `false`, and
487
+ an attempt with somewhere left to go skips the rate-limit wait. A provider
488
+ that can neither `question` nor `operate` counts as a failed attempt and the
489
+ chain moves on.
490
+
491
+ ## OCR
492
+
493
+ `Llm.ocr(document, options?)` turns a document into per-page markdown. Two
494
+ engines answer natively behind one request and response shape: Mistral OCR
495
+ (`MODEL.MISTRAL.OCR`, one synchronous call, the default when no provider or
496
+ model is named) and LlamaParse over LlamaCloud (`MODEL.LLAMAPARSE.*`, a job
497
+ that is submitted, polled, and fetched; `LLAMA_CLOUD_API_KEY`). Every other
498
+ provider answers through emulation, as `Llm.question` does: one structured
499
+ `operate()` call per page. A `model` array is therefore a fallback chain
500
+ across native engines and chat models alike.
501
+
502
+ ```typescript
503
+ import { Llm, LLM } from "@jaypie/llm";
504
+
505
+ const { emulated, markdown, pages, usage } = await Llm.ocr(
506
+ "./scans/invoice.pdf",
507
+ {
508
+ model: [
509
+ LLM.MODEL.MISTRAL.OCR,
510
+ LLM.MODEL.LLAMAPARSE.COST_EFFECTIVE,
511
+ LLM.MODEL.HAIKU, // emulated: any provider that accepts files
512
+ ],
513
+ pages: "1,3-5", // 1-indexed; [1, 3, 4, 5] also works
514
+ tables: "markdown", // or "html"
515
+ },
516
+ );
517
+
518
+ pages[0].page; // 1
519
+ pages[0].markdown;
520
+ pages[0].confidence; // emulated only: the model's self-reported 0..1
521
+ emulated; // true when a chat model transcribed the pages
522
+ usage.pages; // pages billed
523
+ usage.cost; // USD from LLM.PAGE_COST (LlamaParse: from recorded credits; emulated: from COST tokens)
524
+ usage.tokens; // emulated only: every operate() call's usage
525
+ ```
526
+
527
+ Emulation sends each page alone (a PDF is trimmed to one page per call, so
528
+ the model never numbers pages), asks for verbatim Markdown with
529
+ `[UNCLEAR: best guess]` and `[ELEMENT: description]` markers, transcribes a
530
+ page scored under 0.4 once more and keeps the higher score, and runs
531
+ `concurrency` pages at a time (default 5); the first failure stops the rest
532
+ so the next engine starts clean. Unreadable content fails the attempt so the
533
+ chain moves on. A provider with native `ocr` (Mistral) always answers
534
+ natively; a provider with neither `ocr` nor `operate` (`typesafe`) fails its
535
+ attempt and the chain moves on.
536
+
537
+ `document` is an `https://` URL (fetched by the vendor), a `data:` URI, an S3
538
+ key (when `CDK_ENV_BUCKET` is set), a local path, or the `{ file, bucket?,
539
+ data?, pages? }` / `{ image, ... }` objects `operate()` accepts. Set
540
+ `images: true` to download extracted images as `data:` URIs onto
541
+ `images[]` and `pages[].images`. `providerOptions` spreads last onto the
542
+ vendor request: Mistral `OCRRequest` fields (`document_annotation_format`,
543
+ `include_blocks`, ...) or LlamaParse `ParseRequestConfiguration` fields
544
+ (`version`, `agentic_options.custom_prompt`, `processing_options`,
545
+ `output_options`, `disable_cache`, ...). `timeout` (default ten minutes)
546
+ bounds a LlamaParse job; expiry throws `LlmTransientError` so a chain can try
547
+ the next engine. `retry` and `signal` behave as on `operate()`.
548
+
549
+ LlamaParse's API version is pinned per tier in `PROVIDER.LLAMACLOUD.VERSION`
550
+ (each tier accepts its own dated versions); the response `model` reads
551
+ `llamaparse-agentic@2026-09-13` so usage records name the configuration. The
552
+ fast tier returns text only, copied into `markdown`. Failed pages arrive with
553
+ `success: false` and `error` rather than failing the call.
554
+
404
555
  ## Conversation History
405
556
 
406
557
  Continue conversations across calls:
@@ -649,6 +800,8 @@ OPENAI_API_KEY # Required for OpenAI
649
800
  OPENROUTER_API_KEY # Required for OpenRouter
650
801
  XAI_API_KEY # Required for xAI (Grok)
651
802
  META_API_KEY # Required for Meta (Muse Spark); MODEL_API_KEY is read as a fallback
803
+ TYPESAFE_API_KEY # Required for TypeSafe (Jev); emulated questions use the answering provider's key
804
+ LLAMA_CLOUD_API_KEY # Required for LlamaParse (Llm.ocr); Mistral OCR uses MISTRAL_API_KEY
652
805
  ```
653
806
 
654
807
  Keys are resolved via `getEnvSecret()` which supports AWS Secrets Manager.