langfx.js 0.1.0-alpha.0

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.
Files changed (104) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +11 -0
  3. package/README.md +107 -0
  4. package/dist/agentic.d.ts +128 -0
  5. package/dist/agentic.js +265 -0
  6. package/dist/agentic.js.map +1 -0
  7. package/dist/cache.d.ts +32 -0
  8. package/dist/cache.js +135 -0
  9. package/dist/cache.js.map +1 -0
  10. package/dist/cancellation.d.ts +9 -0
  11. package/dist/cancellation.js +70 -0
  12. package/dist/cancellation.js.map +1 -0
  13. package/dist/errors.d.ts +40 -0
  14. package/dist/errors.js +44 -0
  15. package/dist/errors.js.map +1 -0
  16. package/dist/index.d.ts +18 -0
  17. package/dist/index.js +15 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/json-stream.d.ts +73 -0
  20. package/dist/json-stream.js +222 -0
  21. package/dist/json-stream.js.map +1 -0
  22. package/dist/langfunc.d.ts +20 -0
  23. package/dist/langfunc.js +28 -0
  24. package/dist/langfunc.js.map +1 -0
  25. package/dist/language-model.d.ts +78 -0
  26. package/dist/language-model.js +218 -0
  27. package/dist/language-model.js.map +1 -0
  28. package/dist/llms/anthropic.d.ts +30 -0
  29. package/dist/llms/anthropic.js +365 -0
  30. package/dist/llms/anthropic.js.map +1 -0
  31. package/dist/llms/gemini.d.ts +34 -0
  32. package/dist/llms/gemini.js +380 -0
  33. package/dist/llms/gemini.js.map +1 -0
  34. package/dist/llms/images.d.ts +10 -0
  35. package/dist/llms/images.js +43 -0
  36. package/dist/llms/images.js.map +1 -0
  37. package/dist/llms/index.d.ts +7 -0
  38. package/dist/llms/index.js +4 -0
  39. package/dist/llms/index.js.map +1 -0
  40. package/dist/llms/openai.d.ts +30 -0
  41. package/dist/llms/openai.js +398 -0
  42. package/dist/llms/openai.js.map +1 -0
  43. package/dist/llms/transport.d.ts +4 -0
  44. package/dist/llms/transport.js +90 -0
  45. package/dist/llms/transport.js.map +1 -0
  46. package/dist/mapping.d.ts +71 -0
  47. package/dist/mapping.js +190 -0
  48. package/dist/mapping.js.map +1 -0
  49. package/dist/message.d.ts +56 -0
  50. package/dist/message.js +86 -0
  51. package/dist/message.js.map +1 -0
  52. package/dist/python-preview.d.ts +24 -0
  53. package/dist/python-preview.js +368 -0
  54. package/dist/python-preview.js.map +1 -0
  55. package/dist/python-stream.d.ts +15 -0
  56. package/dist/python-stream.js +29 -0
  57. package/dist/python-stream.js.map +1 -0
  58. package/dist/python.d.ts +85 -0
  59. package/dist/python.js +728 -0
  60. package/dist/python.js.map +1 -0
  61. package/dist/query.d.ts +31 -0
  62. package/dist/query.js +151 -0
  63. package/dist/query.js.map +1 -0
  64. package/dist/retry.d.ts +12 -0
  65. package/dist/retry.js +56 -0
  66. package/dist/retry.js.map +1 -0
  67. package/dist/schema/zod.d.ts +4 -0
  68. package/dist/schema/zod.js +7 -0
  69. package/dist/schema/zod.js.map +1 -0
  70. package/dist/schema.d.ts +10 -0
  71. package/dist/schema.js +7 -0
  72. package/dist/schema.js.map +1 -0
  73. package/dist/template.d.ts +14 -0
  74. package/dist/template.js +75 -0
  75. package/dist/template.js.map +1 -0
  76. package/dist/testing/index.d.ts +33 -0
  77. package/dist/testing/index.js +56 -0
  78. package/dist/testing/index.js.map +1 -0
  79. package/dist/tool-call.d.ts +27 -0
  80. package/dist/tool-call.js +16 -0
  81. package/dist/tool-call.js.map +1 -0
  82. package/dist/tools.d.ts +43 -0
  83. package/dist/tools.js +155 -0
  84. package/dist/tools.js.map +1 -0
  85. package/docs/ANTHROPIC.md +43 -0
  86. package/docs/API_DESIGN.md +200 -0
  87. package/docs/GEMINI.md +68 -0
  88. package/docs/IMPLEMENTATION_STATUS.md +77 -0
  89. package/docs/LIVE_TESTING.md +24 -0
  90. package/docs/MAPPING.md +54 -0
  91. package/docs/OPENAI.md +40 -0
  92. package/docs/PORTING_PLAN.md +135 -0
  93. package/docs/PROMPT_PARITY.md +379 -0
  94. package/docs/PYTHON_PROTOCOL.md +109 -0
  95. package/docs/PYTHON_PROTOCOL_PARITY.md +964 -0
  96. package/docs/PYTHON_SCHEMA_EVALUATION.md +93 -0
  97. package/docs/PYTHON_STREAMING_PARITY.md +59 -0
  98. package/docs/RELEASING.md +25 -0
  99. package/docs/RETRIES_AND_CACHE.md +56 -0
  100. package/docs/SESSION_EVENTS.md +40 -0
  101. package/docs/SOURCE_AUDIT.md +133 -0
  102. package/docs/STREAMING.md +76 -0
  103. package/docs/TOOL_STREAMING.md +31 -0
  104. package/package.json +95 -0
@@ -0,0 +1,77 @@
1
+ # Implementation status: core runtime and three providers
2
+
3
+ The implementation includes the M0 foundation, M1 tool loop, and substantial M2 functionality across Gemini, Anthropic, and OpenAI. Version `0.1.0-alpha.0` is prepared for an initial public alpha; publication is a separate step. The design documents remain the target scope; this page records what actually exists.
4
+
5
+ ## Implemented
6
+
7
+ - Default Python-style class/union constructor protocol for prompt-based structured queries (JSON requires explicit selection) with bounded parsing and explicit TypeScript factories; no code evaluation. See [Python protocol](PYTHON_PROTOCOL.md).
8
+
9
+ - Strict TypeScript/ESM build, generated declarations, separate core/testing/Zod exports, and a lockfile.
10
+ - Message classes, ordered content parts, role-preserving ComplexMessage flattening, Image URI/byte/file descriptors.
11
+ - Inherited static templateStr declarations replace Python docstring templates, with constructor overrides across Template/LangFunc/Mapping. Mapping has an empty Template preamble; LfQuery defines its own JSON preamble and demonstration.
12
+ - Synchronous Template rendering with bound variables, own-property paths, composition, literal-text escape hatch, and explicit errors for unsupported syntax.
13
+ - Subclassable LanguageModel with async call/sample and async-iterable stream; capability checks, cancellation, per-call options, output cardinality checks, and usage passthrough.
14
+ - Direct typed query returns, optional returnsMessage, JSON parsing plus runtime validation, prompt-JSON generation instructions, and a native-schema request path for capable custom models. Gemini now implements this request path through its HTTP wire protocol.
15
+ - A structural Schema contract, schema-bearing output classes, and optional Zod conversion. The model-facing schema describes parser input; the local synchronous parser produces the final TypeScript output. Unsupported JSON Schema representations throw instead of degrading to an unconstrained schema. Async schema refinements are not supported. See [Zod's conversion documentation](https://zod.dev/json-schema).
16
+ - Structured `queryStream(prompt, schema, options)` with incremental strict JSON parsing, field/string/container events, immutable unvalidated field values, validated final results, distinct parse/provider/cancellation outcomes, size/depth limits, and upstream cleanup. See [streaming](STREAMING.md).
17
+ - Opt-in bounded retries using Python-style camelCase options, abortable exponential backoff, and no stream retries after any emitted chunk. Async Cache contract and bounded TTL/LRU InMemoryCache for call/sample, scoped to each model instance and application namespace. Cache-hit usage is separate from new provider usage. See [retries and cache](RETRIES_AND_CACHE.md).
18
+ - Mapping<T> extends LangFunc, LfQuery<T>, MappingExample few-shot turns, synchronous rendering/parsing/postprocessing hooks, and MappingError retaining the original response/cause. Prompt-based query delegates to LfQuery; native output bypasses it. Mapping failures evict the response cache entry. See [mapping](MAPPING.md).
19
+ - LangFunc with input/output transformation hooks and text streaming. Streaming with a custom whole-message transformOutput explicitly rejects until transformation semantics are designed.
20
+ - Echo, StaticResponse, StaticSequence, and StaticMapping fake models; text streams preserve whitespace and Unicode. Fake models do not invent usage numbers.
21
+ - Action.call/invoke, Session.query, per-invocation Session views, trace snapshots, query usage aggregation, subscriptions, observer-error isolation, deadlines, cancellation, and bounded concurrentMap.
22
+ - Invocation-scoped debug/info/warning/error/fatal logs, transient log events, latest progress snapshots, chronological allLogs, bounded retention, and invocation metadata updates/events. See [Session events](SESSION_EVENTS.md).
23
+ - A configurable trace-node cap (default 10,000); exceeding it fails explicitly. Query usage is counted once, and queries without reported usage remain separately identifiable.
24
+
25
+ ## First provider and tool loop
26
+
27
+ Gemini/GoogleGenAI subclasses, `lf.llms` and provider subpath exports, injected fetch transport/auth, text/image request conversion, native JSON output requests, complete tool calls/results with retained continuation signatures, text/tool SSE parsing, usage and error classification are implemented. Tool and Agent classes validate registered app functions, preserve tool correlation, enforce step/call/deadline limits, and trace sequential execution. See [Gemini and tools](GEMINI.md) for the supported subset and usage.
28
+
29
+ ## Anthropic adapter
30
+
31
+ Anthropic Messages API text/image calls, complete client tool calls/results, text/tool SSE streaming, usage/error conversion, cancellation, scoped continuation replay, and Agent integration and explicit transcript replay are fixture-tested. Native schema output, thinking are explicit unsupported capabilities. See [Anthropic](ANTHROPIC.md). This adapter has not been verified against live inference.
32
+
33
+ ## OpenAI adapter
34
+
35
+ OpenAI Responses API text/image calls, strict native JSON schema requests, complete client function calls/results, opaque encrypted reasoning continuation, text/tool SSE, cancellation, usage and classified errors are implemented with mocked wire tests. No memory abstraction or server-managed conversation state was added. See [OpenAI](OPENAI.md) for the conservative schema subset and limitations. Live inference remains unverified.
36
+
37
+ ## Verified
38
+
39
+ `npm run check` runs strict type assertions (including forbidden sync/async duplicates), the runtime tests, a browser-platform bundle/import check, and an isolated package-consumer check. `npm run example` runs a credentials-free Node example. The core import is also evaluated in a realm without Node globals, DOM, network APIs, or credentials.
40
+
41
+ The full shared page and worker example logic now runs automatically inside isolated Web-API-only realms, exercising all three mocked provider tool-stream round trips without Node globals, with dynamic string/wasm code generation disabled. The shared demo also checks default Python unions, class construction, tuple/set previews, UNKNOWN, Unicode string deltas, and cancellation. This is not a real browser engine or CORS test.
42
+
43
+ `npm run check:package` creates a temporary npm archive, verifies every declared export and packaged docs/license, extracts it into an isolated consumer, imports core/testing/provider subpaths, runs a query, and compiles against the packed declarations without Zod installed. A separate optional-adapter check supplies the local Zod peer. Temporary files are removed; nothing is published. Both checks are included in `npm run check`.
44
+
45
+ The static example has been checked manually in Safari: both the page and a module Web Worker reported PASS for action invocation, querying, tracing, text and structured JSON streaming, and a Gemini-encoded local-tool round trip using mocked HTTP responses. This is a smoke check, not a full cross-browser compatibility matrix. Run `npm run serve:example` to repeat it.
46
+
47
+ `tests/fixtures/python-parity.json` contains manually transcribed expectations from the pinned Python tests for templates, queries, and normalized stream chunks. Those original fixtures cover the JSON path; the Python-style subset has separate tests and does not claim exact Python prompt parity; this fixture does not claim provider/SSE transport parity. `tests/contracts.ts` checks provider subclassing, typed outputs, class schemas, Action/Session use, synchronous rendering, and Promise/AsyncIterable signatures.
48
+
49
+ ## Not implemented yet
50
+
51
+ Additional provider subclasses; Anthropic native-schema/thinking support; broader live-provider verification; full modality support beyond image descriptors; MCP; persistence; embeddings; evaluation; React bindings. See the porting plan for milestone ordering.
52
+
53
+ Invocation views are invalid after their action finishes. Cancellation stops queued work and rejects waiting calls, but user code/providers must observe the supplied AbortSignal to stop their underlying I/O. Synchronous application code cannot be forcibly interrupted. Stream cleanup likewise relies on cooperative provider generators. Trace results and metadata may refer to application-owned objects; snapshots freeze the trace structure, not arbitrary user values. Invocation IDs are unique within the loaded runtime; persistent/global IDs are a future storage concern.
54
+
55
+ Remaining work includes credentialed live-provider verification and release integration checks. An opt-in, offline-tested seven-request runner is available via `npm run check:live -- <provider> <model-id>`; see [live testing](LIVE_TESTING.md). The seven-request suite passed live on `gemini-3.7-flash` on 2026-09-16. Other providers and browser CORS remain unverified. Gemini generateContent delivers complete streamed calls; partial-argument dialects are not implemented. The fixture-tested browser model → validated local tool → model round trip is complete. The package remains an initial implementation, not the full planned release.
56
+
57
+ ## Live Python prompt comparison
58
+
59
+ The separate `npm run check:prompt-parity` check compares actual Python JSON-protocol LfQuery renders with TypeScript renders and verifies each runtime's query wrapper matches its own LfQuery. **All 5 cases match after fixed protocol adaptations; exact and whitespace-normalized equality remain 0/5.** The command fails on unaccounted differences and is also included in `npm run check`. Adaptations are frozen independently of the actual JS render in `tests/fixtures/prompt-protocol-adaptations.json`. See [captured prompts and differences](PROMPT_PARITY.md).
60
+
61
+ Regenerate the Python fixture with `../langfx/.venv/bin/python scripts/capture-python-prompts.py ../langfx`, then run `npm run check:prompt-parity`. The Python fixture records the actual checkout commit and dirty state. This comparison used clean commit `0a17208b4290e470e398892c6232819225289cc0`, newer than the original source audit; the original manually transcribed parity fixture remains unchanged. No model calls or credentials are required.
62
+
63
+ Conversation memory abstractions and the memory namespace are intentionally excluded from the current scope. Apps own cross-invocation history.
64
+
65
+ ## Default Python-protocol comparison
66
+
67
+ `npm run check:python-parity` is included in `npm run check`. Nine cases captured from Python commit `504233a5c4a10e45684530ff58d7e2bd9d8ae58e` match parsed values/class identity and exact full role/text arrays, with no normalization or adaptations. Exact full-prompt equality is **9/9**. The capture records the explicit PyGlove union workaround required by that Python checkout. See [Python protocol parity](PYTHON_PROTOCOL_PARITY.md); the earlier JSON fixtures remain unchanged.
68
+
69
+ ## Broader Python schema evaluation
70
+
71
+ A separate [feature evaluation](PYTHON_SCHEMA_EVALUATION.md) audits the schema implementation and runs fixed-data differential probes. It identifies gaps beyond the nine exact-prompt fixtures, including numeric union ambiguity, constructor-versus-dictionary acceptance, defaults, constraints, subclass discovery, and unsupported containers. It is an evaluation report, not a claim of complete Python schema compatibility.
72
+
73
+ ## Python browser verification (2026-09-16)
74
+
75
+ The rebuilt local demo was run in Chrome. Both the page and a real module Web Worker displayed PASS for the shared smoke suite, including `examples/python-protocol.ts`. The Python checks exercised default-protocol querying, registered union constructors, tuple/set values, UNKNOWN, character-chunked Unicode previews, final-only factory execution, and cancellation cleanup. Gemini/Anthropic/OpenAI integrations used mocked transports. No credentials or inference requests were used; this does not verify provider CORS, real-provider behavior, or all browsers.
76
+
77
+ Repeat with `npm run serve:example`, then open the displayed localhost URL and check both PASS messages. The automated `npm run check:browser` remains a Web-API-only VM check, not a browser-engine test.
@@ -0,0 +1,24 @@
1
+ # Opt-in live provider checks
2
+
3
+ The default `npm run check` makes no inference requests. Live smoke tests are separate, require an explicit provider/model, and make seven billable requests to that provider's default endpoint:
4
+
5
+ ```sh
6
+ # Supply the selected key in the process environment using your usual secret manager.
7
+ npm run check:live -- openai <model-id>
8
+ npm run check:live -- anthropic <model-id>
9
+ npm run check:live -- gemini <model-id>
10
+ ```
11
+
12
+ The runner reads only the selected provider's `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or `GEMINI_API_KEY`. It does not load .env files, discover credentials, pick a model, or enable retries. Missing arguments/credentials fail before network activity. Credentials remain outside the runtime library's configuration defaults.
13
+
14
+ The suite checks a text call, prompt-based structured JSON, a default-protocol Python union query, a text stream, a default-protocol Python structured stream, and a streamed local tool call followed by a correlated result and final answer. Both Python requests select a registered `SmokeAnswer` constructor from a union and verify its instance and fixed field values. The Python stream additionally checks raw text and decoded string-field progress; malformed, mismatched, or failed output stops the suite before tool execution. It forces the registered `smoke_echo` function, validates its fixed input, and executes it only after successful final stream completion. The function returns a constant test value; it performs no external action. Each model request caps output at 1,024 tokens, with one 60-second deadline across the suite. No trace or response payload is written to disk. Logs contain stage names, not raw errors or responses.
15
+
16
+ A pass applies only to the chosen provider/model and these requests. Native structured output, images, browser CORS/auth, hosted tools, and all model-specific combinations require separate checks. A failed run stops immediately; earlier requests can still be billed. Cancellation requires the provider to observe the supplied signal.
17
+
18
+ ## Recorded live run
19
+
20
+ On 2026-09-16, the seven-request suite passed against Google's default Gemini API endpoint using `gemini-3.7-flash` ([model documentation](https://ai.google.dev/gemini-api/docs/models/gemini-3.7-flash)). The existing `GOOGLE_API_KEY` literal in the local shell configuration was passed as `GEMINI_API_KEY` to this process only; credential values were not printed or persisted.
21
+
22
+ All six stages passed: text, structured JSON, default Python union query, text streaming, default Python structured streaming, and the two-request streamed tool round trip. Each request used a 1,024-output-token cap, no cache, and one attempt. The run completed within the suite's 60-second deadline. No provider response payloads were saved.
23
+
24
+ This is live Node-runtime evidence for this model and these fixed cases. Browser CORS, media, native structured output, other models/providers, and general model quality remain unverified. The default checks continue to run entirely offline.
@@ -0,0 +1,54 @@
1
+ # Mapping and structured queries
2
+
3
+ The prompt-based structured query pipeline follows Python's class hierarchy:
4
+
5
+ `Template → LangFunc → Mapping<T> → LfQuery<T>`
6
+
7
+ `Mapping` owns schema instructions, examples, rendering, parsing, and result transformation. `LfQuery` supplies protocol-specific query instructions; Python-style constructors are the default. `query(prompt, schema, options)` constructs an `LfQuery` and delegates execution to it. Native structured output deliberately bypasses Mapping and sends the schema directly to the model, matching Python's separate path.
8
+
9
+ ```ts
10
+ import * as lf from 'langfx.js';
11
+ import { StaticResponse } from 'langfx.js/testing';
12
+ import { fromZod } from 'langfx.js/schema/zod';
13
+ import { z } from 'zod';
14
+
15
+ const Answer = fromZod(z.object({ answer: z.number() }));
16
+ class RoundedAnswer extends lf.LfQuery<{ answer: number }> {
17
+ override postprocessResult(result: { answer: number }) {
18
+ return { answer: Math.round(result.answer) };
19
+ }
20
+ }
21
+ const mapping = new RoundedAnswer({
22
+ protocol: 'json',
23
+ input: 'Answer {{question}}',
24
+ vars: { question: 'the question' },
25
+ schema: Answer,
26
+ lm: new StaticResponse('{"answer":41.8}'),
27
+ examples: [new lf.MappingExample({ input: 'A sample question', output: { answer: 7 } })],
28
+ });
29
+ const prompt = mapping.render(); // synchronous, no model call
30
+ const response = await mapping.call(); // Message<{ answer: number }>
31
+ console.log(response.result.answer); // 42
32
+ ```
33
+
34
+ `Mapping.call` returns a message, following LangFunc. The `query` wrapper still returns the typed value by default, or the message with `returnsMessage: true`. Model/options/variables can be overridden per call without storing invocation state on the Mapping.
35
+
36
+ The hook sequence is `render → transformInput → model.call → postprocessResponse → parseResult → postprocessResult`. Hooks are synchronous; I/O is async. Default `parseResult` parses JSON and invokes the runtime schema. `postprocessResult` receives the validated result; overriding hooks makes the subclass responsible for the validity of its final transformed value. `transformOutput` wraps the result in an AIMessage and retains the original model response as its source. `mappingRequest` exposes a MappingExample without an output; `preamble` and `renderExample` can also be overridden.
37
+
38
+ For text-only examples, a single system section contains the preamble, labeled few-shot examples, request schema, caller instructions, and leading input system sections, in that order. A user section contains the labeled request and output cue. Examples are quoted instruction data, not separate conversation turns. Its input is quoted data: message roles inside an example are flattened while modality parts are retained. If any example contains a modality, all examples instead precede the request as user sections, preserving example order and allowing provider image inputs. Text-only prompt parity remains unchanged. String example inputs are literal; use an explicit Template for interpolation. Example outputs are JSON input values for the schema, not schema-transformed instances, and are validated before calling the model. An example can supply its own schema; otherwise it inherits the Mapping's schema. `hasOutput` distinguishes a request from an example even when its output is null. Few-shot examples are currently supported only on prompt-based structured queries; text/native calls reject them explicitly.
39
+
40
+ Parsing, schema, and postprocessing failures throw `MappingError`, which extends `OutputValidationError` for compatibility and exposes `lmResponse` and `cause`. The associated model cache entry is evicted before the error reaches the caller. Native structured queries use the same error/eviction behavior. If eviction fails, `cacheInvalidationError` records that failure without hiding the original mapping error. Provider failures remain provider errors. Failed outputs are not automatically repaired or retried.
41
+
42
+ `queryStream(prompt, schema, options)` uses LfQuery rendering for prompt-based requests and retains its incremental JSON parser, final schema validation, and existing terminal-event API. Direct Mapping streaming inherits LangFunc's explicit rejection of whole-response transforms: custom `postprocessResponse`/`parseResult` hooks are not silently applied to incremental chunks. A custom subclass should use `call` until transform-aware Mapping streaming is designed.
43
+
44
+ Python-style constructors are the default, with explicit JSON mode. See [Python protocol](PYTHON_PROTOCOL.md). Python protocol/version registries, arbitrary symbolic inputs, autofix, fallback values, prompt cache tiers, and PyGlove schema/class context are not ported. The public construction and extension structure is retained without reproducing those Python runtime dependencies.
45
+
46
+ ## Class templates and preambles
47
+
48
+ TypeScript uses `static templateStr` as the explicit equivalent of Python's docstring-derived class template. It is inherited by subclasses. `new Greeting({ name: 'Ada' })` binds variables to that class default; `new Greeting('Hi {{name}}!', { name: 'Ada' })` overrides it. LangFunc accepts `new Ask({ lm, vars })` or its existing `(templateStr, options)` form. Comments and docstrings are not read at runtime.
49
+
50
+ Mapping declares `static templateStr = '{{instructions}}{{request}}'`. A subclass can override it, or an instance can pass `templateStr`. Its generated `instructions` and `request` variables are role-aware Messages, so template composition preserves their roles and modalities. Other bound variables remain available. No Jinja blocks or method-call expressions are added.
51
+
52
+ `Mapping.preamble` defaults to an empty Template. `LfQuery.preamble` follows the selected protocol. Explicit JSON mode supplies the JSON-specific contract and built-in arithmetic demonstration. Pass `preamble: new Template(...)` or override its getter to customize it. Templates can refer to `inputTitle`, `outputTitle`, and `schemaTitle`; JSON mode uses `INPUT_OBJECT`, `JSON`, and `SCHEMA`; Python mode matches v2 labels: `REQUEST`, `OUTPUT PYTHON OBJECT`, and `OUTPUT PYTHON TYPE`, including its built-in demonstration. Its JSON Schema/direct-result example deliberately differs from Python's `_type`/`result` protocol. Null is allowed only when the schema permits it.
53
+
54
+ The live Python fixture comparison now passes all five cases after fixed protocol adaptations; exact prompt equality remains false. See [prompt parity](PROMPT_PARITY.md).
package/docs/OPENAI.md ADDED
@@ -0,0 +1,40 @@
1
+ # OpenAI Responses adapter
2
+
3
+ `OpenAI extends LanguageModel` is available as `lf.llms.OpenAI` and from `langfx.js/llms/openai`. It uses the Responses API, matching the current Python OpenAI class. Text generation, client functions, strict native JSON output, and text streaming are implemented and fixture-tested. No live inference has been run.
4
+
5
+ ```ts
6
+ import { OpenAI } from 'langfx.js/llms/openai';
7
+
8
+ const lm = new OpenAI({
9
+ model: 'your-model-id',
10
+ apiKey: async signal => obtainAppCredential(signal), // application-owned
11
+ });
12
+ const result = await lm.call('Explain closures.', { maxTokens: 1024 });
13
+ for await (const chunk of lm.stream('Explain generators.')) {
14
+ console.log(chunk.delta.text);
15
+ }
16
+ ```
17
+
18
+ The default endpoint is `https://api.openai.com/v1/responses`. `baseUrl` changes the API version root; `transport` accepts a fetch-like implementation. Credentials are explicit and sent as a Bearer header; omitting them supports a host-authenticated gateway. There are no environment lookups, SDK dependencies, implicit model defaults, or model-name-based option rewrites. Browser hosting, credentials, and CORS remain application responsibilities.
19
+
20
+ Requests set `store: false`, send the caller's explicit transcript, and include `reasoning.encrypted_content`. They do not use server-managed conversations or previous_response_id. System/user/assistant text stays role-scoped; `maxTokens` maps to max_output_tokens. Returned supported output items are retained in model/endpoint-scoped metadata for subsequent requests. See the [Responses reference](https://developers.openai.com/api/reference/cli/resources/responses/methods/create).
21
+
22
+ Client tools use function_call and function_call_output items, correlated by call_id. The original function-item ID and ordered reasoning items are preserved on replay. Tool schemas are sent with `strict: false` to avoid silently changing their required fields; app-side argument validation still runs before effects. Tool choice supports auto, none, any (mapped to required), and a registered name. See [function calling](https://developers.openai.com/api/docs/guides/function-calling).
23
+
24
+ `nativeStructuredOutput: true` uses `text.format` with json_schema and strict validation. The current adapter accepts an object root, required properties, additionalProperties:false, scalar/nullable types, arrays/items, enum, and nested anyOf. Every object property must be required. Unsupported keywords, optional object properties, and open objects reject before transport. It never silently makes fields required. For Zod schemas, use explicitly strict objects; otherwise use the default prompt-JSON path. Local schema parsing runs on every completed result. This is a conservative subset of [Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs).
25
+
26
+ Text streaming uses output_text.delta and requires response.completed with matching response identity and complete text. Final usage and continuation data come from that completed response. Refusal, failure, incomplete status, missing termination, or inconsistent output cannot produce a success event. Encrypted reasoning is retained as opaque metadata, not emitted as visible text. An absent encrypted continuation rejects rather than silently losing required context. Early consumer exit closes the SSE reader. See [streaming responses](https://developers.openai.com/api/docs/guides/streaming-responses).
27
+
28
+ Shared retry, cancellation, cache, Mapping, queryStream, Agent, and Session behavior applies. HTTP auth/rate/server failures are classified without exposing provider response bodies. Input/output/total token counts are passed through when reported; missing usage stays unknown. Failed-attempt billing is not reconstructed.
29
+
30
+ ## Current limits
31
+
32
+ No files/audio, Chat Completions, Azure-specific auth, hosted tools, background jobs, or server-managed conversation state. Streamed function calls support start/delta/end events, interleaved calls, and strict completion checks. See [tool streaming](TOOL_STREAMING.md). Unknown output item types fail explicitly. Schema constraints beyond the documented subset and model-specific option support remain outside this initial adapter. Compatibility of third-party Responses endpoints is not assumed.
33
+
34
+ `npm run check` covers mocked wire responses, native schemas, tool execution, reasoning continuation, fragmented SSE, error classification, truncation, cancellation, and cache replay. The browser/worker example includes a mocked Responses tool round trip. Live-provider and model-specific behavior still need separately configured verification.
35
+
36
+ ## Image inputs
37
+
38
+ User messages accept ordered text and `Image` parts, including templates and `LfQuery`. HTTP(S) URI images become URL references; bytes and Blob/File images become base64 data URLs in Responses `input_image` blocks with `detail: 'auto'`. The adapter does not download URL images. See the [OpenAI image-input protocol](https://developers.openai.com/api/docs/guides/images-vision).
39
+
40
+ Inline images require PNG, JPEG, GIF, or WebP MIME types and 1 byte–5 MiB per image (a conservative local adapter bound, not a model limit). Blob reads observe cancellation. Other modalities and images in system, assistant, or tool messages reject. Image contents are not decoded or validated locally; model support and provider limits still apply. Image inputs also work with text output streaming. Tests use mocked transport, not live inference.
@@ -0,0 +1,135 @@
1
+ # TypeScript port proposal
2
+
3
+ Status: implementation started. The initial core foundation is implemented; the feature matrix below remains the target release scope, not a list of completed features. See [implementation status](IMPLEMENTATION_STATUS.md). Source baseline: Langfx commit `2a1ea4e8dcd7075bf745ad707f901ece8546f47d`. Source evidence is collected in [SOURCE_AUDIT.md](SOURCE_AUDIT.md).
4
+
5
+ ## Product and compatibility target
6
+
7
+ The first release should let a developer create a typed, streaming agent that calls application functions, maintains a conversation, and exposes its execution trace to the UI. All orchestration runs in TypeScript. Python is neither a runtime dependency nor a service requirement.
8
+
9
+ Target modern browsers and Web Workers first. Keep the core usable in server JavaScript and desktop webviews through the same interfaces. React Native and native shells require explicit transport/storage/model adapters and their own integration tests; do not advertise universal support before testing those environments.
10
+
11
+ Preserve Python's public classes, constructor-based model selection, method responsibilities, query argument order, and direct return values. Keep `lf.query`, subclassable `LanguageModel`/`LangFunc`/`Action`, and `new Session`; do not introduce a mandatory client/factory layer. Adapt syntax mechanically (`new`, options objects, camelCase, async calls), and document substantive differences in the [API mapping](API_DESIGN.md). Python serialized-object compatibility is not a goal. Do not port pygx first: its symbolic trees, contextual lookup, reflection, serialization registry, and HTML extension system would turn this into a separate language-runtime project. TypeScript interfaces alone also cannot replace runtime schema validation: TypeScript types are erased. Use explicit schema values with inferred TypeScript output types. [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/2/classes)
12
+
13
+ ## Execution model decision
14
+
15
+ Use **one async API for I/O, with Python's familiar unprefixed names**. `LanguageModel.call`/`sample`, `LangFunc.call`, `query`, and `Session.query` return Promises. `stream` and `queryStream` return async iterables directly. Do not port synchronous wrappers, blocking async-to-sync bridges, or `acall`/`asample`/`astream`/`aquery` duplicates. Provider hooks follow the same rule, including fake models and cache hits.
16
+
17
+ Keep local operations synchronous: constructors, message manipulation, `Template.render`, local JSON parsing, and runtime schema validation. LLM-assisted parsing, tool execution, embeddings, remote MCP, and persistent storage use async contracts when ported. Async interfaces do not automatically run work in parallel.
18
+
19
+ The [API execution contract](API_DESIGN.md#execution-model-one-async-api-for-io) specifies the method mapping and return types. M0 API/type checks must verify Promise and AsyncIterable signatures, synchronous local helpers, and the absence of sync wrappers and duplicate `a`-prefixed exports.
20
+
21
+ ## Feature decisions
22
+
23
+ P0 means required for the first useful release. P1 means a separate follow-up milestone. Omit means deliberately outside this port's scope, unless a later product requirement changes it.
24
+
25
+ | Langfx feature / source area | Decision | TypeScript treatment and rationale |
26
+ | --- | --- | --- |
27
+ | `Message`, role sections, `ComplexMessage`, provenance | P0, redesign representation | Retain public Message subclasses and ComplexMessage composition. Internally normalize role-tagged messages and ordered content parts to arrays at the provider boundary. Preserve ordering, tool correlation, and provenance IDs. |
28
+ | `Modality`, image/audio/video/PDF/MIME | P0 data model; staged adapter support | Accept bytes, URLs, and browser files via explicit helpers. Ship text/image transport first; other modalities are capability-gated. No automatic downloading or transcoding in constructors. |
29
+ | `Template`, modular prompts, natural-language formatting | P0 class and basic syntax | Retain Template/render, bound variables, composition, and `{{name}}`/property-path interpolation. Query strings use this subset; UserMessage/fromRawStr preserve literal text. Full Jinja is omitted; JavaScript prompt functions cover loops/conditions. |
30
+ | `LangFunc`, input/output transformations | P0 | Retain LangFunc extending Template, model construction options, call/stream, and transformInput/transformOutput hooks. Resolve a Promise to Message as in Python; keep invocation state separate from reusable instances. |
31
+ | `query`, structured inputs, few-shot examples | P0 | One async `query(prompt, schema, { lm, ... })` returning the typed value directly; unstructured overload returns text. Keep returnsMessage, explicit examples/system messages, and independent prompt rendering/output validation. |
32
+ | JSON schema conversion and provider-native structured output | P0 | One schema adapter contract for output and tool arguments. Validate every completed response locally. Use native output constraints where supported, with explicit prompt-JSON fallback. Reject unsupported schema features rather than silently weakening them. |
33
+ | `query_stream`, incremental JSON parser and events | P0 | Text, tool-argument, and structured field events through `AsyncIterable`. Distinguish partial/unvalidated values from the final validated result. This is central to front-end usefulness. |
34
+ | Python structured-output protocol | Restricted default subset | Named classes/unions and constructor-style literals with explicit TypeScript factories. No generated-code evaluation; no full Python compatibility. See [Python protocol](PYTHON_PROTOCOL.md). |
35
+ | `parse`, `describe`, `complete`, `call` helpers | P1 | Preserve LLM-assisted parsing/describing as convenience functions over `query`. Distinguish those from local JSON validation. Rework completion as explicit missing-field paths; never conflate missing, unknown, and null. Fold overlapping `call` behavior into `query`. |
36
+ | `autofix` / repair | P1, JSON only | Bounded, visible repair requests after validation errors, consuming the same run budget. No generated-code execution or silent fallback to a wrong type. |
37
+ | Multi-model queries, multi-sample fan-out | P1 | Explicit batch API with bounded concurrency and per-item results. P0 uses a single candidate per request; do not overload return types based on a model array. |
38
+ | `LanguageModel`, sampling/options/errors/usage | P0 | LanguageModel base class with provider subclasses, public call/sample/stream and protected implementation hooks. Inject transport/credentials; keep typed SamplingOptions extensions, capability checks, errors, and unknown usage. |
39
+ | OpenAI, Anthropic, Gemini and converters | P0, independent entry points | Implement one provider end-to-end, then the other two before claiming three-provider support. Translate messages, tool calls/results, usage, refusal, and streaming events in each adapter. Exact wire endpoints and schema subsets require adapter-time verification. |
40
+ | OpenAI-compatible, Groq, DeepSeek, Azure | P1 | Reuse compatible transport where verified; test each dialect. A common endpoint shape does not imply identical tools/streaming/schema behavior. |
41
+ | Vertex AI and cloud credentials | P1, host integration | Accept host-supplied authorized transport. Do not bundle Python Google Auth or cloud credential discovery into browser core. |
42
+ | Apple Intelligence, llama.cpp | P1 adapter boundary | Apple integration needs a native host bridge. Existing `LlamaCppRemote` is an HTTP client, not in-browser inference. A WebGPU/WASM adapter is new work, not a mechanical port. |
43
+ | Veo/video generation | Defer beyond P1 | Specialized long-running generation/polling is not required for the initial interactive agent runtime. Video input representation does not imply video generation support. |
44
+ | Model catalog, pricing, aliases, `RandomChoice` | P1, simplify | Provider constructors accept model IDs immediately. Add optional versioned catalog metadata, thin named-model subclasses, explicit registration/get, and selection policies later. Do not ship a mandatory historical catalog or treat stale prices as authoritative. |
45
+ | `ToolCall`, tool schema, tool choice | P0 | Explicit named tools with runtime argument validation and app-owned execution functions. Preserve call IDs, output/error messages, and provider continuation metadata. |
46
+ | `Action`, `Session`, trace/events, progress | P0 | Subclassable Action with call hook and invoke lifecycle wrapper; new Session with query/tracing/logging. Invocation-bound Session views carry child context and per-run state. Preserve stable IDs, deadlines, progress, and usage aggregation. |
47
+ | Automatic bounded tool loop | P0 addition | Assemble the existing concepts into a loop with step/tool/deadline budgets, cancellation, policy hooks, and terminal states. Keep lower-level actions independently usable. |
48
+ | `Memory`, `ConversationHistory` | Omit for now | Not commonly needed for this port. Applications own conversation history; no memory namespace or Agent memory option. |
49
+ | Response cache, TTL, cache seeds | P0 interface and memory cache | Canonical keys include provider/model, roles, content, tools, schema, all output-affecting settings, and application namespace. Cache only successful completed model responses; tool execution is never automatically memoized. |
50
+ | Prompt caching / `CachePoint` | P0 metadata; P1 placement policy | Distinct from local response caching. Preserve an explicit hint in the message representation and usage fields; adapters implement only verified support. No promise that client flags disable provider-side implicit caching. |
51
+ | Persistence of cache/conversations/traces | P1 | Async storage interface plus optional IndexedDB adapter. Versioned JSON records and separate binary attachments. Opt-in retention; never persist credentials with records. |
52
+ | Retry/concurrency helpers | P0 semantics | Abortable backoff, bounded queues, per-provider limits, run deadlines. Retry eligible model requests before visible stream output; no automatic replay after output or side effects. No thread pools or synchronous adapters. |
53
+ | Fake models (`Echo`, static response/map/sequence) | P0 | Deterministic model and stream fixtures, including malformed output, tool calls, delays, errors, and cancellation. Keep testing utilities out of production imports. |
54
+ | `mcp` client/session/tool generation | P1 optional module | Remote Streamable HTTP, discovery, explicit tool allowlist, structured/multimodal results, cancellation, and disposal. Runtime JSON Schema replaces dynamically generated Python tool classes. Use an SDK adapter rather than copying a protocol implementation. |
55
+ | MCP stdio / in-process FastMCP | Omit from browser; possible host extension | Browser core cannot spawn a command or host a Python FastMCP object. Desktop/server hosts can supply an external bridge. Do not embed an MCP server into the core. |
56
+ | `EmbeddingModel`, OpenAI/Vertex embeddings | P1 optional module | Small async embedding interface plus adapters. Useful for retrieval but unnecessary for basic agent orchestration; no vector database bundled. |
57
+ | Evaluation examples, metrics, `ActionEval` | P1 subset | Lightweight async dataset runner, match/score callbacks, usage/timing, trace capture, JSON export. Explicit configuration matrices can replace symbolic sweeps. |
58
+ | Evaluation Beam runners, checkpoint monitor, filesystem reports | Omit | Distributed experiment infrastructure and notebook/HTML reporting belong in the Python research toolchain or a future separate product. |
59
+ | Python coding, sandboxing, correction, `function_gen`, `generate_class` | Omit | Do not translate these into unrestricted JavaScript `eval`/`Function`. Applications expose known functions as tools; code execution would require a separately designed isolation product. |
60
+ | Component metaclasses, symbolic rebinding/cloning, wire registries | Omit machinery | Ordinary public classes, explicit runtime schemas/configuration, and versioned JSON. Omitting symbolic machinery does not remove constructors or inheritance. No Python serialized-object compatibility promise. |
61
+ | `context`, `use_settings`, global tracking, sync wrappers | Replace | Model/session defaults plus immutable per-call overrides; invocation-bound Session propagates model, deadline, signal, trace parent, and budget. One Promise/AsyncIterable API. |
62
+ | Console, notebook display, PyGlove HTML views | Omit rendering; P1 UI | Preserve data/events; provide framework-neutral subscriptions, then optional React hooks/viewer. No React dependency in core. |
63
+
64
+ ## Architectural boundaries
65
+
66
+ Start as one TypeScript package with explicit subpath exports, not a multi-package monorepo. The package name is provisional until npm availability and publishing ownership are checked.
67
+
68
+ ```text
69
+ langfx.js messages, prompts, schema contracts, query, actions, sessions
70
+ langfx.js/llms/* LanguageModel subclasses; shared fetch/SSE transport internally
71
+ langfx.js/testing deterministic models and fixtures
72
+ langfx.js/mcp optional remote MCP integration (P1)
73
+ langfx.js/storage optional persistent stores (P1)
74
+ langfx.js/embeddings optional embeddings (P1)
75
+ langfx.js/eval lightweight evaluation (P1)
76
+ langfx.js/react optional UI bindings (P1)
77
+ ```
78
+
79
+ These are proposed exports, not existing imports. Use ESM and generated declarations. Do not import Node built-ins, access the DOM, read environment variables, initialize credentials, or perform network requests at core module import time. The browser/worker core receives platform services explicitly. Support the familiar root `lf.llms` namespace with side-effect-free exports, plus direct provider subpaths. Verify tree-shaking removes unused providers; heavy optional/native SDKs stay outside root imports.
80
+
81
+ Use native `fetch`, response streams, and `AbortSignal` behind an injectable transport. Fetch works in windows and workers; response bodies support streaming, and aborting can cancel a request or body consumption. Implement incremental UTF-8 and SSE framing across arbitrary network chunk boundaries. [Fetch documentation](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
82
+
83
+ Keep runtime schema validation behind `Schema<T>`. Recommend an optional Zod adapter for ergonomic inferred types and a JSON Schema adapter for dynamic MCP tools. Select and pin implementations during M0 after checking their browser/CSP behavior and supported schema subsets. Do not build a new general-purpose schema language. Arbitrary schema transforms/refinements may validate locally without being expressible to the model; surface that distinction explicitly.
84
+
85
+ ## Deployment matrix
86
+
87
+ | Application mode | Agent/tool execution | Model access | Remaining infrastructure |
88
+ | --- | --- | --- | --- |
89
+ | Personal browser app with user credentials | Browser/worker | Direct provider endpoint if its auth/CORS permits | No agent backend; credentials remain accessible to that client environment |
90
+ | Public app using an organization credential | Browser/worker | Narrow authenticated credential gateway | Small server/edge/native service for secrets and access control; can be TypeScript, with no Python orchestration |
91
+ | Desktop or native app | TypeScript host/webview | Host-authorized transport or native model bridge | Platform bridge where needed |
92
+ | Fully local/offline app | Browser/host | Optional on-device model adapter | Model assets and compatible runtime; not a P0 guarantee |
93
+
94
+ Direct HTTP support alone does not establish browser compatibility: the server must permit the requesting origin, headers, and authentication flow. `no-cors` is not a workaround because the resulting opaque response is unavailable to the application. [Fetch CORS documentation](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
95
+
96
+ For remote MCP, negotiate a supported protocol version and test the real endpoint's CORS/auth behavior. The pinned 2025-11-25 transport specification distinguishes stdio from Streamable HTTP and defines session/connection lifecycle rules. Follow the chosen SDK/spec version rather than assuming every server uses the same lifecycle. [MCP transport specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports)
97
+
98
+ ## Agent and streaming invariants
99
+
100
+ 1. Each invocation owns its state, context, trace node, and result. Concurrent calls of one action definition cannot overwrite one another. Children receive explicit parent context; a mutable global “current session” is not acceptable.
101
+ 2. An action/model/tool observes the earliest parent or local deadline. Cancellation stops queued work and aborts cooperative I/O. It cannot forcibly stop arbitrary synchronous application code or guarantee that a remote operation was undone.
102
+ 3. Tool names must resolve to explicitly registered functions, and complete arguments must validate before execution. Partial streamed arguments are display-only. Policy hooks can approve, deny, or request application-mediated approval. Such hooks are a new app integration feature.
103
+ 4. The agent loop exposes separate limits for model steps and tool calls, plus deadline and optional usage/cost limits. Budget reservation must account for parallel work; unknown token/cost usage cannot enforce an exact spending ceiling.
104
+ 5. Each stream has one terminal outcome: success, failure, or cancellation. Early consumer exit closes upstream readers. Only the successful final structured event carries a validated `T`; partial field events never authorize tools or state changes.
105
+ 6. Do not automatically retry a tool with side effects, an interrupted emitted stream, or an entire action that might already have changed app state. Transport retry and user-requested rerun are different operations.
106
+ 7. Keep provider continuation blocks opaque and scoped to the provider/model that issued them. Do not assume reasoning/signature/tool metadata can be copied to a different provider unchanged.
107
+ 8. Keep total billed usage separate from locally cached usage and estimates. Do not count one query twice when aggregating child traces into parents.
108
+ 9. Persistence stores data and trace snapshots, not closures or suspended JavaScript execution. Durable resume requires explicit checkpoints and idempotency design; it is not implied by session serialization.
109
+
110
+ ## Implementation order and release gates
111
+
112
+ | Milestone | Concrete output | Gate |
113
+ | --- | --- | --- |
114
+ | M0: contracts and package | Strict TypeScript build; public classes and schema/context contracts; deterministic fake model; paired Python/TypeScript API examples; frozen parity fixtures; schema-adapter choice | Browser and worker imports succeed without Node shims, DOM access, secrets, or network calls; schema inference is checked at compile time |
115
+ | M1: vertical slice | One provider, text/image query, structured JSON output, streaming, local tool round-trip | Browser demonstration completes model → validated app tool → model answer; malformed output and cancellation behave correctly |
116
+ | M2: first release | Three provider adapters, prompt composition/LangFunc, structured stream events, Action/Session, bounded loop, in-memory cache, usage/retries | Contract tests pass for each advertised capability; concurrent runs stay isolated; real-browser integration and bundle checks pass |
117
+ | M3: app integrations | Remote MCP, IndexedDB, UI binding examples | Real CORS/auth integration, disconnect/reconnect/disposal, persistence migration and clear/export tests |
118
+ | M4: optional capabilities | Embeddings, lightweight evals, more providers, query helpers and repair | Independent entry points and capability-specific contract suites |
119
+
120
+ M1 should include a static browser example with a fake model that works without credentials and a real-provider mode configured by the host. Do not publish a package before M2 or describe the initial core as the complete port.
121
+
122
+ Type-check paired Python/TypeScript usage examples to enforce constructor, subclass, method, argument-order, and return-value consistency. Build semantic fixture tests from the Python tests, not line-by-line translated tests. Preserve input/output messages, schema validation cases, trace ancestry, and event ordering, while allowing deliberate API differences. See the audit's regression table. Python may generate development fixtures but must not be needed to build or run the TypeScript package.
123
+
124
+ The M2 browser suite must also cover cancellation during fetch/backoff/tool execution, UTF-8 and SSE fragmentation, interleaved tool calls, schema violations, provider refusals, concurrent session isolation, cache key collisions, and clean worker imports. Use mocked wire responses for repeatability and a small separately configured live-provider smoke suite. Keep credentials out of fixtures.
125
+
126
+ Measure bundle size with core-only and single-provider entry points before setting a numeric budget. Do not invent performance or package-size claims before an implementation exists.
127
+
128
+ ## Decisions to revisit after the first vertical slice
129
+
130
+ - Runtime schema adapter choice and how much JSON Schema is supported uniformly.
131
+ - Whether model-selected typed actions should use native tool calls, JSON discriminated unions, or both in the initial ergonomic API.
132
+ - First real application host: browser/worker is the default; native bridges may change the next milestone.
133
+ - Need for Jinja features beyond the retained basic interpolation/composition subset; a full compatibility adapter would need its own scope.
134
+
135
+ These questions do not block the recommended port boundary. They should be resolved with the M1 example before stabilizing public API names.