@stainless-api/docs-ai-chat 0.1.0-beta.9 → 1.0.0-beta.36

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/CHANGELOG.md CHANGED
@@ -1,86 +1,97 @@
1
1
  # @stainless-api/docs-ai-chat
2
2
 
3
- ## 0.1.0-beta.9
3
+ ## 1.0.0-beta.36
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - 827e203: allow http as fallback now that API supports it
8
- - 6340cae: fix steelie http fallback
7
+ - Updated dependencies [cd578b7]
8
+ - @stainless-api/docs-ui@0.1.0-beta.71
9
9
 
10
- ## 0.1.0-beta.8
10
+ ## 0.1.0-beta.35
11
11
 
12
- ### Minor Changes
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [93c8f94]
15
+ - @stainless-api/docs-ui@0.1.0-beta.70
16
+
17
+ ## 0.1.0-beta.34
18
+
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [61ba36f]
22
+ - @stainless-api/docs-ui@0.1.0-beta.69
13
23
 
14
- - 883ad46: Add UI for providing feedback on chat responses
24
+ ## 0.1.0-beta.33
15
25
 
16
26
  ### Patch Changes
17
27
 
18
- - Updated dependencies [af54129]
19
- - @stainless-api/docs@0.1.0-beta.66
28
+ - Updated dependencies [a3f1ede]
29
+ - @stainless-api/docs-ui@0.1.0-beta.68
20
30
 
21
- ## 0.1.0-beta.7
31
+ ## 0.1.0-beta.32
22
32
 
23
33
  ### Patch Changes
24
34
 
25
- - Updated dependencies [22a19ed]
26
- - @stainless-api/docs@0.1.0-beta.65
35
+ - @stainless-api/ai-chat@0.1.0-beta.5
36
+ - @stainless-api/docs-ui@0.1.0-beta.67
27
37
 
28
- ## 0.1.0-beta.6
38
+ ## 0.1.0-beta.31
29
39
 
30
40
  ### Patch Changes
31
41
 
32
- - Updated dependencies
33
- - @stainless-api/docs@0.1.0-beta.64
42
+ - Updated dependencies [65a1c9b]
43
+ - Updated dependencies [4f1cee7]
44
+ - Updated dependencies [4c72a83]
45
+ - Updated dependencies [068469b]
46
+ - @stainless-api/docs-ui@0.1.0-beta.66
34
47
 
35
- ## 0.1.0-beta.5
48
+ ## 0.1.0-beta.30
36
49
 
37
50
  ### Patch Changes
38
51
 
39
- - Updated dependencies [d2e3686]
40
- - Updated dependencies [5d6e5fa]
41
- - @stainless-api/docs@0.1.0-beta.63
42
- - @stainless-api/ui-primitives@0.1.0-beta.39
43
- - @stainless-api/docs-ui@0.1.0-beta.52
52
+ - Updated dependencies [b62eb05]
53
+ - @stainless-api/docs-ui@0.1.0-beta.65
54
+ - @stainless-api/ai-chat@0.1.0-beta.4
44
55
 
45
- ## 0.1.0-beta.4
56
+ ## 0.1.0-beta.29
46
57
 
47
58
  ### Patch Changes
48
59
 
49
- - Updated dependencies [3c4a030]
50
- - Updated dependencies [cd86726]
51
- - Updated dependencies [aa9d333]
52
- - Updated dependencies [07a2c87]
53
- - @stainless-api/docs@0.1.0-beta.62
54
- - @stainless-api/docs-ui@0.1.0-beta.51
55
- - @stainless-api/ui-primitives@0.1.0-beta.38
60
+ - Updated dependencies [52ece13]
61
+ - Updated dependencies [3411ffe]
62
+ - Updated dependencies [7439be7]
63
+ - @stainless-api/ai-chat@0.1.0-beta.3
64
+ - @stainless-api/docs-ui@0.1.0-beta.64
56
65
 
57
- ## 0.1.0-beta.3
66
+ ## 0.1.0-beta.28
58
67
 
59
68
  ### Patch Changes
60
69
 
61
- - 77c0d47: steelie: submit on enter
70
+ - Updated dependencies [274cefc]
71
+ - @stainless-api/docs-ui@0.1.0-beta.63
62
72
 
63
- ## 0.1.0-beta.2
73
+ ## 0.1.0-beta.27
64
74
 
65
75
  ### Patch Changes
66
76
 
67
- - a1502cf: fix: close AIChat when clicking on SVG elements outside its bounds
68
- - 88a9894: patch vite optimizeDeps for docs-ai-chat
69
- - Updated dependencies [2a79bae]
70
- - Updated dependencies [88a9894]
71
- - @stainless-api/ui-primitives@0.1.0-beta.38
72
- - @stainless-api/docs@0.1.0-beta.61
73
- - @stainless-api/docs-ui@0.1.0-beta.51
77
+ - Updated dependencies [6ef241e]
78
+ - Updated dependencies [d3a85b5]
79
+ - Updated dependencies [d3a85b5]
80
+ - Updated dependencies [2dcb5fb]
81
+ - @stainless-api/docs-ui@0.1.0-beta.62
74
82
 
75
- ## 0.1.0-beta.1
83
+ ## 0.1.0-beta.26
84
+
85
+ ### Patch Changes
76
86
 
77
- ### Minor Changes
87
+ - Updated dependencies [7155fae]
88
+ - @stainless-api/ai-chat@0.1.0-beta.2
89
+ - @stainless-api/docs-ui@0.1.0-beta.61
78
90
 
79
- - 2d00f0d: first version of docs-ai-chat
91
+ ## 0.1.0-beta.25
80
92
 
81
93
  ### Patch Changes
82
94
 
83
- - Updated dependencies [2d00f0d]
84
- - @stainless-api/docs@0.1.0-beta.60
85
- - @stainless-api/docs-ui@0.1.0-beta.50
86
- - @stainless-api/ui-primitives@0.1.0-beta.37
95
+ - 5c257e2: separate steelie into separate packages
96
+ - Updated dependencies [9dda4cf]
97
+ - @stainless-api/ai-chat@0.1.0-beta.1
package/eslint.config.js CHANGED
@@ -1,2 +1,2 @@
1
- import { config } from '@stainless/eslint-config/react-internal';
1
+ import { config } from '@stainless/eslint-config/react';
2
2
  export default config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stainless-api/docs-ai-chat",
3
- "version": "0.1.0-beta.9",
3
+ "version": "1.0.0-beta.36",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -8,25 +8,16 @@
8
8
  "peerDependencies": {
9
9
  "react": ">=19.0.0",
10
10
  "react-dom": ">=19.0.0",
11
- "@stainless-api/docs-ui": "0.1.0-beta.52",
12
- "@stainless-api/ui-primitives": "0.1.0-beta.39",
13
- "@stainless-api/docs": "0.1.0-beta.66"
11
+ "@stainless-api/docs-ui": "0.1.0-beta.71"
14
12
  },
15
13
  "dependencies": {
16
14
  "@streamparser/json-whatwg": "^0.0.22",
17
- "clsx": "^2.1.1",
18
- "lucide-react": "^0.562.0",
19
- "motion": "^12.24.7",
20
- "react": "^19.2.3",
21
- "react-markdown": "^10.1.0",
22
- "react-syntax-highlighter": "^16.1.0",
23
- "remend": "^1.0.1",
24
- "zod": "^4.3.5"
15
+ "zod": "^4.3.5",
16
+ "@stainless-api/ai-chat": "0.1.0-beta.5"
25
17
  },
26
18
  "devDependencies": {
27
- "@types/react": "19.2.7",
19
+ "@types/react": "19.2.10",
28
20
  "@types/react-dom": "^19.2.3",
29
- "@types/react-syntax-highlighter": "^15.5.13",
30
21
  "typescript": "5.9.3",
31
22
  "@stainless/eslint-config": "0.1.0-beta.1"
32
23
  },
@@ -34,8 +25,8 @@
34
25
  "./plugin": {
35
26
  "import": "./plugin.tsx"
36
27
  },
37
- "./src/AiChat.tsx": {
38
- "import": "./src/AiChat.tsx"
28
+ "./src/DocsChat.tsx": {
29
+ "import": "./src/DocsChat.tsx"
39
30
  }
40
31
  },
41
32
  "scripts": {
package/plugin.tsx CHANGED
@@ -1,3 +1,3 @@
1
- export default function aiChatPlugin() {
2
- return { chatComponentPath: '@stainless-api/docs-ai-chat/src/AiChat.tsx' };
1
+ export default function docsChatPlugin() {
2
+ return { chatComponentPath: '@stainless-api/docs-ai-chat/src/DocsChat.tsx' };
3
3
  }
@@ -0,0 +1,42 @@
1
+ import AiChat from '@stainless-api/ai-chat/src/AiChat.tsx';
2
+ import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
3
+ import { setResponseMetadata, submitResponseFeedback } from './api';
4
+ import { useChat } from './hook';
5
+
6
+ export default function DocsChat({
7
+ projectId,
8
+ language,
9
+ siteTitle,
10
+ }: {
11
+ projectId: string;
12
+ language: DocsLanguage | undefined;
13
+ siteTitle: string | undefined;
14
+ }) {
15
+ const { chatMessages, sendMessage } = useChat({
16
+ projectId,
17
+ language: language ?? 'http',
18
+ siteTitle,
19
+ });
20
+
21
+ const rateMessage = async (spanId: string, rating: 'up' | 'down'): Promise<boolean> => {
22
+ try {
23
+ const { success } = await submitResponseFeedback(spanId, { up: 1 as const, down: 0 as const }[rating]);
24
+ return success;
25
+ } catch {
26
+ return false;
27
+ }
28
+ };
29
+
30
+ const onCopyMessage = (spanId: string) => {
31
+ setResponseMetadata(spanId, { copied_to_clipboard: 'true' }).catch(() => {});
32
+ };
33
+
34
+ return (
35
+ <AiChat
36
+ chatMessages={chatMessages}
37
+ sendMessage={sendMessage}
38
+ rateMessage={rateMessage}
39
+ onCopyMessage={onCopyMessage}
40
+ />
41
+ );
42
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Identifier for tracking unique users in braintrust
3
+ */
4
+ export function getClientId() {
5
+ let clientId = localStorage.getItem('stainless-client-id');
6
+ if (!clientId) {
7
+ clientId = crypto.randomUUID();
8
+ localStorage.setItem('stainless-client-id', clientId);
9
+ }
10
+ return clientId;
11
+ }
package/src/api/index.ts CHANGED
@@ -1,12 +1,37 @@
1
1
  import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
2
2
  import { JSONParser } from '@streamparser/json-whatwg';
3
- import { type RequestBody, responseChunk, type FeedbackRequestBody, feedbackResponseBody } from './schemas';
3
+ import {
4
+ type RequestBody,
5
+ responseChunk,
6
+ type FeedbackRequestBody,
7
+ feedbackResponseBody,
8
+ type MetadataRequestBody,
9
+ metadataResponseBody,
10
+ } from './schemas';
4
11
  import { streamAsyncIterator } from './util';
12
+ import { getClientId } from './client-id';
5
13
 
6
14
  export const API_URL = new URL('https://app.stainless.com/api/');
7
15
  export const CHAT_ENDPOINT = new URL('ai/get-agentic-help', API_URL);
8
16
 
9
17
  export const FEEDBACK_ENDPOINT = (spanId: string) => new URL(`ai/agentic-help/${spanId}/score`, API_URL);
18
+ export const METADATA_ENDPOINT = (spanId: string) => new URL(`ai/agentic-help/${spanId}/metadata`, API_URL);
19
+
20
+ /** Context on what the user is currently viewing to pass to the agent */
21
+ function getPageContext({ siteTitle }: { siteTitle: string | undefined }) {
22
+ const { href } = window.location;
23
+ const markdownUrl = `${href.replace(/\/$/, '')}/index.md`;
24
+ const pageTitle = document.querySelector('h1')?.textContent;
25
+ return [
26
+ `The user is viewing a documentation page${siteTitle ? ` for ${siteTitle}` : ''}.`,
27
+ `- Content URL: ${markdownUrl}`,
28
+ pageTitle && `- Page title: “${pageTitle}”`,
29
+ // TODO: include stainless path here? does the agent know how to use it?
30
+ // TODO: pass more of the page content into context without the agent having to retrieve it
31
+ ]
32
+ .filter(Boolean)
33
+ .join('\n');
34
+ }
10
35
 
11
36
  /**
12
37
  * Stream chat response from the server
@@ -16,12 +41,14 @@ export async function* getChatResponse(
16
41
  query,
17
42
  project,
18
43
  language,
19
- priorMessages,
44
+ sessionId,
45
+ siteTitle,
20
46
  }: {
21
47
  query: string;
22
48
  project: string;
23
49
  language: DocsLanguage;
24
- priorMessages: NonNullable<RequestBody['additionalContext']>['prior_messages'];
50
+ sessionId: string | undefined;
51
+ siteTitle: string | undefined;
25
52
  },
26
53
  abortSignal: AbortSignal,
27
54
  ) {
@@ -34,7 +61,11 @@ export async function* getChatResponse(
34
61
  query,
35
62
  sdk: { project, language },
36
63
  stream: true,
37
- additionalContext: { prior_messages: priorMessages },
64
+ session_id: sessionId,
65
+ additionalContext: {
66
+ intent: getPageContext({ siteTitle }),
67
+ },
68
+ browser_id: getClientId(),
38
69
  } satisfies RequestBody),
39
70
 
40
71
  signal: abortSignal,
@@ -64,3 +95,19 @@ export async function submitResponseFeedback(spanId: string, score: 0 | 1) {
64
95
  if (!res.ok) throw new Error(`Feedback request failed with status ${res.status}`);
65
96
  return feedbackResponseBody.parse(await res.json());
66
97
  }
98
+
99
+ /**
100
+ * Attach a score to a response
101
+ */
102
+ export async function setResponseMetadata(spanId: string, metadata: Record<string, string>) {
103
+ const res = await fetch(METADATA_ENDPOINT(spanId), {
104
+ method: 'PUT',
105
+ headers: {
106
+ 'Content-Type': 'application/json',
107
+ },
108
+ body: JSON.stringify({ metadata } satisfies MetadataRequestBody),
109
+ });
110
+
111
+ if (!res.ok) throw new Error(`Metadata request failed with status ${res.status}`);
112
+ return metadataResponseBody.parse(await res.json());
113
+ }
@@ -14,20 +14,16 @@ export const requestBody = z.object({
14
14
  maxTokens: z.number().optional(),
15
15
  })
16
16
  .optional(),
17
+ session_id: z.string().optional(),
17
18
  additionalContext: z
18
19
  .object({
19
- prior_messages: z.array(
20
- z.object({
21
- role: z.enum(['user', 'assistant']),
22
- content: z.string(),
23
- }),
24
- ),
25
20
  code: z.string().optional(),
26
21
  intent: z.string().optional(),
27
22
  lsp: z.string().optional(),
28
23
  errors: z.string().optional(),
29
24
  })
30
25
  .optional(),
26
+ browser_id: z.string().optional(),
31
27
  });
32
28
  export type RequestBody = z.input<typeof requestBody>;
33
29
 
@@ -50,9 +46,17 @@ export const responseChunk = z.discriminatedUnion('type', [
50
46
  type: z.literal('done'),
51
47
  span_id: z.string(),
52
48
  }),
49
+ z.object({
50
+ type: z.literal('start_session'),
51
+ session_id: z.string(),
52
+ }),
53
53
  ]);
54
54
  export type ResponseChunk = z.infer<typeof responseChunk>;
55
55
 
56
56
  export const feedbackRequestBody = z.object({ score: z.number().min(0).max(1) });
57
57
  export type FeedbackRequestBody = z.infer<typeof feedbackRequestBody>;
58
58
  export const feedbackResponseBody = z.object({ success: z.boolean() });
59
+
60
+ export const metadataRequestBody = z.object({ metadata: z.record(z.string(), z.string()) });
61
+ export type MetadataRequestBody = z.infer<typeof metadataRequestBody>;
62
+ export const metadataResponseBody = z.object({ success: z.boolean() });
package/src/hook.ts CHANGED
@@ -1,39 +1,14 @@
1
+ import type {
2
+ AssistantTextMessage,
3
+ AssistantToolCallMessage,
4
+ ChatMessage,
5
+ UserMessage,
6
+ } from '@stainless-api/ai-chat/src/types';
7
+ import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
1
8
  import { useCallback, useEffect, useReducer, useRef } from 'react';
2
9
  import { getChatResponse } from './api';
3
- import type { DocsLanguage } from '@stainless-api/docs-ui/routing';
4
10
  import type { ResponseChunk } from './api/schemas';
5
11
 
6
- // Base
7
- type BaseMessage = { id: string };
8
- type BaseTextMessage = BaseMessage & { content: string };
9
- // User
10
- type UserMessage = BaseTextMessage & { role: 'user' };
11
- // Assistant
12
- type BaseAssistantMessage = BaseMessage & {
13
- role: 'assistant';
14
- respondingTo: UserMessage['id'];
15
- messageType: ResponseChunk['type'];
16
- };
17
- type AssistantTextMessage = (BaseAssistantMessage & BaseTextMessage) & {
18
- messageType: Extract<BaseAssistantMessage['messageType'], 'text'>;
19
- isComplete: boolean;
20
- };
21
- type AssistantToolCallMessage = BaseAssistantMessage & {
22
- messageType: Extract<BaseAssistantMessage['messageType'], 'tool_use'>;
23
- toolName: string;
24
- input: Record<string, unknown> | undefined;
25
- };
26
- type AssistantDoneMessage = BaseAssistantMessage & {
27
- messageType: Extract<BaseAssistantMessage['messageType'], 'done'>;
28
- spanId: string;
29
- };
30
- // All
31
- export type ChatMessage =
32
- | UserMessage
33
- | AssistantTextMessage
34
- | AssistantToolCallMessage
35
- | AssistantDoneMessage;
36
-
37
12
  //
38
13
  // Reducer
39
14
  //
@@ -63,7 +38,8 @@ type ChatReducerAction =
63
38
  | { type: 'completeMessage'; id: string }
64
39
  | { type: 'addAssistantToolCall'; message: Omit<AssistantToolCallMessage, 'role' | 'id'> }
65
40
  // a response potentially contains multiple messages / tool calls
66
- | { type: 'completeResponse'; respondingTo: UserMessage['id']; spanId: string };
41
+ | { type: 'completeResponse'; respondingTo: UserMessage['id']; spanId: string }
42
+ | { type: 'addError'; respondingTo: UserMessage['id']; errorMessage: string };
67
43
 
68
44
  function chatReducer(state: ChatMessage[], action: ChatReducerAction) {
69
45
  if (action.type === 'addUserMessage') {
@@ -118,6 +94,16 @@ function chatReducer(state: ChatMessage[], action: ChatReducerAction) {
118
94
  } satisfies Extract<ChatMessage, { role: 'assistant' }>);
119
95
  }
120
96
 
97
+ if (action.type === 'addError') {
98
+ return spliceNewMessage(state, {
99
+ role: 'assistant',
100
+ id: crypto.randomUUID(),
101
+ messageType: 'error',
102
+ respondingTo: action.respondingTo,
103
+ errorMessage: action.errorMessage,
104
+ });
105
+ }
106
+
121
107
  return state;
122
108
  }
123
109
 
@@ -125,7 +111,15 @@ function chatReducer(state: ChatMessage[], action: ChatReducerAction) {
125
111
  // Consumable hook
126
112
  //
127
113
 
128
- export function useChat({ projectId, language }: { projectId: string; language: DocsLanguage }) {
114
+ export function useChat({
115
+ projectId,
116
+ language,
117
+ siteTitle,
118
+ }: {
119
+ projectId: string;
120
+ language: DocsLanguage;
121
+ siteTitle: string | undefined;
122
+ }) {
129
123
  // Used to clean up stray streaming requests on unmount (prevent setState on unmounted component)
130
124
  const abortController = useRef(new AbortController());
131
125
  useEffect(() => {
@@ -136,6 +130,7 @@ export function useChat({ projectId, language }: { projectId: string; language:
136
130
  return () => ac.abort('Component unmounted');
137
131
  }, []);
138
132
 
133
+ const sessionId = useRef<string | undefined>(undefined);
139
134
  const [chatMessages, dispatch] = useReducer(chatReducer, []);
140
135
 
141
136
  /** Send a message and stream back the response in chat */
@@ -147,68 +142,78 @@ export function useChat({ projectId, language }: { projectId: string; language:
147
142
  let currentResponseId = crypto.randomUUID(); // for streaming text messages
148
143
  let lastChunkType: ResponseChunk['type'] | undefined = undefined;
149
144
 
150
- for await (const chunk of getChatResponse(
151
- {
152
- query: question,
153
- project: projectId,
154
- language,
155
- priorMessages: chatMessages.filter(
156
- (msg) => msg.role === 'user' || (msg.role === 'assistant' && msg.messageType === 'text'),
157
- ),
158
- },
159
- abortController.current.signal,
160
- )) {
161
- if (abortController.current.signal.aborted) break;
162
-
163
- // mark complete when text messages finish streaming
164
- if (lastChunkType === 'text' && chunk.type !== 'text') {
165
- dispatch({ type: 'completeMessage', id: currentResponseId });
166
- }
145
+ try {
146
+ for await (const chunk of getChatResponse(
147
+ {
148
+ query: question,
149
+ project: projectId,
150
+ language,
151
+ sessionId: sessionId.current,
152
+ siteTitle,
153
+ },
154
+ abortController.current.signal,
155
+ )) {
156
+ if (abortController.current.signal.aborted) break;
157
+
158
+ // store session id at start of session
159
+ if (chunk.type === 'start_session') {
160
+ sessionId.current = chunk.session_id;
161
+ }
167
162
 
168
- if (chunk.type === 'done') {
169
- dispatch({ type: 'completeResponse', respondingTo: userMessageId, spanId: chunk.span_id });
170
- // stop reading from the stream on done
171
- break;
172
- }
163
+ // mark complete when text messages finish streaming
164
+ if (lastChunkType === 'text' && chunk.type !== 'text') {
165
+ dispatch({ type: 'completeMessage', id: currentResponseId });
166
+ }
167
+
168
+ if (chunk.type === 'done') {
169
+ dispatch({ type: 'completeResponse', respondingTo: userMessageId, spanId: chunk.span_id });
170
+ // stop reading from the stream on done
171
+ break;
172
+ }
173
+
174
+ if (chunk.type === 'text') {
175
+ if (lastChunkType !== 'text') {
176
+ // start a new text message
177
+ currentResponseId = crypto.randomUUID();
178
+ dispatch({
179
+ type: 'beginAssistantMessage',
180
+ message: {
181
+ content: chunk.text,
182
+ id: currentResponseId,
183
+ messageType: chunk.type,
184
+ respondingTo: userMessageId,
185
+ },
186
+ });
187
+ } else {
188
+ // continue the current message with the new content
189
+ dispatch({ type: 'streamMessage', id: currentResponseId, newContent: chunk.text });
190
+ }
191
+ }
173
192
 
174
- if (chunk.type === 'text') {
175
- if (lastChunkType !== 'text') {
176
- // start a new text message
177
- currentResponseId = crypto.randomUUID();
193
+ if (chunk.type === 'tool_use') {
178
194
  dispatch({
179
- type: 'beginAssistantMessage',
195
+ type: 'addAssistantToolCall',
180
196
  message: {
181
- content: chunk.text,
182
- id: currentResponseId,
183
- messageType: chunk.type,
184
197
  respondingTo: userMessageId,
198
+ messageType: chunk.type,
199
+ toolName: chunk.name,
200
+ input: chunk.input,
185
201
  },
186
202
  });
187
- } else {
188
- // continue the current message with the new content
189
- dispatch({ type: 'streamMessage', id: currentResponseId, newContent: chunk.text });
190
203
  }
191
- }
192
204
 
193
- if (chunk.type === 'tool_use') {
194
- dispatch({
195
- type: 'addAssistantToolCall',
196
- message: {
197
- respondingTo: userMessageId,
198
- messageType: chunk.type,
199
- toolName: chunk.name,
200
- input: chunk.input,
201
- },
202
- });
205
+ lastChunkType = chunk.type;
203
206
  }
204
-
205
- lastChunkType = chunk.type;
207
+ } catch {
208
+ dispatch({
209
+ type: 'addError',
210
+ respondingTo: userMessageId,
211
+ errorMessage: 'Something went wrong. Please try again.',
212
+ });
206
213
  }
207
214
  },
208
- [language, projectId, chatMessages],
215
+ [language, projectId, siteTitle],
209
216
  );
210
217
 
211
- // TODO: error handling
212
-
213
218
  return { chatMessages, sendMessage };
214
219
  }
package/tsconfig.json CHANGED
@@ -3,5 +3,5 @@
3
3
  "compilerOptions": {
4
4
  "jsx": "react-jsx"
5
5
  },
6
- "include": ["src", "*.d.ts", "plugin.tsx"]
6
+ "include": ["src", "*.d.ts", "plugin.tsx", "*.config.*"]
7
7
  }
@@ -1,7 +0,0 @@
1
- import 'react';
2
-
3
- declare module 'react' {
4
- interface CSSProperties {
5
- [key: `--${string}`]: string | number;
6
- }
7
- }