@bowenqt/qiniu-ai-sdk 0.14.0 → 0.16.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 (107) hide show
  1. package/dist/ai/agent-graph.d.ts +8 -0
  2. package/dist/ai/agent-graph.d.ts.map +1 -1
  3. package/dist/ai/agent-graph.js +23 -3
  4. package/dist/ai/agent-graph.js.map +1 -1
  5. package/dist/ai/agent-graph.mjs +23 -3
  6. package/dist/ai/create-agent.d.ts +101 -0
  7. package/dist/ai/create-agent.d.ts.map +1 -0
  8. package/dist/ai/create-agent.js +88 -0
  9. package/dist/ai/create-agent.js.map +1 -0
  10. package/dist/ai/create-agent.mjs +85 -0
  11. package/dist/ai/generate-object.d.ts +99 -0
  12. package/dist/ai/generate-object.d.ts.map +1 -0
  13. package/dist/ai/generate-object.js +237 -0
  14. package/dist/ai/generate-object.js.map +1 -0
  15. package/dist/ai/generate-object.mjs +201 -0
  16. package/dist/ai/generate-text.d.ts +13 -1
  17. package/dist/ai/generate-text.d.ts.map +1 -1
  18. package/dist/ai/generate-text.js +35 -5
  19. package/dist/ai/generate-text.js.map +1 -1
  20. package/dist/ai/generate-text.mjs +35 -5
  21. package/dist/ai/graph/checkpointer.d.ts +72 -2
  22. package/dist/ai/graph/checkpointer.d.ts.map +1 -1
  23. package/dist/ai/graph/checkpointer.js +99 -1
  24. package/dist/ai/graph/checkpointer.js.map +1 -1
  25. package/dist/ai/graph/checkpointer.mjs +96 -1
  26. package/dist/ai/graph/index.d.ts +2 -2
  27. package/dist/ai/graph/index.d.ts.map +1 -1
  28. package/dist/ai/graph/index.js +5 -1
  29. package/dist/ai/graph/index.js.map +1 -1
  30. package/dist/ai/graph/index.mjs +3 -1
  31. package/dist/ai/graph/postgres-checkpointer.d.ts +1 -1
  32. package/dist/ai/graph/postgres-checkpointer.d.ts.map +1 -1
  33. package/dist/ai/graph/postgres-checkpointer.js +13 -3
  34. package/dist/ai/graph/postgres-checkpointer.js.map +1 -1
  35. package/dist/ai/graph/postgres-checkpointer.mjs +13 -3
  36. package/dist/ai/graph/redis-checkpointer.d.ts +1 -1
  37. package/dist/ai/graph/redis-checkpointer.d.ts.map +1 -1
  38. package/dist/ai/graph/redis-checkpointer.js +6 -2
  39. package/dist/ai/graph/redis-checkpointer.js.map +1 -1
  40. package/dist/ai/graph/redis-checkpointer.mjs +6 -2
  41. package/dist/ai/internal-types.d.ts +14 -0
  42. package/dist/ai/internal-types.d.ts.map +1 -1
  43. package/dist/ai/internal-types.js +15 -0
  44. package/dist/ai/internal-types.js.map +1 -1
  45. package/dist/ai/internal-types.mjs +13 -0
  46. package/dist/ai/memory/index.d.ts +147 -0
  47. package/dist/ai/memory/index.d.ts.map +1 -0
  48. package/dist/ai/memory/index.js +240 -0
  49. package/dist/ai/memory/index.js.map +1 -0
  50. package/dist/ai/memory/index.mjs +234 -0
  51. package/dist/ai/nodes/execute-node.d.ts +2 -1
  52. package/dist/ai/nodes/execute-node.d.ts.map +1 -1
  53. package/dist/ai/nodes/execute-node.js +11 -33
  54. package/dist/ai/nodes/execute-node.js.map +1 -1
  55. package/dist/ai/nodes/execute-node.mjs +11 -33
  56. package/dist/ai/nodes/memory-node.d.ts.map +1 -1
  57. package/dist/ai/nodes/memory-node.js +14 -16
  58. package/dist/ai/nodes/memory-node.js.map +1 -1
  59. package/dist/ai/nodes/memory-node.mjs +15 -17
  60. package/dist/ai/stream-object.d.ts +109 -0
  61. package/dist/ai/stream-object.d.ts.map +1 -0
  62. package/dist/ai/stream-object.js +383 -0
  63. package/dist/ai/stream-object.js.map +1 -0
  64. package/dist/ai/stream-object.mjs +347 -0
  65. package/dist/ai/tool-approval.d.ts +90 -0
  66. package/dist/ai/tool-approval.d.ts.map +1 -0
  67. package/dist/ai/tool-approval.js +151 -0
  68. package/dist/ai/tool-approval.js.map +1 -0
  69. package/dist/ai/tool-approval.mjs +147 -0
  70. package/dist/index.d.ts +17 -5
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +28 -3
  73. package/dist/index.js.map +1 -1
  74. package/dist/index.mjs +16 -4
  75. package/dist/lib/capability-cache.d.ts +72 -0
  76. package/dist/lib/capability-cache.d.ts.map +1 -0
  77. package/dist/lib/capability-cache.js +117 -0
  78. package/dist/lib/capability-cache.js.map +1 -0
  79. package/dist/lib/capability-cache.mjs +113 -0
  80. package/dist/lib/content-converter.d.ts +33 -0
  81. package/dist/lib/content-converter.d.ts.map +1 -0
  82. package/dist/lib/content-converter.js +166 -0
  83. package/dist/lib/content-converter.js.map +1 -0
  84. package/dist/lib/content-converter.mjs +161 -0
  85. package/dist/lib/errors.d.ts +15 -0
  86. package/dist/lib/errors.d.ts.map +1 -1
  87. package/dist/lib/errors.js +13 -1
  88. package/dist/lib/errors.js.map +1 -1
  89. package/dist/lib/errors.mjs +11 -0
  90. package/dist/lib/messages.js +4 -3
  91. package/dist/lib/messages.js.map +1 -1
  92. package/dist/lib/messages.mjs +4 -3
  93. package/dist/lib/partial-json-parser.d.ts +63 -0
  94. package/dist/lib/partial-json-parser.d.ts.map +1 -0
  95. package/dist/lib/partial-json-parser.js +142 -0
  96. package/dist/lib/partial-json-parser.js.map +1 -0
  97. package/dist/lib/partial-json-parser.mjs +137 -0
  98. package/dist/lib/token-estimator.d.ts.map +1 -1
  99. package/dist/lib/token-estimator.js +3 -2
  100. package/dist/lib/token-estimator.js.map +1 -1
  101. package/dist/lib/token-estimator.mjs +3 -2
  102. package/dist/lib/tool-registry.d.ts +21 -0
  103. package/dist/lib/tool-registry.d.ts.map +1 -1
  104. package/dist/lib/tool-registry.js.map +1 -1
  105. package/dist/lib/types.d.ts +20 -4
  106. package/dist/lib/types.d.ts.map +1 -1
  107. package/package.json +1 -1
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Capability cache for model feature detection.
3
+ * Caches model capabilities per client/baseUrl to avoid redundant probing.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { capabilityCache } from './capability-cache.mjs';
8
+ *
9
+ * // Check if model supports streaming JSON
10
+ * const supports = capabilityCache.get(client, 'gemini-2.5-flash', 'stream_json_schema');
11
+ *
12
+ * // Cache result after probing
13
+ * capabilityCache.set(client, 'gemini-2.5-flash', 'stream_json_schema', true);
14
+ * ```
15
+ */
16
+ /**
17
+ * Default TTL for cache entries (1 hour).
18
+ */
19
+ const DEFAULT_TTL_MS = 60 * 60 * 1000;
20
+ /**
21
+ * Capability cache implementation.
22
+ * Scoped by baseUrl + model + capability to avoid cross-provider contamination.
23
+ */
24
+ class CapabilityCache {
25
+ constructor(ttlMs = DEFAULT_TTL_MS) {
26
+ this.cache = new Map();
27
+ this.ttlMs = ttlMs;
28
+ }
29
+ /**
30
+ * Generate cache key from client, model, and capability.
31
+ */
32
+ getKey(client, model, capability) {
33
+ const baseUrl = client.getBaseUrl();
34
+ return `${baseUrl}:${model}:${capability}`;
35
+ }
36
+ /**
37
+ * Get cached capability status.
38
+ * Returns undefined if not cached or expired.
39
+ */
40
+ get(client, model, capability) {
41
+ const key = this.getKey(client, model, capability);
42
+ const entry = this.cache.get(key);
43
+ if (!entry) {
44
+ return undefined;
45
+ }
46
+ // Check expiration
47
+ if (Date.now() - entry.timestamp > this.ttlMs) {
48
+ this.cache.delete(key);
49
+ return undefined;
50
+ }
51
+ return entry.supported;
52
+ }
53
+ /**
54
+ * Set capability status.
55
+ */
56
+ set(client, model, capability, supported) {
57
+ const key = this.getKey(client, model, capability);
58
+ this.cache.set(key, {
59
+ supported,
60
+ timestamp: Date.now(),
61
+ });
62
+ }
63
+ /**
64
+ * Check if capability is cached and supported.
65
+ */
66
+ isSupported(client, model, capability) {
67
+ return this.get(client, model, capability) === true;
68
+ }
69
+ /**
70
+ * Check if capability is cached and not supported.
71
+ */
72
+ isNotSupported(client, model, capability) {
73
+ return this.get(client, model, capability) === false;
74
+ }
75
+ /**
76
+ * Clear all cached entries.
77
+ */
78
+ clear() {
79
+ this.cache.clear();
80
+ }
81
+ /**
82
+ * Clear entries for a specific client.
83
+ */
84
+ clearForClient(client) {
85
+ const baseUrl = client.getBaseUrl();
86
+ const keysToDelete = [];
87
+ for (const key of this.cache.keys()) {
88
+ if (key.startsWith(baseUrl + ':')) {
89
+ keysToDelete.push(key);
90
+ }
91
+ }
92
+ for (const key of keysToDelete) {
93
+ this.cache.delete(key);
94
+ }
95
+ }
96
+ /**
97
+ * Get cache size.
98
+ */
99
+ size() {
100
+ return this.cache.size;
101
+ }
102
+ }
103
+ /**
104
+ * Global capability cache instance.
105
+ */
106
+ export const capabilityCache = new CapabilityCache();
107
+ /**
108
+ * Create a new capability cache with custom TTL (for testing).
109
+ */
110
+ export function createCapabilityCache(ttlMs) {
111
+ return new CapabilityCache(ttlMs);
112
+ }
113
+ //# sourceMappingURL=capability-cache.js.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Content converter for multimodal messages.
3
+ * Normalizes SDK convenience formats to API-compatible formats.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { normalizeContent } from './content-converter';
8
+ *
9
+ * const content: ContentPart[] = [
10
+ * { type: 'text', text: 'Describe this image' },
11
+ * { type: 'image', image: fs.readFileSync('photo.jpg') },
12
+ * ];
13
+ *
14
+ * const normalized = normalizeContent(content);
15
+ * // [{ type: 'text', text: '...' }, { type: 'image_url', image_url: { url: 'data:...' } }]
16
+ * ```
17
+ */
18
+ import type { ContentPart } from './types';
19
+ /**
20
+ * Normalize content parts for API calls.
21
+ * Converts `image` sugar format to `image_url` API format.
22
+ */
23
+ export declare function normalizeContent(content: string | ContentPart[]): string | ContentPart[];
24
+ /**
25
+ * Helper to convert Blob to data URL asynchronously (for browser use).
26
+ * Only available in browser environments.
27
+ */
28
+ export declare function blobToDataUrl(blob: Blob): Promise<string>;
29
+ /**
30
+ * Check if content contains any image parts that need normalization.
31
+ */
32
+ export declare function hasImageParts(content: string | ContentPart[]): boolean;
33
+ //# sourceMappingURL=content-converter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-converter.d.ts","sourceRoot":"","sources":["../../src/lib/content-converter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAoC,MAAM,SAAS,CAAC;AAE7E;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,MAAM,GAAG,WAAW,EAAE,CAQxF;AA+HD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAa/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,OAAO,CAKtE"}
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ /**
3
+ * Content converter for multimodal messages.
4
+ * Normalizes SDK convenience formats to API-compatible formats.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { normalizeContent } from './content-converter';
9
+ *
10
+ * const content: ContentPart[] = [
11
+ * { type: 'text', text: 'Describe this image' },
12
+ * { type: 'image', image: fs.readFileSync('photo.jpg') },
13
+ * ];
14
+ *
15
+ * const normalized = normalizeContent(content);
16
+ * // [{ type: 'text', text: '...' }, { type: 'image_url', image_url: { url: 'data:...' } }]
17
+ * ```
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.normalizeContent = normalizeContent;
21
+ exports.blobToDataUrl = blobToDataUrl;
22
+ exports.hasImageParts = hasImageParts;
23
+ /**
24
+ * Normalize content parts for API calls.
25
+ * Converts `image` sugar format to `image_url` API format.
26
+ */
27
+ function normalizeContent(content) {
28
+ // String content doesn't need normalization
29
+ if (typeof content === 'string') {
30
+ return content;
31
+ }
32
+ // Normalize each content part
33
+ return content.map(part => normalizeContentPart(part));
34
+ }
35
+ /**
36
+ * Normalize a single content part.
37
+ */
38
+ function normalizeContentPart(part) {
39
+ // Already in API format
40
+ if (part.type === 'text' || part.type === 'image_url') {
41
+ return part;
42
+ }
43
+ // Convert image sugar to image_url
44
+ if (part.type === 'image') {
45
+ return {
46
+ type: 'image_url',
47
+ image_url: {
48
+ url: imageSourceToDataUrl(part.image),
49
+ detail: part.detail,
50
+ },
51
+ };
52
+ }
53
+ // Unknown type, return as-is
54
+ return part;
55
+ }
56
+ /**
57
+ * Convert ImageSource to data URL.
58
+ */
59
+ function imageSourceToDataUrl(source) {
60
+ // Already a string (base64 or URL)
61
+ if (typeof source === 'string') {
62
+ // Check if it's already a data URL or regular URL
63
+ if (source.startsWith('data:') || source.startsWith('http://') || source.startsWith('https://')) {
64
+ return source;
65
+ }
66
+ // Assume base64, wrap in data URL
67
+ return `data:image/png;base64,${source}`;
68
+ }
69
+ // URL object
70
+ if (source instanceof URL) {
71
+ return source.toString();
72
+ }
73
+ // Blob (browser)
74
+ if (typeof Blob !== 'undefined' && source instanceof Blob) {
75
+ // Note: Synchronous conversion not possible for Blob
76
+ // For real usage, caller should pre-convert to base64
77
+ throw new Error('Blob must be converted to base64 before passing to normalizeContent. Use blobToDataUrl() helper.');
78
+ }
79
+ // ArrayBuffer or Uint8Array
80
+ if (source instanceof ArrayBuffer) {
81
+ return arrayBufferToDataUrl(new Uint8Array(source));
82
+ }
83
+ if (source instanceof Uint8Array) {
84
+ return arrayBufferToDataUrl(source);
85
+ }
86
+ throw new Error(`Unsupported image source type: ${typeof source}`);
87
+ }
88
+ /**
89
+ * Convert Uint8Array to data URL.
90
+ */
91
+ function arrayBufferToDataUrl(buffer) {
92
+ // Detect MIME type from magic bytes
93
+ const mimeType = detectMimeType(buffer);
94
+ // Convert to base64
95
+ const base64 = uint8ArrayToBase64(buffer);
96
+ return `data:${mimeType};base64,${base64}`;
97
+ }
98
+ /**
99
+ * Detect image MIME type from magic bytes.
100
+ */
101
+ function detectMimeType(buffer) {
102
+ if (buffer.length < 4) {
103
+ return 'application/octet-stream';
104
+ }
105
+ // PNG: 89 50 4E 47
106
+ if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {
107
+ return 'image/png';
108
+ }
109
+ // JPEG: FF D8 FF
110
+ if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
111
+ return 'image/jpeg';
112
+ }
113
+ // GIF: 47 49 46 38
114
+ if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38) {
115
+ return 'image/gif';
116
+ }
117
+ // WebP: 52 49 46 46 ... 57 45 42 50
118
+ if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 &&
119
+ buffer.length > 11 && buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {
120
+ return 'image/webp';
121
+ }
122
+ return 'image/png'; // Default fallback
123
+ }
124
+ /**
125
+ * Convert Uint8Array to base64 string.
126
+ * Works in both Node.js and browser environments.
127
+ */
128
+ function uint8ArrayToBase64(buffer) {
129
+ // Node.js environment
130
+ if (typeof Buffer !== 'undefined') {
131
+ return Buffer.from(buffer).toString('base64');
132
+ }
133
+ // Browser environment
134
+ let binary = '';
135
+ for (let i = 0; i < buffer.length; i++) {
136
+ binary += String.fromCharCode(buffer[i]);
137
+ }
138
+ return btoa(binary);
139
+ }
140
+ /**
141
+ * Helper to convert Blob to data URL asynchronously (for browser use).
142
+ * Only available in browser environments.
143
+ */
144
+ async function blobToDataUrl(blob) {
145
+ // Check if FileReader is available (browser environment)
146
+ const FR = globalThis.FileReader;
147
+ if (!FR) {
148
+ throw new Error('blobToDataUrl is only available in browser environments');
149
+ }
150
+ return new Promise((resolve, reject) => {
151
+ const reader = new FR();
152
+ reader.onloadend = () => resolve(reader.result);
153
+ reader.onerror = reject;
154
+ reader.readAsDataURL(blob);
155
+ });
156
+ }
157
+ /**
158
+ * Check if content contains any image parts that need normalization.
159
+ */
160
+ function hasImageParts(content) {
161
+ if (typeof content === 'string') {
162
+ return false;
163
+ }
164
+ return content.some(part => part.type === 'image');
165
+ }
166
+ //# sourceMappingURL=content-converter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-converter.js","sourceRoot":"","sources":["../../src/lib/content-converter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Content converter for multimodal messages.
3
+ * Normalizes SDK convenience formats to API-compatible formats.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { normalizeContent } from './content-converter.mjs';
8
+ *
9
+ * const content: ContentPart[] = [
10
+ * { type: 'text', text: 'Describe this image' },
11
+ * { type: 'image', image: fs.readFileSync('photo.jpg') },
12
+ * ];
13
+ *
14
+ * const normalized = normalizeContent(content);
15
+ * // [{ type: 'text', text: '...' }, { type: 'image_url', image_url: { url: 'data:...' } }]
16
+ * ```
17
+ */
18
+ /**
19
+ * Normalize content parts for API calls.
20
+ * Converts `image` sugar format to `image_url` API format.
21
+ */
22
+ export function normalizeContent(content) {
23
+ // String content doesn't need normalization
24
+ if (typeof content === 'string') {
25
+ return content;
26
+ }
27
+ // Normalize each content part
28
+ return content.map(part => normalizeContentPart(part));
29
+ }
30
+ /**
31
+ * Normalize a single content part.
32
+ */
33
+ function normalizeContentPart(part) {
34
+ // Already in API format
35
+ if (part.type === 'text' || part.type === 'image_url') {
36
+ return part;
37
+ }
38
+ // Convert image sugar to image_url
39
+ if (part.type === 'image') {
40
+ return {
41
+ type: 'image_url',
42
+ image_url: {
43
+ url: imageSourceToDataUrl(part.image),
44
+ detail: part.detail,
45
+ },
46
+ };
47
+ }
48
+ // Unknown type, return as-is
49
+ return part;
50
+ }
51
+ /**
52
+ * Convert ImageSource to data URL.
53
+ */
54
+ function imageSourceToDataUrl(source) {
55
+ // Already a string (base64 or URL)
56
+ if (typeof source === 'string') {
57
+ // Check if it's already a data URL or regular URL
58
+ if (source.startsWith('data:') || source.startsWith('http://') || source.startsWith('https://')) {
59
+ return source;
60
+ }
61
+ // Assume base64, wrap in data URL
62
+ return `data:image/png;base64,${source}`;
63
+ }
64
+ // URL object
65
+ if (source instanceof URL) {
66
+ return source.toString();
67
+ }
68
+ // Blob (browser)
69
+ if (typeof Blob !== 'undefined' && source instanceof Blob) {
70
+ // Note: Synchronous conversion not possible for Blob
71
+ // For real usage, caller should pre-convert to base64
72
+ throw new Error('Blob must be converted to base64 before passing to normalizeContent. Use blobToDataUrl() helper.');
73
+ }
74
+ // ArrayBuffer or Uint8Array
75
+ if (source instanceof ArrayBuffer) {
76
+ return arrayBufferToDataUrl(new Uint8Array(source));
77
+ }
78
+ if (source instanceof Uint8Array) {
79
+ return arrayBufferToDataUrl(source);
80
+ }
81
+ throw new Error(`Unsupported image source type: ${typeof source}`);
82
+ }
83
+ /**
84
+ * Convert Uint8Array to data URL.
85
+ */
86
+ function arrayBufferToDataUrl(buffer) {
87
+ // Detect MIME type from magic bytes
88
+ const mimeType = detectMimeType(buffer);
89
+ // Convert to base64
90
+ const base64 = uint8ArrayToBase64(buffer);
91
+ return `data:${mimeType};base64,${base64}`;
92
+ }
93
+ /**
94
+ * Detect image MIME type from magic bytes.
95
+ */
96
+ function detectMimeType(buffer) {
97
+ if (buffer.length < 4) {
98
+ return 'application/octet-stream';
99
+ }
100
+ // PNG: 89 50 4E 47
101
+ if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {
102
+ return 'image/png';
103
+ }
104
+ // JPEG: FF D8 FF
105
+ if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
106
+ return 'image/jpeg';
107
+ }
108
+ // GIF: 47 49 46 38
109
+ if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38) {
110
+ return 'image/gif';
111
+ }
112
+ // WebP: 52 49 46 46 ... 57 45 42 50
113
+ if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 &&
114
+ buffer.length > 11 && buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {
115
+ return 'image/webp';
116
+ }
117
+ return 'image/png'; // Default fallback
118
+ }
119
+ /**
120
+ * Convert Uint8Array to base64 string.
121
+ * Works in both Node.js and browser environments.
122
+ */
123
+ function uint8ArrayToBase64(buffer) {
124
+ // Node.js environment
125
+ if (typeof Buffer !== 'undefined') {
126
+ return Buffer.from(buffer).toString('base64');
127
+ }
128
+ // Browser environment
129
+ let binary = '';
130
+ for (let i = 0; i < buffer.length; i++) {
131
+ binary += String.fromCharCode(buffer[i]);
132
+ }
133
+ return btoa(binary);
134
+ }
135
+ /**
136
+ * Helper to convert Blob to data URL asynchronously (for browser use).
137
+ * Only available in browser environments.
138
+ */
139
+ export async function blobToDataUrl(blob) {
140
+ // Check if FileReader is available (browser environment)
141
+ const FR = globalThis.FileReader;
142
+ if (!FR) {
143
+ throw new Error('blobToDataUrl is only available in browser environments');
144
+ }
145
+ return new Promise((resolve, reject) => {
146
+ const reader = new FR();
147
+ reader.onloadend = () => resolve(reader.result);
148
+ reader.onerror = reject;
149
+ reader.readAsDataURL(blob);
150
+ });
151
+ }
152
+ /**
153
+ * Check if content contains any image parts that need normalization.
154
+ */
155
+ export function hasImageParts(content) {
156
+ if (typeof content === 'string') {
157
+ return false;
158
+ }
159
+ return content.some(part => part.type === 'image');
160
+ }
161
+ //# sourceMappingURL=content-converter.js.map
@@ -9,4 +9,19 @@ export declare class MaxStepsExceededError extends AIError {
9
9
  maxSteps: number;
10
10
  constructor(maxSteps: number);
11
11
  }
12
+ /** Validation error details */
13
+ export interface ValidationErrorItem {
14
+ path: (string | number)[];
15
+ message: string;
16
+ }
17
+ /**
18
+ * Error thrown when structured output validation fails.
19
+ */
20
+ export declare class StructuredOutputError extends AIError {
21
+ /** Raw LLM output before validation */
22
+ raw: string;
23
+ /** Validation errors */
24
+ validationErrors: ValidationErrorItem[];
25
+ constructor(message: string, raw: string, validationErrors: ValidationErrorItem[]);
26
+ }
12
27
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,OAAQ,SAAQ,KAAK;gBAClB,OAAO,EAAE,MAAM;CAI9B;AAED,qBAAa,kBAAmB,SAAQ,OAAO;IAC3C,QAAQ,EAAE,MAAM,CAAC;gBAEL,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKhD;AAED,qBAAa,qBAAsB,SAAQ,OAAO;IAC9C,QAAQ,EAAE,MAAM,CAAC;gBAEL,QAAQ,EAAE,MAAM;CAK/B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,OAAQ,SAAQ,KAAK;gBAClB,OAAO,EAAE,MAAM;CAI9B;AAED,qBAAa,kBAAmB,SAAQ,OAAO;IAC3C,QAAQ,EAAE,MAAM,CAAC;gBAEL,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKhD;AAED,qBAAa,qBAAsB,SAAQ,OAAO;IAC9C,QAAQ,EAAE,MAAM,CAAC;gBAEL,QAAQ,EAAE,MAAM;CAK/B;AAED,+BAA+B;AAC/B,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,OAAO;IAC9C,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,wBAAwB;IACxB,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;gBAE5B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,mBAAmB,EAAE;CAMpF"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MaxStepsExceededError = exports.ToolExecutionError = exports.AIError = void 0;
3
+ exports.StructuredOutputError = exports.MaxStepsExceededError = exports.ToolExecutionError = exports.AIError = void 0;
4
4
  class AIError extends Error {
5
5
  constructor(message) {
6
6
  super(message);
@@ -24,4 +24,16 @@ class MaxStepsExceededError extends AIError {
24
24
  }
25
25
  }
26
26
  exports.MaxStepsExceededError = MaxStepsExceededError;
27
+ /**
28
+ * Error thrown when structured output validation fails.
29
+ */
30
+ class StructuredOutputError extends AIError {
31
+ constructor(message, raw, validationErrors) {
32
+ super(message);
33
+ this.name = 'StructuredOutputError';
34
+ this.raw = raw;
35
+ this.validationErrors = validationErrors;
36
+ }
37
+ }
38
+ exports.StructuredOutputError = StructuredOutputError;
27
39
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,OAAQ,SAAQ,KAAK;IAC9B,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;IAC1B,CAAC;CACJ;AALD,0BAKC;AAED,MAAa,kBAAmB,SAAQ,OAAO;IAG3C,YAAY,QAAgB,EAAE,OAAe;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AARD,gDAQC;AAED,MAAa,qBAAsB,SAAQ,OAAO;IAG9C,YAAY,QAAgB;QACxB,KAAK,CAAC,cAAc,QAAQ,uCAAuC,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AARD,sDAQC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,OAAQ,SAAQ,KAAK;IAC9B,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;IAC1B,CAAC;CACJ;AALD,0BAKC;AAED,MAAa,kBAAmB,SAAQ,OAAO;IAG3C,YAAY,QAAgB,EAAE,OAAe;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AARD,gDAQC;AAED,MAAa,qBAAsB,SAAQ,OAAO;IAG9C,YAAY,QAAgB;QACxB,KAAK,CAAC,cAAc,QAAQ,uCAAuC,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AARD,sDAQC;AAQD;;GAEG;AACH,MAAa,qBAAsB,SAAQ,OAAO;IAM9C,YAAY,OAAe,EAAE,GAAW,EAAE,gBAAuC;QAC7E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC7C,CAAC;CACJ;AAZD,sDAYC"}
@@ -18,4 +18,15 @@ export class MaxStepsExceededError extends AIError {
18
18
  this.maxSteps = maxSteps;
19
19
  }
20
20
  }
21
+ /**
22
+ * Error thrown when structured output validation fails.
23
+ */
24
+ export class StructuredOutputError extends AIError {
25
+ constructor(message, raw, validationErrors) {
26
+ super(message);
27
+ this.name = 'StructuredOutputError';
28
+ this.raw = raw;
29
+ this.validationErrors = validationErrors;
30
+ }
31
+ }
21
32
  //# sourceMappingURL=errors.js.map
@@ -49,10 +49,11 @@ function estimateMessageTokens(message) {
49
49
  return 0;
50
50
  }
51
51
  function estimateTokensFromPart(part) {
52
- if (part.type === 'text') {
53
- return estimateTokensFromText(part.text ?? '');
52
+ if (part.type === 'text' && 'text' in part) {
53
+ return estimateTokensFromText(part.text);
54
54
  }
55
- if (part.type === 'image_url') {
55
+ // Both image_url (API format) and image (SDK sugar) count as image tokens
56
+ if (part.type === 'image_url' || part.type === 'image') {
56
57
  return 50;
57
58
  }
58
59
  return 0;
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/lib/messages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/lib/messages.ts"],"names":[],"mappings":"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"}
@@ -45,10 +45,11 @@ function estimateMessageTokens(message) {
45
45
  return 0;
46
46
  }
47
47
  function estimateTokensFromPart(part) {
48
- if (part.type === 'text') {
49
- return estimateTokensFromText(part.text ?? '');
48
+ if (part.type === 'text' && 'text' in part) {
49
+ return estimateTokensFromText(part.text);
50
50
  }
51
- if (part.type === 'image_url') {
51
+ // Both image_url (API format) and image (SDK sugar) count as image tokens
52
+ if (part.type === 'image_url' || part.type === 'image') {
52
53
  return 50;
53
54
  }
54
55
  return 0;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Incremental JSON parser for streaming structured output.
3
+ * Parses partial JSON strings as they arrive, returning the most complete object possible.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const parser = new PartialJsonParser();
8
+ *
9
+ * parser.append('{"title": "Hello');
10
+ * console.log(parser.getPartial()); // { title: 'Hello' }
11
+ *
12
+ * parser.append('", "done": true}');
13
+ * console.log(parser.getPartial()); // { title: 'Hello', done: true }
14
+ * ```
15
+ */
16
+ /**
17
+ * Result of parsing attempt.
18
+ */
19
+ export interface ParseResult<T = unknown> {
20
+ /** Whether the JSON is complete and valid */
21
+ complete: boolean;
22
+ /** The parsed partial object (may be incomplete) */
23
+ value: T | null;
24
+ /** Parsing error if any */
25
+ error?: string;
26
+ }
27
+ /**
28
+ * Incremental JSON parser that handles partial JSON strings.
29
+ */
30
+ export declare class PartialJsonParser {
31
+ private buffer;
32
+ /**
33
+ * Append new content to the buffer.
34
+ */
35
+ append(chunk: string): void;
36
+ /**
37
+ * Get the current buffer content.
38
+ */
39
+ getBuffer(): string;
40
+ /**
41
+ * Reset the parser.
42
+ */
43
+ reset(): void;
44
+ /**
45
+ * Try to parse the current buffer as complete JSON.
46
+ */
47
+ parseComplete<T = unknown>(): ParseResult<T>;
48
+ /**
49
+ * Parse the current buffer, attempting to complete partial JSON.
50
+ * Returns the most complete object possible.
51
+ */
52
+ parsePartial<T = unknown>(): ParseResult<T>;
53
+ /**
54
+ * Attempt to complete partial JSON by adding missing closing brackets.
55
+ */
56
+ private completePartialJson;
57
+ }
58
+ /**
59
+ * Parse a partial JSON string, returning the most complete object possible.
60
+ * Convenience function for one-shot parsing.
61
+ */
62
+ export declare function parsePartialJson<T = unknown>(json: string): ParseResult<T>;
63
+ //# sourceMappingURL=partial-json-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-json-parser.d.ts","sourceRoot":"","sources":["../../src/lib/partial-json-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACpC,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IAChB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,MAAM,CAAM;IAEpB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,aAAa,CAAC,CAAC,GAAG,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC;IAS5C;;;OAGG;IACH,YAAY,CAAC,CAAC,GAAG,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC;IAqB3C;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAwD9B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAI1E"}