@nextsparkjs/theme-default 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/about/business.md +49 -0
- package/about/features.json +302 -0
- package/about/team.md +79 -0
- package/api/ai/chat/stream/route.ts +212 -0
- package/api/ai/orchestrator/route.ts +226 -0
- package/api/ai/single-agent/route.ts +291 -0
- package/api/ai/usage/route.ts +122 -0
- package/blocks/benefits/component.tsx +100 -0
- package/blocks/benefits/config.ts +11 -0
- package/blocks/benefits/examples.ts +85 -0
- package/blocks/benefits/fields.ts +156 -0
- package/blocks/benefits/schema.ts +33 -0
- package/blocks/cta-section/component.tsx +100 -0
- package/blocks/cta-section/config.ts +11 -0
- package/blocks/cta-section/examples.ts +41 -0
- package/blocks/cta-section/fields.ts +89 -0
- package/blocks/cta-section/index.ts +6 -0
- package/blocks/cta-section/schema.ts +32 -0
- package/blocks/cta-section/thumbnail.png +1 -0
- package/blocks/faq-accordion/component.tsx +156 -0
- package/blocks/faq-accordion/config.ts +11 -0
- package/blocks/faq-accordion/examples.ts +77 -0
- package/blocks/faq-accordion/fields.ts +119 -0
- package/blocks/faq-accordion/index.ts +6 -0
- package/blocks/faq-accordion/schema.ts +45 -0
- package/blocks/features-grid/component.tsx +112 -0
- package/blocks/features-grid/config.ts +11 -0
- package/blocks/features-grid/examples.ts +63 -0
- package/blocks/features-grid/fields.ts +97 -0
- package/blocks/features-grid/index.ts +6 -0
- package/blocks/features-grid/schema.ts +40 -0
- package/blocks/features-grid/thumbnail.png +1 -0
- package/blocks/hero/component.tsx +100 -0
- package/blocks/hero/config.ts +11 -0
- package/blocks/hero/examples.ts +35 -0
- package/blocks/hero/fields.ts +60 -0
- package/blocks/hero/index.ts +6 -0
- package/blocks/hero/schema.ts +32 -0
- package/blocks/hero/thumbnail.png +1 -0
- package/blocks/hero/thumbnail.png.txt +6 -0
- package/blocks/hero-with-form/component.tsx +232 -0
- package/blocks/hero-with-form/config.ts +11 -0
- package/blocks/hero-with-form/examples.ts +16 -0
- package/blocks/hero-with-form/fields.ts +207 -0
- package/blocks/hero-with-form/index.ts +6 -0
- package/blocks/hero-with-form/schema.ts +54 -0
- package/blocks/jumbotron/component.tsx +136 -0
- package/blocks/jumbotron/config.ts +11 -0
- package/blocks/jumbotron/examples.ts +36 -0
- package/blocks/jumbotron/fields.ts +202 -0
- package/blocks/jumbotron/index.ts +6 -0
- package/blocks/jumbotron/schema.ts +55 -0
- package/blocks/logo-cloud/component.tsx +154 -0
- package/blocks/logo-cloud/config.ts +11 -0
- package/blocks/logo-cloud/examples.ts +34 -0
- package/blocks/logo-cloud/fields.ts +133 -0
- package/blocks/logo-cloud/index.ts +6 -0
- package/blocks/logo-cloud/schema.ts +46 -0
- package/blocks/post-content/component.tsx +197 -0
- package/blocks/post-content/config.ts +11 -0
- package/blocks/post-content/examples.ts +33 -0
- package/blocks/post-content/fields.ts +165 -0
- package/blocks/post-content/index.ts +4 -0
- package/blocks/post-content/schema.ts +46 -0
- package/blocks/pricing-table/component.tsx +154 -0
- package/blocks/pricing-table/config.ts +11 -0
- package/blocks/pricing-table/examples.ts +96 -0
- package/blocks/pricing-table/fields.ts +161 -0
- package/blocks/pricing-table/index.ts +4 -0
- package/blocks/pricing-table/schema.ts +50 -0
- package/blocks/split-content/component.tsx +135 -0
- package/blocks/split-content/config.ts +11 -0
- package/blocks/split-content/examples.ts +38 -0
- package/blocks/split-content/fields.ts +198 -0
- package/blocks/split-content/index.ts +6 -0
- package/blocks/split-content/schema.ts +67 -0
- package/blocks/stats-counter/component.tsx +124 -0
- package/blocks/stats-counter/config.ts +11 -0
- package/blocks/stats-counter/examples.ts +61 -0
- package/blocks/stats-counter/fields.ts +134 -0
- package/blocks/stats-counter/index.ts +6 -0
- package/blocks/stats-counter/schema.ts +47 -0
- package/blocks/testimonials/component.tsx +114 -0
- package/blocks/testimonials/config.ts +11 -0
- package/blocks/testimonials/examples.ts +65 -0
- package/blocks/testimonials/fields.ts +105 -0
- package/blocks/testimonials/index.ts +6 -0
- package/blocks/testimonials/schema.ts +41 -0
- package/blocks/testimonials/thumbnail.png +1 -0
- package/blocks/text-content/component.tsx +97 -0
- package/blocks/text-content/config.ts +11 -0
- package/blocks/text-content/examples.ts +30 -0
- package/blocks/text-content/fields.ts +88 -0
- package/blocks/text-content/index.ts +6 -0
- package/blocks/text-content/schema.ts +30 -0
- package/blocks/text-content/thumbnail.png +1 -0
- package/blocks/timeline/component.tsx +267 -0
- package/blocks/timeline/config.ts +11 -0
- package/blocks/timeline/examples.ts +68 -0
- package/blocks/timeline/fields.ts +147 -0
- package/blocks/timeline/index.ts +6 -0
- package/blocks/timeline/schema.ts +49 -0
- package/blocks/video-hero/component.tsx +270 -0
- package/blocks/video-hero/config.ts +11 -0
- package/blocks/video-hero/examples.ts +24 -0
- package/blocks/video-hero/fields.ts +98 -0
- package/blocks/video-hero/index.ts +6 -0
- package/blocks/video-hero/schema.ts +39 -0
- package/components/ai-chat/ChatPanel.tsx +575 -0
- package/components/ai-chat/ConversationItem.tsx +266 -0
- package/components/ai-chat/ConversationSidebar.tsx +99 -0
- package/components/ai-chat/MarkdownRenderer.tsx +15 -0
- package/components/ai-chat/Message.tsx +42 -0
- package/components/ai-chat/MessageInput.tsx +49 -0
- package/components/ai-chat/MessageList.tsx +46 -0
- package/components/ai-chat/TypingIndicator.tsx +11 -0
- package/config/app.config.ts +367 -0
- package/config/billing.config.ts +349 -0
- package/config/dashboard.config.ts +506 -0
- package/config/dev.config.ts +104 -0
- package/config/features.config.ts +203 -0
- package/config/flows.config.ts +129 -0
- package/config/permissions.config.ts +245 -0
- package/config/theme.config.ts +74 -0
- package/docs/01-overview/01-introduction.md +335 -0
- package/docs/01-overview/02-customization.md +671 -0
- package/docs/02-features/01-components.md +155 -0
- package/docs/02-features/02-styling.md +139 -0
- package/docs/02-features/03-tasks-entity.md +407 -0
- package/docs/03-ai/01-overview.md +211 -0
- package/docs/03-ai/02-customization.md +436 -0
- package/entities/customers/customers.config.ts +75 -0
- package/entities/customers/customers.fields.ts +165 -0
- package/entities/customers/customers.service.ts +516 -0
- package/entities/customers/customers.types.ts +83 -0
- package/entities/customers/messages/en.json +66 -0
- package/entities/customers/messages/es.json +66 -0
- package/entities/customers/migrations/001_customers_table.sql +102 -0
- package/entities/customers/migrations/002_customers_metas.sql +92 -0
- package/entities/pages/messages/en.json +41 -0
- package/entities/pages/messages/es.json +41 -0
- package/entities/pages/migrations/001_pages_table.sql +112 -0
- package/entities/pages/migrations/002_pages_metas.sql +56 -0
- package/entities/pages/migrations/003_add_status.sql +50 -0
- package/entities/pages/pages-management.service.ts +610 -0
- package/entities/pages/pages.config.ts +94 -0
- package/entities/pages/pages.fields.ts +101 -0
- package/entities/pages/pages.service.ts +290 -0
- package/entities/pages/pages.types.ts +124 -0
- package/entities/posts/components/post-header.tsx +97 -0
- package/entities/posts/messages/en.json +55 -0
- package/entities/posts/messages/es.json +55 -0
- package/entities/posts/migrations/001_posts_table.sql +115 -0
- package/entities/posts/migrations/003_add_status.sql +44 -0
- package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
- package/entities/posts/migrations/006_posts_metas.sql +56 -0
- package/entities/posts/posts.config.ts +101 -0
- package/entities/posts/posts.fields.ts +116 -0
- package/entities/posts/posts.service.ts +376 -0
- package/entities/posts/posts.types.ts +74 -0
- package/entities/tasks/messages/en.json +204 -0
- package/entities/tasks/messages/es.json +204 -0
- package/entities/tasks/migrations/001_tasks_table.sql +105 -0
- package/entities/tasks/migrations/002_task_metas.sql +85 -0
- package/entities/tasks/migrations/sample_data.json +77 -0
- package/entities/tasks/tasks.config.ts +79 -0
- package/entities/tasks/tasks.fields.ts +196 -0
- package/entities/tasks/tasks.service.ts +541 -0
- package/entities/tasks/tasks.types.ts +56 -0
- package/lib/hooks/useAiChat.ts +114 -0
- package/lib/hooks/useConversations.ts +376 -0
- package/lib/hooks/useOrchestratorChat.ts +122 -0
- package/lib/hooks/usePersistentChat.ts +315 -0
- package/lib/hooks/useStreamingChat.ts +127 -0
- package/lib/hooks/useTokenUsage.ts +63 -0
- package/lib/langchain/agents/customer-assistant.md +69 -0
- package/lib/langchain/agents/index.ts +61 -0
- package/lib/langchain/agents/orchestrator.md +59 -0
- package/lib/langchain/agents/page-assistant.md +85 -0
- package/lib/langchain/agents/single-agent.md +46 -0
- package/lib/langchain/agents/task-assistant.md +55 -0
- package/lib/langchain/config.ts +45 -0
- package/lib/langchain/handlers/customer-handler.ts +338 -0
- package/lib/langchain/handlers/page-handler.ts +232 -0
- package/lib/langchain/handlers/task-handler.ts +323 -0
- package/lib/langchain/langchain.config.ts +223 -0
- package/lib/langchain/observability.config.ts +30 -0
- package/lib/langchain/orchestrator.ts +562 -0
- package/lib/langchain/tools/customers.ts +176 -0
- package/lib/langchain/tools/index.ts +10 -0
- package/lib/langchain/tools/orchestrator.ts +92 -0
- package/lib/langchain/tools/pages.ts +289 -0
- package/lib/langchain/tools/tasks.ts +167 -0
- package/lib/scheduled-actions/billing.ts +149 -0
- package/lib/scheduled-actions/index.ts +170 -0
- package/lib/scheduled-actions/webhook.ts +231 -0
- package/lib/selectors.ts +197 -0
- package/messages/de/admin.json +219 -0
- package/messages/de/aiUsage.json +36 -0
- package/messages/de/buttons.json +19 -0
- package/messages/de/categories.json +35 -0
- package/messages/de/common.json +16 -0
- package/messages/de/dev.json +101 -0
- package/messages/de/docs.json +27 -0
- package/messages/de/entities.json +7 -0
- package/messages/de/features.json +119 -0
- package/messages/de/footer.json +22 -0
- package/messages/de/home.json +57 -0
- package/messages/de/index.ts +39 -0
- package/messages/de/mobileNav.json +13 -0
- package/messages/de/navigation.json +8 -0
- package/messages/de/observability.json +74 -0
- package/messages/de/posts.json +54 -0
- package/messages/de/pricing.json +102 -0
- package/messages/de/support.json +9 -0
- package/messages/de/teams.json +8 -0
- package/messages/en/admin.json +219 -0
- package/messages/en/aiUsage.json +36 -0
- package/messages/en/buttons.json +19 -0
- package/messages/en/categories.json +35 -0
- package/messages/en/common.json +16 -0
- package/messages/en/dev.json +106 -0
- package/messages/en/docs.json +27 -0
- package/messages/en/entities.json +7 -0
- package/messages/en/features.json +119 -0
- package/messages/en/footer.json +22 -0
- package/messages/en/home.json +57 -0
- package/messages/en/index.ts +39 -0
- package/messages/en/mobileNav.json +13 -0
- package/messages/en/navigation.json +8 -0
- package/messages/en/observability.json +74 -0
- package/messages/en/posts.json +54 -0
- package/messages/en/pricing.json +102 -0
- package/messages/en/support.json +9 -0
- package/messages/en/teams.json +8 -0
- package/messages/es/admin.json +219 -0
- package/messages/es/aiUsage.json +36 -0
- package/messages/es/buttons.json +19 -0
- package/messages/es/categories.json +35 -0
- package/messages/es/common.json +16 -0
- package/messages/es/dev.json +101 -0
- package/messages/es/docs.json +27 -0
- package/messages/es/entities.json +7 -0
- package/messages/es/features.json +119 -0
- package/messages/es/footer.json +22 -0
- package/messages/es/home.json +57 -0
- package/messages/es/index.ts +39 -0
- package/messages/es/mobileNav.json +13 -0
- package/messages/es/navigation.json +8 -0
- package/messages/es/observability.json +74 -0
- package/messages/es/posts.json +54 -0
- package/messages/es/pricing.json +102 -0
- package/messages/es/support.json +9 -0
- package/messages/es/teams.json +8 -0
- package/messages/fr/admin.json +219 -0
- package/messages/fr/aiUsage.json +36 -0
- package/messages/fr/buttons.json +19 -0
- package/messages/fr/categories.json +35 -0
- package/messages/fr/common.json +16 -0
- package/messages/fr/dev.json +101 -0
- package/messages/fr/docs.json +27 -0
- package/messages/fr/entities.json +7 -0
- package/messages/fr/features.json +119 -0
- package/messages/fr/footer.json +22 -0
- package/messages/fr/home.json +57 -0
- package/messages/fr/index.ts +39 -0
- package/messages/fr/mobileNav.json +13 -0
- package/messages/fr/navigation.json +8 -0
- package/messages/fr/observability.json +74 -0
- package/messages/fr/posts.json +54 -0
- package/messages/fr/pricing.json +102 -0
- package/messages/fr/support.json +9 -0
- package/messages/fr/teams.json +8 -0
- package/messages/it/admin.json +219 -0
- package/messages/it/aiUsage.json +36 -0
- package/messages/it/buttons.json +19 -0
- package/messages/it/categories.json +35 -0
- package/messages/it/common.json +16 -0
- package/messages/it/dev.json +101 -0
- package/messages/it/docs.json +27 -0
- package/messages/it/entities.json +7 -0
- package/messages/it/features.json +119 -0
- package/messages/it/footer.json +22 -0
- package/messages/it/home.json +57 -0
- package/messages/it/index.ts +39 -0
- package/messages/it/mobileNav.json +13 -0
- package/messages/it/navigation.json +8 -0
- package/messages/it/observability.json +74 -0
- package/messages/it/posts.json +54 -0
- package/messages/it/pricing.json +102 -0
- package/messages/it/support.json +9 -0
- package/messages/it/teams.json +8 -0
- package/messages/pt/admin.json +219 -0
- package/messages/pt/aiUsage.json +36 -0
- package/messages/pt/buttons.json +19 -0
- package/messages/pt/categories.json +35 -0
- package/messages/pt/common.json +16 -0
- package/messages/pt/dev.json +101 -0
- package/messages/pt/docs.json +27 -0
- package/messages/pt/entities.json +7 -0
- package/messages/pt/features.json +119 -0
- package/messages/pt/footer.json +22 -0
- package/messages/pt/home.json +57 -0
- package/messages/pt/index.ts +39 -0
- package/messages/pt/mobileNav.json +13 -0
- package/messages/pt/navigation.json +8 -0
- package/messages/pt/observability.json +74 -0
- package/messages/pt/posts.json +54 -0
- package/messages/pt/pricing.json +102 -0
- package/messages/pt/support.json +9 -0
- package/messages/pt/teams.json +8 -0
- package/migrations/089_add_editor_team_role.sql +39 -0
- package/migrations/090_demo_users_teams.sql +540 -0
- package/migrations/091_greek_teams_billing.sql +523 -0
- package/migrations/092_billing_sample_data.sql +774 -0
- package/migrations/093_pages_sample_data.sql +1158 -0
- package/migrations/094_posts_sample_data.sql +278 -0
- package/migrations/095_tasks_sample_data.sql +440 -0
- package/migrations/096_customers_sample_data.sql +358 -0
- package/migrations/097_scheduled_actions_sample_data.sql +111 -0
- package/package.json +22 -0
- package/public/docs/desktop-layout-example.png +0 -0
- package/styles/components.css +11 -0
- package/styles/globals.css +179 -0
- package/templates/(public)/blog/[slug]/page.tsx +65 -0
- package/templates/(public)/layout.tsx +25 -0
- package/templates/(public)/page.tsx +200 -0
- package/templates/(public)/support/page.tsx +321 -0
- package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
- package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
- package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
- package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
- package/templates/superadmin/ai-observability/page.tsx +17 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
4
|
+
import { processMessage } from '@/themes/default/lib/langchain/orchestrator'
|
|
5
|
+
import { dbMemoryStore } from '@/plugins/langchain/lib/db-memory-store'
|
|
6
|
+
import type { ChatMessage } from '@/plugins/langchain/types/langchain.types'
|
|
7
|
+
|
|
8
|
+
// Schema for request validation
|
|
9
|
+
const ChatRequestSchema = z.object({
|
|
10
|
+
message: z.string().min(1).max(2000),
|
|
11
|
+
sessionId: z.string().optional(),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
// Helper to convert messages to API format
|
|
15
|
+
// Filters out internal messages (tool results, system messages, empty AI messages)
|
|
16
|
+
function convertToApiMessages(
|
|
17
|
+
messages: Awaited<ReturnType<typeof dbMemoryStore.getMessages>>
|
|
18
|
+
): ChatMessage[] {
|
|
19
|
+
return messages
|
|
20
|
+
.filter((msg) => {
|
|
21
|
+
const type = msg._getType()
|
|
22
|
+
|
|
23
|
+
// Skip tool messages - these are internal tool results (e.g., API responses)
|
|
24
|
+
if (type === 'tool') return false
|
|
25
|
+
|
|
26
|
+
// Skip system messages - these are internal prompts
|
|
27
|
+
if (type === 'system') return false
|
|
28
|
+
|
|
29
|
+
// Skip AI messages with empty content (these are tool_call-only messages)
|
|
30
|
+
if (type === 'ai') {
|
|
31
|
+
const content = msg.content
|
|
32
|
+
if (!content || (typeof content === 'string' && content.trim() === '')) {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return true
|
|
38
|
+
})
|
|
39
|
+
.map((msg, index) => ({
|
|
40
|
+
id: `msg-${index}`,
|
|
41
|
+
role: msg._getType() === 'human' ? 'user' : 'assistant',
|
|
42
|
+
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
|
|
43
|
+
timestamp: Date.now() - (messages.length - index) * 1000,
|
|
44
|
+
}))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* AI Chat Endpoint for Default Theme
|
|
49
|
+
*
|
|
50
|
+
* Uses the Multi-Agent Orchestrator pattern:
|
|
51
|
+
* - Orchestrator analyzes user intent and routes to specialized agents
|
|
52
|
+
* - Sub-agents: task-assistant, customer-assistant, page-assistant
|
|
53
|
+
* - Handles ambiguous requests by asking for clarification
|
|
54
|
+
*/
|
|
55
|
+
export async function POST(req: NextRequest) {
|
|
56
|
+
try {
|
|
57
|
+
// 1. Dual authentication (API key or session)
|
|
58
|
+
const authResult = await authenticateRequest(req)
|
|
59
|
+
if (!authResult.success || !authResult.user) {
|
|
60
|
+
return NextResponse.json(
|
|
61
|
+
{ success: false, error: 'Unauthorized' },
|
|
62
|
+
{ status: 401 }
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 2. Team context required for team-scoped operations
|
|
67
|
+
const teamId = req.headers.get('x-team-id')
|
|
68
|
+
if (!teamId) {
|
|
69
|
+
return NextResponse.json(
|
|
70
|
+
{ success: false, error: 'Team context required', code: 'TEAM_CONTEXT_REQUIRED' },
|
|
71
|
+
{ status: 400 }
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const userId = authResult.user.id
|
|
76
|
+
const userName = authResult.user.name || 'system'
|
|
77
|
+
|
|
78
|
+
// 3. Parse and Validate Request
|
|
79
|
+
const body = await req.json()
|
|
80
|
+
const result = ChatRequestSchema.safeParse(body)
|
|
81
|
+
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
return NextResponse.json(
|
|
84
|
+
{ success: false, error: 'Invalid request', details: result.error.issues },
|
|
85
|
+
{ status: 400 }
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { message, sessionId: requestedSessionId } = result.data
|
|
90
|
+
|
|
91
|
+
// Generate a session ID if not provided
|
|
92
|
+
const sessionId = requestedSessionId || crypto.randomUUID()
|
|
93
|
+
|
|
94
|
+
// 4. Process through orchestrator (routes to appropriate sub-agent)
|
|
95
|
+
// Uses graph-based orchestrator if LANGCHAIN_USE_GRAPH_ORCHESTRATOR=true
|
|
96
|
+
const response = await processMessage(message, {
|
|
97
|
+
userId,
|
|
98
|
+
teamId,
|
|
99
|
+
sessionId,
|
|
100
|
+
userName,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// 5. Return Response
|
|
104
|
+
return NextResponse.json({
|
|
105
|
+
success: true,
|
|
106
|
+
data: {
|
|
107
|
+
message: response.content,
|
|
108
|
+
sessionId: response.sessionId,
|
|
109
|
+
agentUsed: response.agentUsed,
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error('AI Chat Error:', error)
|
|
114
|
+
return NextResponse.json(
|
|
115
|
+
{ success: false, error: 'Internal Server Error' },
|
|
116
|
+
{ status: 500 }
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* GET - Retrieve conversation history
|
|
123
|
+
*/
|
|
124
|
+
export async function GET(req: NextRequest) {
|
|
125
|
+
const { searchParams } = new URL(req.url)
|
|
126
|
+
const sessionId = searchParams.get('sessionId')
|
|
127
|
+
|
|
128
|
+
// Return API documentation if no sessionId
|
|
129
|
+
if (!sessionId) {
|
|
130
|
+
return NextResponse.json({
|
|
131
|
+
endpoint: '/api/v1/theme/default/ai/orchestrator',
|
|
132
|
+
description: 'Multi-agent orchestrator - routes to specialized sub-agents',
|
|
133
|
+
methods: {
|
|
134
|
+
POST: {
|
|
135
|
+
description: 'Send message to AI assistant',
|
|
136
|
+
body: { message: 'string', sessionId: 'string (optional)' },
|
|
137
|
+
},
|
|
138
|
+
GET: {
|
|
139
|
+
description: 'Get conversation history',
|
|
140
|
+
query: { sessionId: 'string (required)' },
|
|
141
|
+
},
|
|
142
|
+
DELETE: {
|
|
143
|
+
description: 'Clear conversation',
|
|
144
|
+
body: { sessionId: 'string (required)' },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Auth
|
|
151
|
+
const authResult = await authenticateRequest(req)
|
|
152
|
+
if (!authResult.success || !authResult.user) {
|
|
153
|
+
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const teamId = req.headers.get('x-team-id')
|
|
157
|
+
if (!teamId) {
|
|
158
|
+
return NextResponse.json(
|
|
159
|
+
{ success: false, error: 'Team context required' },
|
|
160
|
+
{ status: 400 }
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const context = { userId: authResult.user.id, teamId }
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const session = await dbMemoryStore.getSession(sessionId, context)
|
|
168
|
+
if (!session) {
|
|
169
|
+
return NextResponse.json({ success: false, error: 'Conversation not found' }, { status: 404 })
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const messages = await dbMemoryStore.getMessages(sessionId, context)
|
|
173
|
+
|
|
174
|
+
return NextResponse.json({
|
|
175
|
+
success: true,
|
|
176
|
+
data: {
|
|
177
|
+
sessionId,
|
|
178
|
+
name: session.name,
|
|
179
|
+
messageCount: session.messageCount,
|
|
180
|
+
isPinned: session.isPinned,
|
|
181
|
+
messages: convertToApiMessages(messages),
|
|
182
|
+
},
|
|
183
|
+
})
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error('[AI Chat] Get history error:', error)
|
|
186
|
+
return NextResponse.json({ success: false, error: 'Failed to get history' }, { status: 500 })
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* DELETE - Clear conversation
|
|
192
|
+
*/
|
|
193
|
+
export async function DELETE(req: NextRequest) {
|
|
194
|
+
const authResult = await authenticateRequest(req)
|
|
195
|
+
if (!authResult.success || !authResult.user) {
|
|
196
|
+
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const teamId = req.headers.get('x-team-id')
|
|
200
|
+
if (!teamId) {
|
|
201
|
+
return NextResponse.json(
|
|
202
|
+
{ success: false, error: 'Team context required' },
|
|
203
|
+
{ status: 400 }
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const body = await req.json().catch(() => ({}))
|
|
209
|
+
const sessionId = body.sessionId
|
|
210
|
+
|
|
211
|
+
if (!sessionId) {
|
|
212
|
+
return NextResponse.json({ success: false, error: 'sessionId required' }, { status: 400 })
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
await dbMemoryStore.clearSession(sessionId, { userId: authResult.user.id, teamId })
|
|
216
|
+
|
|
217
|
+
return NextResponse.json({
|
|
218
|
+
success: true,
|
|
219
|
+
message: 'Conversation cleared',
|
|
220
|
+
sessionId,
|
|
221
|
+
})
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('[AI Chat] Clear error:', error)
|
|
224
|
+
return NextResponse.json({ success: false, error: 'Failed to clear' }, { status: 500 })
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* SINGLE AGENT CHAT ENDPOINT
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* A unified agent with access to ALL entity tools (tasks, customers, pages).
|
|
7
|
+
* Use this for simpler interactions where routing overhead is unnecessary.
|
|
8
|
+
*
|
|
9
|
+
* Configuration is centralized in: config/langchain.config.ts
|
|
10
|
+
*
|
|
11
|
+
* ============================================================================
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
15
|
+
import { z } from 'zod'
|
|
16
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
17
|
+
import { createAgent } from '@/plugins/langchain/lib/agent-factory'
|
|
18
|
+
import {
|
|
19
|
+
dbMemoryStore,
|
|
20
|
+
CONVERSATION_LIMITS,
|
|
21
|
+
generateSessionId,
|
|
22
|
+
} from '@/plugins/langchain/lib/db-memory-store'
|
|
23
|
+
import {
|
|
24
|
+
getAgentConfig,
|
|
25
|
+
getAgentModelConfig,
|
|
26
|
+
getAgentTools,
|
|
27
|
+
getAgentPromptName,
|
|
28
|
+
} from '@/themes/default/lib/langchain/langchain.config'
|
|
29
|
+
import { loadSystemPrompt } from '@/themes/default/lib/langchain/agents'
|
|
30
|
+
import type { ChatMessage } from '@/plugins/langchain/types/langchain.types'
|
|
31
|
+
|
|
32
|
+
// Agent name - matches key in AGENTS config
|
|
33
|
+
const AGENT_NAME = 'single-agent'
|
|
34
|
+
|
|
35
|
+
// Request validation
|
|
36
|
+
const ChatRequestSchema = z.object({
|
|
37
|
+
message: z.string().min(1).max(2000),
|
|
38
|
+
sessionId: z.string().optional(),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Helper to convert messages to API format
|
|
42
|
+
// Filters out internal messages (tool results, system messages, empty AI messages)
|
|
43
|
+
function convertToApiMessages(
|
|
44
|
+
messages: Awaited<ReturnType<typeof dbMemoryStore.getMessages>>
|
|
45
|
+
): ChatMessage[] {
|
|
46
|
+
return messages
|
|
47
|
+
.filter((msg) => {
|
|
48
|
+
const type = msg._getType()
|
|
49
|
+
|
|
50
|
+
// Skip tool messages - these are internal tool results (e.g., API responses)
|
|
51
|
+
if (type === 'tool') return false
|
|
52
|
+
|
|
53
|
+
// Skip system messages - these are internal prompts
|
|
54
|
+
if (type === 'system') return false
|
|
55
|
+
|
|
56
|
+
// Skip AI messages with empty content (these are tool_call-only messages)
|
|
57
|
+
if (type === 'ai') {
|
|
58
|
+
const content = msg.content
|
|
59
|
+
if (!content || (typeof content === 'string' && content.trim() === '')) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return true
|
|
65
|
+
})
|
|
66
|
+
.map((msg, index) => ({
|
|
67
|
+
id: `msg-${index}`,
|
|
68
|
+
role: msg._getType() === 'human' ? 'user' : 'assistant',
|
|
69
|
+
content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
|
|
70
|
+
timestamp: Date.now() - (messages.length - index) * 1000,
|
|
71
|
+
}))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* POST - Send message to single agent
|
|
76
|
+
*/
|
|
77
|
+
export async function POST(req: NextRequest) {
|
|
78
|
+
try {
|
|
79
|
+
// 1. Authentication
|
|
80
|
+
const authResult = await authenticateRequest(req)
|
|
81
|
+
if (!authResult.success || !authResult.user) {
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{ success: false, error: 'Unauthorized' },
|
|
84
|
+
{ status: 401 }
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. Team context
|
|
89
|
+
const teamId = req.headers.get('x-team-id')
|
|
90
|
+
if (!teamId) {
|
|
91
|
+
return NextResponse.json(
|
|
92
|
+
{ success: false, error: 'Team context required', code: 'TEAM_CONTEXT_REQUIRED' },
|
|
93
|
+
{ status: 400 }
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const userId = authResult.user.id
|
|
98
|
+
const userName = authResult.user.name || 'system'
|
|
99
|
+
const context = { userId, teamId, userName }
|
|
100
|
+
|
|
101
|
+
// 3. Validate request
|
|
102
|
+
const body = await req.json()
|
|
103
|
+
const validation = ChatRequestSchema.safeParse(body)
|
|
104
|
+
|
|
105
|
+
if (!validation.success) {
|
|
106
|
+
return NextResponse.json(
|
|
107
|
+
{ success: false, error: 'Invalid request', details: validation.error.issues },
|
|
108
|
+
{ status: 400 }
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const { message, sessionId: requestedSessionId } = validation.data
|
|
113
|
+
|
|
114
|
+
// 4. Session management
|
|
115
|
+
let sessionId: string
|
|
116
|
+
let isNewSession = false
|
|
117
|
+
|
|
118
|
+
if (requestedSessionId) {
|
|
119
|
+
sessionId = requestedSessionId
|
|
120
|
+
const existingSession = await dbMemoryStore.getSession(sessionId, context)
|
|
121
|
+
isNewSession = !existingSession
|
|
122
|
+
} else {
|
|
123
|
+
sessionId = generateSessionId(userId)
|
|
124
|
+
isNewSession = true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 5. Check conversation limits
|
|
128
|
+
if (isNewSession) {
|
|
129
|
+
const currentCount = await dbMemoryStore.countSessions(context)
|
|
130
|
+
|
|
131
|
+
if (currentCount >= CONVERSATION_LIMITS.MAX_CONVERSATIONS) {
|
|
132
|
+
return NextResponse.json({
|
|
133
|
+
success: false,
|
|
134
|
+
error: 'CONVERSATION_LIMIT_REACHED',
|
|
135
|
+
message: `Maximum of ${CONVERSATION_LIMITS.MAX_CONVERSATIONS} conversations reached.`,
|
|
136
|
+
data: { currentCount, maxAllowed: CONVERSATION_LIMITS.MAX_CONVERSATIONS },
|
|
137
|
+
}, { status: 400 })
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 6. Get agent configuration from centralized config
|
|
142
|
+
const agentConfig = getAgentConfig(AGENT_NAME)
|
|
143
|
+
if (!agentConfig) {
|
|
144
|
+
throw new Error(`Agent '${AGENT_NAME}' not configured`)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 7. Load system prompt from .md file
|
|
148
|
+
const promptName = getAgentPromptName(AGENT_NAME)
|
|
149
|
+
if (!promptName) {
|
|
150
|
+
throw new Error(`No system prompt configured for agent '${AGENT_NAME}'`)
|
|
151
|
+
}
|
|
152
|
+
const systemPrompt = loadSystemPrompt(promptName as any)
|
|
153
|
+
|
|
154
|
+
// 8. Create agent with config from langchain.config.ts
|
|
155
|
+
const agent = await createAgent({
|
|
156
|
+
sessionId,
|
|
157
|
+
agentName: AGENT_NAME,
|
|
158
|
+
context,
|
|
159
|
+
systemPrompt,
|
|
160
|
+
tools: getAgentTools(AGENT_NAME, context),
|
|
161
|
+
modelConfig: getAgentModelConfig(AGENT_NAME),
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// 9. Process message
|
|
165
|
+
const response = await agent.chat(message)
|
|
166
|
+
|
|
167
|
+
return NextResponse.json({
|
|
168
|
+
success: true,
|
|
169
|
+
data: {
|
|
170
|
+
message: response.content,
|
|
171
|
+
sessionId: response.sessionId,
|
|
172
|
+
isNewSession,
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.error('[Single Agent] Error:', error)
|
|
177
|
+
return NextResponse.json(
|
|
178
|
+
{ success: false, error: 'Failed to process message' },
|
|
179
|
+
{ status: 500 }
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* GET - Retrieve conversation history
|
|
186
|
+
*/
|
|
187
|
+
export async function GET(req: NextRequest) {
|
|
188
|
+
const { searchParams } = new URL(req.url)
|
|
189
|
+
const sessionId = searchParams.get('sessionId')
|
|
190
|
+
|
|
191
|
+
// Return API documentation if no sessionId
|
|
192
|
+
if (!sessionId) {
|
|
193
|
+
const agentConfig = getAgentConfig(AGENT_NAME)
|
|
194
|
+
return NextResponse.json({
|
|
195
|
+
endpoint: '/api/v1/theme/default/ai/single-agent',
|
|
196
|
+
description: agentConfig?.description || 'Single agent chat endpoint',
|
|
197
|
+
agent: AGENT_NAME,
|
|
198
|
+
methods: {
|
|
199
|
+
POST: {
|
|
200
|
+
description: 'Send message to AI assistant',
|
|
201
|
+
body: { message: 'string', sessionId: 'string (optional)' },
|
|
202
|
+
},
|
|
203
|
+
GET: {
|
|
204
|
+
description: 'Get conversation history',
|
|
205
|
+
query: { sessionId: 'string (required)' },
|
|
206
|
+
},
|
|
207
|
+
DELETE: {
|
|
208
|
+
description: 'Clear conversation',
|
|
209
|
+
body: { sessionId: 'string (required)' },
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Auth
|
|
216
|
+
const authResult = await authenticateRequest(req)
|
|
217
|
+
if (!authResult.success || !authResult.user) {
|
|
218
|
+
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const teamId = req.headers.get('x-team-id')
|
|
222
|
+
if (!teamId) {
|
|
223
|
+
return NextResponse.json(
|
|
224
|
+
{ success: false, error: 'Team context required' },
|
|
225
|
+
{ status: 400 }
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const context = { userId: authResult.user.id, teamId }
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const session = await dbMemoryStore.getSession(sessionId, context)
|
|
233
|
+
if (!session) {
|
|
234
|
+
return NextResponse.json({ success: false, error: 'Conversation not found' }, { status: 404 })
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const messages = await dbMemoryStore.getMessages(sessionId, context)
|
|
238
|
+
|
|
239
|
+
return NextResponse.json({
|
|
240
|
+
success: true,
|
|
241
|
+
data: {
|
|
242
|
+
sessionId,
|
|
243
|
+
name: session.name,
|
|
244
|
+
messageCount: session.messageCount,
|
|
245
|
+
isPinned: session.isPinned,
|
|
246
|
+
messages: convertToApiMessages(messages),
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('[Single Agent] Get history error:', error)
|
|
251
|
+
return NextResponse.json({ success: false, error: 'Failed to get history' }, { status: 500 })
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* DELETE - Clear conversation
|
|
257
|
+
*/
|
|
258
|
+
export async function DELETE(req: NextRequest) {
|
|
259
|
+
const authResult = await authenticateRequest(req)
|
|
260
|
+
if (!authResult.success || !authResult.user) {
|
|
261
|
+
return NextResponse.json({ success: false, error: 'Unauthorized' }, { status: 401 })
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const teamId = req.headers.get('x-team-id')
|
|
265
|
+
if (!teamId) {
|
|
266
|
+
return NextResponse.json(
|
|
267
|
+
{ success: false, error: 'Team context required' },
|
|
268
|
+
{ status: 400 }
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const body = await req.json().catch(() => ({}))
|
|
274
|
+
const sessionId = body.sessionId
|
|
275
|
+
|
|
276
|
+
if (!sessionId) {
|
|
277
|
+
return NextResponse.json({ success: false, error: 'sessionId required' }, { status: 400 })
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
await dbMemoryStore.clearSession(sessionId, { userId: authResult.user.id, teamId })
|
|
281
|
+
|
|
282
|
+
return NextResponse.json({
|
|
283
|
+
success: true,
|
|
284
|
+
message: 'Conversation cleared',
|
|
285
|
+
sessionId,
|
|
286
|
+
})
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error('[Single Agent] Clear error:', error)
|
|
289
|
+
return NextResponse.json({ success: false, error: 'Failed to clear' }, { status: 500 })
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* TOKEN USAGE API ENDPOINT
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* Provides token usage statistics for users and teams.
|
|
7
|
+
* Supports different time periods and user/team views.
|
|
8
|
+
*
|
|
9
|
+
* GET /api/ai/usage
|
|
10
|
+
* Query params:
|
|
11
|
+
* - period: 'today' | '7d' | '30d' | 'all' (default: '30d')
|
|
12
|
+
* - type: 'user' | 'team' (default: 'user')
|
|
13
|
+
*
|
|
14
|
+
* Returns:
|
|
15
|
+
* - stats: Aggregated usage statistics
|
|
16
|
+
* - daily: Daily breakdown for charts
|
|
17
|
+
*
|
|
18
|
+
* ============================================================================
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
22
|
+
import { authenticateRequest } from '@nextsparkjs/core/lib/api/auth/dual-auth'
|
|
23
|
+
import { tokenTracker } from '@/plugins/langchain/lib/token-tracker'
|
|
24
|
+
import { queryOne } from '@nextsparkjs/core/lib/db'
|
|
25
|
+
|
|
26
|
+
type Period = 'today' | '7d' | '30d' | 'all'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* GET - Retrieve token usage statistics
|
|
30
|
+
*/
|
|
31
|
+
export async function GET(request: NextRequest) {
|
|
32
|
+
try {
|
|
33
|
+
// 1. Authentication
|
|
34
|
+
const authResult = await authenticateRequest(request)
|
|
35
|
+
if (!authResult.success || !authResult.user) {
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ success: false, error: 'Unauthorized' },
|
|
38
|
+
{ status: 401 }
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 2. Team context
|
|
43
|
+
const teamId = request.headers.get('x-team-id')
|
|
44
|
+
if (!teamId) {
|
|
45
|
+
return NextResponse.json(
|
|
46
|
+
{ success: false, error: 'Team context required', code: 'TEAM_CONTEXT_REQUIRED' },
|
|
47
|
+
{ status: 400 }
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const userId = authResult.user.id
|
|
52
|
+
|
|
53
|
+
// 3. Parse query parameters
|
|
54
|
+
const { searchParams } = new URL(request.url)
|
|
55
|
+
const period = (searchParams.get('period') || '30d') as Period
|
|
56
|
+
const type = searchParams.get('type') || 'user'
|
|
57
|
+
|
|
58
|
+
// Validate period
|
|
59
|
+
if (!['today', '7d', '30d', 'all'].includes(period)) {
|
|
60
|
+
return NextResponse.json(
|
|
61
|
+
{ success: false, error: 'Invalid period. Must be: today, 7d, 30d, or all' },
|
|
62
|
+
{ status: 400 }
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 4. Check permissions for team usage
|
|
67
|
+
if (type === 'team') {
|
|
68
|
+
// Get user's team role to check if they're admin
|
|
69
|
+
const teamMembership = await queryOne<{ role: string }>(
|
|
70
|
+
`SELECT role FROM public.team_members
|
|
71
|
+
WHERE "userId" = $1 AND "teamId" = $2`,
|
|
72
|
+
[userId, teamId]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const isTeamAdmin = teamMembership?.role === 'admin' || teamMembership?.role === 'owner'
|
|
76
|
+
|
|
77
|
+
if (!isTeamAdmin) {
|
|
78
|
+
return NextResponse.json(
|
|
79
|
+
{ success: false, error: 'Team usage requires admin permissions' },
|
|
80
|
+
{ status: 403 }
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const context = {
|
|
86
|
+
userId,
|
|
87
|
+
teamId,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 5. Fetch usage statistics
|
|
91
|
+
const [stats, daily] = await Promise.all([
|
|
92
|
+
type === 'team'
|
|
93
|
+
? tokenTracker.getTeamUsage(teamId, period)
|
|
94
|
+
: tokenTracker.getUsage(context, period),
|
|
95
|
+
tokenTracker.getDailyUsage(
|
|
96
|
+
context,
|
|
97
|
+
period === 'all' ? 365 : period === '30d' ? 30 : period === '7d' ? 7 : 1
|
|
98
|
+
),
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
// 6. Return results
|
|
102
|
+
return NextResponse.json({
|
|
103
|
+
success: true,
|
|
104
|
+
data: {
|
|
105
|
+
stats,
|
|
106
|
+
daily,
|
|
107
|
+
period,
|
|
108
|
+
type,
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('[AI Usage] Error:', error)
|
|
113
|
+
return NextResponse.json(
|
|
114
|
+
{
|
|
115
|
+
success: false,
|
|
116
|
+
error: 'Failed to retrieve usage statistics',
|
|
117
|
+
details: error instanceof Error ? error.message : 'Unknown error',
|
|
118
|
+
},
|
|
119
|
+
{ status: 500 }
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
}
|