auwgent-yaml-lite 0.1.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 +21 -0
- package/README.md +1144 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1153 -0
- package/dist/ir-builder.d.ts +39 -0
- package/dist/ir-builder.d.ts.map +1 -0
- package/dist/parser.d.ts +60 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/tokenizer.d.ts +46 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/types.d.ts +160 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
# @auwgent/yaml-lite
|
|
2
|
+
|
|
3
|
+
A **streaming YAML-Lite parser** designed for AI agents, LLM applications, and generative UI.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@auwgent/yaml-lite)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Why YAML-Lite?](#why-yaml-lite)
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Quick Start](#quick-start)
|
|
15
|
+
- [Core Concepts](#core-concepts)
|
|
16
|
+
- [API Reference](#api-reference)
|
|
17
|
+
- [parseToJSON](#parsetojson)
|
|
18
|
+
- [createStreamingParser](#createstreamingparser)
|
|
19
|
+
- [parseWithDiagnostics](#parsewithdiagnostics)
|
|
20
|
+
- [validate](#validate)
|
|
21
|
+
- [Low-Level APIs](#low-level-apis)
|
|
22
|
+
- [Types Reference](#types-reference)
|
|
23
|
+
- [The Reference System (id/ref)](#the-reference-system-idref)
|
|
24
|
+
- [Use Cases](#use-cases)
|
|
25
|
+
- [YAML-Lite Syntax](#yaml-lite-syntax)
|
|
26
|
+
- [Error Handling](#error-handling)
|
|
27
|
+
- [Best Practices](#best-practices)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Why YAML-Lite?
|
|
32
|
+
|
|
33
|
+
**Problem:** LLMs generate structured output that needs to be parsed. JSON has issues:
|
|
34
|
+
- Syntax errors (missing commas, quotes) are common during generation
|
|
35
|
+
- No partial parsing — you wait for the entire response
|
|
36
|
+
- Verbose, uses more tokens
|
|
37
|
+
|
|
38
|
+
**Solution:** YAML-Lite is a restricted YAML subset designed for LLM output:
|
|
39
|
+
|
|
40
|
+
| Feature | YAML-Lite | JSON | Full YAML |
|
|
41
|
+
|---------|-----------|------|-----------|
|
|
42
|
+
| **Streaming parsing** | ✅ Yes | ❌ No | ❌ Fragile |
|
|
43
|
+
| **Partial documents** | ✅ Valid JSON anytime | ❌ No | ❌ No |
|
|
44
|
+
| **Token efficiency** | ✅ ~30% fewer tokens | ❌ Verbose | ✅ Good |
|
|
45
|
+
| **LLM reliability** | ✅ High | ❌ Syntax errors | ⚠️ Too complex |
|
|
46
|
+
| **Generative UI refs** | ✅ Built-in | ❌ No | ❌ Complex |
|
|
47
|
+
| **Zero dependencies** | ✅ Yes | — | ❌ No |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# npm
|
|
55
|
+
npm install @auwgent/yaml-lite
|
|
56
|
+
|
|
57
|
+
# bun
|
|
58
|
+
bun add @auwgent/yaml-lite
|
|
59
|
+
|
|
60
|
+
# pnpm
|
|
61
|
+
pnpm add @auwgent/yaml-lite
|
|
62
|
+
|
|
63
|
+
# yarn
|
|
64
|
+
yarn add @auwgent/yaml-lite
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
### Basic Parsing
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { parseToJSON } from '@auwgent/yaml-lite';
|
|
75
|
+
|
|
76
|
+
const yaml = `
|
|
77
|
+
user:
|
|
78
|
+
name: John Doe
|
|
79
|
+
email: john@example.com
|
|
80
|
+
roles:
|
|
81
|
+
- admin
|
|
82
|
+
- editor
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
const json = parseToJSON(yaml);
|
|
86
|
+
console.log(json);
|
|
87
|
+
// {
|
|
88
|
+
// user: {
|
|
89
|
+
// name: 'John Doe',
|
|
90
|
+
// email: 'john@example.com',
|
|
91
|
+
// roles: ['admin', 'editor']
|
|
92
|
+
// }
|
|
93
|
+
// }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Streaming from LLM
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { createStreamingParser } from '@auwgent/yaml-lite';
|
|
100
|
+
|
|
101
|
+
const parser = createStreamingParser();
|
|
102
|
+
|
|
103
|
+
// Simulate LLM streaming chunks
|
|
104
|
+
const chunks = [
|
|
105
|
+
'intent:\n',
|
|
106
|
+
' type: tool_call\n',
|
|
107
|
+
' name: search\n',
|
|
108
|
+
' args:\n',
|
|
109
|
+
' query: "weather"\n'
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const chunk of chunks) {
|
|
113
|
+
parser.write(chunk);
|
|
114
|
+
|
|
115
|
+
// Get valid JSON at any point!
|
|
116
|
+
const current = parser.peek();
|
|
117
|
+
console.log('Current state:', JSON.stringify(current));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Output after each chunk:
|
|
121
|
+
// Current state: {"intent":{}}
|
|
122
|
+
// Current state: {"intent":{"type":"tool_call"}}
|
|
123
|
+
// Current state: {"intent":{"type":"tool_call","name":"search"}}
|
|
124
|
+
// Current state: {"intent":{"type":"tool_call","name":"search","args":{}}}
|
|
125
|
+
// Current state: {"intent":{"type":"tool_call","name":"search","args":{"query":"weather"}}}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Core Concepts
|
|
131
|
+
|
|
132
|
+
### 1. Streaming-First Design
|
|
133
|
+
|
|
134
|
+
Unlike traditional parsers that require complete input, YAML-Lite produces valid JSON at **every moment** during parsing. This enables:
|
|
135
|
+
|
|
136
|
+
- **Progressive UI rendering** — Show UI as it generates
|
|
137
|
+
- **Early action execution** — Start tool calls before response completes
|
|
138
|
+
- **Lower latency** — Don't wait for full LLM response
|
|
139
|
+
|
|
140
|
+
### 2. Automatic Type Coercion
|
|
141
|
+
|
|
142
|
+
String values are automatically converted to appropriate types:
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
count: 42 # → number: 42
|
|
146
|
+
price: 19.99 # → number: 19.99
|
|
147
|
+
enabled: true # → boolean: true
|
|
148
|
+
disabled: false # → boolean: false
|
|
149
|
+
empty: null # → null
|
|
150
|
+
name: John # → string: "John"
|
|
151
|
+
quoted: "123" # → string: "123" (quoted = keep as string)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 3. Empty Block Initialization
|
|
155
|
+
|
|
156
|
+
Empty blocks automatically become objects:
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
config:
|
|
160
|
+
database:
|
|
161
|
+
cache:
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Produces:
|
|
165
|
+
```json
|
|
166
|
+
{ "config": { "database": {}, "cache": {} } }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 4. The Reference System
|
|
170
|
+
|
|
171
|
+
Objects with `id` are registered. Objects with `ref` are replaced:
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
components:
|
|
175
|
+
- id: submit_btn
|
|
176
|
+
type: Button
|
|
177
|
+
label: Submit
|
|
178
|
+
|
|
179
|
+
form:
|
|
180
|
+
children:
|
|
181
|
+
- ref: submit_btn # ← Replaced with the Button object
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## API Reference
|
|
187
|
+
|
|
188
|
+
### `parseToJSON`
|
|
189
|
+
|
|
190
|
+
Parse a complete YAML-Lite string to JSON.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
function parseToJSON(input: string, options?: ParserOptions): IRValue
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Parameters:**
|
|
197
|
+
- `input` — The YAML-Lite string to parse
|
|
198
|
+
- `options` — Optional parser configuration
|
|
199
|
+
|
|
200
|
+
**Returns:** The parsed JSON value (object, array, or primitive)
|
|
201
|
+
|
|
202
|
+
**Example:**
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { parseToJSON } from '@auwgent/yaml-lite';
|
|
206
|
+
|
|
207
|
+
// Simple object
|
|
208
|
+
const obj = parseToJSON(`
|
|
209
|
+
name: Alice
|
|
210
|
+
age: 30
|
|
211
|
+
`);
|
|
212
|
+
// { name: 'Alice', age: 30 }
|
|
213
|
+
|
|
214
|
+
// Array
|
|
215
|
+
const arr = parseToJSON(`
|
|
216
|
+
items:
|
|
217
|
+
- apple
|
|
218
|
+
- banana
|
|
219
|
+
- cherry
|
|
220
|
+
`);
|
|
221
|
+
// { items: ['apple', 'banana', 'cherry'] }
|
|
222
|
+
|
|
223
|
+
// Nested structure
|
|
224
|
+
const nested = parseToJSON(`
|
|
225
|
+
user:
|
|
226
|
+
profile:
|
|
227
|
+
name: Bob
|
|
228
|
+
settings:
|
|
229
|
+
theme: dark
|
|
230
|
+
notifications: true
|
|
231
|
+
`);
|
|
232
|
+
// { user: { profile: { name: 'Bob', settings: { theme: 'dark', notifications: true } } } }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**When to use:**
|
|
236
|
+
- Parsing complete LLM responses (non-streaming)
|
|
237
|
+
- Processing saved YAML files
|
|
238
|
+
- Converting YAML config to JSON
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
### `createStreamingParser`
|
|
243
|
+
|
|
244
|
+
Create a parser for incremental/streaming parsing.
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
function createStreamingParser(options?: ParserOptions): {
|
|
248
|
+
write: (chunk: string) => void;
|
|
249
|
+
peek: () => IRValue;
|
|
250
|
+
end: () => IRValue;
|
|
251
|
+
onIntentReady: (handler: (intentType: string) => void) => void;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Returns an object with:**
|
|
256
|
+
|
|
257
|
+
#### `write(chunk: string): void`
|
|
258
|
+
|
|
259
|
+
Feed a chunk of input to the parser. Call this as you receive data from the LLM.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const parser = createStreamingParser();
|
|
263
|
+
parser.write('name: ');
|
|
264
|
+
parser.write('Alice');
|
|
265
|
+
parser.write('\nage: 30');
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### `peek(): IRValue`
|
|
269
|
+
|
|
270
|
+
Get the current parsed state **without** finalizing. Safe to call multiple times. Returns valid JSON even if the document is incomplete.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
parser.write('name: Alice\n');
|
|
274
|
+
const state1 = parser.peek(); // { name: 'Alice' }
|
|
275
|
+
|
|
276
|
+
parser.write('age: 30\n');
|
|
277
|
+
const state2 = parser.peek(); // { name: 'Alice', age: 30 }
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
#### `end(): IRValue`
|
|
281
|
+
|
|
282
|
+
Finalize parsing and return the complete result. Call this when the LLM response is complete.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
parser.write('name: Alice\nage: 30');
|
|
286
|
+
const final = parser.end();
|
|
287
|
+
// { name: 'Alice', age: 30 }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### `onIntentReady(handler: (intentType: string) => void): void`
|
|
291
|
+
|
|
292
|
+
Register a callback that fires when an `intent` block's `type` field is parsed. Enables early action routing.
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const parser = createStreamingParser();
|
|
296
|
+
|
|
297
|
+
parser.onIntentReady((type) => {
|
|
298
|
+
console.log('Intent detected:', type);
|
|
299
|
+
// "tool_call" — start preparing tool execution
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
parser.write('intent:\n');
|
|
303
|
+
parser.write(' type: tool_call\n'); // ← Callback fires here!
|
|
304
|
+
parser.write(' name: search\n');
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Full streaming example:**
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { createStreamingParser } from '@auwgent/yaml-lite';
|
|
311
|
+
|
|
312
|
+
async function processLLMStream(stream: AsyncIterable<string>) {
|
|
313
|
+
const parser = createStreamingParser();
|
|
314
|
+
|
|
315
|
+
// React early to intent type
|
|
316
|
+
parser.onIntentReady((type) => {
|
|
317
|
+
if (type === 'tool_call') {
|
|
318
|
+
console.log('Tool call detected — preparing execution');
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Process stream
|
|
323
|
+
for await (const chunk of stream) {
|
|
324
|
+
parser.write(chunk);
|
|
325
|
+
|
|
326
|
+
// Progressive state
|
|
327
|
+
const current = parser.peek();
|
|
328
|
+
updateUI(current); // Update UI progressively
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Get final result
|
|
332
|
+
return parser.end();
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### `parseWithDiagnostics`
|
|
339
|
+
|
|
340
|
+
Parse with full diagnostics including AST, errors, and unresolved references.
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
function parseWithDiagnostics(input: string, options?: ParserOptions): {
|
|
344
|
+
parse: ParseResult;
|
|
345
|
+
ir: IRResult;
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Returns:**
|
|
350
|
+
- `parse.ast` — The abstract syntax tree
|
|
351
|
+
- `parse.errors` — Parse-time errors
|
|
352
|
+
- `parse.complete` — Whether document was complete
|
|
353
|
+
- `ir.value` — The JSON output
|
|
354
|
+
- `ir.registry` — Map of all registered `id` objects
|
|
355
|
+
- `ir.unresolvedRefs` — List of unresolved reference names
|
|
356
|
+
- `ir.errors` — IR building errors
|
|
357
|
+
|
|
358
|
+
**Example:**
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
import { parseWithDiagnostics } from '@auwgent/yaml-lite';
|
|
362
|
+
|
|
363
|
+
const result = parseWithDiagnostics(`
|
|
364
|
+
button:
|
|
365
|
+
id: my_btn
|
|
366
|
+
label: Click me
|
|
367
|
+
|
|
368
|
+
panel:
|
|
369
|
+
child:
|
|
370
|
+
ref: my_btn
|
|
371
|
+
ref: unknown_ref
|
|
372
|
+
`);
|
|
373
|
+
|
|
374
|
+
console.log('JSON:', result.ir.value);
|
|
375
|
+
console.log('Registry:', [...result.ir.registry.keys()]); // ['my_btn']
|
|
376
|
+
console.log('Unresolved:', result.ir.unresolvedRefs); // ['unknown_ref']
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**When to use:**
|
|
380
|
+
- Debugging parse issues
|
|
381
|
+
- Validating LLM output quality
|
|
382
|
+
- Checking for unresolved references
|
|
383
|
+
- Building development tools
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
### `validate`
|
|
388
|
+
|
|
389
|
+
Validate YAML-Lite input without full parsing. Faster for validation-only use cases.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
function validate(input: string, options?: ParserOptions): true | ParseError[]
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Returns:**
|
|
396
|
+
- `true` if valid
|
|
397
|
+
- Array of `ParseError` objects if invalid
|
|
398
|
+
|
|
399
|
+
**Example:**
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { validate } from '@auwgent/yaml-lite';
|
|
403
|
+
|
|
404
|
+
const valid = validate(`
|
|
405
|
+
name: Alice
|
|
406
|
+
age: 30
|
|
407
|
+
`);
|
|
408
|
+
console.log(valid); // true
|
|
409
|
+
|
|
410
|
+
const invalid = validate(`
|
|
411
|
+
bad indentation
|
|
412
|
+
name: Alice
|
|
413
|
+
`);
|
|
414
|
+
console.log(invalid);
|
|
415
|
+
// [{ message: 'Unexpected indent...', line: 1, column: 3, severity: 'error' }]
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
### Low-Level APIs
|
|
421
|
+
|
|
422
|
+
For advanced use cases, you can access the internal components:
|
|
423
|
+
|
|
424
|
+
#### `Tokenizer` / `tokenize`
|
|
425
|
+
|
|
426
|
+
Convert YAML-Lite string into tokens.
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { tokenize } from '@auwgent/yaml-lite';
|
|
430
|
+
|
|
431
|
+
const tokens = tokenize('name: Alice\nage: 30');
|
|
432
|
+
// [
|
|
433
|
+
// { type: 'KEY', value: 'name', line: 1, column: 1, indent: 0 },
|
|
434
|
+
// { type: 'COLON', value: ':', line: 1, column: 5, indent: 0 },
|
|
435
|
+
// { type: 'SCALAR', value: 'Alice', line: 1, column: 7, indent: 0 },
|
|
436
|
+
// { type: 'NEWLINE', value: '\n', line: 1, column: 12, indent: 0 },
|
|
437
|
+
// ...
|
|
438
|
+
// ]
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
#### `Parser` / `parse`
|
|
442
|
+
|
|
443
|
+
Convert tokens into an AST (Abstract Syntax Tree).
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
import { parse } from '@auwgent/yaml-lite';
|
|
447
|
+
|
|
448
|
+
const result = parse('name: Alice');
|
|
449
|
+
console.log(result.ast);
|
|
450
|
+
// {
|
|
451
|
+
// kind: 'mapping',
|
|
452
|
+
// entries: [{ key: 'name', value: { kind: 'scalar', value: 'Alice', quoted: false } }]
|
|
453
|
+
// }
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
#### `IRBuilder` / `buildIR`
|
|
457
|
+
|
|
458
|
+
Convert AST to JSON IR (Intermediate Representation).
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { parse, buildIR } from '@auwgent/yaml-lite';
|
|
462
|
+
|
|
463
|
+
const parseResult = parse('count: 42');
|
|
464
|
+
const ir = buildIR(parseResult.ast);
|
|
465
|
+
console.log(ir.value);
|
|
466
|
+
// { count: 42 } ← Note: '42' string became number 42
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## Types Reference
|
|
472
|
+
|
|
473
|
+
### Core Output Types
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// JSON-compatible value (what you get from parsing)
|
|
477
|
+
type IRValue = string | number | boolean | null | IRObject | IRArray | IRRef;
|
|
478
|
+
|
|
479
|
+
// Object output
|
|
480
|
+
interface IRObject {
|
|
481
|
+
[key: string]: IRValue;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Array output
|
|
485
|
+
type IRArray = IRValue[];
|
|
486
|
+
|
|
487
|
+
// Unresolved reference (kept as-is if target not found)
|
|
488
|
+
interface IRRef {
|
|
489
|
+
$ref: string;
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Parse Results
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
interface ParseResult {
|
|
497
|
+
ast: ASTNode | null; // The syntax tree
|
|
498
|
+
errors: ParseError[]; // Any parse errors
|
|
499
|
+
complete: boolean; // Was document complete?
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
interface IRResult {
|
|
503
|
+
value: IRValue; // The JSON output
|
|
504
|
+
registry: Map<string, IRValue>; // id → object mapping
|
|
505
|
+
unresolvedRefs: string[]; // Refs that couldn't resolve
|
|
506
|
+
errors: IRError[]; // IR building errors
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Errors
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
interface ParseError {
|
|
514
|
+
message: string;
|
|
515
|
+
severity: 'error' | 'warning' | 'info';
|
|
516
|
+
line: number;
|
|
517
|
+
column: number;
|
|
518
|
+
context?: string; // The problematic line
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
interface IRError {
|
|
522
|
+
message: string;
|
|
523
|
+
severity: 'error' | 'warning' | 'info';
|
|
524
|
+
path: string[]; // Path to the error in the document
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Parser Options
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
interface ParserOptions {
|
|
532
|
+
indentSize?: number; // Spaces per indent (default: 2)
|
|
533
|
+
allowTabs?: boolean; // Allow tab characters (default: false)
|
|
534
|
+
preserveComments?: boolean; // Keep comments in output (default: false)
|
|
535
|
+
strict?: boolean; // Fail on any warning (default: false)
|
|
536
|
+
intentSchema?: IntentSchema; // Schema for intent validation
|
|
537
|
+
middleware?: ParserMiddleware[]; // Event hooks
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
interface IntentSchema {
|
|
541
|
+
requiredKeys: string[]; // Keys required for intent to be valid
|
|
542
|
+
knownTypes?: string[]; // List of valid intent types
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### AST Node Types
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
// Scalar value (string, but may become number/boolean after coercion)
|
|
550
|
+
interface ScalarNode {
|
|
551
|
+
kind: 'scalar';
|
|
552
|
+
value: string;
|
|
553
|
+
quoted: boolean; // Was it "quoted" or 'quoted'?
|
|
554
|
+
line: number;
|
|
555
|
+
column: number;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Mapping (object)
|
|
559
|
+
interface MappingNode {
|
|
560
|
+
kind: 'mapping';
|
|
561
|
+
entries: MappingEntry[];
|
|
562
|
+
line: number;
|
|
563
|
+
column: number;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
interface MappingEntry {
|
|
567
|
+
key: string;
|
|
568
|
+
value: ASTNode;
|
|
569
|
+
line: number;
|
|
570
|
+
column: number;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Sequence (array)
|
|
574
|
+
interface SequenceNode {
|
|
575
|
+
kind: 'sequence';
|
|
576
|
+
items: ASTNode[];
|
|
577
|
+
line: number;
|
|
578
|
+
column: number;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Reference
|
|
582
|
+
interface RefNode {
|
|
583
|
+
kind: 'ref';
|
|
584
|
+
target: string; // The id being referenced
|
|
585
|
+
line: number;
|
|
586
|
+
column: number;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Empty block
|
|
590
|
+
interface EmptyNode {
|
|
591
|
+
kind: 'empty';
|
|
592
|
+
hint?: 'mapping' | 'sequence';
|
|
593
|
+
line: number;
|
|
594
|
+
column: number;
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Tokens
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
type TokenType =
|
|
602
|
+
| 'KEY' // name
|
|
603
|
+
| 'COLON' // :
|
|
604
|
+
| 'DASH' // -
|
|
605
|
+
| 'SCALAR' // unquoted value
|
|
606
|
+
| 'QUOTED' // "quoted" or 'quoted'
|
|
607
|
+
| 'INDENT' // indentation increase
|
|
608
|
+
| 'DEDENT' // indentation decrease
|
|
609
|
+
| 'NEWLINE' // line terminator
|
|
610
|
+
| 'COMMENT' // # comment
|
|
611
|
+
| 'EOF'; // end of input
|
|
612
|
+
|
|
613
|
+
interface Token {
|
|
614
|
+
type: TokenType;
|
|
615
|
+
value: string;
|
|
616
|
+
line: number;
|
|
617
|
+
column: number;
|
|
618
|
+
indent: number; // Indent level (0, 1, 2, ...)
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
---
|
|
623
|
+
|
|
624
|
+
## The Reference System (id/ref)
|
|
625
|
+
|
|
626
|
+
The reference system enables reusable, composable structures — essential for generative UI.
|
|
627
|
+
|
|
628
|
+
### How It Works
|
|
629
|
+
|
|
630
|
+
1. **Registration:** Any object with an `id` key is registered globally
|
|
631
|
+
2. **Resolution:** Any object with only a `ref` key is replaced with the registered object
|
|
632
|
+
3. **Timing:** References are resolved **after** parsing (supports forward refs)
|
|
633
|
+
|
|
634
|
+
### Example: Reusable UI Components
|
|
635
|
+
|
|
636
|
+
```yaml
|
|
637
|
+
# Define reusable components
|
|
638
|
+
components:
|
|
639
|
+
- id: primary_button
|
|
640
|
+
type: Button
|
|
641
|
+
variant: primary
|
|
642
|
+
size: large
|
|
643
|
+
|
|
644
|
+
- id: input_field
|
|
645
|
+
type: Input
|
|
646
|
+
style: outlined
|
|
647
|
+
|
|
648
|
+
# Use them in a form
|
|
649
|
+
login_form:
|
|
650
|
+
type: Form
|
|
651
|
+
action: /login
|
|
652
|
+
children:
|
|
653
|
+
- ref: input_field # Email input
|
|
654
|
+
- ref: input_field # Password input (same style!)
|
|
655
|
+
- ref: primary_button # Submit button
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
Output:
|
|
659
|
+
```json
|
|
660
|
+
{
|
|
661
|
+
"components": [
|
|
662
|
+
{ "id": "primary_button", "type": "Button", "variant": "primary", "size": "large" },
|
|
663
|
+
{ "id": "input_field", "type": "Input", "style": "outlined" }
|
|
664
|
+
],
|
|
665
|
+
"login_form": {
|
|
666
|
+
"type": "Form",
|
|
667
|
+
"action": "/login",
|
|
668
|
+
"children": [
|
|
669
|
+
{ "id": "input_field", "type": "Input", "style": "outlined" },
|
|
670
|
+
{ "id": "input_field", "type": "Input", "style": "outlined" },
|
|
671
|
+
{ "id": "primary_button", "type": "Button", "variant": "primary", "size": "large" }
|
|
672
|
+
]
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Forward References
|
|
678
|
+
|
|
679
|
+
References can point to objects defined later:
|
|
680
|
+
|
|
681
|
+
```yaml
|
|
682
|
+
page:
|
|
683
|
+
header:
|
|
684
|
+
ref: navbar # ← Forward reference (navbar defined below)
|
|
685
|
+
|
|
686
|
+
navbar:
|
|
687
|
+
id: navbar
|
|
688
|
+
type: Navigation
|
|
689
|
+
items:
|
|
690
|
+
- Home
|
|
691
|
+
- About
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Checking Unresolved References
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
const { ir } = parseWithDiagnostics(yaml);
|
|
698
|
+
|
|
699
|
+
if (ir.unresolvedRefs.length > 0) {
|
|
700
|
+
console.warn('Missing components:', ir.unresolvedRefs);
|
|
701
|
+
// Handle gracefully or show error to user
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## Use Cases
|
|
708
|
+
|
|
709
|
+
### 1. AI Agent Tool Calling
|
|
710
|
+
|
|
711
|
+
Execute tools as soon as the intent is clear — don't wait for full response.
|
|
712
|
+
|
|
713
|
+
```typescript
|
|
714
|
+
import { createStreamingParser } from '@auwgent/yaml-lite';
|
|
715
|
+
|
|
716
|
+
async function handleAgentStream(llmStream: AsyncIterable<string>) {
|
|
717
|
+
const parser = createStreamingParser();
|
|
718
|
+
let toolPromise: Promise<any> | null = null;
|
|
719
|
+
|
|
720
|
+
// Start tool execution early
|
|
721
|
+
parser.onIntentReady((type) => {
|
|
722
|
+
if (type === 'tool_call') {
|
|
723
|
+
// Peek at current state to get tool name
|
|
724
|
+
const state = parser.peek() as any;
|
|
725
|
+
if (state.intent?.name) {
|
|
726
|
+
console.log(`Starting ${state.intent.name} tool...`);
|
|
727
|
+
toolPromise = prepareTool(state.intent.name);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
for await (const chunk of llmStream) {
|
|
733
|
+
parser.write(chunk);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const result = parser.end();
|
|
737
|
+
|
|
738
|
+
// Execute with full arguments
|
|
739
|
+
if (result.intent?.type === 'tool_call') {
|
|
740
|
+
await toolPromise; // Tool was already preparing!
|
|
741
|
+
return await executeTool(result.intent.name, result.intent.args);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
**Why this matters:** If the LLM takes 2 seconds to generate the full response, but the intent type appears in 200ms, you save 1.8 seconds of latency.
|
|
747
|
+
|
|
748
|
+
### 2. Generative UI with Streaming Preview
|
|
749
|
+
|
|
750
|
+
Show UI components as they're generated.
|
|
751
|
+
|
|
752
|
+
```typescript
|
|
753
|
+
import { createStreamingParser } from '@auwgent/yaml-lite';
|
|
754
|
+
|
|
755
|
+
async function streamGenerativeUI(llmStream: AsyncIterable<string>) {
|
|
756
|
+
const parser = createStreamingParser();
|
|
757
|
+
|
|
758
|
+
for await (const chunk of llmStream) {
|
|
759
|
+
parser.write(chunk);
|
|
760
|
+
|
|
761
|
+
// Render current UI state
|
|
762
|
+
const ui = parser.peek();
|
|
763
|
+
renderToDOM(ui); // Your renderer
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Final render
|
|
767
|
+
const finalUI = parser.end();
|
|
768
|
+
renderToDOM(finalUI);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function renderToDOM(ui: any) {
|
|
772
|
+
// Map YAML structure to React/Vue/Svelte components
|
|
773
|
+
if (ui.type === 'Card') {
|
|
774
|
+
return <Card title={ui.title}>{renderChildren(ui.children)}</Card>;
|
|
775
|
+
}
|
|
776
|
+
// ... etc
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### 3. Structured Data Extraction
|
|
781
|
+
|
|
782
|
+
Extract structured data from LLM responses reliably.
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
import { parseToJSON } from '@auwgent/yaml-lite';
|
|
786
|
+
|
|
787
|
+
const prompt = `
|
|
788
|
+
Extract the following information from this text:
|
|
789
|
+
"John Smith, age 32, works as a software engineer at Google."
|
|
790
|
+
|
|
791
|
+
Respond in this format:
|
|
792
|
+
person:
|
|
793
|
+
name: <full name>
|
|
794
|
+
age: <number>
|
|
795
|
+
job:
|
|
796
|
+
title: <job title>
|
|
797
|
+
company: <company name>
|
|
798
|
+
`;
|
|
799
|
+
|
|
800
|
+
const llmResponse = `
|
|
801
|
+
person:
|
|
802
|
+
name: John Smith
|
|
803
|
+
age: 32
|
|
804
|
+
job:
|
|
805
|
+
title: software engineer
|
|
806
|
+
company: Google
|
|
807
|
+
`;
|
|
808
|
+
|
|
809
|
+
const data = parseToJSON(llmResponse);
|
|
810
|
+
// { person: { name: 'John Smith', age: 32, job: { title: 'software engineer', company: 'Google' } } }
|
|
811
|
+
|
|
812
|
+
// Type-safe access
|
|
813
|
+
console.log(data.person.name); // 'John Smith'
|
|
814
|
+
console.log(data.person.age); // 32 (number, not string!)
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
### 4. Multi-Step Agent Workflows
|
|
818
|
+
|
|
819
|
+
Parse complex agent workflows with multiple intents.
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
import { parseToJSON } from '@auwgent/yaml-lite';
|
|
823
|
+
|
|
824
|
+
const workflow = parseToJSON(`
|
|
825
|
+
workflow:
|
|
826
|
+
name: Research Task
|
|
827
|
+
steps:
|
|
828
|
+
- intent:
|
|
829
|
+
type: tool_call
|
|
830
|
+
name: web_search
|
|
831
|
+
args:
|
|
832
|
+
query: "latest AI news"
|
|
833
|
+
- intent:
|
|
834
|
+
type: tool_call
|
|
835
|
+
name: summarize
|
|
836
|
+
args:
|
|
837
|
+
max_length: 200
|
|
838
|
+
- intent:
|
|
839
|
+
type: respond
|
|
840
|
+
message: "Here's what I found..."
|
|
841
|
+
`);
|
|
842
|
+
|
|
843
|
+
// Execute steps sequentially
|
|
844
|
+
for (const step of workflow.steps) {
|
|
845
|
+
if (step.intent.type === 'tool_call') {
|
|
846
|
+
const result = await executeTool(step.intent.name, step.intent.args);
|
|
847
|
+
context.addResult(step.intent.name, result);
|
|
848
|
+
} else if (step.intent.type === 'respond') {
|
|
849
|
+
return step.intent.message;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
### 5. Configuration Files
|
|
855
|
+
|
|
856
|
+
Use YAML-Lite for type-safe configuration.
|
|
857
|
+
|
|
858
|
+
```typescript
|
|
859
|
+
import { parseToJSON } from '@auwgent/yaml-lite';
|
|
860
|
+
import { readFileSync } from 'fs';
|
|
861
|
+
|
|
862
|
+
interface AppConfig {
|
|
863
|
+
server: {
|
|
864
|
+
port: number;
|
|
865
|
+
host: string;
|
|
866
|
+
};
|
|
867
|
+
database: {
|
|
868
|
+
url: string;
|
|
869
|
+
poolSize: number;
|
|
870
|
+
};
|
|
871
|
+
features: string[];
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const configYaml = readFileSync('config.yaml', 'utf-8');
|
|
875
|
+
const config = parseToJSON(configYaml) as AppConfig;
|
|
876
|
+
|
|
877
|
+
// Types are preservCastException
|
|
878
|
+
console.log(config.server.port); // number
|
|
879
|
+
console.log(config.features[0]); // string
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
## YAML-Lite Syntax
|
|
885
|
+
|
|
886
|
+
### Supported Features
|
|
887
|
+
|
|
888
|
+
```yaml
|
|
889
|
+
# Comments (ignored in output)
|
|
890
|
+
|
|
891
|
+
# Mappings (objects)
|
|
892
|
+
object:
|
|
893
|
+
key1: value1
|
|
894
|
+
key2: value2
|
|
895
|
+
|
|
896
|
+
# Sequences (arrays)
|
|
897
|
+
list:
|
|
898
|
+
- item1
|
|
899
|
+
- item2
|
|
900
|
+
- item3
|
|
901
|
+
|
|
902
|
+
# Nested structures
|
|
903
|
+
parent:
|
|
904
|
+
child:
|
|
905
|
+
grandchild: value
|
|
906
|
+
|
|
907
|
+
# Scalars with type coercion
|
|
908
|
+
string: hello world
|
|
909
|
+
number: 42
|
|
910
|
+
float: 3.14
|
|
911
|
+
boolean_true: true
|
|
912
|
+
boolean_false: false
|
|
913
|
+
null_value: null
|
|
914
|
+
quoted_string: "keeps as string"
|
|
915
|
+
quoted_number: "123"
|
|
916
|
+
|
|
917
|
+
# Inline JSON arrays
|
|
918
|
+
tags: ["red", "green", "blue"]
|
|
919
|
+
numbers: [1, 2, 3, 4, 5]
|
|
920
|
+
|
|
921
|
+
# Empty blocks (become {})
|
|
922
|
+
config:
|
|
923
|
+
section:
|
|
924
|
+
|
|
925
|
+
# References
|
|
926
|
+
templates:
|
|
927
|
+
- id: my_template
|
|
928
|
+
content: Hello
|
|
929
|
+
|
|
930
|
+
usage:
|
|
931
|
+
item:
|
|
932
|
+
ref: my_template
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
### Not Supported (By Design)
|
|
936
|
+
|
|
937
|
+
These YAML features are **intentionally not supported** to ensure reliability:
|
|
938
|
+
|
|
939
|
+
```yaml
|
|
940
|
+
# ❌ Anchors and aliases
|
|
941
|
+
defaults: &defaults
|
|
942
|
+
timeout: 30
|
|
943
|
+
production:
|
|
944
|
+
<<: *defaults
|
|
945
|
+
|
|
946
|
+
# ❌ Flow style
|
|
947
|
+
object: { key: value }
|
|
948
|
+
array: [1, 2, 3] # ← Actually this IS supported for inline arrays
|
|
949
|
+
|
|
950
|
+
# ❌ Tags
|
|
951
|
+
date: !!timestamp 2024-01-01
|
|
952
|
+
|
|
953
|
+
# ❌ Multiple documents
|
|
954
|
+
---
|
|
955
|
+
doc1: true
|
|
956
|
+
---
|
|
957
|
+
doc2: true
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
**Why?** LLMs produce more reliable output with simpler syntax. The unsupported features add complexity without benefit for LLM use cases.
|
|
961
|
+
|
|
962
|
+
---
|
|
963
|
+
|
|
964
|
+
## Error Handling
|
|
965
|
+
|
|
966
|
+
### Parse Errors
|
|
967
|
+
|
|
968
|
+
```typescript
|
|
969
|
+
import { parseWithDiagnostics, validate } from '@auwgent/yaml-lite';
|
|
970
|
+
|
|
971
|
+
const input = `
|
|
972
|
+
invalid: indentation
|
|
973
|
+
name: value
|
|
974
|
+
`;
|
|
975
|
+
|
|
976
|
+
// Option 1: validate only
|
|
977
|
+
const validation = validate(input);
|
|
978
|
+
if (validation !== true) {
|
|
979
|
+
console.error('Validation failed:', validation);
|
|
980
|
+
// [{ message: 'Unexpected indent at start...', line: 2, severity: 'error' }]
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Option 2: parse with diagnostics
|
|
984
|
+
const { parse, ir } = parseWithDiagnostics(input);
|
|
985
|
+
if (parse.errors.length > 0) {
|
|
986
|
+
console.error('Parse errors:', parse.errors);
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
### Handling Incomplete Documents
|
|
991
|
+
|
|
992
|
+
Streaming documents may be incomplete. The parser handles this gracefully:
|
|
993
|
+
|
|
994
|
+
```typescript
|
|
995
|
+
const parser = createStreamingParser();
|
|
996
|
+
parser.write('partial:\n incomplete:');
|
|
997
|
+
|
|
998
|
+
const state = parser.peek();
|
|
999
|
+
console.log(state); // { partial: { incomplete: {} } }
|
|
1000
|
+
|
|
1001
|
+
// Document is usable even when incomplete!
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
### Unresolved References
|
|
1005
|
+
|
|
1006
|
+
```typescript
|
|
1007
|
+
const { ir } = parseWithDiagnostics(`
|
|
1008
|
+
panel:
|
|
1009
|
+
child:
|
|
1010
|
+
ref: nonexistent
|
|
1011
|
+
`);
|
|
1012
|
+
|
|
1013
|
+
if (ir.unresolvedRefs.length > 0) {
|
|
1014
|
+
console.warn('Unresolved:', ir.unresolvedRefs);
|
|
1015
|
+
// ['nonexistent']
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// The ref is kept as { $ref: 'nonexistent' } in output
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
### Strict Mode
|
|
1022
|
+
|
|
1023
|
+
Enable strict mode to fail on any warning:
|
|
1024
|
+
|
|
1025
|
+
```typescript
|
|
1026
|
+
import { parseToJSON } from '@auwgent/yaml-lite';
|
|
1027
|
+
|
|
1028
|
+
try {
|
|
1029
|
+
parseToJSON(input, { strict: true });
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
console.error('Strict validation failed:', error);
|
|
1032
|
+
}
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
---
|
|
1036
|
+
|
|
1037
|
+
## Best Practices
|
|
1038
|
+
|
|
1039
|
+
### 1. Use Streaming for LLM Output
|
|
1040
|
+
|
|
1041
|
+
Always use `createStreamingParser()` when parsing LLM output:
|
|
1042
|
+
|
|
1043
|
+
```typescript
|
|
1044
|
+
// ✅ Good
|
|
1045
|
+
const parser = createStreamingParser();
|
|
1046
|
+
for await (const chunk of llmStream) {
|
|
1047
|
+
parser.write(chunk);
|
|
1048
|
+
updateUI(parser.peek());
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// ❌ Avoid
|
|
1052
|
+
let fullText = '';
|
|
1053
|
+
for await (const chunk of llmStream) {
|
|
1054
|
+
fullText += chunk;
|
|
1055
|
+
}
|
|
1056
|
+
parseToJSON(fullText); // No progressive updates!
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
### 2. Handle LLM Noise
|
|
1060
|
+
|
|
1061
|
+
LLMs often add conversational text or code fences. Use `extractYAML` to clean it:
|
|
1062
|
+
|
|
1063
|
+
```typescript
|
|
1064
|
+
import { extractYAML, parseToJSON } from '@auwgent/yaml-lite';
|
|
1065
|
+
|
|
1066
|
+
const llmOutput = `Sure! Here's the YAML:
|
|
1067
|
+
\`\`\`yaml
|
|
1068
|
+
text: Hello
|
|
1069
|
+
tool_call:
|
|
1070
|
+
name: search
|
|
1071
|
+
\`\`\`
|
|
1072
|
+
Let me know if you need changes!`;
|
|
1073
|
+
|
|
1074
|
+
const cleanYaml = extractYAML(llmOutput);
|
|
1075
|
+
const json = parseToJSON(cleanYaml);
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
### 3. Handle Partial State
|
|
1079
|
+
|
|
1080
|
+
During streaming, state may be incomplete:
|
|
1081
|
+
|
|
1082
|
+
```typescript
|
|
1083
|
+
const state = parser.peek();
|
|
1084
|
+
|
|
1085
|
+
// Safe access
|
|
1086
|
+
if (state?.intent?.name) {
|
|
1087
|
+
executeTool(state.intent.name, state.intent.args || {});
|
|
1088
|
+
}
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
### 4. Use TypeScript
|
|
1092
|
+
|
|
1093
|
+
Define types for your expected structures:
|
|
1094
|
+
|
|
1095
|
+
```typescript
|
|
1096
|
+
interface ToolCallIntent {
|
|
1097
|
+
type: 'tool_call';
|
|
1098
|
+
name: string;
|
|
1099
|
+
args: Record<string, unknown>;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
interface RespondIntent {
|
|
1103
|
+
type: 'respond';
|
|
1104
|
+
message: string;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
type Intent = ToolCallIntent | RespondIntent;
|
|
1108
|
+
|
|
1109
|
+
interface AgentOutput {
|
|
1110
|
+
intent: Intent;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
const output = parser.end() as AgentOutput;
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
### 5. Quote Strings That Look Like Other Types
|
|
1117
|
+
|
|
1118
|
+
If a string should stay a string, quote it:
|
|
1119
|
+
|
|
1120
|
+
```yaml
|
|
1121
|
+
# ❌ Will become boolean
|
|
1122
|
+
flag: true
|
|
1123
|
+
|
|
1124
|
+
# ✅ Stays string
|
|
1125
|
+
flag: "true"
|
|
1126
|
+
|
|
1127
|
+
# ❌ Will become number
|
|
1128
|
+
code: 12345
|
|
1129
|
+
|
|
1130
|
+
# ✅ Stays string
|
|
1131
|
+
code: "12345"
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
---
|
|
1135
|
+
|
|
1136
|
+
## License
|
|
1137
|
+
|
|
1138
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
1139
|
+
|
|
1140
|
+
---
|
|
1141
|
+
|
|
1142
|
+
## Contributing
|
|
1143
|
+
|
|
1144
|
+
Contributions welcome! Please read our contributing guidelines before submitting PRs.
|