ai-functions 2.1.3 → 2.4.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 (284) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +90 -1
  3. package/README.md +38 -0
  4. package/dist/ai-promise.d.ts +3 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +135 -64
  7. package/dist/ai-promise.js.map +1 -1
  8. package/dist/ai-schemas.d.ts +56 -0
  9. package/dist/ai-schemas.d.ts.map +1 -0
  10. package/dist/ai-schemas.js +53 -0
  11. package/dist/ai-schemas.js.map +1 -0
  12. package/dist/ai.d.ts +16 -242
  13. package/dist/ai.d.ts.map +1 -1
  14. package/dist/ai.js +51 -858
  15. package/dist/ai.js.map +1 -1
  16. package/dist/batch/anthropic.d.ts +6 -4
  17. package/dist/batch/anthropic.d.ts.map +1 -1
  18. package/dist/batch/anthropic.js +83 -145
  19. package/dist/batch/anthropic.js.map +1 -1
  20. package/dist/batch/bedrock.d.ts +8 -30
  21. package/dist/batch/bedrock.d.ts.map +1 -1
  22. package/dist/batch/bedrock.js +155 -338
  23. package/dist/batch/bedrock.js.map +1 -1
  24. package/dist/batch/cloudflare.d.ts +8 -20
  25. package/dist/batch/cloudflare.d.ts.map +1 -1
  26. package/dist/batch/cloudflare.js +68 -189
  27. package/dist/batch/cloudflare.js.map +1 -1
  28. package/dist/batch/google.d.ts +6 -20
  29. package/dist/batch/google.d.ts.map +1 -1
  30. package/dist/batch/google.js +70 -238
  31. package/dist/batch/google.js.map +1 -1
  32. package/dist/batch/index.d.ts +4 -1
  33. package/dist/batch/index.d.ts.map +1 -1
  34. package/dist/batch/index.js +4 -1
  35. package/dist/batch/index.js.map +1 -1
  36. package/dist/batch/memory.d.ts +1 -1
  37. package/dist/batch/memory.d.ts.map +1 -1
  38. package/dist/batch/memory.js +14 -10
  39. package/dist/batch/memory.js.map +1 -1
  40. package/dist/batch/openai.d.ts +11 -14
  41. package/dist/batch/openai.d.ts.map +1 -1
  42. package/dist/batch/openai.js +52 -156
  43. package/dist/batch/openai.js.map +1 -1
  44. package/dist/batch/provider.d.ts +111 -0
  45. package/dist/batch/provider.d.ts.map +1 -0
  46. package/dist/batch/provider.js +233 -0
  47. package/dist/batch/provider.js.map +1 -0
  48. package/dist/batch-map.d.ts.map +1 -1
  49. package/dist/batch-map.js +23 -17
  50. package/dist/batch-map.js.map +1 -1
  51. package/dist/batch-queue.d.ts +65 -0
  52. package/dist/batch-queue.d.ts.map +1 -1
  53. package/dist/batch-queue.js +169 -14
  54. package/dist/batch-queue.js.map +1 -1
  55. package/dist/budget.d.ts.map +1 -1
  56. package/dist/budget.js +27 -14
  57. package/dist/budget.js.map +1 -1
  58. package/dist/cache.d.ts +23 -0
  59. package/dist/cache.d.ts.map +1 -1
  60. package/dist/cache.js +36 -15
  61. package/dist/cache.js.map +1 -1
  62. package/dist/context.d.ts +26 -8
  63. package/dist/context.d.ts.map +1 -1
  64. package/dist/context.js +64 -62
  65. package/dist/context.js.map +1 -1
  66. package/dist/digital-objects-registry.d.ts +229 -0
  67. package/dist/digital-objects-registry.d.ts.map +1 -0
  68. package/dist/digital-objects-registry.js +617 -0
  69. package/dist/digital-objects-registry.js.map +1 -0
  70. package/dist/embeddings.d.ts +2 -2
  71. package/dist/embeddings.d.ts.map +1 -1
  72. package/dist/errors.d.ts +22 -0
  73. package/dist/errors.d.ts.map +1 -0
  74. package/dist/errors.js +35 -0
  75. package/dist/errors.js.map +1 -0
  76. package/dist/eval/runner.d.ts +8 -0
  77. package/dist/eval/runner.d.ts.map +1 -1
  78. package/dist/eval/runner.js +41 -35
  79. package/dist/eval/runner.js.map +1 -1
  80. package/dist/eval-log/in-memory.d.ts +34 -0
  81. package/dist/eval-log/in-memory.d.ts.map +1 -0
  82. package/dist/eval-log/in-memory.js +84 -0
  83. package/dist/eval-log/in-memory.js.map +1 -0
  84. package/dist/eval-log/index.d.ts +29 -0
  85. package/dist/eval-log/index.d.ts.map +1 -0
  86. package/dist/eval-log/index.js +39 -0
  87. package/dist/eval-log/index.js.map +1 -0
  88. package/dist/eval-log/types.d.ts +101 -0
  89. package/dist/eval-log/types.d.ts.map +1 -0
  90. package/dist/eval-log/types.js +16 -0
  91. package/dist/eval-log/types.js.map +1 -0
  92. package/dist/function-registry.d.ts +176 -0
  93. package/dist/function-registry.d.ts.map +1 -0
  94. package/dist/function-registry.js +685 -0
  95. package/dist/function-registry.js.map +1 -0
  96. package/dist/generate.d.ts +9 -3
  97. package/dist/generate.d.ts.map +1 -1
  98. package/dist/generate.js +18 -18
  99. package/dist/generate.js.map +1 -1
  100. package/dist/index.d.ts +18 -11
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +35 -18
  103. package/dist/index.js.map +1 -1
  104. package/dist/logger.d.ts +118 -0
  105. package/dist/logger.d.ts.map +1 -0
  106. package/dist/logger.js +187 -0
  107. package/dist/logger.js.map +1 -0
  108. package/dist/middleware/budget.d.ts +84 -0
  109. package/dist/middleware/budget.d.ts.map +1 -0
  110. package/dist/middleware/budget.js +110 -0
  111. package/dist/middleware/budget.js.map +1 -0
  112. package/dist/middleware/cache.d.ts +103 -0
  113. package/dist/middleware/cache.d.ts.map +1 -0
  114. package/dist/middleware/cache.js +228 -0
  115. package/dist/middleware/cache.js.map +1 -0
  116. package/dist/middleware/embed-cache.d.ts +99 -0
  117. package/dist/middleware/embed-cache.d.ts.map +1 -0
  118. package/dist/middleware/embed-cache.js +128 -0
  119. package/dist/middleware/embed-cache.js.map +1 -0
  120. package/dist/middleware/index.d.ts +11 -0
  121. package/dist/middleware/index.d.ts.map +1 -0
  122. package/dist/middleware/index.js +11 -0
  123. package/dist/middleware/index.js.map +1 -0
  124. package/dist/middleware/trace.d.ts +103 -0
  125. package/dist/middleware/trace.d.ts.map +1 -0
  126. package/dist/middleware/trace.js +176 -0
  127. package/dist/middleware/trace.js.map +1 -0
  128. package/dist/primitives.d.ts +120 -1
  129. package/dist/primitives.d.ts.map +1 -1
  130. package/dist/primitives.js +398 -26
  131. package/dist/primitives.js.map +1 -1
  132. package/dist/retry.d.ts +66 -1
  133. package/dist/retry.d.ts.map +1 -1
  134. package/dist/retry.js +115 -8
  135. package/dist/retry.js.map +1 -1
  136. package/dist/sandbox.d.ts +36 -0
  137. package/dist/sandbox.d.ts.map +1 -0
  138. package/dist/sandbox.js +44 -0
  139. package/dist/sandbox.js.map +1 -0
  140. package/dist/schema.js +2 -2
  141. package/dist/schema.js.map +1 -1
  142. package/dist/telemetry.d.ts +128 -0
  143. package/dist/telemetry.d.ts.map +1 -0
  144. package/dist/telemetry.js +285 -0
  145. package/dist/telemetry.js.map +1 -0
  146. package/dist/template.d.ts.map +1 -1
  147. package/dist/template.js +6 -1
  148. package/dist/template.js.map +1 -1
  149. package/dist/tool-orchestration.d.ts +66 -4
  150. package/dist/tool-orchestration.d.ts.map +1 -1
  151. package/dist/tool-orchestration.js +123 -23
  152. package/dist/tool-orchestration.js.map +1 -1
  153. package/dist/type-guards.d.ts +28 -0
  154. package/dist/type-guards.d.ts.map +1 -0
  155. package/dist/type-guards.js +29 -0
  156. package/dist/type-guards.js.map +1 -0
  157. package/dist/types.d.ts +155 -19
  158. package/dist/types.d.ts.map +1 -1
  159. package/dist/types.js +36 -1
  160. package/dist/types.js.map +1 -1
  161. package/dist/wrap-for-v3.d.ts +80 -0
  162. package/dist/wrap-for-v3.d.ts.map +1 -0
  163. package/dist/wrap-for-v3.js +89 -0
  164. package/dist/wrap-for-v3.js.map +1 -0
  165. package/examples/00-quickstart.ts +232 -0
  166. package/examples/01-rag-chatbot.ts +212 -0
  167. package/examples/02-multi-agent-research.ts +290 -0
  168. package/examples/03-email-classification.ts +379 -0
  169. package/examples/04-content-moderation.ts +400 -0
  170. package/examples/05-document-extraction.ts +455 -0
  171. package/examples/06-streaming-chat-nextjs.ts +437 -0
  172. package/examples/07-cloudflare-worker.ts +483 -0
  173. package/examples/08-batch-processing.ts +491 -0
  174. package/examples/09-budget-constrained.ts +527 -0
  175. package/examples/10-tool-orchestration.ts +565 -0
  176. package/examples/11-retry-resilience.ts +403 -0
  177. package/examples/12-caching-strategies.ts +422 -0
  178. package/examples/README.md +145 -0
  179. package/package.json +29 -25
  180. package/src/ai-promise.ts +226 -140
  181. package/src/ai-schemas.ts +122 -0
  182. package/src/ai.ts +71 -1176
  183. package/src/batch/anthropic.ts +96 -161
  184. package/src/batch/bedrock.ts +203 -454
  185. package/src/batch/cloudflare.ts +99 -282
  186. package/src/batch/google.ts +91 -297
  187. package/src/batch/index.ts +4 -1
  188. package/src/batch/memory.ts +15 -10
  189. package/src/batch/openai.ts +65 -193
  190. package/src/batch/provider.ts +336 -0
  191. package/src/batch-map.ts +29 -24
  192. package/src/batch-queue.ts +200 -11
  193. package/src/budget.ts +31 -18
  194. package/src/cache.ts +45 -17
  195. package/src/context.ts +106 -77
  196. package/src/digital-objects-registry.ts +750 -0
  197. package/src/errors.ts +37 -0
  198. package/src/eval/runner.ts +60 -36
  199. package/src/eval-log/in-memory.ts +90 -0
  200. package/src/eval-log/index.ts +46 -0
  201. package/src/eval-log/types.ts +110 -0
  202. package/src/function-registry.ts +874 -0
  203. package/src/generate.ts +33 -28
  204. package/src/index.ts +122 -21
  205. package/src/logger.ts +232 -0
  206. package/src/middleware/budget.ts +171 -0
  207. package/src/middleware/cache.ts +299 -0
  208. package/src/middleware/embed-cache.ts +195 -0
  209. package/src/middleware/index.ts +23 -0
  210. package/src/middleware/trace.ts +248 -0
  211. package/src/primitives.ts +589 -62
  212. package/src/retry.ts +144 -18
  213. package/src/sandbox.ts +52 -0
  214. package/src/schema.ts +8 -8
  215. package/src/telemetry.ts +403 -0
  216. package/src/template.ts +8 -4
  217. package/src/tool-orchestration.ts +213 -48
  218. package/src/type-guards.ts +31 -0
  219. package/src/types.ts +186 -27
  220. package/src/wrap-for-v3.ts +105 -0
  221. package/test/ai-promise.test.ts +1080 -0
  222. package/test/ai-proxy.test.ts +1 -1
  223. package/test/batch-autosubmit-errors.test.ts +49 -37
  224. package/test/batch-blog-posts.test.ts +87 -129
  225. package/test/core-functions.test.ts +183 -579
  226. package/test/decide.test.ts +154 -322
  227. package/test/define.test.ts +211 -8
  228. package/test/digital-objects-registry.test.ts +760 -0
  229. package/test/embedding-cache-middleware.test.ts +140 -0
  230. package/test/fill-template.test.ts +89 -0
  231. package/test/generate-core.test.ts +140 -229
  232. package/test/implicit-batch.test.ts +22 -65
  233. package/test/retry-policy-integration.test.ts +117 -0
  234. package/test/sandbox-execution.test.ts +155 -0
  235. package/test/schema.test.ts +55 -19
  236. package/test/template.test.ts +1164 -0
  237. package/test/tool-orchestration.test.ts +270 -0
  238. package/test/wrap-for-v3.test.ts +612 -0
  239. package/vitest.config.js +6 -0
  240. package/vitest.config.ts +20 -0
  241. package/LICENSE +0 -21
  242. package/dist/rpc/auth.d.ts +0 -69
  243. package/dist/rpc/auth.d.ts.map +0 -1
  244. package/dist/rpc/auth.js +0 -136
  245. package/dist/rpc/auth.js.map +0 -1
  246. package/dist/rpc/client.d.ts +0 -62
  247. package/dist/rpc/client.d.ts.map +0 -1
  248. package/dist/rpc/client.js +0 -103
  249. package/dist/rpc/client.js.map +0 -1
  250. package/dist/rpc/deferred.d.ts +0 -60
  251. package/dist/rpc/deferred.d.ts.map +0 -1
  252. package/dist/rpc/deferred.js +0 -96
  253. package/dist/rpc/deferred.js.map +0 -1
  254. package/dist/rpc/index.d.ts +0 -22
  255. package/dist/rpc/index.d.ts.map +0 -1
  256. package/dist/rpc/index.js +0 -38
  257. package/dist/rpc/index.js.map +0 -1
  258. package/dist/rpc/local.d.ts +0 -42
  259. package/dist/rpc/local.d.ts.map +0 -1
  260. package/dist/rpc/local.js +0 -50
  261. package/dist/rpc/local.js.map +0 -1
  262. package/dist/rpc/server.d.ts +0 -165
  263. package/dist/rpc/server.d.ts.map +0 -1
  264. package/dist/rpc/server.js +0 -405
  265. package/dist/rpc/server.js.map +0 -1
  266. package/dist/rpc/session.d.ts +0 -32
  267. package/dist/rpc/session.d.ts.map +0 -1
  268. package/dist/rpc/session.js +0 -43
  269. package/dist/rpc/session.js.map +0 -1
  270. package/dist/rpc/transport.d.ts +0 -306
  271. package/dist/rpc/transport.d.ts.map +0 -1
  272. package/dist/rpc/transport.js +0 -731
  273. package/dist/rpc/transport.js.map +0 -1
  274. package/src/batch/anthropic.js +0 -256
  275. package/src/batch/bedrock.js +0 -584
  276. package/src/batch/cloudflare.js +0 -287
  277. package/src/batch/google.js +0 -359
  278. package/src/batch/index.js +0 -30
  279. package/src/batch/memory.js +0 -187
  280. package/src/batch/openai.js +0 -402
  281. package/src/eval/index.js +0 -7
  282. package/src/eval/models.js +0 -119
  283. package/src/eval/runner.js +0 -147
  284. package/test/schema.test.js +0 -96
@@ -1,187 +0,0 @@
1
- /**
2
- * In-Memory Batch Adapter for Testing
3
- *
4
- * Simulates batch processing locally for testing purposes.
5
- * Executes requests immediately (or with configurable delay).
6
- *
7
- * @packageDocumentation
8
- */
9
- import { registerBatchAdapter, } from '../batch-queue.js';
10
- import { generateObject, generateText } from '../generate.js';
11
- // ============================================================================
12
- // Storage
13
- // ============================================================================
14
- const batches = new Map();
15
- let batchCounter = 0;
16
- let memoryOptions = {};
17
- /**
18
- * Configure the memory adapter
19
- */
20
- export function configureMemoryAdapter(options) {
21
- memoryOptions = options;
22
- }
23
- /**
24
- * Clear all stored batches (for testing)
25
- */
26
- export function clearBatches() {
27
- batches.clear();
28
- batchCounter = 0;
29
- }
30
- /**
31
- * Get all stored batches (for testing)
32
- */
33
- export function getBatches() {
34
- return batches;
35
- }
36
- // ============================================================================
37
- // Memory Batch Adapter
38
- // ============================================================================
39
- const memoryAdapter = {
40
- async submit(items, options) {
41
- const batchId = `batch_memory_${++batchCounter}`;
42
- const batch = {
43
- id: batchId,
44
- items: [...items],
45
- options,
46
- status: 'pending',
47
- results: [],
48
- createdAt: new Date(),
49
- };
50
- batches.set(batchId, batch);
51
- // Start processing asynchronously
52
- const completion = processMemoryBatch(batch);
53
- const job = {
54
- id: batchId,
55
- provider: 'openai', // Simulating OpenAI
56
- status: 'pending',
57
- totalItems: items.length,
58
- completedItems: 0,
59
- failedItems: 0,
60
- createdAt: batch.createdAt,
61
- webhookUrl: options.webhookUrl,
62
- };
63
- return { job, completion };
64
- },
65
- async getStatus(batchId) {
66
- const batch = batches.get(batchId);
67
- if (!batch) {
68
- throw new Error(`Batch not found: ${batchId}`);
69
- }
70
- const completedItems = batch.results.filter((r) => r.status === 'completed').length;
71
- const failedItems = batch.results.filter((r) => r.status === 'failed').length;
72
- return {
73
- id: batch.id,
74
- provider: 'openai',
75
- status: batch.status === 'completed' ? 'completed' : batch.status === 'failed' ? 'failed' : 'in_progress',
76
- totalItems: batch.items.length,
77
- completedItems,
78
- failedItems,
79
- createdAt: batch.createdAt,
80
- completedAt: batch.completedAt,
81
- };
82
- },
83
- async cancel(batchId) {
84
- const batch = batches.get(batchId);
85
- if (!batch) {
86
- throw new Error(`Batch not found: ${batchId}`);
87
- }
88
- batch.status = 'failed';
89
- },
90
- async getResults(batchId) {
91
- const batch = batches.get(batchId);
92
- if (!batch) {
93
- throw new Error(`Batch not found: ${batchId}`);
94
- }
95
- return batch.results;
96
- },
97
- async waitForCompletion(batchId) {
98
- const batch = batches.get(batchId);
99
- if (!batch) {
100
- throw new Error(`Batch not found: ${batchId}`);
101
- }
102
- // Wait for completion
103
- while (batch.status !== 'completed' && batch.status !== 'failed') {
104
- await new Promise((resolve) => setTimeout(resolve, 100));
105
- }
106
- return batch.results;
107
- },
108
- };
109
- // ============================================================================
110
- // Processing
111
- // ============================================================================
112
- async function processMemoryBatch(batch) {
113
- batch.status = 'in_progress';
114
- // Optional delay
115
- if (memoryOptions.delay) {
116
- await new Promise((resolve) => setTimeout(resolve, memoryOptions.delay));
117
- }
118
- const results = [];
119
- for (const item of batch.items) {
120
- try {
121
- // Simulate failure
122
- if (memoryOptions.failureRate && Math.random() < memoryOptions.failureRate) {
123
- throw new Error('Simulated failure');
124
- }
125
- let result;
126
- if (memoryOptions.handler) {
127
- // Use custom handler
128
- result = await memoryOptions.handler(item);
129
- }
130
- else if (item.schema) {
131
- // Generate structured output
132
- const response = await generateObject({
133
- model: batch.options.model || 'sonnet',
134
- schema: item.schema,
135
- prompt: item.prompt,
136
- system: item.options?.system,
137
- temperature: item.options?.temperature,
138
- maxTokens: item.options?.maxTokens,
139
- });
140
- result = response.object;
141
- }
142
- else {
143
- // Generate text
144
- const response = await generateText({
145
- model: batch.options.model || 'sonnet',
146
- prompt: item.prompt,
147
- system: item.options?.system,
148
- temperature: item.options?.temperature,
149
- maxTokens: item.options?.maxTokens,
150
- });
151
- result = response.text;
152
- }
153
- results.push({
154
- id: item.id,
155
- customId: item.id,
156
- status: 'completed',
157
- result,
158
- usage: {
159
- promptTokens: 100,
160
- completionTokens: 200,
161
- totalTokens: 300,
162
- },
163
- });
164
- }
165
- catch (error) {
166
- results.push({
167
- id: item.id,
168
- customId: item.id,
169
- status: 'failed',
170
- error: error instanceof Error ? error.message : 'Unknown error',
171
- });
172
- }
173
- }
174
- batch.results = results;
175
- batch.status = results.every((r) => r.status === 'completed') ? 'completed' : 'failed';
176
- batch.completedAt = new Date();
177
- return results;
178
- }
179
- // ============================================================================
180
- // Register Adapter
181
- // ============================================================================
182
- registerBatchAdapter('openai', memoryAdapter);
183
- registerBatchAdapter('anthropic', memoryAdapter);
184
- registerBatchAdapter('google', memoryAdapter);
185
- registerBatchAdapter('bedrock', memoryAdapter);
186
- registerBatchAdapter('cloudflare', memoryAdapter);
187
- export { memoryAdapter };
@@ -1,402 +0,0 @@
1
- /**
2
- * OpenAI Batch API Adapter
3
- *
4
- * Implements batch processing using OpenAI's Batch API:
5
- * - 50% cost discount
6
- * - 24-hour turnaround
7
- * - Up to 50,000 requests per batch
8
- *
9
- * @see https://platform.openai.com/docs/guides/batch
10
- *
11
- * @packageDocumentation
12
- */
13
- import { registerBatchAdapter, registerFlexAdapter, } from '../batch-queue.js';
14
- import { schema as convertSchema } from '../schema.js';
15
- // ============================================================================
16
- // OpenAI Client
17
- // ============================================================================
18
- let openaiApiKey;
19
- let openaiBaseUrl = 'https://api.openai.com/v1';
20
- /**
21
- * Configure the OpenAI client
22
- */
23
- export function configureOpenAI(options) {
24
- if (options.apiKey)
25
- openaiApiKey = options.apiKey;
26
- if (options.baseUrl)
27
- openaiBaseUrl = options.baseUrl;
28
- }
29
- function getApiKey() {
30
- const key = openaiApiKey || process.env.OPENAI_API_KEY;
31
- if (!key) {
32
- throw new Error('OpenAI API key not configured. Set OPENAI_API_KEY or call configureOpenAI()');
33
- }
34
- return key;
35
- }
36
- async function openaiRequest(method, path, body) {
37
- const url = `${openaiBaseUrl}${path}`;
38
- const response = await fetch(url, {
39
- method,
40
- headers: {
41
- Authorization: `Bearer ${getApiKey()}`,
42
- 'Content-Type': 'application/json',
43
- },
44
- body: body ? JSON.stringify(body) : undefined,
45
- });
46
- if (!response.ok) {
47
- const error = await response.text();
48
- throw new Error(`OpenAI API error: ${response.status} ${error}`);
49
- }
50
- return response.json();
51
- }
52
- async function uploadFile(content, purpose) {
53
- const formData = new FormData();
54
- formData.append('purpose', purpose);
55
- formData.append('file', new Blob([content], { type: 'application/jsonl' }), 'batch.jsonl');
56
- const response = await fetch(`${openaiBaseUrl}/files`, {
57
- method: 'POST',
58
- headers: {
59
- Authorization: `Bearer ${getApiKey()}`,
60
- },
61
- body: formData,
62
- });
63
- if (!response.ok) {
64
- const error = await response.text();
65
- throw new Error(`OpenAI file upload error: ${response.status} ${error}`);
66
- }
67
- return response.json();
68
- }
69
- async function downloadFile(fileId) {
70
- const response = await fetch(`${openaiBaseUrl}/files/${fileId}/content`, {
71
- headers: {
72
- Authorization: `Bearer ${getApiKey()}`,
73
- },
74
- });
75
- if (!response.ok) {
76
- const error = await response.text();
77
- throw new Error(`OpenAI file download error: ${response.status} ${error}`);
78
- }
79
- return response.text();
80
- }
81
- // ============================================================================
82
- // Status Mapping
83
- // ============================================================================
84
- function mapStatus(status) {
85
- const statusMap = {
86
- validating: 'validating',
87
- in_progress: 'in_progress',
88
- finalizing: 'finalizing',
89
- completed: 'completed',
90
- failed: 'failed',
91
- expired: 'expired',
92
- cancelling: 'cancelling',
93
- cancelled: 'cancelled',
94
- };
95
- return statusMap[status] || 'pending';
96
- }
97
- // ============================================================================
98
- // OpenAI Batch Adapter
99
- // ============================================================================
100
- const openaiAdapter = {
101
- async submit(items, options) {
102
- const model = options.model || 'gpt-4o';
103
- // Build JSONL content
104
- const requests = items.map((item) => {
105
- const request = {
106
- custom_id: item.id,
107
- method: 'POST',
108
- url: '/v1/chat/completions',
109
- body: {
110
- model,
111
- messages: [
112
- ...(item.options?.system ? [{ role: 'system', content: item.options.system }] : []),
113
- { role: 'user', content: item.prompt },
114
- ],
115
- max_tokens: item.options?.maxTokens,
116
- temperature: item.options?.temperature,
117
- },
118
- };
119
- // Add JSON schema if provided
120
- if (item.schema) {
121
- const zodSchema = convertSchema(item.schema);
122
- // Convert Zod to JSON Schema (simplified - you'd want a proper converter)
123
- request.body.response_format = {
124
- type: 'json_schema',
125
- json_schema: {
126
- name: 'response',
127
- schema: zodToJsonSchema(zodSchema),
128
- },
129
- };
130
- }
131
- return request;
132
- });
133
- const jsonlContent = requests.map((r) => JSON.stringify(r)).join('\n');
134
- // Upload the input file
135
- const inputFile = await uploadFile(jsonlContent, 'batch');
136
- // Create the batch
137
- const batch = await openaiRequest('POST', '/batches', {
138
- input_file_id: inputFile.id,
139
- endpoint: '/v1/chat/completions',
140
- completion_window: '24h',
141
- metadata: options.metadata,
142
- });
143
- const job = {
144
- id: batch.id,
145
- provider: 'openai',
146
- status: mapStatus(batch.status),
147
- totalItems: items.length,
148
- completedItems: 0,
149
- failedItems: 0,
150
- createdAt: new Date(batch.created_at * 1000),
151
- expiresAt: batch.expires_at ? new Date(batch.expires_at * 1000) : undefined,
152
- webhookUrl: options.webhookUrl,
153
- inputFileId: batch.input_file_id,
154
- };
155
- // Create completion promise
156
- const completion = this.waitForCompletion(batch.id);
157
- return { job, completion };
158
- },
159
- async getStatus(batchId) {
160
- const batch = await openaiRequest('GET', `/batches/${batchId}`);
161
- return {
162
- id: batch.id,
163
- provider: 'openai',
164
- status: mapStatus(batch.status),
165
- totalItems: batch.request_counts.total,
166
- completedItems: batch.request_counts.completed,
167
- failedItems: batch.request_counts.failed,
168
- createdAt: new Date(batch.created_at * 1000),
169
- startedAt: batch.in_progress_at ? new Date(batch.in_progress_at * 1000) : undefined,
170
- completedAt: batch.completed_at ? new Date(batch.completed_at * 1000) : undefined,
171
- expiresAt: batch.expires_at ? new Date(batch.expires_at * 1000) : undefined,
172
- inputFileId: batch.input_file_id,
173
- outputFileId: batch.output_file_id || undefined,
174
- errorFileId: batch.error_file_id || undefined,
175
- };
176
- },
177
- async cancel(batchId) {
178
- await openaiRequest('POST', `/batches/${batchId}/cancel`);
179
- },
180
- async getResults(batchId) {
181
- const status = await this.getStatus(batchId);
182
- if (status.status !== 'completed' && status.status !== 'failed') {
183
- throw new Error(`Batch not complete. Status: ${status.status}`);
184
- }
185
- const results = [];
186
- // Download and parse output file
187
- if (status.outputFileId) {
188
- const content = await downloadFile(status.outputFileId);
189
- const lines = content.trim().split('\n');
190
- for (const line of lines) {
191
- const response = JSON.parse(line);
192
- if (response.error) {
193
- results.push({
194
- id: response.custom_id,
195
- customId: response.custom_id,
196
- status: 'failed',
197
- error: response.error.message,
198
- });
199
- }
200
- else if (response.response) {
201
- const content = response.response.body.choices[0]?.message?.content;
202
- let result = content;
203
- // Try to parse JSON if it looks like JSON
204
- if (content?.startsWith('{') || content?.startsWith('[')) {
205
- try {
206
- result = JSON.parse(content);
207
- }
208
- catch {
209
- // Keep as string
210
- }
211
- }
212
- results.push({
213
- id: response.custom_id,
214
- customId: response.custom_id,
215
- status: 'completed',
216
- result,
217
- usage: {
218
- promptTokens: response.response.body.usage.prompt_tokens,
219
- completionTokens: response.response.body.usage.completion_tokens,
220
- totalTokens: response.response.body.usage.total_tokens,
221
- },
222
- });
223
- }
224
- }
225
- }
226
- // Download and parse error file
227
- if (status.errorFileId) {
228
- const content = await downloadFile(status.errorFileId);
229
- const lines = content.trim().split('\n');
230
- for (const line of lines) {
231
- const response = JSON.parse(line);
232
- results.push({
233
- id: response.custom_id,
234
- customId: response.custom_id,
235
- status: 'failed',
236
- error: response.error?.message || 'Unknown error',
237
- });
238
- }
239
- }
240
- return results;
241
- },
242
- async waitForCompletion(batchId, pollInterval = 5000) {
243
- while (true) {
244
- const status = await this.getStatus(batchId);
245
- if (status.status === 'completed' || status.status === 'failed') {
246
- return this.getResults(batchId);
247
- }
248
- if (status.status === 'cancelled' || status.status === 'expired') {
249
- throw new Error(`Batch ${status.status}`);
250
- }
251
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
252
- }
253
- },
254
- };
255
- // ============================================================================
256
- // Helpers
257
- // ============================================================================
258
- /**
259
- * Simple Zod to JSON Schema converter
260
- * In production, use a proper library like zod-to-json-schema
261
- */
262
- function zodToJsonSchema(zodSchema) {
263
- // This is a simplified converter - in production use zod-to-json-schema
264
- const schema = zodSchema;
265
- if (!schema._def) {
266
- return { type: 'object' };
267
- }
268
- const typeName = schema._def.typeName;
269
- switch (typeName) {
270
- case 'ZodString':
271
- return { type: 'string' };
272
- case 'ZodNumber':
273
- return { type: 'number' };
274
- case 'ZodBoolean':
275
- return { type: 'boolean' };
276
- case 'ZodArray':
277
- return { type: 'array', items: zodToJsonSchema(schema._def.type) };
278
- case 'ZodObject': {
279
- const shape = schema._def.shape();
280
- const properties = {};
281
- for (const [key, value] of Object.entries(shape)) {
282
- properties[key] = zodToJsonSchema(value);
283
- }
284
- return { type: 'object', properties, required: Object.keys(properties) };
285
- }
286
- default:
287
- return { type: 'object' };
288
- }
289
- }
290
- // ============================================================================
291
- // Register Adapter
292
- // ============================================================================
293
- // ============================================================================
294
- // OpenAI Flex Adapter
295
- // ============================================================================
296
- /**
297
- * OpenAI Flex Adapter
298
- *
299
- * Flex processing uses concurrent requests with a service tier that provides
300
- * ~50% discount similar to batch, but with much faster turnaround (minutes vs 24hr).
301
- *
302
- * This is ideal for 5-500 items where you need results quickly but still want
303
- * cost savings.
304
- *
305
- * Note: As of 2024, OpenAI doesn't have an official "flex" tier API.
306
- * This adapter implements concurrent processing as a middle ground.
307
- * When OpenAI adds official flex support, this can be updated.
308
- */
309
- const openaiFlexAdapter = {
310
- async submitFlex(items, options) {
311
- const model = options.model || 'gpt-4o';
312
- const CONCURRENCY = 10; // Higher concurrency for flex tier
313
- const results = [];
314
- // Process items concurrently in batches
315
- for (let i = 0; i < items.length; i += CONCURRENCY) {
316
- const batch = items.slice(i, i + CONCURRENCY);
317
- const batchResults = await Promise.all(batch.map(async (item) => {
318
- try {
319
- return await processOpenAIItem(item, model);
320
- }
321
- catch (error) {
322
- return {
323
- id: item.id,
324
- customId: item.id,
325
- status: 'failed',
326
- error: error instanceof Error ? error.message : 'Unknown error',
327
- };
328
- }
329
- }));
330
- results.push(...batchResults);
331
- }
332
- return results;
333
- },
334
- };
335
- /**
336
- * Process a single item via OpenAI Chat Completions API
337
- */
338
- async function processOpenAIItem(item, model) {
339
- const messages = [];
340
- if (item.options?.system) {
341
- messages.push({ role: 'system', content: item.options.system });
342
- }
343
- messages.push({ role: 'user', content: item.prompt });
344
- const body = {
345
- model,
346
- messages,
347
- max_tokens: item.options?.maxTokens,
348
- temperature: item.options?.temperature,
349
- };
350
- // Add JSON schema if provided
351
- if (item.schema) {
352
- const zodSchema = convertSchema(item.schema);
353
- body.response_format = {
354
- type: 'json_schema',
355
- json_schema: {
356
- name: 'response',
357
- schema: zodToJsonSchema(zodSchema),
358
- },
359
- };
360
- }
361
- const response = await fetch(`${openaiBaseUrl}/chat/completions`, {
362
- method: 'POST',
363
- headers: {
364
- Authorization: `Bearer ${getApiKey()}`,
365
- 'Content-Type': 'application/json',
366
- },
367
- body: JSON.stringify(body),
368
- });
369
- if (!response.ok) {
370
- const error = await response.text();
371
- throw new Error(`OpenAI API error: ${response.status} ${error}`);
372
- }
373
- const data = (await response.json());
374
- const content = data.choices[0]?.message?.content;
375
- let result = content;
376
- // Try to parse JSON if schema was provided or content looks like JSON
377
- if (content && (item.schema || content.startsWith('{') || content.startsWith('['))) {
378
- try {
379
- result = JSON.parse(content);
380
- }
381
- catch {
382
- // Keep as string
383
- }
384
- }
385
- return {
386
- id: item.id,
387
- customId: item.id,
388
- status: 'completed',
389
- result,
390
- usage: {
391
- promptTokens: data.usage.prompt_tokens,
392
- completionTokens: data.usage.completion_tokens,
393
- totalTokens: data.usage.total_tokens,
394
- },
395
- };
396
- }
397
- // ============================================================================
398
- // Register Adapters
399
- // ============================================================================
400
- registerBatchAdapter('openai', openaiAdapter);
401
- registerFlexAdapter('openai', openaiFlexAdapter);
402
- export { openaiAdapter, openaiFlexAdapter };
package/src/eval/index.js DELETED
@@ -1,7 +0,0 @@
1
- /**
2
- * AI Functions Eval Suite
3
- *
4
- * @packageDocumentation
5
- */
6
- export * from './models.js';
7
- export * from './runner.js';