@pixygon/chatbot-react 0.1.0

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/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @pixygon/chatbot-react
2
+
3
+ React + MUI + RTK Query components for the Pixygon chatbot.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @pixygon/chatbot-react
9
+ # Peer deps the host already has:
10
+ # - react ≥18
11
+ # - @mui/material, @mui/icons-material, @emotion/react, @emotion/styled ≥11
12
+ # - @reduxjs/toolkit ≥2, react-redux ≥9
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { createChatbotComponents } from "@pixygon/chatbot-react";
19
+ import { baseApi } from "@/apis/baseApi";
20
+ import { useAppSelector } from "@/store/store";
21
+
22
+ export const chatbot = createChatbotComponents({
23
+ apiSlice: baseApi,
24
+ pathPrefix: "/tenants", // or "/companies"
25
+ useTenantId: () => useAppSelector((s) => s.global.activeTenantId),
26
+ apiBase: import.meta.env.VITE_API_URL,
27
+ brand: {
28
+ name: "TotalHMS",
29
+ tagline: "Powered by TotalHMS",
30
+ primaryColor: "#8FB7C9",
31
+ },
32
+ });
33
+ ```
34
+
35
+ Then drop into your router:
36
+
37
+ ```tsx
38
+ <Route path="/knowledge" element={<chatbot.KnowledgePage />} />
39
+ <Route path="/chat" element={<chatbot.ChatPage />} />
40
+ <Route path="/chat-analytics" element={<chatbot.ChatAnalyticsPage />} />
41
+ <Route path="/embed/chat/:tenantSlug" element={<EmbedRoute />} />
42
+ ```
43
+
44
+ `EmbedChatPage` reads `tenantSlug` from props, so the host wires it through:
45
+
46
+ ```tsx
47
+ function EmbedRoute() {
48
+ const { tenantSlug } = useParams();
49
+ return <chatbot.EmbedChatPage tenantSlug={tenantSlug} />;
50
+ }
51
+ ```
52
+
53
+ Settings card (drops into the host's settings page, host supplies the
54
+ tenant + save handler since the Tenant model lives in the host):
55
+
56
+ ```tsx
57
+ <chatbot.ChatbotSettings
58
+ tenant={activeTenant}
59
+ onSave={(patch) => updateTenant({ tenantId, patch }).unwrap()}
60
+ />
61
+ ```
62
+
63
+ ## What you get
64
+
65
+ - `KnowledgePage` — list + paste-text upload + delete
66
+ - `ChatPage` — operator chat UI with sessionId-keyed threads
67
+ - `ChatAnalyticsPage` — KPI tiles, knowledge gaps, top questions, keywords,
68
+ semantic clusters, cost timeseries, source usage, drill-down dialog
69
+ - `EmbedChatPage` — iframe-friendly chat UI for the public anonymous surface
70
+ - `ChatbotSettings` — cost cap input + copy-pasteable embed snippet
71
+
72
+ The bootstrap snippet is served as a separate static file at
73
+ `@pixygon/chatbot-react/chatbot.js`. Symlink or copy to your app's `public/`:
74
+
75
+ ```bash
76
+ cp node_modules/@pixygon/chatbot-react/public/chatbot.js public/chatbot.js
77
+ ```
78
+
79
+ ## RTK Query tag types
80
+
81
+ The host's `baseApi` should include these tags so invalidation works:
82
+
83
+ ```ts
84
+ tagTypes: [..., "Knowledge", "ChatConversation"]
85
+ ```
86
+
87
+ ## Hooks-only API
88
+
89
+ If you only want the RTK Query hooks without the pages, use `injectChatbotEndpoints`:
90
+
91
+ ```ts
92
+ import { injectChatbotEndpoints } from "@pixygon/chatbot-react";
93
+ const { useListDocumentsQuery, useSendMessageMutation, ... } =
94
+ injectChatbotEndpoints(baseApi, { pathPrefix: "/tenants" });
95
+ ```
@@ -0,0 +1,220 @@
1
+ import * as react from 'react';
2
+ import { Api, BaseQueryFn, EndpointDefinitions } from '@reduxjs/toolkit/query';
3
+
4
+ interface ChatbotBrand {
5
+ /** Display name shown in the embed header, system prompt, etc. */
6
+ name: string;
7
+ /** Optional tagline under the header. */
8
+ tagline?: string;
9
+ /** Primary color for the launcher button + header. CSS-compatible. */
10
+ primaryColor?: string;
11
+ }
12
+ interface ChatbotComponentConfig {
13
+ /** The host's RTK Query baseApi. Endpoints get injected onto it. */
14
+ apiSlice: Api<BaseQueryFn, EndpointDefinitions, string, string>;
15
+ /** Path prefix the server router was mounted under. */
16
+ pathPrefix: string;
17
+ /** Hook the host implements to surface the active tenant id. */
18
+ useTenantId: () => string | null | undefined;
19
+ /** Origin of the host API, e.g. "http://localhost:4000/v1". Used by the
20
+ * anonymous EmbedChatPage which talks to the public route directly. */
21
+ apiBase: string;
22
+ /** Branding for the embed widget + ChatbotSettings copy. */
23
+ brand?: ChatbotBrand;
24
+ }
25
+ interface ResolvedChatbotConfig extends ChatbotComponentConfig {
26
+ brand: Required<ChatbotBrand>;
27
+ }
28
+
29
+ interface TenantWithCap {
30
+ _id: string;
31
+ slug: string;
32
+ chatCostCapUsdMonthly?: number | null;
33
+ }
34
+
35
+ interface KnowledgeDocument {
36
+ _id: string;
37
+ tenantId?: string;
38
+ companyId?: string;
39
+ title: string;
40
+ source?: string;
41
+ sourceType: "text" | "url" | "file";
42
+ status: "pending" | "processing" | "ready" | "failed";
43
+ chunkCount: number;
44
+ processedAt?: string;
45
+ lastError?: string;
46
+ tags: string[];
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ }
50
+ interface ChatCitation {
51
+ documentTitle: string;
52
+ snippet: string;
53
+ chunkId?: string;
54
+ documentId?: string;
55
+ similarity?: number;
56
+ }
57
+ interface ChatTurn {
58
+ _id?: string;
59
+ role: "user" | "assistant" | "system";
60
+ content: string;
61
+ citedChunkIds?: string[];
62
+ tokensInput?: number;
63
+ tokensOutput?: number;
64
+ costUsd?: number;
65
+ model?: string;
66
+ rating?: number;
67
+ retrievalSimilarity?: number;
68
+ retrievedCount?: number;
69
+ createdAt: string;
70
+ }
71
+ interface ChatConversation {
72
+ _id: string;
73
+ sessionId: string;
74
+ userId?: string;
75
+ title: string;
76
+ messages: ChatTurn[];
77
+ totalTokensInput: number;
78
+ totalTokensOutput: number;
79
+ totalCostUsd: number;
80
+ lastMessageAt: string;
81
+ createdAt: string;
82
+ }
83
+ interface ConversationSummary {
84
+ _id: string;
85
+ sessionId: string;
86
+ userId?: string;
87
+ title: string;
88
+ messageCount: number;
89
+ totalCostUsd: number;
90
+ totalTokensInput: number;
91
+ totalTokensOutput: number;
92
+ lastMessageAt: string;
93
+ createdAt: string;
94
+ }
95
+ interface ChatOverview {
96
+ conversations: number;
97
+ messages: number;
98
+ totalCostUsd: number;
99
+ totalTokensInput: number;
100
+ totalTokensOutput: number;
101
+ ratingsHelpful: number;
102
+ ratingsUnhelpful: number;
103
+ ratingsUnrated: number;
104
+ }
105
+ interface TopQuestion {
106
+ question: string;
107
+ normalized: string;
108
+ count: number;
109
+ lastAskedAt: string;
110
+ }
111
+ interface Keyword {
112
+ term: string;
113
+ count: number;
114
+ }
115
+ interface CostDay {
116
+ date: string;
117
+ costUsd: number;
118
+ tokensInput: number;
119
+ tokensOutput: number;
120
+ messages: number;
121
+ }
122
+ interface KnowledgeGap {
123
+ question: string;
124
+ normalized: string;
125
+ count: number;
126
+ avgSimilarity: number;
127
+ negativeRatings: number;
128
+ noRetrievalCount: number;
129
+ gapScore: number;
130
+ lastAskedAt: string;
131
+ }
132
+ interface DocumentUsage {
133
+ documentId: string;
134
+ title: string;
135
+ citationCount: number;
136
+ uniqueConversations: number;
137
+ chunkCount: number;
138
+ citedChunkCount: number;
139
+ status: "unused" | "underused" | "active";
140
+ }
141
+ interface ConversationMatch {
142
+ _id: string;
143
+ sessionId: string;
144
+ title: string;
145
+ messageCount: number;
146
+ question: string;
147
+ assistantReply: string;
148
+ rating?: number;
149
+ retrievalSimilarity?: number;
150
+ createdAt: string;
151
+ }
152
+ interface SemanticCluster {
153
+ representative: string;
154
+ count: number;
155
+ members: string[];
156
+ avgSimilarity: number;
157
+ }
158
+ /**
159
+ * Inject all chatbot endpoints into the host's RTK Query baseApi. The host
160
+ * decides the path prefix — `/tenants` for hosts that use TotalHMS-style
161
+ * URLs, `/companies` for Recurrify-style.
162
+ *
163
+ * const chatbotHooks = injectChatbotEndpoints(baseApi, { pathPrefix: "/companies" });
164
+ * const { useListDocumentsQuery, useSendMessageMutation } = chatbotHooks;
165
+ *
166
+ * The host should add "Knowledge" + "ChatConversation" to baseApi's
167
+ * tagTypes when constructing it.
168
+ */
169
+ declare function injectChatbotEndpoints(baseApi: Api<BaseQueryFn, EndpointDefinitions, string, string>, opts?: {
170
+ pathPrefix: string;
171
+ }): any;
172
+ type ChatbotHooks = ReturnType<typeof injectChatbotEndpoints>;
173
+
174
+ /**
175
+ * Build the chatbot React surface for a host. Returns ready-to-mount page
176
+ * components closed over the host's API slice + branding.
177
+ *
178
+ * const chatbot = createChatbotComponents({
179
+ * apiSlice: baseApi,
180
+ * pathPrefix: "/tenants",
181
+ * useTenantId: () => useAppSelector((s) => s.global.activeTenantId),
182
+ * apiBase: import.meta.env.VITE_API_URL,
183
+ * brand: { name: "TotalHMS", primaryColor: "#8FB7C9" },
184
+ * });
185
+ *
186
+ * <Route path="/knowledge" element={<chatbot.KnowledgePage />} />
187
+ * <Route path="/chat" element={<chatbot.ChatPage />} />
188
+ * <Route path="/chat-analytics" element={<chatbot.ChatAnalyticsPage />} />
189
+ * <Route path="/embed/chat/:tenantSlug" element={<chatbot.EmbedChatPage />} />
190
+ */
191
+ declare function createChatbotComponents(cfg: ChatbotComponentConfig): {
192
+ hooks: any;
193
+ KnowledgePage: () => react.JSX.Element;
194
+ ChatPage: () => react.JSX.Element;
195
+ ChatAnalyticsPage: () => react.JSX.Element;
196
+ EmbedChatPage: (props: {
197
+ tenantSlug?: string;
198
+ }) => react.JSX.Element;
199
+ ChatbotSettings: (props: {
200
+ tenant: TenantWithCap | null | undefined;
201
+ onSave: (patch: {
202
+ chatCostCapUsdMonthly: number | null;
203
+ }) => Promise<void>;
204
+ embedScriptUrl?: string;
205
+ }) => react.JSX.Element | null;
206
+ KnowledgePageBase: (props: {
207
+ tenantId?: string | null;
208
+ }) => react.JSX.Element;
209
+ ChatPageBase: (props: {
210
+ tenantId?: string | null;
211
+ }) => react.JSX.Element;
212
+ ChatAnalyticsPageBase: (props: {
213
+ tenantId?: string | null;
214
+ }) => react.JSX.Element;
215
+ EmbedChatPageBase: (props: {
216
+ tenantSlug?: string;
217
+ }) => react.JSX.Element;
218
+ };
219
+
220
+ export { type ChatCitation, type ChatConversation, type ChatOverview, type ChatTurn, type ChatbotBrand, type ChatbotComponentConfig, type ChatbotHooks, type ConversationMatch, type ConversationSummary, type CostDay, type DocumentUsage, type Keyword, type KnowledgeDocument, type KnowledgeGap, type ResolvedChatbotConfig, type SemanticCluster, type TopQuestion, createChatbotComponents, injectChatbotEndpoints };