@mariozechner/pi-ai 0.5.39 → 0.5.41

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/README.md CHANGED
@@ -71,8 +71,19 @@ for await (const event of s) {
71
71
  case 'thinking_end':
72
72
  console.log('[Thinking complete]');
73
73
  break;
74
- case 'toolCall':
74
+ case 'toolcall_start':
75
+ console.log(`\n[Tool call started: index ${event.contentIndex}]`);
76
+ break;
77
+ case 'toolcall_delta':
78
+ // Partial tool arguments are being streamed
79
+ const partialCall = event.partial.content[event.contentIndex];
80
+ if (partialCall.type === 'toolCall') {
81
+ console.log(`[Streaming args for ${partialCall.name}]`);
82
+ }
83
+ break;
84
+ case 'toolcall_end':
75
85
  console.log(`\nTool called: ${event.toolCall.name}`);
86
+ console.log(`Arguments: ${JSON.stringify(event.toolCall.arguments)}`);
76
87
  break;
77
88
  case 'done':
78
89
  console.log(`\nFinished: ${event.reason}`);
@@ -84,9 +95,10 @@ for await (const event of s) {
84
95
  }
85
96
 
86
97
  // Get the final message after streaming, add it to the context
87
- const finalMessage = await s.finalMessage();
98
+ const finalMessage = await s.result();
88
99
  context.messages.push(finalMessage);
89
100
 
101
+ // Handle tool calls if any
90
102
  // Handle tool calls if any
91
103
  const toolCalls = finalMessage.content.filter(b => b.type === 'toolCall');
92
104
  for (const call of toolCalls) {
@@ -194,6 +206,70 @@ for (const block of response.content) {
194
206
  }
195
207
  ```
196
208
 
209
+ ### Streaming Tool Calls with Partial JSON
210
+
211
+ During streaming, tool call arguments are progressively parsed as they arrive. This enables real-time UI updates before the complete arguments are available:
212
+
213
+ ```typescript
214
+ const s = stream(model, context);
215
+
216
+ for await (const event of s) {
217
+ if (event.type === 'toolcall_delta') {
218
+ const toolCall = event.partial.content[event.contentIndex];
219
+
220
+ // toolCall.arguments contains partially parsed JSON during streaming
221
+ // This allows for progressive UI updates
222
+ if (toolCall.type === 'toolCall' && toolCall.arguments) {
223
+ // BE DEFENSIVE: arguments may be incomplete
224
+ // Example: Show file path being written even before content is complete
225
+ if (toolCall.name === 'write_file' && toolCall.arguments.path) {
226
+ console.log(`Writing to: ${toolCall.arguments.path}`);
227
+
228
+ // Content might be partial or missing
229
+ if (toolCall.arguments.content) {
230
+ console.log(`Content preview: ${toolCall.arguments.content.substring(0, 100)}...`);
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ if (event.type === 'toolcall_end') {
237
+ // Here toolCall.arguments is complete and validated
238
+ const toolCall = event.toolCall;
239
+ console.log(`Tool completed: ${toolCall.name}`, toolCall.arguments);
240
+ }
241
+ }
242
+ ```
243
+
244
+ **Important notes about partial tool arguments:**
245
+ - During `toolcall_delta` events, `arguments` contains the best-effort parse of partial JSON
246
+ - Fields may be missing or incomplete - always check for existence before use
247
+ - String values may be truncated mid-word
248
+ - Arrays may be incomplete
249
+ - Nested objects may be partially populated
250
+ - At minimum, `arguments` will be an empty object `{}`, never `undefined`
251
+ - Full validation only occurs at `toolcall_end` when arguments are complete
252
+ - The Google provider does not support function call streaming. Instead, you will receive a single `toolcall_delta` event with the full arguments.
253
+
254
+ ### Complete Event Reference
255
+
256
+ All streaming events emitted during assistant message generation:
257
+
258
+ | Event Type | Description | Key Properties |
259
+ |------------|-------------|----------------|
260
+ | `start` | Stream begins | `partial`: Initial assistant message structure |
261
+ | `text_start` | Text block starts | `contentIndex`: Position in content array |
262
+ | `text_delta` | Text chunk received | `delta`: New text, `contentIndex`: Position |
263
+ | `text_end` | Text block complete | `content`: Full text, `contentIndex`: Position |
264
+ | `thinking_start` | Thinking block starts | `contentIndex`: Position in content array |
265
+ | `thinking_delta` | Thinking chunk received | `delta`: New text, `contentIndex`: Position |
266
+ | `thinking_end` | Thinking block complete | `content`: Full thinking, `contentIndex`: Position |
267
+ | `toolcall_start` | Tool call begins | `contentIndex`: Position in content array |
268
+ | `toolcall_delta` | Tool arguments streaming | `delta`: JSON chunk, `partial.content[contentIndex].arguments`: Partial parsed args |
269
+ | `toolcall_end` | Tool call complete | `toolCall`: Complete validated tool call with `id`, `name`, `arguments` |
270
+ | `done` | Stream complete | `reason`: Stop reason, `message`: Final assistant message |
271
+ | `error` | Error occurred | `error`: Error message, `partial`: Partial message before error |
272
+
197
273
  ## Image Input
198
274
 
199
275
  Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
@@ -358,7 +434,7 @@ for await (const event of s) {
358
434
  }
359
435
 
360
436
  // Get results (may be partial if aborted)
361
- const response = await s.finalMessage();
437
+ const response = await s.result();
362
438
  if (response.stopReason === 'error') {
363
439
  console.log('Error:', response.error);
364
440
  console.log('Partial content received:', response.content);
@@ -642,26 +718,26 @@ for await (const event of stream) {
642
718
  case 'agent_start':
643
719
  console.log('Agent started');
644
720
  break;
645
-
721
+
646
722
  case 'turn_start':
647
723
  console.log('New turn started');
648
724
  break;
649
-
725
+
650
726
  case 'message_start':
651
727
  console.log(`${event.message.role} message started`);
652
728
  break;
653
-
729
+
654
730
  case 'message_update':
655
731
  // Only for assistant messages during streaming
656
732
  if (event.message.content.some(c => c.type === 'text')) {
657
733
  console.log('Assistant:', event.message.content);
658
734
  }
659
735
  break;
660
-
736
+
661
737
  case 'tool_execution_start':
662
738
  console.log(`Calling ${event.toolName} with:`, event.args);
663
739
  break;
664
-
740
+
665
741
  case 'tool_execution_end':
666
742
  if (event.isError) {
667
743
  console.error(`Tool failed:`, event.result);
@@ -669,11 +745,11 @@ for await (const event of stream) {
669
745
  console.log(`Tool result:`, event.result.output);
670
746
  }
671
747
  break;
672
-
748
+
673
749
  case 'turn_end':
674
750
  console.log(`Turn ended with ${event.toolResults.length} tool calls`);
675
751
  break;
676
-
752
+
677
753
  case 'agent_end':
678
754
  console.log(`Agent completed with ${event.messages.length} new messages`);
679
755
  break;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Attempts to parse potentially incomplete JSON during streaming.
3
+ * Always returns a valid object, even if the JSON is incomplete.
4
+ *
5
+ * @param partialJson The partial JSON string from streaming
6
+ * @returns Parsed object or empty object if parsing fails
7
+ */
8
+ export declare function parseStreamingJson<T = any>(partialJson: string | undefined): T;
9
+ //# sourceMappingURL=json-parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-parse.d.ts","sourceRoot":"","sources":["../src/json-parse.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAkB9E"}
@@ -0,0 +1,29 @@
1
+ import { parse as partialParse } from "partial-json";
2
+ /**
3
+ * Attempts to parse potentially incomplete JSON during streaming.
4
+ * Always returns a valid object, even if the JSON is incomplete.
5
+ *
6
+ * @param partialJson The partial JSON string from streaming
7
+ * @returns Parsed object or empty object if parsing fails
8
+ */
9
+ export function parseStreamingJson(partialJson) {
10
+ if (!partialJson || partialJson.trim() === "") {
11
+ return {};
12
+ }
13
+ // Try standard parsing first (fastest for complete JSON)
14
+ try {
15
+ return JSON.parse(partialJson);
16
+ }
17
+ catch {
18
+ // Try partial-json for incomplete JSON
19
+ try {
20
+ const result = partialParse(partialJson);
21
+ return (result ?? {});
22
+ }
23
+ catch {
24
+ // If all parsing fails, return empty object
25
+ return {};
26
+ }
27
+ }
28
+ }
29
+ //# sourceMappingURL=json-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-parse.js","sourceRoot":"","sources":["../src/json-parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAU,WAA+B;IAC1E,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC/C,OAAO,EAAO,CAAC;IAChB,CAAC;IAED,yDAAyD;IACzD,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAM,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACR,uCAAuC;QACvC,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;YACzC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAM,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACR,4CAA4C;YAC5C,OAAO,EAAO,CAAC;QAChB,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["import { parse as partialParse } from \"partial-json\";\n\n/**\n * Attempts to parse potentially incomplete JSON during streaming.\n * Always returns a valid object, even if the JSON is incomplete.\n *\n * @param partialJson The partial JSON string from streaming\n * @returns Parsed object or empty object if parsing fails\n */\nexport function parseStreamingJson<T = any>(partialJson: string | undefined): T {\n\tif (!partialJson || partialJson.trim() === \"\") {\n\t\treturn {} as T;\n\t}\n\n\t// Try standard parsing first (fastest for complete JSON)\n\ttry {\n\t\treturn JSON.parse(partialJson) as T;\n\t} catch {\n\t\t// Try partial-json for incomplete JSON\n\t\ttry {\n\t\t\tconst result = partialParse(partialJson);\n\t\t\treturn (result ?? {}) as T;\n\t\t} catch {\n\t\t\t// If all parsing fails, return empty object\n\t\t\treturn {} as T;\n\t\t}\n\t}\n}\n"]}
@@ -1408,6 +1408,40 @@ export declare const MODELS: {
1408
1408
  };
1409
1409
  };
1410
1410
  readonly openrouter: {
1411
+ readonly "qwen/qwen3-coder-flash": {
1412
+ id: string;
1413
+ name: string;
1414
+ api: "openai-completions";
1415
+ provider: string;
1416
+ baseUrl: string;
1417
+ reasoning: false;
1418
+ input: "text"[];
1419
+ cost: {
1420
+ input: number;
1421
+ output: number;
1422
+ cacheRead: number;
1423
+ cacheWrite: number;
1424
+ };
1425
+ contextWindow: number;
1426
+ maxTokens: number;
1427
+ };
1428
+ readonly "qwen/qwen3-coder-plus": {
1429
+ id: string;
1430
+ name: string;
1431
+ api: "openai-completions";
1432
+ provider: string;
1433
+ baseUrl: string;
1434
+ reasoning: false;
1435
+ input: "text"[];
1436
+ cost: {
1437
+ input: number;
1438
+ output: number;
1439
+ cacheRead: number;
1440
+ cacheWrite: number;
1441
+ };
1442
+ contextWindow: number;
1443
+ maxTokens: number;
1444
+ };
1411
1445
  readonly "qwen/qwen3-next-80b-a3b-thinking": {
1412
1446
  id: string;
1413
1447
  name: string;
@@ -2513,6 +2547,23 @@ export declare const MODELS: {
2513
2547
  contextWindow: number;
2514
2548
  maxTokens: number;
2515
2549
  };
2550
+ readonly "microsoft/phi-4-multimodal-instruct": {
2551
+ id: string;
2552
+ name: string;
2553
+ api: "openai-completions";
2554
+ provider: string;
2555
+ baseUrl: string;
2556
+ reasoning: false;
2557
+ input: ("text" | "image")[];
2558
+ cost: {
2559
+ input: number;
2560
+ output: number;
2561
+ cacheRead: number;
2562
+ cacheWrite: number;
2563
+ };
2564
+ contextWindow: number;
2565
+ maxTokens: number;
2566
+ };
2516
2567
  readonly "qwen/qwq-32b": {
2517
2568
  id: string;
2518
2569
  name: string;
@@ -2938,7 +2989,7 @@ export declare const MODELS: {
2938
2989
  contextWindow: number;
2939
2990
  maxTokens: number;
2940
2991
  };
2941
- readonly "cohere/command-r-plus-08-2024": {
2992
+ readonly "cohere/command-r-08-2024": {
2942
2993
  id: string;
2943
2994
  name: string;
2944
2995
  api: "openai-completions";
@@ -2955,7 +3006,7 @@ export declare const MODELS: {
2955
3006
  contextWindow: number;
2956
3007
  maxTokens: number;
2957
3008
  };
2958
- readonly "cohere/command-r-08-2024": {
3009
+ readonly "cohere/command-r-plus-08-2024": {
2959
3010
  id: string;
2960
3011
  name: string;
2961
3012
  api: "openai-completions";
@@ -3159,7 +3210,7 @@ export declare const MODELS: {
3159
3210
  contextWindow: number;
3160
3211
  maxTokens: number;
3161
3212
  };
3162
- readonly "meta-llama/llama-3-70b-instruct": {
3213
+ readonly "meta-llama/llama-3-8b-instruct": {
3163
3214
  id: string;
3164
3215
  name: string;
3165
3216
  api: "openai-completions";
@@ -3176,7 +3227,7 @@ export declare const MODELS: {
3176
3227
  contextWindow: number;
3177
3228
  maxTokens: number;
3178
3229
  };
3179
- readonly "meta-llama/llama-3-8b-instruct": {
3230
+ readonly "meta-llama/llama-3-70b-instruct": {
3180
3231
  id: string;
3181
3232
  name: string;
3182
3233
  api: "openai-completions";
@@ -1 +1 @@
1
- {"version":3,"file":"models.generated.d.ts","sourceRoot":"","sources":["../src/models.generated.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsyGT,CAAC"}
1
+ {"version":3,"file":"models.generated.d.ts","sourceRoot":"","sources":["../src/models.generated.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAy1GT,CAAC"}