@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,453 @@
|
|
|
1
|
+
# Content Generation Use Case
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
One of the most common and proven use cases for the AI plugin is **automated content generation**. This use case is already in production, generating marketing copy, product descriptions, blog posts, and more.
|
|
6
|
+
|
|
7
|
+
## Real-World Application
|
|
8
|
+
|
|
9
|
+
**Status:** ✅ In Production
|
|
10
|
+
**Use:** Content creation workflows, automated copywriting
|
|
11
|
+
**Models:** GPT-4o Mini, Claude Haiku, Llama 3.2
|
|
12
|
+
**Results:** 10x faster content production with consistent quality
|
|
13
|
+
|
|
14
|
+
## Common Content Types
|
|
15
|
+
|
|
16
|
+
### 1. Product Descriptions
|
|
17
|
+
|
|
18
|
+
**Use Case:** E-commerce, catalogs, marketplaces
|
|
19
|
+
|
|
20
|
+
**Example Implementation:**
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// app/api/content/product-description/route.ts
|
|
24
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
25
|
+
import { generateText } from 'ai'
|
|
26
|
+
import { selectModel, calculateCost, extractTokens } from '@/contents/plugins/ai/lib/core-utils'
|
|
27
|
+
import { authenticateRequest } from '@/core/lib/api/auth/dual-auth'
|
|
28
|
+
|
|
29
|
+
export async function POST(request: NextRequest) {
|
|
30
|
+
const auth = await authenticateRequest(request)
|
|
31
|
+
if (!auth.success) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
32
|
+
|
|
33
|
+
const { productName, features, targetAudience = 'general consumers' } = await request.json()
|
|
34
|
+
|
|
35
|
+
const selection = await selectModel('gpt-4o-mini')
|
|
36
|
+
|
|
37
|
+
const result = await generateText({
|
|
38
|
+
model: selection.model,
|
|
39
|
+
system: `You are an expert e-commerce copywriter. Write compelling, SEO-friendly product descriptions that convert.`,
|
|
40
|
+
prompt: `
|
|
41
|
+
Product: ${productName}
|
|
42
|
+
Features: ${features.join(', ')}
|
|
43
|
+
Target Audience: ${targetAudience}
|
|
44
|
+
|
|
45
|
+
Write a compelling product description (100-150 words) that:
|
|
46
|
+
- Highlights key benefits
|
|
47
|
+
- Uses persuasive language
|
|
48
|
+
- Includes relevant keywords
|
|
49
|
+
- Ends with a call-to-action
|
|
50
|
+
`.trim(),
|
|
51
|
+
maxOutputTokens: 300,
|
|
52
|
+
temperature: 0.7
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
const tokens = extractTokens(result)
|
|
56
|
+
const cost = calculateCost(tokens, selection.costConfig)
|
|
57
|
+
|
|
58
|
+
return NextResponse.json({
|
|
59
|
+
description: result.text,
|
|
60
|
+
wordCount: result.text.split(' ').length,
|
|
61
|
+
cost,
|
|
62
|
+
tokens
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Usage:**
|
|
68
|
+
```bash
|
|
69
|
+
curl -X POST /api/content/product-description \
|
|
70
|
+
-H "Content-Type: application/json" \
|
|
71
|
+
-d '{
|
|
72
|
+
"productName": "Wireless Noise-Canceling Headphones",
|
|
73
|
+
"features": ["Active noise cancellation", "30-hour battery", "Bluetooth 5.0", "Comfortable ear cups"],
|
|
74
|
+
"targetAudience": "remote workers and travelers"
|
|
75
|
+
}'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Response:**
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"description": "Experience uninterrupted focus with our premium Wireless Noise-Canceling Headphones. Featuring advanced active noise cancellation technology, these headphones create your personal sound sanctuary wherever you go. With an impressive 30-hour battery life and Bluetooth 5.0 connectivity, you'll enjoy seamless audio all day long. The plush, comfortable ear cups ensure hours of fatigue-free listening, perfect for remote work, travel, or simply escaping into your favorite music. Upgrade your audio experience today and discover the difference true wireless freedom makes.",
|
|
82
|
+
"wordCount": 89,
|
|
83
|
+
"cost": 0.00032,
|
|
84
|
+
"tokens": { "input": 85, "output": 105, "total": 190 }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2. Blog Post Generation
|
|
89
|
+
|
|
90
|
+
**Use Case:** Content marketing, SEO, thought leadership
|
|
91
|
+
|
|
92
|
+
**Example Implementation:**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// app/api/content/blog-post/route.ts
|
|
96
|
+
export async function POST(request: NextRequest) {
|
|
97
|
+
const { topic, keywords, tone = 'professional', length = 'medium' } = await request.json()
|
|
98
|
+
|
|
99
|
+
const lengthGuide = {
|
|
100
|
+
short: { words: '400-600', tokens: 800 },
|
|
101
|
+
medium: { words: '800-1200', tokens: 1500 },
|
|
102
|
+
long: { words: '1500-2000', tokens: 2500 }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const guide = lengthGuide[length] || lengthGuide.medium
|
|
106
|
+
|
|
107
|
+
const selection = await selectModel('claude-3-5-haiku-20241022')
|
|
108
|
+
|
|
109
|
+
const result = await generateText({
|
|
110
|
+
model: selection.model,
|
|
111
|
+
system: `You are a professional content writer specializing in engaging, SEO-optimized blog posts.`,
|
|
112
|
+
prompt: `
|
|
113
|
+
Topic: ${topic}
|
|
114
|
+
Target Keywords: ${keywords.join(', ')}
|
|
115
|
+
Tone: ${tone}
|
|
116
|
+
Length: ${guide.words} words
|
|
117
|
+
|
|
118
|
+
Write a complete blog post with:
|
|
119
|
+
1. Attention-grabbing title
|
|
120
|
+
2. Compelling introduction
|
|
121
|
+
3. Well-structured body with H2/H3 headings
|
|
122
|
+
4. Key takeaways
|
|
123
|
+
5. Strong conclusion with CTA
|
|
124
|
+
|
|
125
|
+
Include keywords naturally and maintain ${tone} tone throughout.
|
|
126
|
+
`.trim(),
|
|
127
|
+
maxOutputTokens: guide.tokens,
|
|
128
|
+
temperature: 0.8
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const tokens = extractTokens(result)
|
|
132
|
+
const cost = calculateCost(tokens, selection.costConfig)
|
|
133
|
+
|
|
134
|
+
// Parse title and content
|
|
135
|
+
const lines = result.text.split('\n')
|
|
136
|
+
const title = lines[0].replace(/^#+\s*/, '')
|
|
137
|
+
const content = lines.slice(1).join('\n').trim()
|
|
138
|
+
|
|
139
|
+
return NextResponse.json({
|
|
140
|
+
title,
|
|
141
|
+
content,
|
|
142
|
+
wordCount: content.split(' ').length,
|
|
143
|
+
cost,
|
|
144
|
+
tokens
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 3. Marketing Copy
|
|
150
|
+
|
|
151
|
+
**Use Case:** Ads, landing pages, email campaigns
|
|
152
|
+
|
|
153
|
+
**Example - Email Subject Lines:**
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// app/api/content/email-subject/route.ts
|
|
157
|
+
export async function POST(request: NextRequest) {
|
|
158
|
+
const { campaign, offer, audience } = await request.json()
|
|
159
|
+
|
|
160
|
+
const selection = await selectModel('gpt-4o-mini')
|
|
161
|
+
|
|
162
|
+
const result = await generateText({
|
|
163
|
+
model: selection.model,
|
|
164
|
+
system: `You are a direct response copywriter specializing in high-converting email subject lines.`,
|
|
165
|
+
prompt: `
|
|
166
|
+
Campaign: ${campaign}
|
|
167
|
+
Offer: ${offer}
|
|
168
|
+
Audience: ${audience}
|
|
169
|
+
|
|
170
|
+
Generate 10 compelling email subject lines that:
|
|
171
|
+
- Create urgency or curiosity
|
|
172
|
+
- Are 40-60 characters
|
|
173
|
+
- Include power words
|
|
174
|
+
- Encourage opens
|
|
175
|
+
|
|
176
|
+
Format: One per line, numbered
|
|
177
|
+
`.trim(),
|
|
178
|
+
maxOutputTokens: 400,
|
|
179
|
+
temperature: 0.9 // High creativity
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// Parse subject lines
|
|
183
|
+
const subjectLines = result.text
|
|
184
|
+
.split('\n')
|
|
185
|
+
.filter(line => line.match(/^\d+\./))
|
|
186
|
+
.map(line => line.replace(/^\d+\.\s*/, '').trim())
|
|
187
|
+
|
|
188
|
+
return NextResponse.json({
|
|
189
|
+
subjectLines,
|
|
190
|
+
count: subjectLines.length,
|
|
191
|
+
cost: calculateCost(extractTokens(result), selection.costConfig)
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 4. Social Media Posts
|
|
197
|
+
|
|
198
|
+
**Use Case:** Social media management, engagement
|
|
199
|
+
|
|
200
|
+
**Example - Multi-Platform Posts:**
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// app/api/content/social-post/route.ts
|
|
204
|
+
export async function POST(request: NextRequest) {
|
|
205
|
+
const { topic, platforms = ['twitter', 'linkedin', 'facebook'] } = await request.json()
|
|
206
|
+
|
|
207
|
+
const selection = await selectModel('gpt-4o-mini')
|
|
208
|
+
|
|
209
|
+
const platformGuides = {
|
|
210
|
+
twitter: { limit: '280 characters', style: 'concise and witty' },
|
|
211
|
+
linkedin: { limit: '150 words', style: 'professional and insightful' },
|
|
212
|
+
facebook: { limit: '100 words', style: 'conversational and engaging' },
|
|
213
|
+
instagram: { limit: '150 words', style: 'visual and inspiring' }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const posts = {}
|
|
217
|
+
|
|
218
|
+
for (const platform of platforms) {
|
|
219
|
+
const guide = platformGuides[platform]
|
|
220
|
+
|
|
221
|
+
const result = await generateText({
|
|
222
|
+
model: selection.model,
|
|
223
|
+
system: `You are a social media expert creating ${guide.style} content for ${platform}.`,
|
|
224
|
+
prompt: `
|
|
225
|
+
Topic: ${topic}
|
|
226
|
+
Platform: ${platform}
|
|
227
|
+
Limit: ${guide.limit}
|
|
228
|
+
Style: ${guide.style}
|
|
229
|
+
|
|
230
|
+
Create an engaging post with:
|
|
231
|
+
${platform === 'twitter' ? '- Relevant hashtags (2-3)' : ''}
|
|
232
|
+
${platform === 'instagram' ? '- Relevant hashtags (5-10)' : ''}
|
|
233
|
+
${platform === 'linkedin' ? '- Professional tone' : ''}
|
|
234
|
+
- Call-to-action
|
|
235
|
+
- Platform-appropriate formatting
|
|
236
|
+
`.trim(),
|
|
237
|
+
maxOutputTokens: 200,
|
|
238
|
+
temperature: 0.8
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
posts[platform] = result.text
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return NextResponse.json({ posts })
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Batch Processing
|
|
249
|
+
|
|
250
|
+
**Generate Multiple Items at Once:**
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// app/api/content/batch-descriptions/route.ts
|
|
254
|
+
export async function POST(request: NextRequest) {
|
|
255
|
+
const { products } = await request.json() // Array of products
|
|
256
|
+
|
|
257
|
+
const selection = await selectModel('gpt-4o-mini')
|
|
258
|
+
const results = []
|
|
259
|
+
let totalCost = 0
|
|
260
|
+
|
|
261
|
+
for (const product of products) {
|
|
262
|
+
const result = await generateText({
|
|
263
|
+
model: selection.model,
|
|
264
|
+
system: 'You are a product copywriter.',
|
|
265
|
+
prompt: `Write a 50-word description for: ${product.name}`,
|
|
266
|
+
maxOutputTokens: 100
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
const tokens = extractTokens(result)
|
|
270
|
+
const cost = calculateCost(tokens, selection.costConfig)
|
|
271
|
+
totalCost += cost
|
|
272
|
+
|
|
273
|
+
results.push({
|
|
274
|
+
productId: product.id,
|
|
275
|
+
description: result.text,
|
|
276
|
+
cost
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return NextResponse.json({
|
|
281
|
+
results,
|
|
282
|
+
count: results.length,
|
|
283
|
+
totalCost
|
|
284
|
+
})
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Content Refinement
|
|
289
|
+
|
|
290
|
+
**Improve Existing Content:**
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// app/api/content/refine/route.ts
|
|
294
|
+
export async function POST(request: NextRequest) {
|
|
295
|
+
const { content, instructions = 'Improve clarity and engagement' } = await request.json()
|
|
296
|
+
|
|
297
|
+
const selection = await selectModel('claude-3-5-haiku-20241022')
|
|
298
|
+
|
|
299
|
+
const result = await generateText({
|
|
300
|
+
model: selection.model,
|
|
301
|
+
system: 'You are an expert editor improving content quality.',
|
|
302
|
+
prompt: `
|
|
303
|
+
Original Content:
|
|
304
|
+
${content}
|
|
305
|
+
|
|
306
|
+
Instructions:
|
|
307
|
+
${instructions}
|
|
308
|
+
|
|
309
|
+
Provide the refined version with improvements in:
|
|
310
|
+
- Clarity and readability
|
|
311
|
+
- Engagement and flow
|
|
312
|
+
- Grammar and style
|
|
313
|
+
- SEO optimization (if applicable)
|
|
314
|
+
`.trim(),
|
|
315
|
+
maxOutputTokens: content.split(' ').length * 2, // Allow expansion
|
|
316
|
+
temperature: 0.6
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
return NextResponse.json({
|
|
320
|
+
original: content,
|
|
321
|
+
refined: result.text,
|
|
322
|
+
improvement: 'Content refined successfully',
|
|
323
|
+
cost: calculateCost(extractTokens(result), selection.costConfig)
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Integration with Entities
|
|
329
|
+
|
|
330
|
+
**Link Generated Content to Products:**
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { AIHistoryService } from '@/contents/plugins/ai/lib/ai-history-service'
|
|
334
|
+
|
|
335
|
+
export async function generateAndSaveDescription(productId: string) {
|
|
336
|
+
// Start operation tracking
|
|
337
|
+
const historyId = await AIHistoryService.startOperation({
|
|
338
|
+
userId: session.user.id,
|
|
339
|
+
operation: 'generate',
|
|
340
|
+
model: 'gpt-4o-mini',
|
|
341
|
+
provider: 'openai',
|
|
342
|
+
relatedEntityType: 'products',
|
|
343
|
+
relatedEntityId: productId
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
// Get product data
|
|
348
|
+
const product = await getProduct(productId)
|
|
349
|
+
|
|
350
|
+
// Generate description
|
|
351
|
+
const result = await generateText({
|
|
352
|
+
model: selection.model,
|
|
353
|
+
prompt: `Write a description for: ${product.name}`
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
// Update product
|
|
357
|
+
await updateProduct(productId, {
|
|
358
|
+
description: result.text,
|
|
359
|
+
ai_generated: true
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
// Complete tracking
|
|
363
|
+
await AIHistoryService.completeOperation({
|
|
364
|
+
historyId,
|
|
365
|
+
tokensUsed: result.usage.totalTokens,
|
|
366
|
+
tokensInput: result.usage.inputTokens,
|
|
367
|
+
tokensOutput: result.usage.outputTokens,
|
|
368
|
+
creditsUsed: 0,
|
|
369
|
+
estimatedCost: cost,
|
|
370
|
+
balanceAfter: user.balance,
|
|
371
|
+
userId: session.user.id,
|
|
372
|
+
metas: {
|
|
373
|
+
contentType: 'product-description',
|
|
374
|
+
productName: product.name
|
|
375
|
+
}
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
return { description: result.text, cost }
|
|
379
|
+
} catch (error) {
|
|
380
|
+
await AIHistoryService.failOperation({
|
|
381
|
+
historyId,
|
|
382
|
+
errorMessage: error.message
|
|
383
|
+
})
|
|
384
|
+
throw error
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Cost Optimization
|
|
390
|
+
|
|
391
|
+
### 1. Use Appropriate Models
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// ✅ Development: Free local model
|
|
395
|
+
const devModel = 'llama3.2:3b'
|
|
396
|
+
|
|
397
|
+
// ✅ Production bulk: Cheap cloud model
|
|
398
|
+
const bulkModel = 'gpt-4o-mini' // $0.00015 input / $0.0006 output
|
|
399
|
+
|
|
400
|
+
// ✅ Production premium: Quality model
|
|
401
|
+
const premiumModel = 'gpt-4o' // $0.0025 input / $0.01 output
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### 2. Optimize Token Usage
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
// ✅ Good: Specific, concise prompt
|
|
408
|
+
const prompt = `Write a 50-word product description for ${productName}`
|
|
409
|
+
// Uses ~15 input tokens
|
|
410
|
+
|
|
411
|
+
// ❌ Bad: Verbose, wasteful prompt
|
|
412
|
+
const prompt = `
|
|
413
|
+
I need you to please help me write a comprehensive product description.
|
|
414
|
+
The product is called ${productName} and I would like you to make it
|
|
415
|
+
engaging and informative. Please use around 50 words or so. Thank you!
|
|
416
|
+
`
|
|
417
|
+
// Uses ~40 input tokens (2.6x more expensive)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### 3. Cache Common Prompts
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
// Cache system prompts
|
|
424
|
+
const SYSTEM_PROMPTS = {
|
|
425
|
+
productDescription: 'You are an expert e-commerce copywriter...',
|
|
426
|
+
blogPost: 'You are a professional content writer...',
|
|
427
|
+
emailSubject: 'You are a direct response copywriter...'
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Reuse across requests
|
|
431
|
+
const result = await generateText({
|
|
432
|
+
model: selection.model,
|
|
433
|
+
system: SYSTEM_PROMPTS.productDescription,
|
|
434
|
+
prompt: userPrompt
|
|
435
|
+
})
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Best Practices
|
|
439
|
+
|
|
440
|
+
1. **Validate Input** - Ensure product/content data is complete
|
|
441
|
+
2. **Set Appropriate Temperature** - Lower for factual, higher for creative
|
|
442
|
+
3. **Limit Output Tokens** - Don't over-allocate
|
|
443
|
+
4. **Track Costs** - Monitor per-generation costs
|
|
444
|
+
5. **A/B Test** - Compare models and prompts
|
|
445
|
+
6. **Save Examples** - Use `saveExample: true` for training
|
|
446
|
+
7. **Batch When Possible** - Generate multiple items efficiently
|
|
447
|
+
8. **Link to Entities** - Track which content was generated for what
|
|
448
|
+
|
|
449
|
+
## Next Steps
|
|
450
|
+
|
|
451
|
+
- **[Semantic Search](./02-semantic-search.md)** - Search content by meaning
|
|
452
|
+
- **[AI Auditing](./03-ai-auditing.md)** - Quality control with AI
|
|
453
|
+
- **[Custom Endpoints](../04-advanced-usage/02-custom-endpoints.md)** - Build specialized generators
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI History Entity Configuration
|
|
3
|
+
*
|
|
4
|
+
* Generic audit trail for all AI interactions across plugins.
|
|
5
|
+
* Tracks operations, costs, performance metrics, and results.
|
|
6
|
+
* Supports polymorphic relationships to any entity type.
|
|
7
|
+
*
|
|
8
|
+
* Updated according to new 5-section structure from refactoring plan.
|
|
9
|
+
* All table names, API paths, and metadata are now derived automatically from slug.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { History } from 'lucide-react'
|
|
13
|
+
import type { EntityConfig } from '@nextsparkjs/core/lib/entities/types'
|
|
14
|
+
import { aiHistoryFields } from './ai-history.fields'
|
|
15
|
+
|
|
16
|
+
export const aiHistoryEntityConfig: EntityConfig = {
|
|
17
|
+
// ==========================================
|
|
18
|
+
// 1. BASIC IDENTIFICATION
|
|
19
|
+
// ==========================================
|
|
20
|
+
slug: 'ai-history', // Single source of truth - derives tableName, apiPath, metaTableName, i18nNamespace
|
|
21
|
+
enabled: true,
|
|
22
|
+
names: {
|
|
23
|
+
singular: 'AI History',
|
|
24
|
+
plural: 'AI History'
|
|
25
|
+
},
|
|
26
|
+
icon: History,
|
|
27
|
+
|
|
28
|
+
// ==========================================
|
|
29
|
+
// 2. ACCESS AND SCOPE CONFIGURATION
|
|
30
|
+
// ==========================================
|
|
31
|
+
access: {
|
|
32
|
+
public: false, // Users can only see their own history
|
|
33
|
+
api: true, // Has external API via API key
|
|
34
|
+
metadata: true, // ✅ Uses ai_history_metas table for flexible metadata
|
|
35
|
+
shared: false // REQUIRED: Private data (user-owned)
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
// ==========================================
|
|
39
|
+
// 3. UI/UX FEATURES
|
|
40
|
+
// ==========================================
|
|
41
|
+
ui: {
|
|
42
|
+
dashboard: {
|
|
43
|
+
showInMenu: true,
|
|
44
|
+
showInTopbar: false
|
|
45
|
+
},
|
|
46
|
+
public: {
|
|
47
|
+
hasArchivePage: false,
|
|
48
|
+
hasSinglePage: true
|
|
49
|
+
},
|
|
50
|
+
features: {
|
|
51
|
+
searchable: true, // Search by operation, model, provider, relatedEntityType
|
|
52
|
+
sortable: true, // Sort by date, cost, status, tokens
|
|
53
|
+
filterable: true, // Filter by status, operation, provider, model
|
|
54
|
+
bulkOperations: true, // Delete multiple history items
|
|
55
|
+
importExport: false // Sensitive data, keep internal
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// ==========================================
|
|
60
|
+
// 4. PERMISSIONS SYSTEM
|
|
61
|
+
// ==========================================
|
|
62
|
+
permissions: {
|
|
63
|
+
actions: [
|
|
64
|
+
{
|
|
65
|
+
action: 'create',
|
|
66
|
+
label: 'Create AI history',
|
|
67
|
+
description: 'Can create AI history entries (typically via API)',
|
|
68
|
+
roles: ['owner', 'admin', 'member'],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
action: 'read',
|
|
72
|
+
label: 'View AI history',
|
|
73
|
+
description: 'Can view AI history entries (users see their own via API filtering)',
|
|
74
|
+
roles: ['owner', 'admin', 'member'],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
action: 'list',
|
|
78
|
+
label: 'List AI history',
|
|
79
|
+
description: 'Can list AI history entries',
|
|
80
|
+
roles: ['owner', 'admin', 'member'],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
action: 'update',
|
|
84
|
+
label: 'Edit AI history',
|
|
85
|
+
description: 'Can modify AI history entries (immutable for most users)',
|
|
86
|
+
roles: ['owner', 'admin'],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
action: 'delete',
|
|
90
|
+
label: 'Delete AI history',
|
|
91
|
+
description: 'Can delete AI history entries (users can delete their own)',
|
|
92
|
+
roles: ['owner', 'admin', 'member'],
|
|
93
|
+
dangerous: true,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// ==========================================
|
|
99
|
+
// 5. INTERNATIONALIZATION
|
|
100
|
+
// ==========================================
|
|
101
|
+
i18n: {
|
|
102
|
+
fallbackLocale: 'en',
|
|
103
|
+
loaders: {
|
|
104
|
+
es: () => import('./messages/es.json'),
|
|
105
|
+
en: () => import('./messages/en.json')
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
// ==========================================
|
|
110
|
+
// FIELDS (imported from separate file)
|
|
111
|
+
// ==========================================
|
|
112
|
+
fields: aiHistoryFields,
|
|
113
|
+
|
|
114
|
+
// ==========================================
|
|
115
|
+
// AUTOMATIC SYSTEM DERIVATIONS
|
|
116
|
+
// ==========================================
|
|
117
|
+
// The following properties are automatically derived from the slug:
|
|
118
|
+
// - tableName: 'ai_history' (slug with dashes replaced by underscores)
|
|
119
|
+
// - metaTableName: 'ai_history_metas' (tableName + '_metas')
|
|
120
|
+
// - apiPath: '/api/v1/ai-history' (slug API Route)
|
|
121
|
+
// - i18nNamespace: 'ai-history' (slug as namespace)
|
|
122
|
+
// - foreignKey in metadata: 'entityId' (generic for all entities)
|
|
123
|
+
}
|