@stacksjs/ai 0.70.55 → 0.70.56

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 (46) hide show
  1. package/package.json +10 -7
  2. package/src/agents/claude/index.ts +0 -310
  3. package/src/agents/index.ts +0 -7
  4. package/src/buddy.ts +0 -619
  5. package/src/drivers/anthropic/index.ts +0 -430
  6. package/src/drivers/claude-agent-sdk/index.ts +0 -370
  7. package/src/drivers/index.ts +0 -14
  8. package/src/drivers/ollama/index.ts +0 -514
  9. package/src/drivers/openai/index.ts +0 -529
  10. package/src/image.ts +0 -607
  11. package/src/index.ts +0 -53
  12. package/src/mcp.ts +0 -658
  13. package/src/personalization.ts +0 -490
  14. package/src/search.ts +0 -555
  15. package/src/text.ts +0 -79
  16. package/src/types.ts +0 -229
  17. package/src/utils/client-bedrock-runtime.ts +0 -51
  18. package/src/utils/client-bedrock.ts +0 -75
  19. package/src/utils/model-access.ts +0 -32
  20. package/src/utils/retry.ts +0 -124
  21. package/src/utils/tokens.ts +0 -159
  22. package/src/utils/usage.ts +0 -98
  23. package/src/utils/vision.ts +0 -119
  24. /package/dist/{src/agents → agents}/claude/index.d.ts +0 -0
  25. /package/dist/{src/agents → agents}/index.d.ts +0 -0
  26. /package/dist/{src/buddy.d.ts → buddy.d.ts} +0 -0
  27. /package/dist/{src/drivers → drivers}/anthropic/index.d.ts +0 -0
  28. /package/dist/{src/drivers → drivers}/claude-agent-sdk/index.d.ts +0 -0
  29. /package/dist/{src/drivers → drivers}/index.d.ts +0 -0
  30. /package/dist/{src/drivers → drivers}/ollama/index.d.ts +0 -0
  31. /package/dist/{src/drivers → drivers}/openai/index.d.ts +0 -0
  32. /package/dist/{src/image.d.ts → image.d.ts} +0 -0
  33. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  34. /package/dist/{src/index.js → index.js} +0 -0
  35. /package/dist/{src/mcp.d.ts → mcp.d.ts} +0 -0
  36. /package/dist/{src/personalization.d.ts → personalization.d.ts} +0 -0
  37. /package/dist/{src/search.d.ts → search.d.ts} +0 -0
  38. /package/dist/{src/text.d.ts → text.d.ts} +0 -0
  39. /package/dist/{src/types.d.ts → types.d.ts} +0 -0
  40. /package/dist/{src/utils → utils}/client-bedrock-runtime.d.ts +0 -0
  41. /package/dist/{src/utils → utils}/client-bedrock.d.ts +0 -0
  42. /package/dist/{src/utils → utils}/model-access.d.ts +0 -0
  43. /package/dist/{src/utils → utils}/retry.d.ts +0 -0
  44. /package/dist/{src/utils → utils}/tokens.d.ts +0 -0
  45. /package/dist/{src/utils → utils}/usage.d.ts +0 -0
  46. /package/dist/{src/utils → utils}/vision.d.ts +0 -0
@@ -1,490 +0,0 @@
1
- /**
2
- * AI Personalization Module
3
- *
4
- * Provides AI-powered personalization features including content recommendations,
5
- * user profiling, sentiment analysis, content classification, and summarization.
6
- */
7
-
8
- import type { AIResult } from './types'
9
-
10
- // ============================================================================
11
- // Types
12
- // ============================================================================
13
-
14
- export interface UserProfile {
15
- id: string
16
- preferences: Record<string, number>
17
- interactions: Interaction[]
18
- segments: string[]
19
- metadata?: Record<string, unknown>
20
- }
21
-
22
- export interface Interaction {
23
- type: 'view' | 'click' | 'purchase' | 'like' | 'dislike' | 'share' | 'bookmark' | 'custom'
24
- itemId: string
25
- timestamp: number
26
- weight?: number
27
- metadata?: Record<string, unknown>
28
- }
29
-
30
- export interface ContentItem {
31
- id: string
32
- content: string
33
- category?: string
34
- tags?: string[]
35
- metadata?: Record<string, unknown>
36
- }
37
-
38
- export interface RecommendationOptions {
39
- provider?: 'anthropic' | 'openai' | 'ollama'
40
- model?: string
41
- maxTokens?: number
42
- temperature?: number
43
- limit?: number
44
- }
45
-
46
- export interface RecommendationResult {
47
- recommendations: Array<{
48
- itemId: string
49
- score: number
50
- reason: string
51
- }>
52
- model: string
53
- provider: string
54
- }
55
-
56
- export interface SentimentResult {
57
- sentiment: 'positive' | 'negative' | 'neutral' | 'mixed'
58
- score: number
59
- confidence: number
60
- aspects?: Array<{
61
- aspect: string
62
- sentiment: 'positive' | 'negative' | 'neutral'
63
- score: number
64
- }>
65
- }
66
-
67
- export interface ClassificationResult {
68
- label: string
69
- confidence: number
70
- allLabels: Array<{
71
- label: string
72
- confidence: number
73
- }>
74
- }
75
-
76
- export interface SummaryOptions {
77
- provider?: 'anthropic' | 'openai' | 'ollama'
78
- model?: string
79
- maxLength?: number
80
- style?: 'concise' | 'detailed' | 'bullet-points'
81
- language?: string
82
- }
83
-
84
- // ============================================================================
85
- // Sentiment Analysis
86
- // ============================================================================
87
-
88
- /**
89
- * Analyze the sentiment of text using AI.
90
- */
91
- export async function analyzeSentiment(
92
- text: string,
93
- options: {
94
- provider?: 'anthropic' | 'openai' | 'ollama'
95
- model?: string
96
- aspects?: string[]
97
- } = {},
98
- ): Promise<SentimentResult> {
99
- const { provider = 'anthropic', aspects } = options
100
-
101
- const aspectInstruction = aspects
102
- ? `\nAlso analyze sentiment for these specific aspects: ${aspects.join(', ')}`
103
- : ''
104
-
105
- const systemPrompt = `You are a sentiment analysis expert. Analyze the sentiment of the given text and respond with ONLY valid JSON in this exact format:
106
- {
107
- "sentiment": "positive" | "negative" | "neutral" | "mixed",
108
- "score": <number from -1.0 to 1.0>,
109
- "confidence": <number from 0.0 to 1.0>${aspects ? `,
110
- "aspects": [{"aspect": "<name>", "sentiment": "positive" | "negative" | "neutral", "score": <number>}]` : ''}
111
- }${aspectInstruction}`
112
-
113
- const result = await callProvider(provider, systemPrompt, text, options.model)
114
-
115
- try {
116
- return JSON.parse(result.content) as SentimentResult
117
- }
118
- catch {
119
- // Fallback: extract sentiment from text response
120
- const lower = result.content.toLowerCase()
121
- const isPositive = lower.includes('positive')
122
- const isNegative = lower.includes('negative')
123
- return {
124
- sentiment: isPositive && isNegative ? 'mixed' : isPositive ? 'positive' : isNegative ? 'negative' : 'neutral',
125
- score: isPositive ? 0.5 : isNegative ? -0.5 : 0,
126
- confidence: 0.5,
127
- }
128
- }
129
- }
130
-
131
- // ============================================================================
132
- // Text Classification
133
- // ============================================================================
134
-
135
- /**
136
- * Classify text into one of the provided labels.
137
- */
138
- export async function classifyText(
139
- text: string,
140
- labels: string[],
141
- options: {
142
- provider?: 'anthropic' | 'openai' | 'ollama'
143
- model?: string
144
- multiLabel?: boolean
145
- } = {},
146
- ): Promise<ClassificationResult> {
147
- const { provider = 'anthropic', multiLabel = false } = options
148
-
149
- const systemPrompt = `You are a text classification expert. Classify the given text into ${multiLabel ? 'one or more of' : 'exactly one of'} these categories: ${labels.join(', ')}.
150
-
151
- Respond with ONLY valid JSON in this exact format:
152
- {
153
- "label": "<primary label>",
154
- "confidence": <number from 0.0 to 1.0>,
155
- "allLabels": [{"label": "<label>", "confidence": <number>}]
156
- }
157
-
158
- Include ALL provided labels in allLabels with their confidence scores, sorted by confidence descending.`
159
-
160
- const result = await callProvider(provider, systemPrompt, text, options.model)
161
-
162
- try {
163
- return JSON.parse(result.content) as ClassificationResult
164
- }
165
- catch {
166
- return {
167
- label: labels[0],
168
- confidence: 0.5,
169
- allLabels: labels.map(l => ({ label: l, confidence: 1 / labels.length })),
170
- }
171
- }
172
- }
173
-
174
- // ============================================================================
175
- // Smart Summarization
176
- // ============================================================================
177
-
178
- /**
179
- * Generate an intelligent summary of text.
180
- *
181
- * Not exported as a bare top-level identifier because `text.ts` already
182
- * exposes `summarize`, and `index.ts` re-exports both via `export *`.
183
- * Reach this multi-provider variant through the `personalization`
184
- * namespace export below: `personalization.summarize(...)`.
185
- */
186
- async function summarize(
187
- text: string,
188
- options: SummaryOptions = {},
189
- ): Promise<AIResult> {
190
- const {
191
- provider = 'anthropic',
192
- style = 'concise',
193
- maxLength,
194
- language,
195
- } = options
196
-
197
- let styleInstruction: string
198
- switch (style) {
199
- case 'bullet-points':
200
- styleInstruction = 'Use bullet points to organize the key points.'
201
- break
202
- case 'detailed':
203
- styleInstruction = 'Provide a detailed summary covering all major points.'
204
- break
205
- default:
206
- styleInstruction = 'Be concise and focus on the most important points.'
207
- }
208
-
209
- const lengthInstruction = maxLength ? ` Keep the summary under ${maxLength} words.` : ''
210
- const languageInstruction = language ? ` Write the summary in ${language}.` : ''
211
-
212
- const systemPrompt = `You are an expert summarizer. ${styleInstruction}${lengthInstruction}${languageInstruction}`
213
-
214
- return callProvider(provider, systemPrompt, `Summarize the following text:\n\n${text}`, options.model)
215
- }
216
-
217
- // ============================================================================
218
- // Content Recommendations
219
- // ============================================================================
220
-
221
- /**
222
- * Generate personalized content recommendations based on user profile and available items.
223
- */
224
- export async function recommend(
225
- profile: UserProfile,
226
- items: ContentItem[],
227
- options: RecommendationOptions = {},
228
- ): Promise<RecommendationResult> {
229
- const { provider = 'anthropic', limit = 5 } = options
230
-
231
- const profileSummary = buildProfileSummary(profile)
232
- const itemsList = items.map(item =>
233
- `ID: ${item.id} | Category: ${item.category || 'none'} | Tags: ${item.tags?.join(', ') || 'none'} | Content: ${item.content.slice(0, 200)}`,
234
- ).join('\n')
235
-
236
- const systemPrompt = `You are a recommendation engine. Based on the user profile, recommend the most relevant items. Respond with ONLY valid JSON:
237
- {
238
- "recommendations": [
239
- {"itemId": "<id>", "score": <0.0-1.0>, "reason": "<brief reason>"}
240
- ]
241
- }
242
-
243
- Return at most ${limit} recommendations, sorted by relevance score descending.`
244
-
245
- const prompt = `User Profile:\n${profileSummary}\n\nAvailable Items:\n${itemsList}`
246
- const result = await callProvider(provider, systemPrompt, prompt, options.model)
247
-
248
- try {
249
- const parsed = JSON.parse(result.content) as { recommendations: Array<{ itemId: string; score: number; reason: string }> }
250
- return {
251
- recommendations: parsed.recommendations.slice(0, limit),
252
- model: result.model,
253
- provider: provider,
254
- }
255
- }
256
- catch {
257
- return {
258
- recommendations: [],
259
- model: result.model,
260
- provider: provider,
261
- }
262
- }
263
- }
264
-
265
- function buildProfileSummary(profile: UserProfile): string {
266
- const topPreferences = Object.entries(profile.preferences)
267
- .sort(([, a], [, b]) => b - a)
268
- .slice(0, 10)
269
- .map(([key, value]) => `${key}: ${value.toFixed(2)}`)
270
- .join(', ')
271
-
272
- const recentInteractions = profile.interactions
273
- .sort((a, b) => b.timestamp - a.timestamp)
274
- .slice(0, 10)
275
- .map(i => `${i.type} on ${i.itemId}`)
276
- .join(', ')
277
-
278
- return `Segments: ${profile.segments.join(', ')}
279
- Top Preferences: ${topPreferences || 'none'}
280
- Recent Interactions: ${recentInteractions || 'none'}`
281
- }
282
-
283
- // ============================================================================
284
- // User Profile Building
285
- // ============================================================================
286
-
287
- /**
288
- * Create a new empty user profile.
289
- */
290
- export function createProfile(id: string, segments: string[] = []): UserProfile {
291
- return {
292
- id,
293
- preferences: {},
294
- interactions: [],
295
- segments,
296
- }
297
- }
298
-
299
- /**
300
- * Record a user interaction and update preferences.
301
- */
302
- export function recordInteraction(profile: UserProfile, interaction: Interaction): UserProfile {
303
- profile.interactions.push(interaction)
304
-
305
- // Update preference weights based on interaction type
306
- const weights: Record<string, number> = {
307
- view: 0.1,
308
- click: 0.3,
309
- like: 0.5,
310
- share: 0.6,
311
- bookmark: 0.7,
312
- purchase: 1.0,
313
- dislike: -0.5,
314
- custom: interaction.weight || 0.3,
315
- }
316
-
317
- const weight = weights[interaction.type] || 0.1
318
- const key = interaction.itemId
319
- profile.preferences[key] = (profile.preferences[key] || 0) + weight
320
-
321
- return profile
322
- }
323
-
324
- /**
325
- * Extract keywords/topics from user interactions using AI.
326
- */
327
- export async function extractUserInterests(
328
- profile: UserProfile,
329
- items: ContentItem[],
330
- options: { provider?: 'anthropic' | 'openai' | 'ollama'; model?: string } = {},
331
- ): Promise<string[]> {
332
- const { provider = 'anthropic' } = options
333
-
334
- const interactedItemIds = new Set(profile.interactions.map(i => i.itemId))
335
- const interactedItems = items.filter(item => interactedItemIds.has(item.id))
336
-
337
- if (interactedItems.length === 0) return profile.segments
338
-
339
- const contentSample = interactedItems
340
- .slice(0, 20)
341
- .map(item => item.content.slice(0, 200))
342
- .join('\n---\n')
343
-
344
- const systemPrompt = `Extract the main interests/topics from the user's interaction history. Return ONLY a JSON array of strings, e.g. ["technology", "cooking", "travel"]. Maximum 10 interests.`
345
-
346
- const result = await callProvider(provider, systemPrompt, contentSample, options.model)
347
-
348
- try {
349
- return JSON.parse(result.content) as string[]
350
- }
351
- catch {
352
- return profile.segments
353
- }
354
- }
355
-
356
- // ============================================================================
357
- // Provider Helper
358
- // ============================================================================
359
-
360
- async function callProvider(
361
- provider: string,
362
- systemPrompt: string,
363
- userMessage: string,
364
- model?: string,
365
- ): Promise<AIResult> {
366
- if (provider === 'anthropic') {
367
- const apiKey = process.env.ANTHROPIC_API_KEY
368
- if (!apiKey) throw new Error('ANTHROPIC_API_KEY required.')
369
-
370
- const response = await fetch('https://api.anthropic.com/v1/messages', {
371
- method: 'POST',
372
- headers: {
373
- 'Content-Type': 'application/json',
374
- 'x-api-key': apiKey,
375
- 'anthropic-version': '2023-06-01',
376
- },
377
- body: JSON.stringify({
378
- model: model || 'claude-sonnet-4-20250514',
379
- max_tokens: 4096,
380
- system: systemPrompt,
381
- messages: [{ role: 'user', content: userMessage }],
382
- }),
383
- })
384
-
385
- if (!response.ok) {
386
- const error = await response.text()
387
- throw new Error(`Claude API error: ${error}`)
388
- }
389
-
390
- const data = (await response.json()) as any
391
- return {
392
- content: data.content[0].text,
393
- model: data.model,
394
- usage: {
395
- promptTokens: data.usage?.input_tokens || 0,
396
- completionTokens: data.usage?.output_tokens || 0,
397
- totalTokens: (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0),
398
- },
399
- finishReason: data.stop_reason,
400
- }
401
- }
402
-
403
- if (provider === 'openai') {
404
- const apiKey = process.env.OPENAI_API_KEY
405
- if (!apiKey) throw new Error('OPENAI_API_KEY required.')
406
-
407
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
408
- method: 'POST',
409
- headers: {
410
- 'Content-Type': 'application/json',
411
- 'Authorization': `Bearer ${apiKey}`,
412
- },
413
- body: JSON.stringify({
414
- model: model || 'gpt-4o',
415
- max_tokens: 4096,
416
- messages: [
417
- { role: 'system', content: systemPrompt },
418
- { role: 'user', content: userMessage },
419
- ],
420
- }),
421
- })
422
-
423
- if (!response.ok) {
424
- const error = await response.text()
425
- throw new Error(`OpenAI API error: ${error}`)
426
- }
427
-
428
- const data = (await response.json()) as any
429
- return {
430
- content: data.choices[0].message.content,
431
- model: data.model,
432
- usage: {
433
- promptTokens: data.usage?.prompt_tokens || 0,
434
- completionTokens: data.usage?.completion_tokens || 0,
435
- totalTokens: data.usage?.total_tokens || 0,
436
- },
437
- finishReason: data.choices[0].finish_reason,
438
- }
439
- }
440
-
441
- if (provider === 'ollama') {
442
- const host = process.env.OLLAMA_HOST || 'http://localhost:11434'
443
-
444
- const response = await fetch(`${host}/api/chat`, {
445
- method: 'POST',
446
- headers: { 'Content-Type': 'application/json' },
447
- body: JSON.stringify({
448
- model: model || 'llama3.2',
449
- messages: [
450
- { role: 'system', content: systemPrompt },
451
- { role: 'user', content: userMessage },
452
- ],
453
- stream: false,
454
- }),
455
- })
456
-
457
- if (!response.ok) {
458
- const error = await response.text()
459
- throw new Error(`Ollama API error: ${error}`)
460
- }
461
-
462
- const data = (await response.json()) as any
463
- return {
464
- content: data.message.content,
465
- model: data.model,
466
- usage: {
467
- promptTokens: data.prompt_eval_count || 0,
468
- completionTokens: data.eval_count || 0,
469
- totalTokens: (data.prompt_eval_count || 0) + (data.eval_count || 0),
470
- },
471
- finishReason: data.done_reason || 'stop',
472
- }
473
- }
474
-
475
- throw new Error(`Provider not supported: ${provider}`)
476
- }
477
-
478
- // ============================================================================
479
- // Exports
480
- // ============================================================================
481
-
482
- export const personalization = {
483
- analyzeSentiment,
484
- classifyText,
485
- summarize,
486
- recommend,
487
- createProfile,
488
- recordInteraction,
489
- extractUserInterests,
490
- }