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.
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +107 -0
- package/dist/agentic.d.ts +128 -0
- package/dist/agentic.js +265 -0
- package/dist/agentic.js.map +1 -0
- package/dist/cache.d.ts +32 -0
- package/dist/cache.js +135 -0
- package/dist/cache.js.map +1 -0
- package/dist/cancellation.d.ts +9 -0
- package/dist/cancellation.js +70 -0
- package/dist/cancellation.js.map +1 -0
- package/dist/errors.d.ts +40 -0
- package/dist/errors.js +44 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/json-stream.d.ts +73 -0
- package/dist/json-stream.js +222 -0
- package/dist/json-stream.js.map +1 -0
- package/dist/langfunc.d.ts +20 -0
- package/dist/langfunc.js +28 -0
- package/dist/langfunc.js.map +1 -0
- package/dist/language-model.d.ts +78 -0
- package/dist/language-model.js +218 -0
- package/dist/language-model.js.map +1 -0
- package/dist/llms/anthropic.d.ts +30 -0
- package/dist/llms/anthropic.js +365 -0
- package/dist/llms/anthropic.js.map +1 -0
- package/dist/llms/gemini.d.ts +34 -0
- package/dist/llms/gemini.js +380 -0
- package/dist/llms/gemini.js.map +1 -0
- package/dist/llms/images.d.ts +10 -0
- package/dist/llms/images.js +43 -0
- package/dist/llms/images.js.map +1 -0
- package/dist/llms/index.d.ts +7 -0
- package/dist/llms/index.js +4 -0
- package/dist/llms/index.js.map +1 -0
- package/dist/llms/openai.d.ts +30 -0
- package/dist/llms/openai.js +398 -0
- package/dist/llms/openai.js.map +1 -0
- package/dist/llms/transport.d.ts +4 -0
- package/dist/llms/transport.js +90 -0
- package/dist/llms/transport.js.map +1 -0
- package/dist/mapping.d.ts +71 -0
- package/dist/mapping.js +190 -0
- package/dist/mapping.js.map +1 -0
- package/dist/message.d.ts +56 -0
- package/dist/message.js +86 -0
- package/dist/message.js.map +1 -0
- package/dist/python-preview.d.ts +24 -0
- package/dist/python-preview.js +368 -0
- package/dist/python-preview.js.map +1 -0
- package/dist/python-stream.d.ts +15 -0
- package/dist/python-stream.js +29 -0
- package/dist/python-stream.js.map +1 -0
- package/dist/python.d.ts +85 -0
- package/dist/python.js +728 -0
- package/dist/python.js.map +1 -0
- package/dist/query.d.ts +31 -0
- package/dist/query.js +151 -0
- package/dist/query.js.map +1 -0
- package/dist/retry.d.ts +12 -0
- package/dist/retry.js +56 -0
- package/dist/retry.js.map +1 -0
- package/dist/schema/zod.d.ts +4 -0
- package/dist/schema/zod.js +7 -0
- package/dist/schema/zod.js.map +1 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +7 -0
- package/dist/schema.js.map +1 -0
- package/dist/template.d.ts +14 -0
- package/dist/template.js +75 -0
- package/dist/template.js.map +1 -0
- package/dist/testing/index.d.ts +33 -0
- package/dist/testing/index.js +56 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tool-call.d.ts +27 -0
- package/dist/tool-call.js +16 -0
- package/dist/tool-call.js.map +1 -0
- package/dist/tools.d.ts +43 -0
- package/dist/tools.js +155 -0
- package/dist/tools.js.map +1 -0
- package/docs/ANTHROPIC.md +43 -0
- package/docs/API_DESIGN.md +200 -0
- package/docs/GEMINI.md +68 -0
- package/docs/IMPLEMENTATION_STATUS.md +77 -0
- package/docs/LIVE_TESTING.md +24 -0
- package/docs/MAPPING.md +54 -0
- package/docs/OPENAI.md +40 -0
- package/docs/PORTING_PLAN.md +135 -0
- package/docs/PROMPT_PARITY.md +379 -0
- package/docs/PYTHON_PROTOCOL.md +109 -0
- package/docs/PYTHON_PROTOCOL_PARITY.md +964 -0
- package/docs/PYTHON_SCHEMA_EVALUATION.md +93 -0
- package/docs/PYTHON_STREAMING_PARITY.md +59 -0
- package/docs/RELEASING.md +25 -0
- package/docs/RETRIES_AND_CACHE.md +56 -0
- package/docs/SESSION_EVENTS.md +40 -0
- package/docs/SOURCE_AUDIT.md +133 -0
- package/docs/STREAMING.md +76 -0
- package/docs/TOOL_STREAMING.md +31 -0
- package/package.json +95 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# LfQuery prompt parity report
|
|
2
|
+
|
|
3
|
+
Python source: `0a17208b4290e470e398892c6232819225289cc0` (clean checkout), protocol `json:1.0`.
|
|
4
|
+
|
|
5
|
+
Python prompts were executed, not transcribed. Both runtimes were also checked against their query wrapper. Inputs are equivalent already-rendered messages, with equivalent integer/object schemas. No inference or credentials were used.
|
|
6
|
+
|
|
7
|
+
Normalization only trims section boundaries and converts CRLF to LF. It does not erase roles, labels, examples, or schema representations.
|
|
8
|
+
|
|
9
|
+
| Case | Exact parity | Normalized parity | Protocol-adapted parity | Python roles | JS roles |
|
|
10
|
+
| --- | --- | --- | --- | --- | --- |
|
|
11
|
+
| integer | FAIL | FAIL | PASS | system, user | system, user |
|
|
12
|
+
| object | FAIL | FAIL | PASS | system, user | system, user |
|
|
13
|
+
| system-message | FAIL | FAIL | PASS | system, user | system, user |
|
|
14
|
+
| few-shot | FAIL | FAIL | PASS | system, user | system, user |
|
|
15
|
+
| few-shot-and-system | FAIL | FAIL | PASS | system, user | system, user |
|
|
16
|
+
|
|
17
|
+
## Differences
|
|
18
|
+
|
|
19
|
+
- Prompt structure is aligned: protocol preamble and demonstration, examples, request schema, caller instructions, then labeled user request and output cue.
|
|
20
|
+
- Fixed adaptations are listed in `tests/fixtures/prompt-protocol-adaptations.json`. They replace only the protocol-specific preamble, known schema representations, and example result envelopes; roles, order, and user text remain intact.
|
|
21
|
+
- Deliberate protocol differences: Python’s JSON protocol uses a `result` envelope, Python-style schema descriptions, and `_type` instructions. JS uses standard JSON Schema and direct output values. Literal prompt equality requires resolving these differences, not merely copying the preamble.
|
|
22
|
+
|
|
23
|
+
This comparison does not cover media, custom class templates, native output, Python-protocol prompts, provider wire encoding, or response quality. Exact equality is still not claimed; it does not claim full semantic equivalence.
|
|
24
|
+
|
|
25
|
+
## Captured prompts
|
|
26
|
+
|
|
27
|
+
### integer
|
|
28
|
+
|
|
29
|
+
**Python**
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
[system]
|
|
33
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
34
|
+
|
|
35
|
+
INSTRUCTIONS:
|
|
36
|
+
1. If the schema has `_type`, carry it over to the JSON output.
|
|
37
|
+
2. If a field from the schema cannot be extracted from the response, use null as the JSON value.
|
|
38
|
+
|
|
39
|
+
SCHEMA:
|
|
40
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": int}}
|
|
41
|
+
|
|
42
|
+
INPUT_OBJECT:
|
|
43
|
+
1 + 1 =
|
|
44
|
+
|
|
45
|
+
JSON:
|
|
46
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": 2}}
|
|
47
|
+
|
|
48
|
+
SCHEMA:
|
|
49
|
+
{"result": int}
|
|
50
|
+
|
|
51
|
+
[user]
|
|
52
|
+
INPUT_OBJECT:
|
|
53
|
+
What is 6 * 7?
|
|
54
|
+
|
|
55
|
+
JSON:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**TypeScript**
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
[system]
|
|
63
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
64
|
+
|
|
65
|
+
INSTRUCTIONS:
|
|
66
|
+
1. Return only JSON matching the JSON Schema, without Markdown fences or a result wrapper.
|
|
67
|
+
2. Use null only when the schema permits it; do not invent missing facts.
|
|
68
|
+
|
|
69
|
+
SCHEMA:
|
|
70
|
+
{"type":"object","properties":{"final_answer":{"type":"integer"}},"required":["final_answer"]}
|
|
71
|
+
|
|
72
|
+
INPUT_OBJECT:
|
|
73
|
+
1 + 1 =
|
|
74
|
+
|
|
75
|
+
JSON:
|
|
76
|
+
{"final_answer":2}
|
|
77
|
+
|
|
78
|
+
SCHEMA:
|
|
79
|
+
{"type":"integer"}
|
|
80
|
+
|
|
81
|
+
[user]
|
|
82
|
+
INPUT_OBJECT:
|
|
83
|
+
What is 6 * 7?
|
|
84
|
+
|
|
85
|
+
JSON:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### object
|
|
90
|
+
|
|
91
|
+
**Python**
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
[system]
|
|
95
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
96
|
+
|
|
97
|
+
INSTRUCTIONS:
|
|
98
|
+
1. If the schema has `_type`, carry it over to the JSON output.
|
|
99
|
+
2. If a field from the schema cannot be extracted from the response, use null as the JSON value.
|
|
100
|
+
|
|
101
|
+
SCHEMA:
|
|
102
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": int}}
|
|
103
|
+
|
|
104
|
+
INPUT_OBJECT:
|
|
105
|
+
1 + 1 =
|
|
106
|
+
|
|
107
|
+
JSON:
|
|
108
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": 2}}
|
|
109
|
+
|
|
110
|
+
SCHEMA:
|
|
111
|
+
{"result": {"answer": int}}
|
|
112
|
+
|
|
113
|
+
[user]
|
|
114
|
+
INPUT_OBJECT:
|
|
115
|
+
What is 6 * 7?
|
|
116
|
+
|
|
117
|
+
JSON:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**TypeScript**
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
[system]
|
|
125
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
126
|
+
|
|
127
|
+
INSTRUCTIONS:
|
|
128
|
+
1. Return only JSON matching the JSON Schema, without Markdown fences or a result wrapper.
|
|
129
|
+
2. Use null only when the schema permits it; do not invent missing facts.
|
|
130
|
+
|
|
131
|
+
SCHEMA:
|
|
132
|
+
{"type":"object","properties":{"final_answer":{"type":"integer"}},"required":["final_answer"]}
|
|
133
|
+
|
|
134
|
+
INPUT_OBJECT:
|
|
135
|
+
1 + 1 =
|
|
136
|
+
|
|
137
|
+
JSON:
|
|
138
|
+
{"final_answer":2}
|
|
139
|
+
|
|
140
|
+
SCHEMA:
|
|
141
|
+
{"type":"object","properties":{"answer":{"type":"integer"}},"required":["answer"]}
|
|
142
|
+
|
|
143
|
+
[user]
|
|
144
|
+
INPUT_OBJECT:
|
|
145
|
+
What is 6 * 7?
|
|
146
|
+
|
|
147
|
+
JSON:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### system-message
|
|
152
|
+
|
|
153
|
+
**Python**
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
[system]
|
|
157
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
158
|
+
|
|
159
|
+
INSTRUCTIONS:
|
|
160
|
+
1. If the schema has `_type`, carry it over to the JSON output.
|
|
161
|
+
2. If a field from the schema cannot be extracted from the response, use null as the JSON value.
|
|
162
|
+
|
|
163
|
+
SCHEMA:
|
|
164
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": int}}
|
|
165
|
+
|
|
166
|
+
INPUT_OBJECT:
|
|
167
|
+
1 + 1 =
|
|
168
|
+
|
|
169
|
+
JSON:
|
|
170
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": 2}}
|
|
171
|
+
|
|
172
|
+
SCHEMA:
|
|
173
|
+
{"result": int}
|
|
174
|
+
|
|
175
|
+
Be concise.
|
|
176
|
+
|
|
177
|
+
[user]
|
|
178
|
+
INPUT_OBJECT:
|
|
179
|
+
What is 6 * 7?
|
|
180
|
+
|
|
181
|
+
JSON:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**TypeScript**
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
[system]
|
|
189
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
190
|
+
|
|
191
|
+
INSTRUCTIONS:
|
|
192
|
+
1. Return only JSON matching the JSON Schema, without Markdown fences or a result wrapper.
|
|
193
|
+
2. Use null only when the schema permits it; do not invent missing facts.
|
|
194
|
+
|
|
195
|
+
SCHEMA:
|
|
196
|
+
{"type":"object","properties":{"final_answer":{"type":"integer"}},"required":["final_answer"]}
|
|
197
|
+
|
|
198
|
+
INPUT_OBJECT:
|
|
199
|
+
1 + 1 =
|
|
200
|
+
|
|
201
|
+
JSON:
|
|
202
|
+
{"final_answer":2}
|
|
203
|
+
|
|
204
|
+
SCHEMA:
|
|
205
|
+
{"type":"integer"}
|
|
206
|
+
|
|
207
|
+
Be concise.
|
|
208
|
+
|
|
209
|
+
[user]
|
|
210
|
+
INPUT_OBJECT:
|
|
211
|
+
What is 6 * 7?
|
|
212
|
+
|
|
213
|
+
JSON:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### few-shot
|
|
218
|
+
|
|
219
|
+
**Python**
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
[system]
|
|
223
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
224
|
+
|
|
225
|
+
INSTRUCTIONS:
|
|
226
|
+
1. If the schema has `_type`, carry it over to the JSON output.
|
|
227
|
+
2. If a field from the schema cannot be extracted from the response, use null as the JSON value.
|
|
228
|
+
|
|
229
|
+
SCHEMA:
|
|
230
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": int}}
|
|
231
|
+
|
|
232
|
+
INPUT_OBJECT:
|
|
233
|
+
1 + 1 =
|
|
234
|
+
|
|
235
|
+
JSON:
|
|
236
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": 2}}
|
|
237
|
+
|
|
238
|
+
SCHEMA:
|
|
239
|
+
{"result": int}
|
|
240
|
+
|
|
241
|
+
INPUT_OBJECT:
|
|
242
|
+
1 + 1?
|
|
243
|
+
|
|
244
|
+
JSON:
|
|
245
|
+
{"result": 2}
|
|
246
|
+
|
|
247
|
+
SCHEMA:
|
|
248
|
+
{"result": int}
|
|
249
|
+
|
|
250
|
+
[user]
|
|
251
|
+
INPUT_OBJECT:
|
|
252
|
+
What is 6 * 7?
|
|
253
|
+
|
|
254
|
+
JSON:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**TypeScript**
|
|
259
|
+
|
|
260
|
+
```text
|
|
261
|
+
[system]
|
|
262
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
263
|
+
|
|
264
|
+
INSTRUCTIONS:
|
|
265
|
+
1. Return only JSON matching the JSON Schema, without Markdown fences or a result wrapper.
|
|
266
|
+
2. Use null only when the schema permits it; do not invent missing facts.
|
|
267
|
+
|
|
268
|
+
SCHEMA:
|
|
269
|
+
{"type":"object","properties":{"final_answer":{"type":"integer"}},"required":["final_answer"]}
|
|
270
|
+
|
|
271
|
+
INPUT_OBJECT:
|
|
272
|
+
1 + 1 =
|
|
273
|
+
|
|
274
|
+
JSON:
|
|
275
|
+
{"final_answer":2}
|
|
276
|
+
|
|
277
|
+
SCHEMA:
|
|
278
|
+
{"type":"integer"}
|
|
279
|
+
|
|
280
|
+
INPUT_OBJECT:
|
|
281
|
+
1 + 1?
|
|
282
|
+
|
|
283
|
+
JSON:
|
|
284
|
+
2
|
|
285
|
+
|
|
286
|
+
SCHEMA:
|
|
287
|
+
{"type":"integer"}
|
|
288
|
+
|
|
289
|
+
[user]
|
|
290
|
+
INPUT_OBJECT:
|
|
291
|
+
What is 6 * 7?
|
|
292
|
+
|
|
293
|
+
JSON:
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### few-shot-and-system
|
|
298
|
+
|
|
299
|
+
**Python**
|
|
300
|
+
|
|
301
|
+
```text
|
|
302
|
+
[system]
|
|
303
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
304
|
+
|
|
305
|
+
INSTRUCTIONS:
|
|
306
|
+
1. If the schema has `_type`, carry it over to the JSON output.
|
|
307
|
+
2. If a field from the schema cannot be extracted from the response, use null as the JSON value.
|
|
308
|
+
|
|
309
|
+
SCHEMA:
|
|
310
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": int}}
|
|
311
|
+
|
|
312
|
+
INPUT_OBJECT:
|
|
313
|
+
1 + 1 =
|
|
314
|
+
|
|
315
|
+
JSON:
|
|
316
|
+
{"result": {"_type": "langfx.structured.query.Answer", "final_answer": 2}}
|
|
317
|
+
|
|
318
|
+
SCHEMA:
|
|
319
|
+
{"result": int}
|
|
320
|
+
|
|
321
|
+
INPUT_OBJECT:
|
|
322
|
+
1 + 1?
|
|
323
|
+
|
|
324
|
+
JSON:
|
|
325
|
+
{"result": 2}
|
|
326
|
+
|
|
327
|
+
SCHEMA:
|
|
328
|
+
{"result": int}
|
|
329
|
+
|
|
330
|
+
Be concise.
|
|
331
|
+
|
|
332
|
+
[user]
|
|
333
|
+
INPUT_OBJECT:
|
|
334
|
+
What is 6 * 7?
|
|
335
|
+
|
|
336
|
+
JSON:
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**TypeScript**
|
|
341
|
+
|
|
342
|
+
```text
|
|
343
|
+
[system]
|
|
344
|
+
Please respond to the last INPUT_OBJECT with JSON according to SCHEMA:
|
|
345
|
+
|
|
346
|
+
INSTRUCTIONS:
|
|
347
|
+
1. Return only JSON matching the JSON Schema, without Markdown fences or a result wrapper.
|
|
348
|
+
2. Use null only when the schema permits it; do not invent missing facts.
|
|
349
|
+
|
|
350
|
+
SCHEMA:
|
|
351
|
+
{"type":"object","properties":{"final_answer":{"type":"integer"}},"required":["final_answer"]}
|
|
352
|
+
|
|
353
|
+
INPUT_OBJECT:
|
|
354
|
+
1 + 1 =
|
|
355
|
+
|
|
356
|
+
JSON:
|
|
357
|
+
{"final_answer":2}
|
|
358
|
+
|
|
359
|
+
SCHEMA:
|
|
360
|
+
{"type":"integer"}
|
|
361
|
+
|
|
362
|
+
INPUT_OBJECT:
|
|
363
|
+
1 + 1?
|
|
364
|
+
|
|
365
|
+
JSON:
|
|
366
|
+
2
|
|
367
|
+
|
|
368
|
+
SCHEMA:
|
|
369
|
+
{"type":"integer"}
|
|
370
|
+
|
|
371
|
+
Be concise.
|
|
372
|
+
|
|
373
|
+
[user]
|
|
374
|
+
INPUT_OBJECT:
|
|
375
|
+
What is 6 * 7?
|
|
376
|
+
|
|
377
|
+
JSON:
|
|
378
|
+
|
|
379
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Python-style constructor protocol
|
|
2
|
+
|
|
3
|
+
Python-style output is the default for prompt-based structured `query`, `Session.query`, `Mapping`, and `LfQuery`. Set `protocol: 'json'` explicitly for JSON Schema/Zod schemas without Python codecs. No schema means ordinary text output, unchanged. JSON Schema also supports unions; the benefit here is named constructor syntax and explicit construction of TypeScript class instances.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import * as lf from 'langfx.js';
|
|
7
|
+
|
|
8
|
+
class Foo { constructor(readonly text: string) {} }
|
|
9
|
+
class Bar { constructor(readonly count: number) {} }
|
|
10
|
+
|
|
11
|
+
const FooSchema = lf.python.classSchema({
|
|
12
|
+
name: 'Foo',
|
|
13
|
+
fields: { text: lf.python.str },
|
|
14
|
+
create: fields => new Foo(fields.text),
|
|
15
|
+
isInstance: (value): value is Foo => value instanceof Foo,
|
|
16
|
+
decompose: value => ({ text: value.text }),
|
|
17
|
+
});
|
|
18
|
+
const BarSchema = lf.python.classSchema({
|
|
19
|
+
name: 'Bar',
|
|
20
|
+
fields: { count: lf.python.int },
|
|
21
|
+
create: fields => new Bar(fields.count),
|
|
22
|
+
isInstance: (value): value is Bar => value instanceof Bar,
|
|
23
|
+
decompose: value => ({ count: value.count }),
|
|
24
|
+
});
|
|
25
|
+
const FooOrBar = lf.python.union(FooSchema, BarSchema);
|
|
26
|
+
const result = await lf.query('Choose a response.', FooOrBar, {
|
|
27
|
+
lm, // protocol: 'python' is the default
|
|
28
|
+
}); // Foo | Bar
|
|
29
|
+
if (result instanceof Foo) console.log(result.text);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Schemas can also be assigned to a class's static `schema` property, allowing `lf.python.union(Foo, Bar)`. JavaScript cannot overload the `|` operator for runtime schemas.
|
|
33
|
+
|
|
34
|
+
The prompt renders `Union[Foo, Bar]` and Python class declarations. Responses such as `Foo(text="hello")` or `output = Bar(count=7)` are parsed locally. One complete Markdown fence (optionally tagged `python`) is accepted. Nested constructors, `list(schema)`, string-keyed `dict(schema)`, `str`, `int`, `float`, `bool`, and `none` are supported. `union(schema, none)` expresses nullable values. Fields are required unless a validated default is supplied; extra fields reject unless an `extraFields` schema is supplied. Integer values must be safe JavaScript integers.
|
|
35
|
+
|
|
36
|
+
The parser accepts only data literals and registered keyword constructors. It never uses eval/Function or runs generated Python/JavaScript. Imports, attributes, positional arguments, arithmetic, comprehensions, executable statements, and unknown constructors reject. Parsing is bounded to 1,048,576 UTF-16 units and depth 64. Class factories and schema codecs are application code and must be pure; union validation may probe alternatives. An entire response's syntax is checked before decoding invokes factories. Type validation of nested structures may invoke inner factories before an outer validation failure.
|
|
37
|
+
|
|
38
|
+
`decompose` and `isInstance` support few-shot examples: provide actual instances in `MappingExample.output`. The formatter renders constructor syntax and the example is checked against its schema. `lf.python.parse`, compact `format`, expanded fenced `formatExample`, and `schemaText` expose the same local operations. These schemas also carry JSON Schema; Unions try alternatives in declaration order, including overlapping numeric alternatives. Constructor names distinguish otherwise identical class field shapes.
|
|
39
|
+
|
|
40
|
+
This is a supported subset, not a Python interpreter or a promise of parity for all schemas. A missing-value object model, automatic Python enum/class introspection, arbitrary Zod-to-Python conversion, repair/code execution, and complete Python incremental-parser parity are not implemented. Native structured output is JSON-specific and cannot be combined with `protocol: 'python'`. Structured `queryStream` emits raw `textDelta` events and best-effort frozen field previews in Python mode, then parses and validates once at completion. See [streaming](STREAMING.md) for the supported preview syntax and its limits. `nativeStructuredOutput: true` selects JSON when protocol is omitted. Existing JSON prompt fixtures and their documented adaptations are unchanged.
|
|
41
|
+
|
|
42
|
+
## Python comparison
|
|
43
|
+
|
|
44
|
+
`npm run check:python-parity` checks nine real Python captures: integer, class, both union branches, nested classes, few-shot examples, a nullable class list, nested examples, and examples with a system message. Full role/text arrays match exactly (9/9), without normalization, as do parsed values/class identity. See [the full comparison](PYTHON_PROTOCOL_PARITY.md).
|
|
45
|
+
|
|
46
|
+
Regenerate captures with `../langfx/.venv/bin/python scripts/capture-python-protocol.py ../langfx`. The fixture records the actual Python commit and dirty state. Normal checks require only Node. The captured Python checkout requires explicit PyGlove ValueSpecs for top-level union cases; raw `Foo | Bar` annotations are rejected in its prompt-schema conversion path.
|
|
47
|
+
|
|
48
|
+
## Broader feature evaluation
|
|
49
|
+
|
|
50
|
+
The nine exact-prompt cases do not imply general Python schema parity. See [the comprehensive feature evaluation](PYTHON_SCHEMA_EVALUATION.md) for constraints, defaults, string grammar, inheritance, recursive types, and observed validation differences. `npm run evaluate:schemas` reports known gaps rather than treating them as passing parity tests.
|
|
51
|
+
|
|
52
|
+
## Schema features
|
|
53
|
+
|
|
54
|
+
The built-in helpers support the following explicit definitions:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const p = lf.python;
|
|
58
|
+
const Choice = p.literal('accept', 'reject');
|
|
59
|
+
const Score = p.integer({ min: 0, max: 10 });
|
|
60
|
+
const Confidence = p.number({ min: 0, max: 1 });
|
|
61
|
+
const Label = p.string({ pattern: '[A-Z]+' });
|
|
62
|
+
const Scores = p.list(Score, { minSize: 1, maxSize: 5 });
|
|
63
|
+
const Pair = p.tuple(p.str, Score); // TypeScript tuple array
|
|
64
|
+
const Result = p.record({ decision: Choice, score: Score });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`classSchema` accepts `defaults`, `description`, `fieldDescriptions`, `excludeFromPrompt`, and `extraFields`. Defaults are validated and copied through the field codec for each new instance. Excluded fields need defaults and are omitted from schema declarations; serialized values still contain them, matching Python. Use `extraFields: p.any` to allow additional data-valued keywords, or provide a narrower schema.
|
|
68
|
+
|
|
69
|
+
Use `withSubclasses(BaseSchema, ChildSchema)` to explicitly allow child constructors. Declare inherited fields in each child schema. The query graph renders registered child declarations as `class Child(Base)`. Standalone child schemas retain their own declarations; registration does not discover classes globally. Conflicting parent registrations reject. `lazy('Node', () => NodeSchema)` provides named recursive references; define its target before rendering or parsing. Exported JSON Schema includes the reachable `$defs`.
|
|
70
|
+
|
|
71
|
+
The data parser supports tuples, hexadecimal/octal/binary and underscore-separated numbers, raw/triple/adjacent strings, and common Unicode/control escapes. Integer schemas reject floating literals such as `7.0`. Booleans remain distinct from numbers, integers must be safe JavaScript integers, and duplicate dictionary keys reject. Regex constraints use JavaScript syntax with a start anchor; Python-specific regex syntax is not portable. `any` accepts data literals without executing constructors; JavaScript values do not preserve Python's numeric or tuple identity when a schema does not specify it.
|
|
72
|
+
|
|
73
|
+
## Sets and unknown values
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const Numbers = lf.python.set(lf.python.int);
|
|
77
|
+
const numbers = lf.python.parse(Numbers, '{1, 2, 1}'); // Set<number>
|
|
78
|
+
const empty = lf.python.parse(Numbers, 'set()');
|
|
79
|
+
|
|
80
|
+
const MaybeCount = lf.python.allowUnknown(lf.python.int);
|
|
81
|
+
const count = lf.python.parse(MaybeCount, 'UNKNOWN'); // number | typeof lf.python.UNKNOWN
|
|
82
|
+
if (count === lf.python.UNKNOWN) {
|
|
83
|
+
// The model could not provide a value.
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`set()` accepts primitive elements; `set(itemSchema)` adds element validation. This is stricter than the evaluated Python checkout, which renders `set[int]` as `set` and does not enforce its element annotation. Collections, constructors, and UNKNOWN cannot be set elements in this implementation. Boolean/numeric duplicates follow Python equality (`True` equals `1`). Formatting follows JavaScript insertion order, so exact set text may differ from Python. JSON mode represents sets as arrays and parses them into `Set` instances.
|
|
88
|
+
|
|
89
|
+
`any` accepts `UNKNOWN` directly, including nested occurrences. Typed schemas require `allowUnknown` at each field that may contain the marker, preserving accurate TypeScript return types. The wrapper retains the underlying prompt annotation and JSON Schema; UNKNOWN has no JSON wire representation. Unlike Python, ordinary `int`, class, and collection schemas do not silently accept UNKNOWN. Bare `MISSING` and `Missing()` reject in both response parsers; defaults remain the way to handle omitted fields.
|
|
90
|
+
|
|
91
|
+
## Dataclass-style examples
|
|
92
|
+
|
|
93
|
+
For an explicitly registered class matching a Python dataclass, supply its signature description and request compact examples:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
class Data { constructor(readonly count: number) {} }
|
|
97
|
+
const DataSchema = lf.python.classSchema({
|
|
98
|
+
name: 'Data',
|
|
99
|
+
fields: { count: lf.python.int },
|
|
100
|
+
description: 'Data(count: int)',
|
|
101
|
+
exampleFormat: 'compact',
|
|
102
|
+
create: fields => new Data(fields.count),
|
|
103
|
+
isInstance: (value): value is Data => value instanceof Data,
|
|
104
|
+
decompose: value => ({ count: value.count }),
|
|
105
|
+
});
|
|
106
|
+
// formatExample(DataSchema, new Data(1)) emits fenced `output = Data(count=1)`.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The setting affects few-shot/example formatting and propagates through collection schemas, unions, subclass registration, unknown-value wrappers, and recursive references. Other classes retain expanded formatting by default. Inside a compact constructor, nested values also render compactly. Conflicting example styles for the same class name reject. This is explicit presentation metadata; no Python dataclass introspection or automatic signature/default generation is performed.
|