ai-functions 2.1.3 → 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 (277) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +55 -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 +116 -0
  93. package/dist/function-registry.d.ts.map +1 -0
  94. package/dist/function-registry.js +546 -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/schema.js +2 -2
  137. package/dist/schema.js.map +1 -1
  138. package/dist/telemetry.d.ts +128 -0
  139. package/dist/telemetry.d.ts.map +1 -0
  140. package/dist/telemetry.js +285 -0
  141. package/dist/telemetry.js.map +1 -0
  142. package/dist/template.d.ts.map +1 -1
  143. package/dist/template.js +6 -1
  144. package/dist/template.js.map +1 -1
  145. package/dist/tool-orchestration.d.ts +66 -4
  146. package/dist/tool-orchestration.d.ts.map +1 -1
  147. package/dist/tool-orchestration.js +123 -23
  148. package/dist/tool-orchestration.js.map +1 -1
  149. package/dist/type-guards.d.ts +28 -0
  150. package/dist/type-guards.d.ts.map +1 -0
  151. package/dist/type-guards.js +29 -0
  152. package/dist/type-guards.js.map +1 -0
  153. package/dist/types.d.ts +135 -17
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +36 -1
  156. package/dist/types.js.map +1 -1
  157. package/dist/wrap-for-v3.d.ts +80 -0
  158. package/dist/wrap-for-v3.d.ts.map +1 -0
  159. package/dist/wrap-for-v3.js +89 -0
  160. package/dist/wrap-for-v3.js.map +1 -0
  161. package/examples/00-quickstart.ts +232 -0
  162. package/examples/01-rag-chatbot.ts +212 -0
  163. package/examples/02-multi-agent-research.ts +290 -0
  164. package/examples/03-email-classification.ts +379 -0
  165. package/examples/04-content-moderation.ts +400 -0
  166. package/examples/05-document-extraction.ts +455 -0
  167. package/examples/06-streaming-chat-nextjs.ts +437 -0
  168. package/examples/07-cloudflare-worker.ts +483 -0
  169. package/examples/08-batch-processing.ts +491 -0
  170. package/examples/09-budget-constrained.ts +527 -0
  171. package/examples/10-tool-orchestration.ts +565 -0
  172. package/examples/11-retry-resilience.ts +403 -0
  173. package/examples/12-caching-strategies.ts +422 -0
  174. package/examples/README.md +145 -0
  175. package/package.json +28 -25
  176. package/src/ai-promise.ts +226 -140
  177. package/src/ai-schemas.ts +122 -0
  178. package/src/ai.ts +69 -1176
  179. package/src/batch/anthropic.ts +96 -161
  180. package/src/batch/bedrock.ts +203 -454
  181. package/src/batch/cloudflare.ts +99 -282
  182. package/src/batch/google.ts +91 -297
  183. package/src/batch/index.ts +4 -1
  184. package/src/batch/memory.ts +15 -10
  185. package/src/batch/openai.ts +65 -193
  186. package/src/batch/provider.ts +336 -0
  187. package/src/batch-map.ts +29 -24
  188. package/src/batch-queue.ts +200 -11
  189. package/src/budget.ts +31 -18
  190. package/src/cache.ts +45 -17
  191. package/src/context.ts +106 -77
  192. package/src/digital-objects-registry.ts +750 -0
  193. package/src/errors.ts +37 -0
  194. package/src/eval/runner.ts +60 -36
  195. package/src/eval-log/in-memory.ts +90 -0
  196. package/src/eval-log/index.ts +46 -0
  197. package/src/eval-log/types.ts +110 -0
  198. package/src/function-registry.ts +671 -0
  199. package/src/generate.ts +33 -28
  200. package/src/index.ts +119 -21
  201. package/src/logger.ts +232 -0
  202. package/src/middleware/budget.ts +171 -0
  203. package/src/middleware/cache.ts +299 -0
  204. package/src/middleware/embed-cache.ts +195 -0
  205. package/src/middleware/index.ts +23 -0
  206. package/src/middleware/trace.ts +248 -0
  207. package/src/primitives.ts +589 -62
  208. package/src/retry.ts +144 -18
  209. package/src/schema.ts +8 -8
  210. package/src/telemetry.ts +403 -0
  211. package/src/template.ts +8 -4
  212. package/src/tool-orchestration.ts +213 -48
  213. package/src/type-guards.ts +31 -0
  214. package/src/types.ts +164 -25
  215. package/src/wrap-for-v3.ts +105 -0
  216. package/test/ai-promise.test.ts +1080 -0
  217. package/test/ai-proxy.test.ts +1 -1
  218. package/test/batch-autosubmit-errors.test.ts +49 -37
  219. package/test/batch-blog-posts.test.ts +87 -129
  220. package/test/core-functions.test.ts +183 -579
  221. package/test/decide.test.ts +154 -322
  222. package/test/define.test.ts +211 -8
  223. package/test/digital-objects-registry.test.ts +760 -0
  224. package/test/embedding-cache-middleware.test.ts +140 -0
  225. package/test/generate-core.test.ts +140 -229
  226. package/test/implicit-batch.test.ts +22 -65
  227. package/test/retry-policy-integration.test.ts +117 -0
  228. package/test/schema.test.ts +55 -19
  229. package/test/template.test.ts +1164 -0
  230. package/test/tool-orchestration.test.ts +270 -0
  231. package/test/wrap-for-v3.test.ts +612 -0
  232. package/vitest.config.js +6 -0
  233. package/vitest.config.ts +20 -0
  234. package/LICENSE +0 -21
  235. package/dist/rpc/auth.d.ts +0 -69
  236. package/dist/rpc/auth.d.ts.map +0 -1
  237. package/dist/rpc/auth.js +0 -136
  238. package/dist/rpc/auth.js.map +0 -1
  239. package/dist/rpc/client.d.ts +0 -62
  240. package/dist/rpc/client.d.ts.map +0 -1
  241. package/dist/rpc/client.js +0 -103
  242. package/dist/rpc/client.js.map +0 -1
  243. package/dist/rpc/deferred.d.ts +0 -60
  244. package/dist/rpc/deferred.d.ts.map +0 -1
  245. package/dist/rpc/deferred.js +0 -96
  246. package/dist/rpc/deferred.js.map +0 -1
  247. package/dist/rpc/index.d.ts +0 -22
  248. package/dist/rpc/index.d.ts.map +0 -1
  249. package/dist/rpc/index.js +0 -38
  250. package/dist/rpc/index.js.map +0 -1
  251. package/dist/rpc/local.d.ts +0 -42
  252. package/dist/rpc/local.d.ts.map +0 -1
  253. package/dist/rpc/local.js +0 -50
  254. package/dist/rpc/local.js.map +0 -1
  255. package/dist/rpc/server.d.ts +0 -165
  256. package/dist/rpc/server.d.ts.map +0 -1
  257. package/dist/rpc/server.js +0 -405
  258. package/dist/rpc/server.js.map +0 -1
  259. package/dist/rpc/session.d.ts +0 -32
  260. package/dist/rpc/session.d.ts.map +0 -1
  261. package/dist/rpc/session.js +0 -43
  262. package/dist/rpc/session.js.map +0 -1
  263. package/dist/rpc/transport.d.ts +0 -306
  264. package/dist/rpc/transport.d.ts.map +0 -1
  265. package/dist/rpc/transport.js +0 -731
  266. package/dist/rpc/transport.js.map +0 -1
  267. package/src/batch/anthropic.js +0 -256
  268. package/src/batch/bedrock.js +0 -584
  269. package/src/batch/cloudflare.js +0 -287
  270. package/src/batch/google.js +0 -359
  271. package/src/batch/index.js +0 -30
  272. package/src/batch/memory.js +0 -187
  273. package/src/batch/openai.js +0 -402
  274. package/src/eval/index.js +0 -7
  275. package/src/eval/models.js +0 -119
  276. package/src/eval/runner.js +0 -147
  277. package/test/schema.test.js +0 -96
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Content Moderation Pipeline Example
3
+ *
4
+ * This example demonstrates building a content moderation pipeline using ai-functions.
5
+ * It shows how to:
6
+ * - Check content for policy violations
7
+ * - Classify content severity
8
+ * - Handle edge cases with human review
9
+ * - Build a multi-stage moderation pipeline
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * ANTHROPIC_API_KEY=sk-... npx tsx examples/04-content-moderation.ts
14
+ * ```
15
+ */
16
+
17
+ import { ai, is, list, configure, withRetry, FallbackChain } from '../src/index.js'
18
+
19
+ // ============================================================================
20
+ // Types
21
+ // ============================================================================
22
+
23
+ interface ModerationResult {
24
+ contentId: string
25
+ decision: 'approved' | 'flagged' | 'rejected' | 'needs_review'
26
+ categories: ViolationCategory[]
27
+ severity: 'none' | 'low' | 'medium' | 'high' | 'severe'
28
+ confidence: number
29
+ reasoning: string
30
+ suggestedAction: string
31
+ }
32
+
33
+ interface ViolationCategory {
34
+ category: string
35
+ detected: boolean
36
+ confidence: number
37
+ examples?: string[]
38
+ }
39
+
40
+ interface ContentItem {
41
+ id: string
42
+ type: 'text' | 'comment' | 'review' | 'bio'
43
+ content: string
44
+ author?: string
45
+ context?: string
46
+ }
47
+
48
+ // ============================================================================
49
+ // Sample Content
50
+ // ============================================================================
51
+
52
+ const sampleContent: ContentItem[] = [
53
+ {
54
+ id: 'content-1',
55
+ type: 'review',
56
+ content: 'Great product! Really helped with my workflow. Highly recommend to anyone.',
57
+ author: 'happy_user',
58
+ },
59
+ {
60
+ id: 'content-2',
61
+ type: 'comment',
62
+ content: 'This is terrible! The developers are incompetent idiots who should be fired!',
63
+ author: 'angry_person',
64
+ },
65
+ {
66
+ id: 'content-3',
67
+ type: 'bio',
68
+ content: 'Follow me on MyWebsite.com for free stuff! DM for exclusive deals!!!',
69
+ author: 'spammer_account',
70
+ },
71
+ {
72
+ id: 'content-4',
73
+ type: 'review',
74
+ content: "I didn't like this product. It wasn't what I expected and the quality was poor.",
75
+ author: 'honest_reviewer',
76
+ },
77
+ {
78
+ id: 'content-5',
79
+ type: 'comment',
80
+ content:
81
+ 'Let me share some really helpful information: To solve this, try restarting the app and clearing cache.',
82
+ author: 'helpful_user',
83
+ },
84
+ ]
85
+
86
+ // ============================================================================
87
+ // Moderation Checks
88
+ // ============================================================================
89
+
90
+ const POLICY_CATEGORIES = [
91
+ 'hate_speech',
92
+ 'harassment',
93
+ 'spam',
94
+ 'personal_attacks',
95
+ 'misinformation',
96
+ 'self_promotion',
97
+ 'profanity',
98
+ 'threats',
99
+ ]
100
+
101
+ /**
102
+ * Quick initial check - fast binary decision
103
+ */
104
+ async function quickCheck(content: ContentItem): Promise<boolean> {
105
+ return is`This content is clearly safe and requires no further moderation review:
106
+ "${content.content}"
107
+
108
+ Content is safe if it:
109
+ - Is constructive or helpful
110
+ - Contains no insults, threats, or harassment
111
+ - Is not spam or self-promotion
112
+ - Is appropriate for a general audience`
113
+ }
114
+
115
+ /**
116
+ * Detailed category analysis
117
+ */
118
+ async function analyzeCategories(content: ContentItem): Promise<ViolationCategory[]> {
119
+ const results: ViolationCategory[] = []
120
+
121
+ // Check each category
122
+ for (const category of POLICY_CATEGORIES) {
123
+ const detected = await is`This content violates the "${category}" policy:
124
+ "${content.content}"
125
+
126
+ ${getCategoryDescription(category)}`
127
+
128
+ results.push({
129
+ category,
130
+ detected,
131
+ confidence: detected ? 0.8 : 0.9, // Simplified confidence
132
+ })
133
+ }
134
+
135
+ return results
136
+ }
137
+
138
+ function getCategoryDescription(category: string): string {
139
+ const descriptions: Record<string, string> = {
140
+ hate_speech: 'Content that attacks or demeans people based on protected characteristics',
141
+ harassment: 'Content that targets individuals with abuse, intimidation, or threats',
142
+ spam: 'Unsolicited promotional content, repetitive messages, or commercial solicitation',
143
+ personal_attacks: 'Direct insults or attacks on specific individuals',
144
+ misinformation: 'Demonstrably false information presented as fact',
145
+ self_promotion: 'Excessive self-promotion or advertising',
146
+ profanity: 'Explicit language or vulgar content',
147
+ threats: 'Threats of violence or harm to individuals or groups',
148
+ }
149
+ return descriptions[category] || 'Content that violates community guidelines'
150
+ }
151
+
152
+ /**
153
+ * Determine severity based on violations
154
+ */
155
+ async function assessSeverity(
156
+ content: ContentItem,
157
+ violations: ViolationCategory[]
158
+ ): Promise<{
159
+ severity: ModerationResult['severity']
160
+ reasoning: string
161
+ }> {
162
+ const detectedViolations = violations.filter((v) => v.detected)
163
+
164
+ if (detectedViolations.length === 0) {
165
+ return { severity: 'none', reasoning: 'No policy violations detected' }
166
+ }
167
+
168
+ const { severity, reasoning } = await ai`Assess the severity of these policy violations:
169
+
170
+ Content: "${content.content}"
171
+
172
+ Detected violations: ${detectedViolations.map((v) => v.category).join(', ')}
173
+
174
+ Provide:
175
+ - severity: one of [low, medium, high, severe] based on potential harm
176
+ - reasoning: brief explanation of the severity assessment`
177
+
178
+ return {
179
+ severity: severity as ModerationResult['severity'],
180
+ reasoning: reasoning as string,
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Determine final action
186
+ */
187
+ async function determineAction(
188
+ severity: ModerationResult['severity'],
189
+ violations: ViolationCategory[]
190
+ ): Promise<{
191
+ decision: ModerationResult['decision']
192
+ suggestedAction: string
193
+ }> {
194
+ const detectedViolations = violations.filter((v) => v.detected)
195
+
196
+ // Clear approval
197
+ if (severity === 'none') {
198
+ return {
199
+ decision: 'approved',
200
+ suggestedAction: 'No action needed - content is appropriate',
201
+ }
202
+ }
203
+
204
+ // Auto-reject severe violations
205
+ if (severity === 'severe') {
206
+ return {
207
+ decision: 'rejected',
208
+ suggestedAction: 'Remove content and warn user account',
209
+ }
210
+ }
211
+
212
+ // High severity needs review
213
+ if (severity === 'high') {
214
+ return {
215
+ decision: 'needs_review',
216
+ suggestedAction: 'Escalate to human moderator for final decision',
217
+ }
218
+ }
219
+
220
+ // Medium/low - flag for monitoring
221
+ const { action } = await ai`Recommend an action for content with ${severity} severity violations:
222
+ Violations: ${detectedViolations.map((v) => v.category).join(', ')}
223
+
224
+ Provide a brief action recommendation.`
225
+
226
+ return {
227
+ decision: 'flagged',
228
+ suggestedAction: action as string,
229
+ }
230
+ }
231
+
232
+ // ============================================================================
233
+ // Moderation Pipeline
234
+ // ============================================================================
235
+
236
+ async function moderateContent(content: ContentItem): Promise<ModerationResult> {
237
+ console.log(`\nModerating [${content.id}]: "${content.content.substring(0, 40)}..."`)
238
+
239
+ // Stage 1: Quick check
240
+ const isClearlySafe = await quickCheck(content)
241
+ console.log(` Quick check: ${isClearlySafe ? 'Safe' : 'Needs analysis'}`)
242
+
243
+ if (isClearlySafe) {
244
+ return {
245
+ contentId: content.id,
246
+ decision: 'approved',
247
+ categories: POLICY_CATEGORIES.map((c) => ({
248
+ category: c,
249
+ detected: false,
250
+ confidence: 0.95,
251
+ })),
252
+ severity: 'none',
253
+ confidence: 0.95,
254
+ reasoning: 'Content passed quick safety check',
255
+ suggestedAction: 'No action needed',
256
+ }
257
+ }
258
+
259
+ // Stage 2: Detailed category analysis
260
+ const violations = await analyzeCategories(content)
261
+ const detectedCount = violations.filter((v) => v.detected).length
262
+ console.log(` Category analysis: ${detectedCount} violations detected`)
263
+
264
+ // Stage 3: Severity assessment
265
+ const { severity, reasoning } = await assessSeverity(content, violations)
266
+ console.log(` Severity: ${severity}`)
267
+
268
+ // Stage 4: Action determination
269
+ const { decision, suggestedAction } = await determineAction(severity, violations)
270
+ console.log(` Decision: ${decision}`)
271
+
272
+ return {
273
+ contentId: content.id,
274
+ decision,
275
+ categories: violations,
276
+ severity,
277
+ confidence: 0.85, // Simplified
278
+ reasoning,
279
+ suggestedAction,
280
+ }
281
+ }
282
+
283
+ // ============================================================================
284
+ // Batch Processing with Resilience
285
+ // ============================================================================
286
+
287
+ async function batchModerate(contents: ContentItem[]): Promise<ModerationResult[]> {
288
+ console.log(`\n${'='.repeat(60)}`)
289
+ console.log('Content Moderation Pipeline')
290
+ console.log('='.repeat(60))
291
+
292
+ const results: ModerationResult[] = []
293
+
294
+ for (const content of contents) {
295
+ try {
296
+ // Use retry logic for resilience
297
+ const result = await withRetry(async () => moderateContent(content), {
298
+ maxRetries: 2,
299
+ baseDelay: 1000,
300
+ })
301
+ results.push(result)
302
+ } catch (error) {
303
+ // If moderation fails, flag for human review
304
+ results.push({
305
+ contentId: content.id,
306
+ decision: 'needs_review',
307
+ categories: [],
308
+ severity: 'medium',
309
+ confidence: 0,
310
+ reasoning: `Moderation failed: ${(error as Error).message}`,
311
+ suggestedAction: 'Manual review required due to processing error',
312
+ })
313
+ }
314
+ }
315
+
316
+ return results
317
+ }
318
+
319
+ // ============================================================================
320
+ // Report Generation
321
+ // ============================================================================
322
+
323
+ function generateReport(results: ModerationResult[]): void {
324
+ console.log(`\n${'='.repeat(60)}`)
325
+ console.log('Moderation Report')
326
+ console.log('='.repeat(60))
327
+
328
+ const stats = {
329
+ approved: results.filter((r) => r.decision === 'approved').length,
330
+ flagged: results.filter((r) => r.decision === 'flagged').length,
331
+ rejected: results.filter((r) => r.decision === 'rejected').length,
332
+ needs_review: results.filter((r) => r.decision === 'needs_review').length,
333
+ }
334
+
335
+ console.log('\nSummary:')
336
+ console.log(` Approved: ${stats.approved}`)
337
+ console.log(` Flagged: ${stats.flagged}`)
338
+ console.log(` Rejected: ${stats.rejected}`)
339
+ console.log(` Needs Review: ${stats.needs_review}`)
340
+
341
+ console.log('\nDetailed Results:')
342
+ for (const result of results) {
343
+ const detectedCategories = result.categories.filter((c) => c.detected).map((c) => c.category)
344
+
345
+ console.log(`
346
+ [${result.contentId}]
347
+ Decision: ${result.decision.toUpperCase()}
348
+ Severity: ${result.severity}
349
+ Confidence: ${(result.confidence * 100).toFixed(0)}%
350
+ ${
351
+ detectedCategories.length > 0
352
+ ? `Violations: ${detectedCategories.join(', ')}`
353
+ : 'No violations'
354
+ }
355
+ Action: ${result.suggestedAction}`)
356
+ }
357
+
358
+ // Violation breakdown
359
+ console.log('\nViolation Breakdown:')
360
+ const violationCounts: Record<string, number> = {}
361
+ for (const result of results) {
362
+ for (const cat of result.categories.filter((c) => c.detected)) {
363
+ violationCounts[cat.category] = (violationCounts[cat.category] || 0) + 1
364
+ }
365
+ }
366
+
367
+ for (const [category, count] of Object.entries(violationCounts)) {
368
+ console.log(` ${category}: ${count}`)
369
+ }
370
+ }
371
+
372
+ // ============================================================================
373
+ // Main Example
374
+ // ============================================================================
375
+
376
+ async function main() {
377
+ console.log('\n=== Content Moderation Pipeline Example ===\n')
378
+
379
+ // Configure the AI provider
380
+ configure({
381
+ model: 'sonnet',
382
+ provider: 'anthropic',
383
+ })
384
+
385
+ // Process all sample content
386
+ const results = await batchModerate(sampleContent)
387
+
388
+ // Generate report
389
+ generateReport(results)
390
+ }
391
+
392
+ main()
393
+ .then(() => {
394
+ console.log('\n=== Example Complete ===\n')
395
+ process.exit(0)
396
+ })
397
+ .catch((error) => {
398
+ console.error('\nError:', error.message)
399
+ process.exit(1)
400
+ })