@nextsparkjs/plugin-ai 0.1.0-beta.1
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.
- package/.env.example +79 -0
- package/README.md +529 -0
- package/api/README.md +65 -0
- package/api/ai-history/[id]/route.ts +112 -0
- package/api/embeddings/route.ts +129 -0
- package/api/generate/route.ts +160 -0
- package/docs/01-getting-started/01-introduction.md +237 -0
- package/docs/01-getting-started/02-installation.md +447 -0
- package/docs/01-getting-started/03-configuration.md +416 -0
- package/docs/02-features/01-text-generation.md +523 -0
- package/docs/02-features/02-embeddings.md +241 -0
- package/docs/02-features/03-ai-history.md +549 -0
- package/docs/03-advanced-usage/01-core-utilities.md +500 -0
- package/docs/04-use-cases/01-content-generation.md +453 -0
- package/entities/ai-history/ai-history.config.ts +123 -0
- package/entities/ai-history/ai-history.fields.ts +330 -0
- package/entities/ai-history/messages/en.json +56 -0
- package/entities/ai-history/messages/es.json +56 -0
- package/entities/ai-history/migrations/001_ai_history_table.sql +167 -0
- package/entities/ai-history/migrations/002_ai_history_metas.sql +103 -0
- package/lib/ai-history-meta-service.ts +379 -0
- package/lib/ai-history-service.ts +391 -0
- package/lib/ai-sdk.ts +7 -0
- package/lib/core-utils.ts +217 -0
- package/lib/plugin-env.ts +252 -0
- package/lib/sanitize.ts +122 -0
- package/lib/save-example.ts +237 -0
- package/lib/server-env.ts +104 -0
- package/package.json +23 -0
- package/plugin.config.ts +55 -0
- package/public/docs/login-404-error.png +0 -0
- package/tsconfig.json +47 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/types/ai.types.ts +51 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
# AI History
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
**AI History** is an audit trail system that tracks every AI operation in your application. It provides complete visibility into AI usage, costs, performance, and outcomes.
|
|
6
|
+
|
|
7
|
+
**Key Benefits:**
|
|
8
|
+
- **Cost Tracking** - Monitor token usage and spending across all AI operations
|
|
9
|
+
- **Performance Monitoring** - Track response times and success rates
|
|
10
|
+
- **Audit Trail** - Complete record of all AI interactions
|
|
11
|
+
- **Entity Linking** - Connect AI operations to application entities
|
|
12
|
+
- **Debugging** - Investigate issues and optimize prompts
|
|
13
|
+
|
|
14
|
+
## Database Schema
|
|
15
|
+
|
|
16
|
+
### AI History Table
|
|
17
|
+
|
|
18
|
+
```sql
|
|
19
|
+
CREATE TABLE ai_history (
|
|
20
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
21
|
+
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
22
|
+
|
|
23
|
+
-- Operation details
|
|
24
|
+
operation VARCHAR(50) NOT NULL, -- 'generate', 'refine', 'analyze', etc.
|
|
25
|
+
status VARCHAR(20) NOT NULL, -- 'pending', 'processing', 'completed', 'failed'
|
|
26
|
+
|
|
27
|
+
-- AI configuration
|
|
28
|
+
model VARCHAR(100) NOT NULL,
|
|
29
|
+
provider VARCHAR(50), -- 'openai', 'anthropic', 'ollama'
|
|
30
|
+
|
|
31
|
+
-- Token usage and costs
|
|
32
|
+
tokens_used INTEGER DEFAULT 0,
|
|
33
|
+
tokens_input INTEGER, -- Prompt tokens
|
|
34
|
+
tokens_output INTEGER, -- Completion tokens
|
|
35
|
+
credits_used DECIMAL(10,4) DEFAULT 0,
|
|
36
|
+
estimated_cost DECIMAL(10,6) DEFAULT 0,
|
|
37
|
+
balance_after DECIMAL(10,4),
|
|
38
|
+
|
|
39
|
+
-- Entity relationship (polymorphic)
|
|
40
|
+
related_entity_type VARCHAR(100), -- 'products', 'articles', 'clients'
|
|
41
|
+
related_entity_id UUID,
|
|
42
|
+
|
|
43
|
+
-- Timestamps
|
|
44
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
45
|
+
updated_at TIMESTAMP DEFAULT NOW(),
|
|
46
|
+
completed_at TIMESTAMP
|
|
47
|
+
);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### AI History Metadata Table
|
|
51
|
+
|
|
52
|
+
```sql
|
|
53
|
+
CREATE TABLE ai_history_metas (
|
|
54
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
55
|
+
entity_id UUID REFERENCES ai_history(id) ON DELETE CASCADE,
|
|
56
|
+
meta_key VARCHAR(255) NOT NULL,
|
|
57
|
+
meta_value TEXT,
|
|
58
|
+
created_at TIMESTAMP DEFAULT NOW()
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Flexible Metadata:**
|
|
63
|
+
- `sourceOperationId` - Link to original operation
|
|
64
|
+
- `userInstruction` - Original user input
|
|
65
|
+
- `systemPrompt` - System prompt used
|
|
66
|
+
- `parameters` - JSON of generation parameters
|
|
67
|
+
- Custom fields as needed
|
|
68
|
+
|
|
69
|
+
## AI History Service
|
|
70
|
+
|
|
71
|
+
### Import
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { AIHistoryService } from '@/contents/plugins/ai/lib/ai-history-service'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Operation Lifecycle
|
|
78
|
+
|
|
79
|
+
**1. Start Operation**
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const historyId = await AIHistoryService.startOperation({
|
|
83
|
+
userId: 'user-id',
|
|
84
|
+
operation: 'generate',
|
|
85
|
+
model: 'gpt-4o-mini',
|
|
86
|
+
provider: 'openai',
|
|
87
|
+
relatedEntityType: 'products',
|
|
88
|
+
relatedEntityId: 'product-uuid'
|
|
89
|
+
})
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**2. Update Status (Optional)**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
await AIHistoryService.updateToProcessing(historyId)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**3. Complete Operation**
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
await AIHistoryService.completeOperation({
|
|
102
|
+
historyId,
|
|
103
|
+
tokensUsed: 250,
|
|
104
|
+
tokensInput: 100,
|
|
105
|
+
tokensOutput: 150,
|
|
106
|
+
creditsUsed: 0,
|
|
107
|
+
estimatedCost: 0.00045,
|
|
108
|
+
balanceAfter: 10.50,
|
|
109
|
+
userId: 'user-id',
|
|
110
|
+
metas: {
|
|
111
|
+
userInstruction: 'Generate product description',
|
|
112
|
+
systemPrompt: 'You are a product copywriter...',
|
|
113
|
+
resultLength: 500
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**4. Fail Operation (If Error)**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
await AIHistoryService.failOperation({
|
|
122
|
+
historyId,
|
|
123
|
+
errorMessage: 'Rate limit exceeded',
|
|
124
|
+
tokensUsed: 50 // Partial tokens before failure
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Methods Reference
|
|
129
|
+
|
|
130
|
+
#### `startOperation(params)`
|
|
131
|
+
|
|
132
|
+
**Parameters:**
|
|
133
|
+
```typescript
|
|
134
|
+
{
|
|
135
|
+
userId: string // Required
|
|
136
|
+
operation: AIOperation // 'generate' | 'refine' | 'analyze' | 'chat' | 'completion' | 'other'
|
|
137
|
+
model: string // Model name (e.g., 'gpt-4o-mini')
|
|
138
|
+
provider?: AIProvider // 'openai' | 'anthropic' | 'ollama'
|
|
139
|
+
relatedEntityType?: string // Entity type (e.g., 'products')
|
|
140
|
+
relatedEntityId?: string // Entity ID
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Returns:** `Promise<string>` - History record ID
|
|
145
|
+
|
|
146
|
+
**Usage:**
|
|
147
|
+
```typescript
|
|
148
|
+
const historyId = await AIHistoryService.startOperation({
|
|
149
|
+
userId: session.user.id,
|
|
150
|
+
operation: 'generate',
|
|
151
|
+
model: 'llama3.2:3b',
|
|
152
|
+
provider: 'ollama'
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `completeOperation(params)`
|
|
157
|
+
|
|
158
|
+
**Parameters:**
|
|
159
|
+
```typescript
|
|
160
|
+
{
|
|
161
|
+
historyId: string // Required: ID from startOperation
|
|
162
|
+
tokensUsed: number // Total tokens
|
|
163
|
+
tokensInput?: number // Input tokens (optional but recommended)
|
|
164
|
+
tokensOutput?: number // Output tokens (optional but recommended)
|
|
165
|
+
creditsUsed: number // Credits deducted (if using credit system)
|
|
166
|
+
estimatedCost: number // Cost in USD
|
|
167
|
+
balanceAfter: number // User balance after operation
|
|
168
|
+
userId: string // Required for metadata
|
|
169
|
+
metas?: Record<string, any> // Optional metadata
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Returns:** `Promise<void>`
|
|
174
|
+
|
|
175
|
+
**Usage:**
|
|
176
|
+
```typescript
|
|
177
|
+
await AIHistoryService.completeOperation({
|
|
178
|
+
historyId,
|
|
179
|
+
tokensUsed: result.usage.totalTokens,
|
|
180
|
+
tokensInput: result.usage.inputTokens,
|
|
181
|
+
tokensOutput: result.usage.outputTokens,
|
|
182
|
+
creditsUsed: 0,
|
|
183
|
+
estimatedCost: 0.00045,
|
|
184
|
+
balanceAfter: user.balance,
|
|
185
|
+
userId: session.user.id,
|
|
186
|
+
metas: {
|
|
187
|
+
temperature: 0.7,
|
|
188
|
+
maxTokens: 500,
|
|
189
|
+
responseLength: result.text.length
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### `failOperation(params)`
|
|
195
|
+
|
|
196
|
+
**Parameters:**
|
|
197
|
+
```typescript
|
|
198
|
+
{
|
|
199
|
+
historyId: string // Required
|
|
200
|
+
errorMessage: string // Error description
|
|
201
|
+
tokensUsed?: number // Partial tokens if any
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Returns:** `Promise<void>`
|
|
206
|
+
|
|
207
|
+
**Usage:**
|
|
208
|
+
```typescript
|
|
209
|
+
try {
|
|
210
|
+
// AI operation
|
|
211
|
+
} catch (error) {
|
|
212
|
+
await AIHistoryService.failOperation({
|
|
213
|
+
historyId,
|
|
214
|
+
errorMessage: error.message,
|
|
215
|
+
tokensUsed: partialTokens
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Entity Linking
|
|
221
|
+
|
|
222
|
+
### Link Operation to Entity
|
|
223
|
+
|
|
224
|
+
**During Creation:**
|
|
225
|
+
```typescript
|
|
226
|
+
const historyId = await AIHistoryService.startOperation({
|
|
227
|
+
userId: 'user-id',
|
|
228
|
+
operation: 'analyze',
|
|
229
|
+
model: 'claude-3-5-haiku-20241022',
|
|
230
|
+
relatedEntityType: 'clients',
|
|
231
|
+
relatedEntityId: clientId
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**After Creation (via API):**
|
|
236
|
+
```bash
|
|
237
|
+
PATCH /api/v1/plugin/ai/ai-history/:id
|
|
238
|
+
|
|
239
|
+
{
|
|
240
|
+
"relatedEntityType": "products",
|
|
241
|
+
"relatedEntityId": "product-uuid"
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Use Cases
|
|
246
|
+
|
|
247
|
+
**1. Product Content Generation**
|
|
248
|
+
```typescript
|
|
249
|
+
// Generate product description
|
|
250
|
+
const historyId = await AIHistoryService.startOperation({
|
|
251
|
+
userId: session.user.id,
|
|
252
|
+
operation: 'generate',
|
|
253
|
+
model: 'gpt-4o-mini',
|
|
254
|
+
relatedEntityType: 'products',
|
|
255
|
+
relatedEntityId: productId
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// ... generate content ...
|
|
259
|
+
|
|
260
|
+
// Link shows which product this content was generated for
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**2. Client Analysis**
|
|
264
|
+
```typescript
|
|
265
|
+
// Analyze client data
|
|
266
|
+
const historyId = await AIHistoryService.startOperation({
|
|
267
|
+
userId: session.user.id,
|
|
268
|
+
operation: 'analyze',
|
|
269
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
270
|
+
relatedEntityType: 'clients',
|
|
271
|
+
relatedEntityId: clientId
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
// History linked to specific client
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**3. Content Auditing**
|
|
278
|
+
```typescript
|
|
279
|
+
// Audit article content
|
|
280
|
+
const historyId = await AIHistoryService.startOperation({
|
|
281
|
+
userId: session.user.id,
|
|
282
|
+
operation: 'analyze',
|
|
283
|
+
model: 'gpt-4o',
|
|
284
|
+
relatedEntityType: 'articles',
|
|
285
|
+
relatedEntityId: articleId
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
// Track which articles have been audited
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Querying History
|
|
292
|
+
|
|
293
|
+
### Get User's History
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const history = await AIHistoryService.getUserHistory(userId, {
|
|
297
|
+
limit: 50,
|
|
298
|
+
offset: 0
|
|
299
|
+
})
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Filter by Operation
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
const generations = await query(`
|
|
306
|
+
SELECT * FROM ai_history
|
|
307
|
+
WHERE user_id = $1
|
|
308
|
+
AND operation = 'generate'
|
|
309
|
+
ORDER BY created_at DESC
|
|
310
|
+
LIMIT 50
|
|
311
|
+
`, [userId])
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Calculate Total Costs
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
const result = await queryOne(`
|
|
318
|
+
SELECT
|
|
319
|
+
SUM(estimated_cost) as total_cost,
|
|
320
|
+
SUM(tokens_used) as total_tokens,
|
|
321
|
+
COUNT(*) as operation_count
|
|
322
|
+
FROM ai_history
|
|
323
|
+
WHERE user_id = $1
|
|
324
|
+
AND created_at >= NOW() - INTERVAL '30 days'
|
|
325
|
+
`, [userId])
|
|
326
|
+
|
|
327
|
+
console.log(`Monthly cost: $${result.total_cost}`)
|
|
328
|
+
console.log(`Total tokens: ${result.total_tokens}`)
|
|
329
|
+
console.log(`Operations: ${result.operation_count}`)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Group by Model
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
const modelStats = await query(`
|
|
336
|
+
SELECT
|
|
337
|
+
model,
|
|
338
|
+
COUNT(*) as usage_count,
|
|
339
|
+
SUM(estimated_cost) as total_cost,
|
|
340
|
+
AVG(tokens_used) as avg_tokens
|
|
341
|
+
FROM ai_history
|
|
342
|
+
WHERE user_id = $1
|
|
343
|
+
GROUP BY model
|
|
344
|
+
ORDER BY usage_count DESC
|
|
345
|
+
`, [userId])
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Dashboard Integration
|
|
349
|
+
|
|
350
|
+
### AI History Entity
|
|
351
|
+
|
|
352
|
+
The AI History entity is automatically available in the dashboard:
|
|
353
|
+
|
|
354
|
+
**Routes:**
|
|
355
|
+
- `/dashboard/ai-history` - List view
|
|
356
|
+
- `/dashboard/ai-history/:id` - Detail view
|
|
357
|
+
|
|
358
|
+
**Features:**
|
|
359
|
+
- Searchable by operation, model, provider, entity type
|
|
360
|
+
- Sortable by date, cost, status, tokens
|
|
361
|
+
- Filterable by status, operation, provider
|
|
362
|
+
- Bulk delete operations
|
|
363
|
+
|
|
364
|
+
### Permissions
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
permissions: {
|
|
368
|
+
read: ['admin', 'colaborator'], // Users see their own via RLS
|
|
369
|
+
create: ['admin', 'colaborator'], // Created by API
|
|
370
|
+
update: ['admin'], // Immutable (admin only)
|
|
371
|
+
delete: ['admin', 'colaborator'] // Users can delete own history
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Real-World Example
|
|
376
|
+
|
|
377
|
+
### Complete Content Generation Flow
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
export async function generateProductDescription(productId: string) {
|
|
381
|
+
const session = await auth()
|
|
382
|
+
if (!session?.user) throw new Error('Unauthorized')
|
|
383
|
+
|
|
384
|
+
// 1. Start operation tracking
|
|
385
|
+
const historyId = await AIHistoryService.startOperation({
|
|
386
|
+
userId: session.user.id,
|
|
387
|
+
operation: 'generate',
|
|
388
|
+
model: 'gpt-4o-mini',
|
|
389
|
+
provider: 'openai',
|
|
390
|
+
relatedEntityType: 'products',
|
|
391
|
+
relatedEntityId: productId
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
// 2. Update status
|
|
396
|
+
await AIHistoryService.updateToProcessing(historyId)
|
|
397
|
+
|
|
398
|
+
// 3. Get product data
|
|
399
|
+
const product = await getProduct(productId)
|
|
400
|
+
|
|
401
|
+
// 4. Generate content
|
|
402
|
+
const result = await generateText({
|
|
403
|
+
model: openai('gpt-4o-mini'),
|
|
404
|
+
prompt: `Generate a compelling product description for: ${product.name}`,
|
|
405
|
+
maxTokens: 300
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
// 5. Calculate cost
|
|
409
|
+
const tokens = {
|
|
410
|
+
input: result.usage.inputTokens,
|
|
411
|
+
output: result.usage.outputTokens,
|
|
412
|
+
total: result.usage.totalTokens
|
|
413
|
+
}
|
|
414
|
+
const cost = calculateCost(tokens, { input: 0.00015, output: 0.0006 })
|
|
415
|
+
|
|
416
|
+
// 6. Complete operation
|
|
417
|
+
await AIHistoryService.completeOperation({
|
|
418
|
+
historyId,
|
|
419
|
+
tokensUsed: tokens.total,
|
|
420
|
+
tokensInput: tokens.input,
|
|
421
|
+
tokensOutput: tokens.output,
|
|
422
|
+
creditsUsed: 0,
|
|
423
|
+
estimatedCost: cost,
|
|
424
|
+
balanceAfter: session.user.balance,
|
|
425
|
+
userId: session.user.id,
|
|
426
|
+
metas: {
|
|
427
|
+
productName: product.name,
|
|
428
|
+
promptTemplate: 'product-description-v1',
|
|
429
|
+
generatedLength: result.text.length
|
|
430
|
+
}
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
// 7. Return result
|
|
434
|
+
return {
|
|
435
|
+
description: result.text,
|
|
436
|
+
cost,
|
|
437
|
+
tokens,
|
|
438
|
+
historyId
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
} catch (error) {
|
|
442
|
+
// 8. Handle failure
|
|
443
|
+
await AIHistoryService.failOperation({
|
|
444
|
+
historyId,
|
|
445
|
+
errorMessage: error.message
|
|
446
|
+
})
|
|
447
|
+
throw error
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Metadata Examples
|
|
453
|
+
|
|
454
|
+
### Store Custom Data
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
await AIHistoryService.completeOperation({
|
|
458
|
+
historyId,
|
|
459
|
+
// ... other params
|
|
460
|
+
metas: {
|
|
461
|
+
// Prompt information
|
|
462
|
+
userInstruction: 'Write a blog post about AI',
|
|
463
|
+
systemPrompt: 'You are a technical writer...',
|
|
464
|
+
|
|
465
|
+
// Generation parameters
|
|
466
|
+
temperature: 0.7,
|
|
467
|
+
maxTokens: 1000,
|
|
468
|
+
model: 'gpt-4o-mini',
|
|
469
|
+
|
|
470
|
+
// Results
|
|
471
|
+
responseLength: 850,
|
|
472
|
+
responseQuality: 'high',
|
|
473
|
+
|
|
474
|
+
// Context
|
|
475
|
+
sourceDocument: 'outline-v3.md',
|
|
476
|
+
targetAudience: 'developers',
|
|
477
|
+
|
|
478
|
+
// Custom fields
|
|
479
|
+
campaignId: 'summer-2024',
|
|
480
|
+
approved: false
|
|
481
|
+
}
|
|
482
|
+
})
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Query Metadata
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
// Find all operations with specific metadata
|
|
489
|
+
const operations = await query(`
|
|
490
|
+
SELECT h.*
|
|
491
|
+
FROM ai_history h
|
|
492
|
+
JOIN ai_history_metas m ON h.id = m.entity_id
|
|
493
|
+
WHERE h.user_id = $1
|
|
494
|
+
AND m.meta_key = 'campaignId'
|
|
495
|
+
AND m.meta_value = 'summer-2024'
|
|
496
|
+
`, [userId])
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Performance Optimization
|
|
500
|
+
|
|
501
|
+
### Index for Common Queries
|
|
502
|
+
|
|
503
|
+
```sql
|
|
504
|
+
-- Index for user queries
|
|
505
|
+
CREATE INDEX idx_ai_history_user_date
|
|
506
|
+
ON ai_history(user_id, created_at DESC);
|
|
507
|
+
|
|
508
|
+
-- Index for entity relationships
|
|
509
|
+
CREATE INDEX idx_ai_history_entity
|
|
510
|
+
ON ai_history(related_entity_type, related_entity_id);
|
|
511
|
+
|
|
512
|
+
-- Index for status tracking
|
|
513
|
+
CREATE INDEX idx_ai_history_status
|
|
514
|
+
ON ai_history(status, created_at);
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Pagination
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
async function getUserHistoryPaginated(
|
|
521
|
+
userId: string,
|
|
522
|
+
page: number = 1,
|
|
523
|
+
pageSize: number = 50
|
|
524
|
+
) {
|
|
525
|
+
const offset = (page - 1) * pageSize
|
|
526
|
+
|
|
527
|
+
return await query(`
|
|
528
|
+
SELECT * FROM ai_history
|
|
529
|
+
WHERE user_id = $1
|
|
530
|
+
ORDER BY created_at DESC
|
|
531
|
+
LIMIT $2 OFFSET $3
|
|
532
|
+
`, [userId, pageSize, offset])
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Best Practices
|
|
537
|
+
|
|
538
|
+
1. **Always Track Operations** - Even failed ones provide valuable data
|
|
539
|
+
2. **Use Meaningful Operation Names** - Make it easy to filter and analyze
|
|
540
|
+
3. **Link to Entities** - Connect AI work to business objects
|
|
541
|
+
4. **Store Relevant Metadata** - Helps with debugging and optimization
|
|
542
|
+
5. **Monitor Costs** - Set up alerts for unusual spending
|
|
543
|
+
6. **Clean Old Data** - Archive or delete history after retention period
|
|
544
|
+
|
|
545
|
+
## Next Steps
|
|
546
|
+
|
|
547
|
+
- **[Text Generation](./01-text-generation.md)** - Generate AI content
|
|
548
|
+
- **[Core Utilities](../04-advanced-usage/01-core-utilities.md)** - Use in custom endpoints
|
|
549
|
+
- **[Custom Endpoints](../04-advanced-usage/02-custom-endpoints.md)** - Build with history tracking
|