@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 +95 -0
- package/dist/index.d.ts +220 -0
- package/dist/index.js +1086 -0
- package/package.json +46 -0
- package/public/chatbot.js +81 -0
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
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -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 };
|