@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,315 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useEffect, useRef } from 'react'
|
|
4
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
5
|
+
import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
|
|
6
|
+
import { authClient } from '@nextsparkjs/core/lib/auth-client'
|
|
7
|
+
|
|
8
|
+
export interface Message {
|
|
9
|
+
id: string
|
|
10
|
+
role: 'user' | 'assistant'
|
|
11
|
+
content: string
|
|
12
|
+
timestamp: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface ChatResponse {
|
|
16
|
+
success: boolean
|
|
17
|
+
data?: {
|
|
18
|
+
message: string
|
|
19
|
+
sessionId: string
|
|
20
|
+
isNewSession?: boolean
|
|
21
|
+
}
|
|
22
|
+
error?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface HistoryResponse {
|
|
26
|
+
success: boolean
|
|
27
|
+
data?: {
|
|
28
|
+
sessionId: string
|
|
29
|
+
name: string | null
|
|
30
|
+
messageCount: number
|
|
31
|
+
isPinned: boolean
|
|
32
|
+
messages: Message[]
|
|
33
|
+
}
|
|
34
|
+
error?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ClearResponse {
|
|
38
|
+
success: boolean
|
|
39
|
+
message?: string
|
|
40
|
+
error?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const STORAGE_KEY = 'persistent-chat-messages'
|
|
44
|
+
const CONVERSATIONS_QUERY_KEY = 'conversations'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Persistent Chat Hook
|
|
48
|
+
*
|
|
49
|
+
* Manages chat messages for a specific conversation session.
|
|
50
|
+
* If sessionId is provided, uses that session.
|
|
51
|
+
* If not provided, creates a new session on first message.
|
|
52
|
+
*
|
|
53
|
+
* Features:
|
|
54
|
+
* - Loads message history from server when switching sessions
|
|
55
|
+
* - Caches messages in localStorage for quick UI
|
|
56
|
+
* - Supports multiple conversations
|
|
57
|
+
*/
|
|
58
|
+
export function usePersistentChat(externalSessionId?: string | null) {
|
|
59
|
+
const [messages, setMessages] = useState<Message[]>([])
|
|
60
|
+
const [input, setInput] = useState('')
|
|
61
|
+
const [error, setError] = useState<string | null>(null)
|
|
62
|
+
const [isInitialized, setIsInitialized] = useState(false)
|
|
63
|
+
const [isLoadingHistory, setIsLoadingHistory] = useState(false)
|
|
64
|
+
const lastLoadedSessionRef = useRef<string | null>(null)
|
|
65
|
+
const { currentTeam } = useTeamContext()
|
|
66
|
+
const queryClient = useQueryClient()
|
|
67
|
+
|
|
68
|
+
// Get current user session
|
|
69
|
+
const { data: session } = useQuery({
|
|
70
|
+
queryKey: ['session'],
|
|
71
|
+
queryFn: async () => {
|
|
72
|
+
const { data } = await authClient.getSession()
|
|
73
|
+
return data
|
|
74
|
+
},
|
|
75
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const userId = session?.user?.id
|
|
79
|
+
const teamId = currentTeam?.id
|
|
80
|
+
|
|
81
|
+
// Use external session ID if provided
|
|
82
|
+
const sessionId = externalSessionId || undefined
|
|
83
|
+
|
|
84
|
+
// Storage key is specific to the session
|
|
85
|
+
const storageKey = sessionId ? `${STORAGE_KEY}-${sessionId}` : null
|
|
86
|
+
|
|
87
|
+
// Load messages from localStorage when session changes
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!userId || !sessionId) {
|
|
90
|
+
setMessages([])
|
|
91
|
+
setIsInitialized(true)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Already loaded this session
|
|
96
|
+
if (lastLoadedSessionRef.current === sessionId) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Try to load from localStorage first for instant UI
|
|
101
|
+
if (storageKey) {
|
|
102
|
+
try {
|
|
103
|
+
const stored = localStorage.getItem(storageKey)
|
|
104
|
+
if (stored) {
|
|
105
|
+
const parsed = JSON.parse(stored) as Message[]
|
|
106
|
+
setMessages(parsed)
|
|
107
|
+
} else {
|
|
108
|
+
setMessages([])
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
setMessages([])
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
setIsInitialized(true)
|
|
116
|
+
lastLoadedSessionRef.current = sessionId
|
|
117
|
+
|
|
118
|
+
// Then fetch from server to get the authoritative history
|
|
119
|
+
if (teamId) {
|
|
120
|
+
fetchHistory(sessionId)
|
|
121
|
+
}
|
|
122
|
+
}, [userId, sessionId, storageKey, teamId])
|
|
123
|
+
|
|
124
|
+
// Fetch message history from server
|
|
125
|
+
const fetchHistory = async (sid: string) => {
|
|
126
|
+
if (!teamId) return
|
|
127
|
+
|
|
128
|
+
setIsLoadingHistory(true)
|
|
129
|
+
try {
|
|
130
|
+
const headers: Record<string, string> = {
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
'x-team-id': teamId,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const response = await fetch(
|
|
136
|
+
`/api/v1/theme/default/ai/single-agent?sessionId=${encodeURIComponent(sid)}`,
|
|
137
|
+
{ method: 'GET', headers }
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if (response.ok) {
|
|
141
|
+
const data: HistoryResponse = await response.json()
|
|
142
|
+
if (data.success && data.data?.messages) {
|
|
143
|
+
setMessages(data.data.messages)
|
|
144
|
+
// Update localStorage
|
|
145
|
+
if (storageKey) {
|
|
146
|
+
try {
|
|
147
|
+
localStorage.setItem(storageKey, JSON.stringify(data.data.messages))
|
|
148
|
+
} catch {
|
|
149
|
+
// Ignore
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
// Ignore fetch errors, we have localStorage data
|
|
156
|
+
} finally {
|
|
157
|
+
setIsLoadingHistory(false)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Save messages to localStorage when they change
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (!storageKey || !isInitialized) return
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
localStorage.setItem(storageKey, JSON.stringify(messages))
|
|
167
|
+
} catch {
|
|
168
|
+
// Ignore storage errors (quota exceeded, etc.)
|
|
169
|
+
}
|
|
170
|
+
}, [messages, storageKey, isInitialized])
|
|
171
|
+
|
|
172
|
+
const sendMutation = useMutation({
|
|
173
|
+
mutationFn: async (message: string): Promise<ChatResponse> => {
|
|
174
|
+
const headers: Record<string, string> = {
|
|
175
|
+
'Content-Type': 'application/json',
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (teamId) {
|
|
179
|
+
headers['x-team-id'] = teamId
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const response = await fetch('/api/v1/theme/default/ai/single-agent', {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers,
|
|
185
|
+
body: JSON.stringify({ message, sessionId }),
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
const errorData = await response.json().catch(() => ({}))
|
|
190
|
+
throw new Error(errorData.error || 'Failed to send message')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return response.json()
|
|
194
|
+
},
|
|
195
|
+
onMutate: (message) => {
|
|
196
|
+
setError(null)
|
|
197
|
+
// Optimistic update - add user message immediately
|
|
198
|
+
const userMessage: Message = {
|
|
199
|
+
id: crypto.randomUUID(),
|
|
200
|
+
role: 'user',
|
|
201
|
+
content: message,
|
|
202
|
+
timestamp: Date.now(),
|
|
203
|
+
}
|
|
204
|
+
setMessages((prev) => [...prev, userMessage])
|
|
205
|
+
setInput('')
|
|
206
|
+
},
|
|
207
|
+
onSuccess: (data) => {
|
|
208
|
+
if (data.success && data.data) {
|
|
209
|
+
const aiMessage: Message = {
|
|
210
|
+
id: crypto.randomUUID(),
|
|
211
|
+
role: 'assistant',
|
|
212
|
+
content: data.data.message,
|
|
213
|
+
timestamp: Date.now(),
|
|
214
|
+
}
|
|
215
|
+
setMessages((prev) => [...prev, aiMessage])
|
|
216
|
+
|
|
217
|
+
// If a new session was created, invalidate conversations list
|
|
218
|
+
if (data.data.isNewSession) {
|
|
219
|
+
queryClient.invalidateQueries({ queryKey: [CONVERSATIONS_QUERY_KEY] })
|
|
220
|
+
}
|
|
221
|
+
} else if (data.error) {
|
|
222
|
+
setError(data.error)
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
onError: (err: Error) => {
|
|
226
|
+
setError(err.message)
|
|
227
|
+
// Remove the optimistic user message on error
|
|
228
|
+
setMessages((prev) => prev.slice(0, -1))
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const clearMutation = useMutation({
|
|
233
|
+
mutationFn: async (): Promise<ClearResponse> => {
|
|
234
|
+
if (!sessionId) {
|
|
235
|
+
throw new Error('No session to clear')
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const headers: Record<string, string> = {
|
|
239
|
+
'Content-Type': 'application/json',
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (teamId) {
|
|
243
|
+
headers['x-team-id'] = teamId
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const response = await fetch('/api/v1/theme/default/ai/single-agent', {
|
|
247
|
+
method: 'DELETE',
|
|
248
|
+
headers,
|
|
249
|
+
body: JSON.stringify({ sessionId }),
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
if (!response.ok) {
|
|
253
|
+
const errorData = await response.json().catch(() => ({}))
|
|
254
|
+
throw new Error(errorData.error || 'Failed to clear session')
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return response.json()
|
|
258
|
+
},
|
|
259
|
+
onSuccess: () => {
|
|
260
|
+
// Clear local messages
|
|
261
|
+
setMessages([])
|
|
262
|
+
setError(null)
|
|
263
|
+
setInput('')
|
|
264
|
+
|
|
265
|
+
// Clear localStorage for this session
|
|
266
|
+
if (storageKey) {
|
|
267
|
+
try {
|
|
268
|
+
localStorage.removeItem(storageKey)
|
|
269
|
+
} catch {
|
|
270
|
+
// Ignore
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Invalidate related queries
|
|
275
|
+
queryClient.invalidateQueries({ queryKey: ['chat-history'] })
|
|
276
|
+
queryClient.invalidateQueries({ queryKey: [CONVERSATIONS_QUERY_KEY] })
|
|
277
|
+
},
|
|
278
|
+
onError: (err: Error) => {
|
|
279
|
+
setError(`Failed to clear conversation: ${err.message}`)
|
|
280
|
+
},
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
const sendMessage = useCallback(() => {
|
|
284
|
+
if (!input.trim() || sendMutation.isPending) return
|
|
285
|
+
sendMutation.mutate(input)
|
|
286
|
+
}, [input, sendMutation])
|
|
287
|
+
|
|
288
|
+
const clearConversation = useCallback(() => {
|
|
289
|
+
if (clearMutation.isPending) return
|
|
290
|
+
clearMutation.mutate()
|
|
291
|
+
}, [clearMutation])
|
|
292
|
+
|
|
293
|
+
// Reload history from server
|
|
294
|
+
const reloadHistory = useCallback(() => {
|
|
295
|
+
if (sessionId && teamId) {
|
|
296
|
+
fetchHistory(sessionId)
|
|
297
|
+
}
|
|
298
|
+
}, [sessionId, teamId])
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
messages,
|
|
302
|
+
input,
|
|
303
|
+
setInput,
|
|
304
|
+
error,
|
|
305
|
+
isLoading: sendMutation.isPending,
|
|
306
|
+
isClearing: clearMutation.isPending,
|
|
307
|
+
isLoadingHistory,
|
|
308
|
+
sendMessage,
|
|
309
|
+
clearConversation,
|
|
310
|
+
reloadHistory,
|
|
311
|
+
sessionId,
|
|
312
|
+
isReady: isInitialized && !!userId,
|
|
313
|
+
hasSession: !!sessionId,
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useRef } from 'react'
|
|
4
|
+
import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
|
|
5
|
+
|
|
6
|
+
interface StreamChunk {
|
|
7
|
+
type: 'token' | 'done' | 'error' | 'tool_start' | 'tool_end'
|
|
8
|
+
content?: string
|
|
9
|
+
fullContent?: string
|
|
10
|
+
agentUsed?: string
|
|
11
|
+
tokenUsage?: {
|
|
12
|
+
inputTokens: number
|
|
13
|
+
outputTokens: number
|
|
14
|
+
totalTokens: number
|
|
15
|
+
}
|
|
16
|
+
error?: string
|
|
17
|
+
toolName?: string
|
|
18
|
+
result?: unknown
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface UseStreamingChatOptions {
|
|
22
|
+
agentName: string
|
|
23
|
+
sessionId?: string
|
|
24
|
+
onToken?: (token: string) => void
|
|
25
|
+
onComplete?: (fullContent: string) => void
|
|
26
|
+
onError?: (error: string) => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function useStreamingChat(options: UseStreamingChatOptions) {
|
|
30
|
+
const { agentName, sessionId, onToken, onComplete, onError } = options
|
|
31
|
+
const { currentTeam } = useTeamContext()
|
|
32
|
+
|
|
33
|
+
const [isStreaming, setIsStreaming] = useState(false)
|
|
34
|
+
const [partialContent, setPartialContent] = useState('')
|
|
35
|
+
const [error, setError] = useState<string | null>(null)
|
|
36
|
+
const abortControllerRef = useRef<AbortController | null>(null)
|
|
37
|
+
|
|
38
|
+
const streamMessage = useCallback(async (message: string) => {
|
|
39
|
+
setIsStreaming(true)
|
|
40
|
+
setPartialContent('')
|
|
41
|
+
setError(null)
|
|
42
|
+
|
|
43
|
+
abortControllerRef.current = new AbortController()
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const headers: Record<string, string> = {
|
|
47
|
+
'Content-Type': 'application/json'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Include team context
|
|
51
|
+
if (currentTeam?.id) {
|
|
52
|
+
headers['x-team-id'] = currentTeam.id
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const response = await fetch('/api/ai/chat/stream', {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers,
|
|
58
|
+
body: JSON.stringify({ message, agentName, sessionId }),
|
|
59
|
+
signal: abortControllerRef.current.signal,
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
throw new Error(`HTTP ${response.status}`)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const reader = response.body?.getReader()
|
|
67
|
+
if (!reader) throw new Error('No response body')
|
|
68
|
+
|
|
69
|
+
const decoder = new TextDecoder()
|
|
70
|
+
let fullContent = ''
|
|
71
|
+
|
|
72
|
+
while (true) {
|
|
73
|
+
const { done, value } = await reader.read()
|
|
74
|
+
if (done) break
|
|
75
|
+
|
|
76
|
+
const text = decoder.decode(value)
|
|
77
|
+
const lines = text.split('\n')
|
|
78
|
+
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.startsWith('data: ')) {
|
|
81
|
+
const data = line.slice(6)
|
|
82
|
+
if (data === '[DONE]') continue
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const chunk: StreamChunk = JSON.parse(data)
|
|
86
|
+
|
|
87
|
+
if (chunk.type === 'token' && chunk.content) {
|
|
88
|
+
fullContent += chunk.content
|
|
89
|
+
setPartialContent(fullContent)
|
|
90
|
+
onToken?.(chunk.content)
|
|
91
|
+
} else if (chunk.type === 'done' && chunk.fullContent) {
|
|
92
|
+
onComplete?.(chunk.fullContent)
|
|
93
|
+
} else if (chunk.type === 'error' && chunk.error) {
|
|
94
|
+
setError(chunk.error)
|
|
95
|
+
onError?.(chunk.error)
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
// Ignore parse errors
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof Error && err.name !== 'AbortError') {
|
|
105
|
+
const message = err.message
|
|
106
|
+
setError(message)
|
|
107
|
+
onError?.(message)
|
|
108
|
+
}
|
|
109
|
+
} finally {
|
|
110
|
+
setIsStreaming(false)
|
|
111
|
+
abortControllerRef.current = null
|
|
112
|
+
}
|
|
113
|
+
}, [agentName, sessionId, currentTeam?.id, onToken, onComplete, onError])
|
|
114
|
+
|
|
115
|
+
const cancel = useCallback(() => {
|
|
116
|
+
abortControllerRef.current?.abort()
|
|
117
|
+
setIsStreaming(false)
|
|
118
|
+
}, [])
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
streamMessage,
|
|
122
|
+
isStreaming,
|
|
123
|
+
partialContent,
|
|
124
|
+
error,
|
|
125
|
+
cancel,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useQuery } from '@tanstack/react-query'
|
|
4
|
+
import { useTeamContext } from '@nextsparkjs/core/contexts/TeamContext'
|
|
5
|
+
|
|
6
|
+
type Period = 'today' | '7d' | '30d' | 'all'
|
|
7
|
+
|
|
8
|
+
interface UsageStats {
|
|
9
|
+
totalTokens: number
|
|
10
|
+
totalCost: number
|
|
11
|
+
inputTokens: number
|
|
12
|
+
outputTokens: number
|
|
13
|
+
requestCount: number
|
|
14
|
+
byModel: Record<string, { tokens: number; cost: number }>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface DailyUsage {
|
|
18
|
+
date: string
|
|
19
|
+
tokens: number
|
|
20
|
+
cost: number
|
|
21
|
+
requests: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface UsageResponse {
|
|
25
|
+
success: boolean
|
|
26
|
+
data: {
|
|
27
|
+
stats: UsageStats
|
|
28
|
+
daily: DailyUsage[]
|
|
29
|
+
period: Period
|
|
30
|
+
type: 'user' | 'team'
|
|
31
|
+
}
|
|
32
|
+
error?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useTokenUsage(period: Period = '30d', type: 'user' | 'team' = 'user') {
|
|
36
|
+
const { currentTeam } = useTeamContext()
|
|
37
|
+
|
|
38
|
+
return useQuery<UsageResponse>({
|
|
39
|
+
queryKey: ['ai-usage', period, type],
|
|
40
|
+
queryFn: async () => {
|
|
41
|
+
const headers: Record<string, string> = {
|
|
42
|
+
'Content-Type': 'application/json'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Include team context
|
|
46
|
+
if (currentTeam?.id) {
|
|
47
|
+
headers['x-team-id'] = currentTeam.id
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const response = await fetch(`/api/v1/theme/default/ai/usage?period=${period}&type=${type}`, {
|
|
51
|
+
headers
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
const errorData = await response.json().catch(() => ({}))
|
|
56
|
+
throw new Error(errorData.error || 'Failed to fetch usage')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return response.json()
|
|
60
|
+
},
|
|
61
|
+
enabled: !!currentTeam?.id,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
You are a customer management AI assistant for the Boilerplate application.
|
|
2
|
+
|
|
3
|
+
## CRITICAL RULE - MUST FOLLOW
|
|
4
|
+
|
|
5
|
+
**YOU MUST ALWAYS USE TOOLS TO GET DATA. NEVER FABRICATE OR IMAGINE CUSTOMER INFORMATION.**
|
|
6
|
+
|
|
7
|
+
Before responding with ANY customer information, you MUST:
|
|
8
|
+
1. Call the appropriate tool (list_customers, search_customers, get_customer)
|
|
9
|
+
2. Wait for the tool result
|
|
10
|
+
3. ONLY THEN respond based on the REAL data from the tool
|
|
11
|
+
|
|
12
|
+
If a tool returns an error or empty results, tell the user honestly - NEVER make up fake customers.
|
|
13
|
+
|
|
14
|
+
## Your Capabilities
|
|
15
|
+
- List, search, and view customer details (using tools)
|
|
16
|
+
- Create new customers with all their information
|
|
17
|
+
- Update existing customer data
|
|
18
|
+
- Delete customers (with confirmation)
|
|
19
|
+
|
|
20
|
+
## Customer Fields
|
|
21
|
+
- **name**: Company or customer name (required)
|
|
22
|
+
- **account**: Account number (required, numeric, must be unique)
|
|
23
|
+
- **office**: Office location/branch (required)
|
|
24
|
+
- **phone**: Contact phone number (optional)
|
|
25
|
+
- **salesRep**: Assigned sales representative name (optional)
|
|
26
|
+
- **visitDays**: Days for in-person visits - lun, mar, mie, jue, vie (optional)
|
|
27
|
+
- **contactDays**: Days for phone/email contact - lun, mar, mie, jue, vie (optional)
|
|
28
|
+
|
|
29
|
+
## Available Tools - USE THEM
|
|
30
|
+
|
|
31
|
+
| Tool | When to use |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| **list_customers** | User asks to see customers, all clients |
|
|
34
|
+
| **search_customers** | User wants to find specific customers by name, office, account |
|
|
35
|
+
| **get_customer** | User asks about a specific customer by ID |
|
|
36
|
+
| **create_customer** | User wants to create a new customer |
|
|
37
|
+
| **update_customer** | User wants to modify an existing customer |
|
|
38
|
+
| **delete_customer** | User wants to remove a customer |
|
|
39
|
+
|
|
40
|
+
## Handling Contextual Updates
|
|
41
|
+
|
|
42
|
+
When the user says "modificalo", "cambialo", "actualízalo" (modify it, change it, update it) with new data:
|
|
43
|
+
1. Look at the conversation history to identify which customer they're referring to
|
|
44
|
+
2. Get the customer ID from your previous search/get results
|
|
45
|
+
3. Call update_customer with that ID and the new values
|
|
46
|
+
4. Confirm the update with a link
|
|
47
|
+
|
|
48
|
+
**Example:**
|
|
49
|
+
- Previous context: You showed StartupXYZ (id: customer-everpoint-002, phone: +1 512 555 0102)
|
|
50
|
+
- User: "modificalo, su nuevo telefono es +1 457 45465245"
|
|
51
|
+
- YOU: Call update_customer with customerId="customer-everpoint-002" and phone="+1 457 45465245"
|
|
52
|
+
|
|
53
|
+
## Correct Workflow
|
|
54
|
+
|
|
55
|
+
1. User: "Show me customers from office Central"
|
|
56
|
+
2. YOU: Call search_customers tool with query "Central"
|
|
57
|
+
3. Tool returns: [{id: "1", name: "TechStart", account: 1001, office: "Central"}, ...]
|
|
58
|
+
4. YOU: Format and show the REAL customers from the tool result
|
|
59
|
+
|
|
60
|
+
## Response Format
|
|
61
|
+
- Use Spanish when the user writes in Spanish, English otherwise
|
|
62
|
+
- After creating or updating a customer, provide a link: [Customer Name](/dashboard/customers/{id})
|
|
63
|
+
- When listing customers, summarize key info: name, office, salesRep
|
|
64
|
+
- Always confirm before deleting a customer
|
|
65
|
+
|
|
66
|
+
## What NOT to do
|
|
67
|
+
- NEVER respond with example/fake customer data
|
|
68
|
+
- NEVER imagine what customers the user might have
|
|
69
|
+
- NEVER skip calling tools before responding about customers
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
const AGENTS_DIR = path.join(process.cwd(), 'contents/themes/default/lib/langchain/agents')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Agent names that have .md prompt files in the agents directory.
|
|
8
|
+
*/
|
|
9
|
+
export type AgentName = 'orchestrator' | 'task-assistant' | 'customer-assistant' | 'page-assistant' | 'single-agent'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Load a system prompt from markdown file
|
|
13
|
+
*
|
|
14
|
+
* @param agentName - Name of the agent (without .md extension)
|
|
15
|
+
* @returns The system prompt content as a string
|
|
16
|
+
* @throws Error if the agent prompt file is not found
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const systemPrompt = loadSystemPrompt('task-assistant')
|
|
21
|
+
* const agent = await createAgent({ systemPrompt, ... })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function loadSystemPrompt(agentName: AgentName): string {
|
|
25
|
+
const filePath = path.join(AGENTS_DIR, `${agentName}.md`)
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(filePath)) {
|
|
28
|
+
throw new Error(`Agent prompt not found: ${agentName}. Expected file: ${filePath}`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return fs.readFileSync(filePath, 'utf-8')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get list of available agent names
|
|
36
|
+
*
|
|
37
|
+
* Scans the agents directory for .md files and returns their names
|
|
38
|
+
*
|
|
39
|
+
* @returns Array of agent names (without .md extension)
|
|
40
|
+
*/
|
|
41
|
+
export function getAvailableAgents(): string[] {
|
|
42
|
+
if (!fs.existsSync(AGENTS_DIR)) {
|
|
43
|
+
return []
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const files = fs.readdirSync(AGENTS_DIR)
|
|
47
|
+
return files
|
|
48
|
+
.filter(f => f.endsWith('.md'))
|
|
49
|
+
.map(f => f.replace('.md', ''))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if an agent exists
|
|
54
|
+
*
|
|
55
|
+
* @param agentName - Name of the agent to check
|
|
56
|
+
* @returns true if the agent prompt file exists
|
|
57
|
+
*/
|
|
58
|
+
export function agentExists(agentName: string): boolean {
|
|
59
|
+
const filePath = path.join(AGENTS_DIR, `${agentName}.md`)
|
|
60
|
+
return fs.existsSync(filePath)
|
|
61
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
You are an AI Orchestrator that routes user requests to specialized agents.
|
|
2
|
+
|
|
3
|
+
## CRITICAL RULE
|
|
4
|
+
|
|
5
|
+
**YOU CAN ONLY DO TWO THINGS:**
|
|
6
|
+
1. Call a routing tool (route_to_task, route_to_customer, route_to_page)
|
|
7
|
+
2. Respond to simple greetings
|
|
8
|
+
|
|
9
|
+
**NEVER claim to perform actions like creating, updating, or deleting data.** You don't have those tools. Only the specialized agents do.
|
|
10
|
+
|
|
11
|
+
## Your Job
|
|
12
|
+
|
|
13
|
+
1. Analyze the user's message AND the conversation history
|
|
14
|
+
2. Decide which agent should handle it
|
|
15
|
+
3. Call the appropriate routing tool OR respond to greetings only
|
|
16
|
+
|
|
17
|
+
## Routing Rules
|
|
18
|
+
|
|
19
|
+
**route_to_customer** - Use when:
|
|
20
|
+
- User mentions customers, clients, accounts (cliente, customer, cuenta)
|
|
21
|
+
- User wants to modify, update, or change something about a previously discussed customer
|
|
22
|
+
- User references a customer from earlier in the conversation (e.g., "modificalo", "cambialo", "actualiza su...")
|
|
23
|
+
|
|
24
|
+
**route_to_task** - Use when:
|
|
25
|
+
- User mentions tasks, to-dos, work items (tarea, task, pendiente)
|
|
26
|
+
- User wants to create, update, or list tasks
|
|
27
|
+
- User asks for suggestions to add to a task
|
|
28
|
+
|
|
29
|
+
**route_to_page** - Use when:
|
|
30
|
+
- User mentions pages, content, website (página, page, contenido)
|
|
31
|
+
- User wants to create or modify landing pages, blocks
|
|
32
|
+
|
|
33
|
+
## Context Awareness
|
|
34
|
+
|
|
35
|
+
**IMPORTANT:** When the user says "modificalo", "cambialo", "actualízalo", "bórralo" (modify it, change it, update it, delete it):
|
|
36
|
+
- Look at the conversation history to determine WHAT they're referring to
|
|
37
|
+
- If you were just discussing a customer → route_to_customer
|
|
38
|
+
- If you were just discussing a task → route_to_task
|
|
39
|
+
- If you were just discussing a page → route_to_page
|
|
40
|
+
|
|
41
|
+
## Direct Response (ONLY for greetings)
|
|
42
|
+
|
|
43
|
+
Respond directly ONLY for:
|
|
44
|
+
- "Hola" → "¡Hola! ¿En qué puedo ayudarte?"
|
|
45
|
+
- "Hello" → "Hello! How can I help you?"
|
|
46
|
+
- "¿Quién eres?" → "Soy tu asistente para tareas, clientes y páginas."
|
|
47
|
+
|
|
48
|
+
For EVERYTHING else, use a routing tool.
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
|
|
52
|
+
| User says | Action |
|
|
53
|
+
|-----------|--------|
|
|
54
|
+
| "Hola" | Respond: "¡Hola! ¿En qué puedo ayudarte?" |
|
|
55
|
+
| "Muéstrame mis tareas" | route_to_task |
|
|
56
|
+
| "Para la tarea X, sugiereme recetas" | route_to_task |
|
|
57
|
+
| "Lista de clientes" | route_to_customer |
|
|
58
|
+
| "modificalo, su nuevo telefono es..." | route_to_customer (context: talking about customer) |
|
|
59
|
+
| "Crea una landing page" | route_to_page |
|