ai-functions 2.1.1 → 2.3.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 (286) hide show
  1. package/.turbo/turbo-build.log +1 -4
  2. package/CHANGELOG.md +68 -1
  3. package/README.md +397 -157
  4. package/dist/ai-promise.d.ts +50 -3
  5. package/dist/ai-promise.d.ts.map +1 -1
  6. package/dist/ai-promise.js +410 -51
  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 +54 -837
  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 +272 -0
  56. package/dist/budget.d.ts.map +1 -0
  57. package/dist/budget.js +513 -0
  58. package/dist/budget.js.map +1 -0
  59. package/dist/cache.d.ts +295 -0
  60. package/dist/cache.d.ts.map +1 -0
  61. package/dist/cache.js +433 -0
  62. package/dist/cache.js.map +1 -0
  63. package/dist/context.d.ts +42 -8
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js +64 -62
  66. package/dist/context.js.map +1 -1
  67. package/dist/digital-objects-registry.d.ts +229 -0
  68. package/dist/digital-objects-registry.d.ts.map +1 -0
  69. package/dist/digital-objects-registry.js +617 -0
  70. package/dist/digital-objects-registry.js.map +1 -0
  71. package/dist/embeddings.d.ts +2 -2
  72. package/dist/embeddings.d.ts.map +1 -1
  73. package/dist/errors.d.ts +22 -0
  74. package/dist/errors.d.ts.map +1 -0
  75. package/dist/errors.js +35 -0
  76. package/dist/errors.js.map +1 -0
  77. package/dist/eval/runner.d.ts +10 -1
  78. package/dist/eval/runner.d.ts.map +1 -1
  79. package/dist/eval/runner.js +41 -35
  80. package/dist/eval/runner.js.map +1 -1
  81. package/dist/eval-log/in-memory.d.ts +34 -0
  82. package/dist/eval-log/in-memory.d.ts.map +1 -0
  83. package/dist/eval-log/in-memory.js +84 -0
  84. package/dist/eval-log/in-memory.js.map +1 -0
  85. package/dist/eval-log/index.d.ts +29 -0
  86. package/dist/eval-log/index.d.ts.map +1 -0
  87. package/dist/eval-log/index.js +39 -0
  88. package/dist/eval-log/index.js.map +1 -0
  89. package/dist/eval-log/types.d.ts +101 -0
  90. package/dist/eval-log/types.d.ts.map +1 -0
  91. package/dist/eval-log/types.js +16 -0
  92. package/dist/eval-log/types.js.map +1 -0
  93. package/dist/function-registry.d.ts +116 -0
  94. package/dist/function-registry.d.ts.map +1 -0
  95. package/dist/function-registry.js +546 -0
  96. package/dist/function-registry.js.map +1 -0
  97. package/dist/generate.d.ts +9 -3
  98. package/dist/generate.d.ts.map +1 -1
  99. package/dist/generate.js +18 -22
  100. package/dist/generate.js.map +1 -1
  101. package/dist/index.d.ts +35 -20
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +89 -42
  104. package/dist/index.js.map +1 -1
  105. package/dist/logger.d.ts +118 -0
  106. package/dist/logger.d.ts.map +1 -0
  107. package/dist/logger.js +187 -0
  108. package/dist/logger.js.map +1 -0
  109. package/dist/middleware/budget.d.ts +84 -0
  110. package/dist/middleware/budget.d.ts.map +1 -0
  111. package/dist/middleware/budget.js +110 -0
  112. package/dist/middleware/budget.js.map +1 -0
  113. package/dist/middleware/cache.d.ts +103 -0
  114. package/dist/middleware/cache.d.ts.map +1 -0
  115. package/dist/middleware/cache.js +228 -0
  116. package/dist/middleware/cache.js.map +1 -0
  117. package/dist/middleware/embed-cache.d.ts +99 -0
  118. package/dist/middleware/embed-cache.d.ts.map +1 -0
  119. package/dist/middleware/embed-cache.js +128 -0
  120. package/dist/middleware/embed-cache.js.map +1 -0
  121. package/dist/middleware/index.d.ts +11 -0
  122. package/dist/middleware/index.d.ts.map +1 -0
  123. package/dist/middleware/index.js +11 -0
  124. package/dist/middleware/index.js.map +1 -0
  125. package/dist/middleware/trace.d.ts +103 -0
  126. package/dist/middleware/trace.d.ts.map +1 -0
  127. package/dist/middleware/trace.js +176 -0
  128. package/dist/middleware/trace.js.map +1 -0
  129. package/dist/primitives.d.ts +120 -1
  130. package/dist/primitives.d.ts.map +1 -1
  131. package/dist/primitives.js +398 -26
  132. package/dist/primitives.js.map +1 -1
  133. package/dist/retry.d.ts +368 -0
  134. package/dist/retry.d.ts.map +1 -0
  135. package/dist/retry.js +646 -0
  136. package/dist/retry.js.map +1 -0
  137. package/dist/schema.d.ts.map +1 -1
  138. package/dist/schema.js +2 -10
  139. package/dist/schema.js.map +1 -1
  140. package/dist/telemetry.d.ts +128 -0
  141. package/dist/telemetry.d.ts.map +1 -0
  142. package/dist/telemetry.js +285 -0
  143. package/dist/telemetry.js.map +1 -0
  144. package/dist/template.d.ts.map +1 -1
  145. package/dist/template.js +6 -1
  146. package/dist/template.js.map +1 -1
  147. package/dist/tool-orchestration.d.ts +453 -0
  148. package/dist/tool-orchestration.d.ts.map +1 -0
  149. package/dist/tool-orchestration.js +763 -0
  150. package/dist/tool-orchestration.js.map +1 -0
  151. package/dist/type-guards.d.ts +28 -0
  152. package/dist/type-guards.d.ts.map +1 -0
  153. package/dist/type-guards.js +29 -0
  154. package/dist/type-guards.js.map +1 -0
  155. package/dist/types.d.ts +135 -17
  156. package/dist/types.d.ts.map +1 -1
  157. package/dist/types.js +36 -1
  158. package/dist/types.js.map +1 -1
  159. package/dist/wrap-for-v3.d.ts +80 -0
  160. package/dist/wrap-for-v3.d.ts.map +1 -0
  161. package/dist/wrap-for-v3.js +89 -0
  162. package/dist/wrap-for-v3.js.map +1 -0
  163. package/examples/00-quickstart.ts +232 -0
  164. package/examples/01-rag-chatbot.ts +212 -0
  165. package/examples/02-multi-agent-research.ts +290 -0
  166. package/examples/03-email-classification.ts +379 -0
  167. package/examples/04-content-moderation.ts +400 -0
  168. package/examples/05-document-extraction.ts +455 -0
  169. package/examples/06-streaming-chat-nextjs.ts +437 -0
  170. package/examples/07-cloudflare-worker.ts +483 -0
  171. package/examples/08-batch-processing.ts +491 -0
  172. package/examples/09-budget-constrained.ts +527 -0
  173. package/examples/10-tool-orchestration.ts +565 -0
  174. package/examples/11-retry-resilience.ts +403 -0
  175. package/examples/12-caching-strategies.ts +422 -0
  176. package/examples/README.md +145 -0
  177. package/package.json +10 -6
  178. package/src/ai-promise.ts +528 -99
  179. package/src/ai-schemas.ts +122 -0
  180. package/src/ai.ts +69 -1153
  181. package/src/batch/anthropic.ts +96 -161
  182. package/src/batch/bedrock.ts +203 -454
  183. package/src/batch/cloudflare.ts +99 -282
  184. package/src/batch/google.ts +91 -297
  185. package/src/batch/index.ts +4 -1
  186. package/src/batch/memory.ts +15 -10
  187. package/src/batch/openai.ts +65 -193
  188. package/src/batch/provider.ts +336 -0
  189. package/src/batch-map.ts +29 -24
  190. package/src/batch-queue.ts +200 -11
  191. package/src/budget.ts +740 -0
  192. package/src/cache.ts +681 -0
  193. package/src/context.ts +122 -76
  194. package/src/digital-objects-registry.ts +750 -0
  195. package/src/errors.ts +37 -0
  196. package/src/eval/runner.ts +63 -38
  197. package/src/eval-log/in-memory.ts +90 -0
  198. package/src/eval-log/index.ts +46 -0
  199. package/src/eval-log/types.ts +110 -0
  200. package/src/function-registry.ts +671 -0
  201. package/src/generate.ts +33 -33
  202. package/src/index.ts +325 -49
  203. package/src/logger.ts +232 -0
  204. package/src/middleware/budget.ts +171 -0
  205. package/src/middleware/cache.ts +299 -0
  206. package/src/middleware/embed-cache.ts +195 -0
  207. package/src/middleware/index.ts +23 -0
  208. package/src/middleware/trace.ts +248 -0
  209. package/src/primitives.ts +589 -62
  210. package/src/retry.ts +902 -0
  211. package/src/schema.ts +8 -17
  212. package/src/telemetry.ts +403 -0
  213. package/src/template.ts +8 -4
  214. package/src/tool-orchestration.ts +1173 -0
  215. package/src/type-guards.ts +31 -0
  216. package/src/types.ts +164 -25
  217. package/src/wrap-for-v3.ts +105 -0
  218. package/test/ai-promise.test.ts +1080 -0
  219. package/test/ai-proxy.test.ts +1 -1
  220. package/test/backward-compat.test.ts +147 -0
  221. package/test/batch-autosubmit-errors.test.ts +610 -0
  222. package/test/batch-blog-posts.test.ts +87 -129
  223. package/test/budget-tracking.test.ts +800 -0
  224. package/test/cache.test.ts +712 -0
  225. package/test/context-isolation.test.ts +687 -0
  226. package/test/core-functions.test.ts +183 -579
  227. package/test/decide.test.ts +154 -322
  228. package/test/define.test.ts +211 -8
  229. package/test/digital-objects-registry.test.ts +760 -0
  230. package/test/embedding-cache-middleware.test.ts +140 -0
  231. package/test/evals/deterministic.eval.test.ts +376 -0
  232. package/test/generate-core.test.ts +140 -229
  233. package/test/implicit-batch.test.ts +22 -65
  234. package/test/json-parse-error-handling.test.ts +463 -0
  235. package/test/retry-policy-integration.test.ts +117 -0
  236. package/test/retry.test.ts +1016 -0
  237. package/test/schema.test.ts +55 -19
  238. package/test/streaming.test.ts +316 -0
  239. package/test/template.test.ts +1164 -0
  240. package/test/tool-orchestration.test.ts +1040 -0
  241. package/test/wrap-for-v3.test.ts +612 -0
  242. package/vitest.config.js +6 -0
  243. package/vitest.config.ts +20 -0
  244. package/dist/rpc/auth.d.ts +0 -69
  245. package/dist/rpc/auth.d.ts.map +0 -1
  246. package/dist/rpc/auth.js +0 -136
  247. package/dist/rpc/auth.js.map +0 -1
  248. package/dist/rpc/client.d.ts +0 -62
  249. package/dist/rpc/client.d.ts.map +0 -1
  250. package/dist/rpc/client.js +0 -103
  251. package/dist/rpc/client.js.map +0 -1
  252. package/dist/rpc/deferred.d.ts +0 -60
  253. package/dist/rpc/deferred.d.ts.map +0 -1
  254. package/dist/rpc/deferred.js +0 -96
  255. package/dist/rpc/deferred.js.map +0 -1
  256. package/dist/rpc/index.d.ts +0 -22
  257. package/dist/rpc/index.d.ts.map +0 -1
  258. package/dist/rpc/index.js +0 -38
  259. package/dist/rpc/index.js.map +0 -1
  260. package/dist/rpc/local.d.ts +0 -42
  261. package/dist/rpc/local.d.ts.map +0 -1
  262. package/dist/rpc/local.js +0 -50
  263. package/dist/rpc/local.js.map +0 -1
  264. package/dist/rpc/server.d.ts +0 -165
  265. package/dist/rpc/server.d.ts.map +0 -1
  266. package/dist/rpc/server.js +0 -405
  267. package/dist/rpc/server.js.map +0 -1
  268. package/dist/rpc/session.d.ts +0 -32
  269. package/dist/rpc/session.d.ts.map +0 -1
  270. package/dist/rpc/session.js +0 -43
  271. package/dist/rpc/session.js.map +0 -1
  272. package/dist/rpc/transport.d.ts +0 -306
  273. package/dist/rpc/transport.d.ts.map +0 -1
  274. package/dist/rpc/transport.js +0 -731
  275. package/dist/rpc/transport.js.map +0 -1
  276. package/src/batch/anthropic.js +0 -256
  277. package/src/batch/bedrock.js +0 -584
  278. package/src/batch/cloudflare.js +0 -287
  279. package/src/batch/google.js +0 -359
  280. package/src/batch/index.js +0 -30
  281. package/src/batch/memory.js +0 -187
  282. package/src/batch/openai.js +0 -402
  283. package/src/eval/index.js +0 -7
  284. package/src/eval/models.js +0 -119
  285. package/src/eval/runner.js +0 -147
  286. package/test/schema.test.js +0 -96
@@ -0,0 +1,483 @@
1
+ /**
2
+ * Edge Function Deployment Example (Cloudflare Workers)
3
+ *
4
+ * This example demonstrates deploying ai-functions on Cloudflare Workers.
5
+ * It shows how to:
6
+ * - Configure for edge runtime
7
+ * - Handle request/response patterns
8
+ * - Use Workers AI or external providers
9
+ * - Implement rate limiting and caching
10
+ *
11
+ * Note: This file demonstrates the patterns. To actually deploy, you would:
12
+ * 1. npm create cloudflare@latest
13
+ * 2. Add ai-functions as a dependency
14
+ * 3. Use this code in src/index.ts
15
+ *
16
+ * @example
17
+ * ```bash
18
+ * # Local development
19
+ * npx wrangler dev
20
+ *
21
+ * # Deploy
22
+ * npx wrangler deploy
23
+ * ```
24
+ */
25
+
26
+ import {
27
+ ai,
28
+ write,
29
+ list,
30
+ is,
31
+ configure,
32
+ MemoryCache,
33
+ withRetry,
34
+ GenerationCache,
35
+ } from '../src/index.js'
36
+
37
+ // ============================================================================
38
+ // Types
39
+ // ============================================================================
40
+
41
+ interface Env {
42
+ AI_GATEWAY_URL?: string
43
+ ANTHROPIC_API_KEY?: string
44
+ OPENAI_API_KEY?: string
45
+ KV_CACHE?: unknown // KVNamespace in actual Workers
46
+ RATE_LIMIT_KV?: unknown
47
+ }
48
+
49
+ interface RequestBody {
50
+ action: 'generate' | 'classify' | 'extract' | 'summarize'
51
+ input: string
52
+ options?: Record<string, unknown>
53
+ }
54
+
55
+ interface APIResponse<T = unknown> {
56
+ success: boolean
57
+ data?: T
58
+ error?: string
59
+ meta?: {
60
+ requestId: string
61
+ latencyMs: number
62
+ cached: boolean
63
+ }
64
+ }
65
+
66
+ // ============================================================================
67
+ // Worker Handler
68
+ // ============================================================================
69
+
70
+ /**
71
+ * Main worker handler (Cloudflare Workers format)
72
+ *
73
+ * In actual wrangler.toml:
74
+ * ```toml
75
+ * name = "ai-functions-worker"
76
+ * main = "src/index.ts"
77
+ * compatibility_date = "2024-01-01"
78
+ *
79
+ * [vars]
80
+ * AI_GATEWAY_URL = "https://your-gateway.com"
81
+ *
82
+ * [[kv_namespaces]]
83
+ * binding = "KV_CACHE"
84
+ * id = "your-kv-namespace-id"
85
+ * ```
86
+ */
87
+ async function handleRequest(request: Request, env: Env): Promise<Response> {
88
+ const startTime = Date.now()
89
+ const requestId = crypto.randomUUID()
90
+
91
+ // Configure ai-functions
92
+ configure({
93
+ model: 'sonnet',
94
+ provider: 'anthropic',
95
+ // In production, use env variables
96
+ })
97
+
98
+ // CORS headers for API
99
+ const corsHeaders = {
100
+ 'Access-Control-Allow-Origin': '*',
101
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
102
+ 'Access-Control-Allow-Headers': 'Content-Type',
103
+ }
104
+
105
+ // Handle OPTIONS for CORS
106
+ if (request.method === 'OPTIONS') {
107
+ return new Response(null, { headers: corsHeaders })
108
+ }
109
+
110
+ try {
111
+ // Parse request
112
+ const body = (await request.json()) as RequestBody
113
+
114
+ // Process based on action
115
+ let result: unknown
116
+ let cached = false
117
+
118
+ switch (body.action) {
119
+ case 'generate':
120
+ result = await handleGenerate(body.input, body.options)
121
+ break
122
+ case 'classify':
123
+ result = await handleClassify(body.input, body.options)
124
+ break
125
+ case 'extract':
126
+ result = await handleExtract(body.input, body.options)
127
+ break
128
+ case 'summarize':
129
+ result = await handleSummarize(body.input, body.options)
130
+ break
131
+ default:
132
+ throw new Error(`Unknown action: ${body.action}`)
133
+ }
134
+
135
+ const response: APIResponse = {
136
+ success: true,
137
+ data: result,
138
+ meta: {
139
+ requestId,
140
+ latencyMs: Date.now() - startTime,
141
+ cached,
142
+ },
143
+ }
144
+
145
+ return new Response(JSON.stringify(response), {
146
+ headers: {
147
+ 'Content-Type': 'application/json',
148
+ ...corsHeaders,
149
+ },
150
+ })
151
+ } catch (error) {
152
+ const response: APIResponse = {
153
+ success: false,
154
+ error: (error as Error).message,
155
+ meta: {
156
+ requestId,
157
+ latencyMs: Date.now() - startTime,
158
+ cached: false,
159
+ },
160
+ }
161
+
162
+ return new Response(JSON.stringify(response), {
163
+ status: 500,
164
+ headers: {
165
+ 'Content-Type': 'application/json',
166
+ ...corsHeaders,
167
+ },
168
+ })
169
+ }
170
+ }
171
+
172
+ // ============================================================================
173
+ // Action Handlers
174
+ // ============================================================================
175
+
176
+ async function handleGenerate(input: string, options?: Record<string, unknown>): Promise<unknown> {
177
+ const { type = 'text', schema } = options || {}
178
+
179
+ if (type === 'text') {
180
+ return write`${input}`
181
+ }
182
+
183
+ if (schema) {
184
+ return ai`${input}`
185
+ }
186
+
187
+ return ai`${input}`
188
+ }
189
+
190
+ async function handleClassify(input: string, options?: Record<string, unknown>): Promise<unknown> {
191
+ const { categories = ['positive', 'negative', 'neutral'] } = options || {}
192
+
193
+ const { category, confidence, reasoning } =
194
+ await ai`Classify this text into one of these categories: ${(categories as string[]).join(', ')}
195
+
196
+ Text: "${input}"
197
+
198
+ Provide:
199
+ - category: the best matching category
200
+ - confidence: confidence score 0-1
201
+ - reasoning: brief explanation`
202
+
203
+ return { category, confidence, reasoning }
204
+ }
205
+
206
+ async function handleExtract(input: string, options?: Record<string, unknown>): Promise<unknown> {
207
+ const { fields } = options || {}
208
+
209
+ if (fields && Array.isArray(fields)) {
210
+ const fieldDescriptions = (fields as string[]).map((f) => `- ${f}`).join('\n')
211
+
212
+ return ai`Extract the following fields from this text:
213
+ ${fieldDescriptions}
214
+
215
+ Text: "${input}"`
216
+ }
217
+
218
+ // Default entity extraction
219
+ const { entities, dates, amounts, names } = await ai`Extract key information from this text:
220
+
221
+ "${input}"
222
+
223
+ Provide:
224
+ - entities: array of key entities mentioned
225
+ - dates: array of dates found
226
+ - amounts: array of monetary amounts or quantities
227
+ - names: array of person/company names`
228
+
229
+ return { entities, dates, amounts, names }
230
+ }
231
+
232
+ async function handleSummarize(input: string, options?: Record<string, unknown>): Promise<unknown> {
233
+ const { maxLength = 100, style = 'concise' } = options || {}
234
+
235
+ const summary = await write`Summarize this text in a ${style} style, maximum ${maxLength} words:
236
+
237
+ "${input}"`
238
+
239
+ return { summary, wordCount: summary.split(/\s+/).length }
240
+ }
241
+
242
+ // ============================================================================
243
+ // Middleware Utilities
244
+ // ============================================================================
245
+
246
+ /**
247
+ * Simple in-memory rate limiter (use KV in production)
248
+ */
249
+ class RateLimiter {
250
+ private requests = new Map<string, number[]>()
251
+ private maxRequests: number
252
+ private windowMs: number
253
+
254
+ constructor(maxRequests = 100, windowMs = 60000) {
255
+ this.maxRequests = maxRequests
256
+ this.windowMs = windowMs
257
+ }
258
+
259
+ check(key: string): boolean {
260
+ const now = Date.now()
261
+ const windowStart = now - this.windowMs
262
+
263
+ let timestamps = this.requests.get(key) || []
264
+ timestamps = timestamps.filter((t) => t > windowStart)
265
+
266
+ if (timestamps.length >= this.maxRequests) {
267
+ return false
268
+ }
269
+
270
+ timestamps.push(now)
271
+ this.requests.set(key, timestamps)
272
+ return true
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Request caching utility
278
+ */
279
+ class RequestCache {
280
+ private cache: GenerationCache
281
+
282
+ constructor() {
283
+ this.cache = new GenerationCache({
284
+ defaultTTL: 3600000, // 1 hour
285
+ maxSize: 1000,
286
+ })
287
+ }
288
+
289
+ async get(key: string): Promise<unknown | null> {
290
+ return this.cache.get({ prompt: key, model: 'cache' })
291
+ }
292
+
293
+ async set(key: string, value: unknown): Promise<void> {
294
+ await this.cache.set({ prompt: key, model: 'cache' }, value)
295
+ }
296
+ }
297
+
298
+ // ============================================================================
299
+ // Streaming Response Handler
300
+ // ============================================================================
301
+
302
+ async function handleStreamingRequest(request: Request, env: Env): Promise<Response> {
303
+ const { input } = (await request.json()) as { input: string }
304
+
305
+ // Configure for streaming
306
+ configure({
307
+ model: 'sonnet',
308
+ provider: 'anthropic',
309
+ })
310
+
311
+ const response = write`${input}`
312
+ const stream = response.stream()
313
+
314
+ // Create a TransformStream for SSE
315
+ const { readable, writable } = new TransformStream()
316
+ const writer = writable.getWriter()
317
+ const encoder = new TextEncoder()
318
+
319
+ // Stream in background
320
+ ;(async () => {
321
+ try {
322
+ for await (const chunk of stream.textStream) {
323
+ await writer.write(encoder.encode(`data: ${JSON.stringify({ text: chunk })}\n\n`))
324
+ }
325
+ await writer.write(encoder.encode('data: [DONE]\n\n'))
326
+ } finally {
327
+ await writer.close()
328
+ }
329
+ })()
330
+
331
+ return new Response(readable, {
332
+ headers: {
333
+ 'Content-Type': 'text/event-stream',
334
+ 'Cache-Control': 'no-cache',
335
+ Connection: 'keep-alive',
336
+ },
337
+ })
338
+ }
339
+
340
+ // ============================================================================
341
+ // Simulated Worker Execution
342
+ // ============================================================================
343
+
344
+ async function simulateWorkerExecution(): Promise<void> {
345
+ console.log('\n=== Simulating Cloudflare Worker Execution ===\n')
346
+
347
+ const rateLimiter = new RateLimiter(10, 10000) // 10 requests per 10 seconds
348
+ const cache = new RequestCache()
349
+
350
+ // Simulate various API requests
351
+ const testRequests = [
352
+ {
353
+ action: 'generate' as const,
354
+ input: 'Write a tagline for a coffee shop',
355
+ },
356
+ {
357
+ action: 'classify' as const,
358
+ input: 'I absolutely love this product! Best purchase ever!',
359
+ options: { categories: ['positive', 'negative', 'neutral'] },
360
+ },
361
+ {
362
+ action: 'extract' as const,
363
+ input: 'Contact John Smith at john@example.com or call 555-1234',
364
+ options: { fields: ['name', 'email', 'phone'] },
365
+ },
366
+ {
367
+ action: 'summarize' as const,
368
+ input:
369
+ 'The quick brown fox jumps over the lazy dog. This sentence contains every letter of the alphabet and is commonly used for testing purposes.',
370
+ options: { maxLength: 20, style: 'brief' },
371
+ },
372
+ ]
373
+
374
+ for (const body of testRequests) {
375
+ console.log(`\n--- ${body.action.toUpperCase()} Request ---`)
376
+ console.log(`Input: "${body.input.substring(0, 50)}..."`)
377
+
378
+ // Check rate limit
379
+ const clientIP = '127.0.0.1'
380
+ if (!rateLimiter.check(clientIP)) {
381
+ console.log('Rate limited!')
382
+ continue
383
+ }
384
+
385
+ // Create mock request
386
+ const request = new Request('https://worker.example.com/api', {
387
+ method: 'POST',
388
+ body: JSON.stringify(body),
389
+ headers: { 'Content-Type': 'application/json' },
390
+ })
391
+
392
+ // Handle request
393
+ const startTime = Date.now()
394
+ const response = await handleRequest(request, {} as Env)
395
+ const result = (await response.json()) as APIResponse
396
+
397
+ console.log(`Response: ${response.status}`)
398
+ console.log(`Latency: ${Date.now() - startTime}ms`)
399
+ console.log(`Result:`, JSON.stringify(result.data, null, 2).substring(0, 200))
400
+ }
401
+ }
402
+
403
+ // ============================================================================
404
+ // Export Worker
405
+ // ============================================================================
406
+
407
+ /**
408
+ * In actual Cloudflare Worker (src/index.ts):
409
+ *
410
+ * ```ts
411
+ * export default {
412
+ * async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
413
+ * // Check for streaming request
414
+ * if (request.url.includes('/stream')) {
415
+ * return handleStreamingRequest(request, env)
416
+ * }
417
+ * return handleRequest(request, env)
418
+ * },
419
+ * }
420
+ * ```
421
+ */
422
+
423
+ // ============================================================================
424
+ // Main Example
425
+ // ============================================================================
426
+
427
+ async function main() {
428
+ console.log('\n=== Cloudflare Worker Example ===\n')
429
+
430
+ // Configure the AI provider (for local simulation)
431
+ configure({
432
+ model: 'sonnet',
433
+ provider: 'anthropic',
434
+ })
435
+
436
+ // Show worker code structure
437
+ console.log('Worker Code Structure:')
438
+ console.log(`
439
+ // wrangler.toml
440
+ name = "ai-functions-api"
441
+ main = "src/index.ts"
442
+ compatibility_date = "2024-01-01"
443
+
444
+ [vars]
445
+ AI_MODEL = "sonnet"
446
+
447
+ // src/index.ts
448
+ import { ai, write, configure } from 'ai-functions'
449
+
450
+ export default {
451
+ async fetch(request, env, ctx) {
452
+ configure({ model: env.AI_MODEL })
453
+
454
+ const { prompt } = await request.json()
455
+ const response = await write\`\${prompt}\`
456
+
457
+ return new Response(JSON.stringify({ response }), {
458
+ headers: { 'Content-Type': 'application/json' }
459
+ })
460
+ }
461
+ }
462
+ `)
463
+
464
+ // Run simulation
465
+ await simulateWorkerExecution()
466
+
467
+ console.log('\n--- Deployment Instructions ---')
468
+ console.log('1. npm create cloudflare@latest my-ai-worker')
469
+ console.log('2. cd my-ai-worker && npm install ai-functions')
470
+ console.log('3. Copy the handler code to src/index.ts')
471
+ console.log('4. npx wrangler dev (for local testing)')
472
+ console.log('5. npx wrangler deploy (for production)')
473
+ }
474
+
475
+ main()
476
+ .then(() => {
477
+ console.log('\n=== Example Complete ===\n')
478
+ process.exit(0)
479
+ })
480
+ .catch((error) => {
481
+ console.error('\nError:', error.message)
482
+ process.exit(1)
483
+ })