@stainless-api/docs-ai-chat 0.1.0-beta.6 → 0.1.0-beta.61

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,61 +1,300 @@
1
1
  # @stainless-api/docs-ai-chat
2
2
 
3
- ## 0.1.0-beta.6
3
+ ## 0.1.0-beta.61
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - Updated dependencies
8
- - @stainless-api/docs@0.1.0-beta.64
7
+ - Updated dependencies [374c0e4]
8
+ - @stainless-api/docs-ui@0.1.0-beta.90
9
+ - @stainless-api/ai-chat@0.1.0-beta.14
9
10
 
10
- ## 0.1.0-beta.5
11
+ ## 0.1.0-beta.60
11
12
 
12
13
  ### Patch Changes
13
14
 
14
- - Updated dependencies [d2e3686]
15
- - Updated dependencies [5d6e5fa]
16
- - @stainless-api/docs@0.1.0-beta.63
17
- - @stainless-api/ui-primitives@0.1.0-beta.39
18
- - @stainless-api/docs-ui@0.1.0-beta.52
15
+ - Updated dependencies [005419c]
16
+ - @stainless-api/docs-ui@0.1.0-beta.89
19
17
 
20
- ## 0.1.0-beta.4
18
+ ## 0.1.0-beta.59
21
19
 
22
20
  ### Patch Changes
23
21
 
24
- - Updated dependencies [3c4a030]
25
- - Updated dependencies [cd86726]
26
- - Updated dependencies [aa9d333]
27
- - Updated dependencies [07a2c87]
28
- - @stainless-api/docs@0.1.0-beta.62
29
- - @stainless-api/docs-ui@0.1.0-beta.51
30
- - @stainless-api/ui-primitives@0.1.0-beta.38
22
+ - Updated dependencies [af5a5d7]
23
+ - @stainless-api/docs-ui@0.1.0-beta.88
31
24
 
32
- ## 0.1.0-beta.3
25
+ ## 0.1.0-beta.58
26
+
27
+ ### Minor Changes
28
+
29
+ - f22893c: Add interactive examples to ai chat
33
30
 
34
31
  ### Patch Changes
35
32
 
36
- - 77c0d47: steelie: submit on enter
33
+ - Updated dependencies [f22893c]
34
+ - @stainless-api/ai-chat@0.1.0-beta.13
37
35
 
38
- ## 0.1.0-beta.2
36
+ ## 0.1.0-beta.57
39
37
 
40
38
  ### Patch Changes
41
39
 
42
- - a1502cf: fix: close AIChat when clicking on SVG elements outside its bounds
43
- - 88a9894: patch vite optimizeDeps for docs-ai-chat
44
- - Updated dependencies [2a79bae]
45
- - Updated dependencies [88a9894]
46
- - @stainless-api/ui-primitives@0.1.0-beta.38
47
- - @stainless-api/docs@0.1.0-beta.61
48
- - @stainless-api/docs-ui@0.1.0-beta.51
40
+ - Updated dependencies [7701d8f]
41
+ - Updated dependencies [5460e81]
42
+ - @stainless-api/docs-ui@0.1.0-beta.87
49
43
 
50
- ## 0.1.0-beta.1
44
+ ## 0.1.0-beta.56
45
+
46
+ ### Patch Changes
47
+
48
+ - Updated dependencies [d096f7d]
49
+ - @stainless-api/ai-chat@0.1.0-beta.12
50
+
51
+ ## 0.1.0-beta.55
52
+
53
+ ### Patch Changes
54
+
55
+ - Updated dependencies [848cff4]
56
+ - Updated dependencies [ea2b90c]
57
+ - @stainless-api/docs-ui@0.1.0-beta.86
58
+ - @stainless-api/ai-chat@0.1.0-beta.11
59
+
60
+ ## 0.1.0-beta.54
61
+
62
+ ### Patch Changes
63
+
64
+ - Updated dependencies [93a3767]
65
+ - @stainless-api/docs-ui@0.1.0-beta.85
66
+
67
+ ## 0.1.0-beta.53
68
+
69
+ ### Patch Changes
70
+
71
+ - Updated dependencies [438a593]
72
+ - @stainless-api/docs-ui@0.1.0-beta.84
73
+
74
+ ## 0.1.0-beta.52
75
+
76
+ ### Patch Changes
77
+
78
+ - Updated dependencies [1c38511]
79
+ - @stainless-api/docs-ui@0.1.0-beta.83
80
+
81
+ ## 0.1.0-beta.51
82
+
83
+ ### Patch Changes
84
+
85
+ - Updated dependencies [37cc2ac]
86
+ - @stainless-api/docs-ui@0.1.0-beta.82
87
+
88
+ ## 0.1.0-beta.50
89
+
90
+ ### Patch Changes
91
+
92
+ - Updated dependencies [134885b]
93
+ - Updated dependencies [5ba9135]
94
+ - Updated dependencies [df0c958]
95
+ - Updated dependencies [e641d33]
96
+ - @stainless-api/docs-ui@0.1.0-beta.81
97
+
98
+ ## 0.1.0-beta.49
99
+
100
+ ### Patch Changes
101
+
102
+ - Updated dependencies [05cd046]
103
+ - @stainless-api/docs-ui@0.1.0-beta.80
104
+
105
+ ## 0.1.0-beta.48
106
+
107
+ ### Patch Changes
108
+
109
+ - 8838d9f: remember presentation when window shrinks
110
+ - Updated dependencies [8838d9f]
111
+ - @stainless-api/ai-chat@0.1.0-beta.10
112
+
113
+ ## 0.1.0-beta.47
114
+
115
+ ### Minor Changes
116
+
117
+ - a8ed299: Adds the ability for AI chat to move into a sidebar “panel” position.
118
+
119
+ ### Patch Changes
120
+
121
+ - Updated dependencies [5b3b798]
122
+ - Updated dependencies [a8ed299]
123
+ - @stainless-api/ai-chat@0.1.0-beta.9
124
+
125
+ ## 0.1.0-beta.46
51
126
 
52
127
  ### Minor Changes
53
128
 
54
- - 2d00f0d: first version of docs-ai-chat
129
+ - 9d24589: Steelie empty state
130
+ - 5263b85: Loading state for ai chat
131
+
132
+ ### Patch Changes
133
+
134
+ - 9d24589: Various steelie UI fixes
135
+ - Updated dependencies [9d24589]
136
+ - Updated dependencies [9d24589]
137
+ - Updated dependencies [5263b85]
138
+ - @stainless-api/ai-chat@0.1.0-beta.8
139
+
140
+ ## 0.1.0-beta.45
141
+
142
+ ### Patch Changes
143
+
144
+ - Updated dependencies [6d9933d]
145
+ - @stainless-api/docs-ui@0.1.0-beta.79
146
+ - @stainless-api/ai-chat@0.1.0-beta.7
147
+
148
+ ## 0.1.0-beta.44
149
+
150
+ ### Patch Changes
151
+
152
+ - Updated dependencies [54add64]
153
+ - @stainless-api/docs-ui@0.1.0-beta.78
154
+
155
+ ## 0.1.0-beta.43
156
+
157
+ ### Patch Changes
158
+
159
+ - Updated dependencies [21e50d3]
160
+ - @stainless-api/docs-ui@0.1.0-beta.77
161
+
162
+ ## 0.1.0-beta.42
163
+
164
+ ### Patch Changes
165
+
166
+ - Updated dependencies [95fe66c]
167
+ - Updated dependencies [5701494]
168
+ - @stainless-api/docs-ui@0.1.0-beta.76
169
+ - @stainless-api/ai-chat@0.1.0-beta.6
170
+
171
+ ## 0.1.0-beta.41
172
+
173
+ ### Patch Changes
174
+
175
+ - 6e762e2: fix steelie for multi-turn conversations
176
+
177
+ ## 0.1.0-beta.40
178
+
179
+ ### Patch Changes
180
+
181
+ - Updated dependencies [2919b0a]
182
+ - Updated dependencies [e005e5c]
183
+ - @stainless-api/docs-ui@0.1.0-beta.75
184
+
185
+ ## 0.1.0-beta.39
186
+
187
+ ### Patch Changes
188
+
189
+ - Updated dependencies [415629f]
190
+ - @stainless-api/docs-ui@0.1.0-beta.74
191
+
192
+ ## 0.1.0-beta.38
193
+
194
+ ### Patch Changes
195
+
196
+ - Updated dependencies [5c36876]
197
+ - @stainless-api/docs-ui@0.1.0-beta.73
198
+
199
+ ## 0.1.0-beta.37
200
+
201
+ ### Patch Changes
202
+
203
+ - Updated dependencies [6b86a8b]
204
+ - @stainless-api/docs-ui@0.1.0-beta.72
205
+
206
+ ## 0.1.0-beta.36
207
+
208
+ ### Patch Changes
209
+
210
+ - Updated dependencies [cd578b7]
211
+ - @stainless-api/docs-ui@0.1.0-beta.71
212
+
213
+ ## 0.1.0-beta.35
214
+
215
+ ### Patch Changes
216
+
217
+ - Updated dependencies [93c8f94]
218
+ - @stainless-api/docs-ui@0.1.0-beta.70
219
+
220
+ ## 0.1.0-beta.34
221
+
222
+ ### Patch Changes
223
+
224
+ - Updated dependencies [61ba36f]
225
+ - @stainless-api/docs-ui@0.1.0-beta.69
226
+
227
+ ## 0.1.0-beta.33
228
+
229
+ ### Patch Changes
230
+
231
+ - Updated dependencies [a3f1ede]
232
+ - @stainless-api/docs-ui@0.1.0-beta.68
233
+
234
+ ## 0.1.0-beta.32
235
+
236
+ ### Patch Changes
237
+
238
+ - @stainless-api/ai-chat@0.1.0-beta.5
239
+ - @stainless-api/docs-ui@0.1.0-beta.67
240
+
241
+ ## 0.1.0-beta.31
242
+
243
+ ### Patch Changes
244
+
245
+ - Updated dependencies [65a1c9b]
246
+ - Updated dependencies [4f1cee7]
247
+ - Updated dependencies [4c72a83]
248
+ - Updated dependencies [068469b]
249
+ - @stainless-api/docs-ui@0.1.0-beta.66
250
+
251
+ ## 0.1.0-beta.30
252
+
253
+ ### Patch Changes
254
+
255
+ - Updated dependencies [b62eb05]
256
+ - @stainless-api/docs-ui@0.1.0-beta.65
257
+ - @stainless-api/ai-chat@0.1.0-beta.4
258
+
259
+ ## 0.1.0-beta.29
260
+
261
+ ### Patch Changes
262
+
263
+ - Updated dependencies [52ece13]
264
+ - Updated dependencies [3411ffe]
265
+ - Updated dependencies [7439be7]
266
+ - @stainless-api/ai-chat@0.1.0-beta.3
267
+ - @stainless-api/docs-ui@0.1.0-beta.64
268
+
269
+ ## 0.1.0-beta.28
270
+
271
+ ### Patch Changes
272
+
273
+ - Updated dependencies [274cefc]
274
+ - @stainless-api/docs-ui@0.1.0-beta.63
275
+
276
+ ## 0.1.0-beta.27
277
+
278
+ ### Patch Changes
279
+
280
+ - Updated dependencies [6ef241e]
281
+ - Updated dependencies [d3a85b5]
282
+ - Updated dependencies [d3a85b5]
283
+ - Updated dependencies [2dcb5fb]
284
+ - @stainless-api/docs-ui@0.1.0-beta.62
285
+
286
+ ## 0.1.0-beta.26
287
+
288
+ ### Patch Changes
289
+
290
+ - Updated dependencies [7155fae]
291
+ - @stainless-api/ai-chat@0.1.0-beta.2
292
+ - @stainless-api/docs-ui@0.1.0-beta.61
293
+
294
+ ## 0.1.0-beta.25
55
295
 
56
296
  ### Patch Changes
57
297
 
58
- - Updated dependencies [2d00f0d]
59
- - @stainless-api/docs@0.1.0-beta.60
60
- - @stainless-api/docs-ui@0.1.0-beta.50
61
- - @stainless-api/ui-primitives@0.1.0-beta.37
298
+ - 5c257e2: separate steelie into separate packages
299
+ - Updated dependencies [9dda4cf]
300
+ - @stainless-api/ai-chat@0.1.0-beta.1
package/ambient.d.ts CHANGED
@@ -2,3 +2,9 @@ declare module '*.module.css' {
2
2
  const classes: { [key: string]: string };
3
3
  export default classes;
4
4
  }
5
+
6
+ declare module 'virtual:stl-docs-ai-chat-examples' {
7
+ import type { ExamplePrompt } from '@stainless-api/ai-chat/src/types';
8
+
9
+ export const examples: ExamplePrompt[] | undefined;
10
+ }
@@ -0,0 +1,2 @@
1
+ import { config } from '@stainless/eslint-config/react';
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.6",
3
+ "version": "0.1.0-beta.61",
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": "0.1.0-beta.64",
12
- "@stainless-api/docs-ui": "0.1.0-beta.52",
13
- "@stainless-api/ui-primitives": "0.1.0-beta.39"
11
+ "@stainless-api/docs-ui": "0.1.0-beta.90"
14
12
  },
15
13
  "dependencies": {
16
14
  "@streamparser/json-whatwg": "^0.0.22",
17
- "clsx": "^2.1.1",
18
- "lucide-react": "^0.561.0",
19
- "motion": "^12.23.25",
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.1.13"
15
+ "zod": "^4.3.6",
16
+ "@stainless-api/ai-chat": "0.1.0-beta.14"
25
17
  },
26
18
  "devDependencies": {
27
- "@types/react": "19.2.7",
19
+ "@types/react": "19.2.14",
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,12 +25,12 @@
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": {
42
- "lint": "eslint .",
33
+ "lint": "eslint --flag unstable_native_nodejs_ts_config .",
43
34
  "check:types": "tsc --noEmit"
44
35
  }
45
36
  }
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,60 @@
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 { useSyncExternalStore } from 'react';
5
+ import { useChat } from './hook';
6
+
7
+ const examplesPromise = import('virtual:stl-docs-ai-chat-examples')
8
+ .then((mod) => mod.examples)
9
+ .catch(() => undefined);
10
+
11
+ function onCopyMessage(spanId: string) {
12
+ setResponseMetadata(spanId, { copied_to_clipboard: 'true' }).catch(() => {});
13
+ }
14
+
15
+ async function rateMessage(spanId: string, rating: 'up' | 'down'): Promise<boolean> {
16
+ try {
17
+ const { success } = await submitResponseFeedback(spanId, { up: 1 as const, down: 0 as const }[rating]);
18
+ return success;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+
24
+ export default function DocsChat({
25
+ projectId,
26
+ language,
27
+ siteTitle,
28
+ }: {
29
+ projectId: string;
30
+ language: DocsLanguage | undefined;
31
+ siteTitle: string | undefined;
32
+ }) {
33
+ const { chatMessages, sendMessage } = useChat({
34
+ projectId,
35
+ language: language ?? 'http',
36
+ siteTitle,
37
+ });
38
+
39
+ // panel mode is supported only on larger viewports
40
+ const supportsPanel = useSyncExternalStore(
41
+ (cb) => {
42
+ window.addEventListener('resize', cb);
43
+ return () => window.removeEventListener('resize', cb);
44
+ },
45
+ () => window.innerWidth >= 968,
46
+ () => false,
47
+ );
48
+
49
+ return (
50
+ <AiChat
51
+ chatMessages={chatMessages}
52
+ sendMessage={sendMessage}
53
+ rateMessage={rateMessage}
54
+ onCopyMessage={onCopyMessage}
55
+ siteTitle={siteTitle}
56
+ promptExamples={examplesPromise}
57
+ supportsPanel={supportsPanel}
58
+ />
59
+ );
60
+ }
@@ -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,9 +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 } 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
- export const CHAT_ENDPOINT = 'https://app.stainless.com/api/ai/get-agentic-help';
14
+ export const API_URL = new URL('https://app.stainless.com/api/');
15
+ export const CHAT_ENDPOINT = new URL('ai/get-agentic-help', API_URL);
16
+
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
+ }
7
35
 
8
36
  /**
9
37
  * Stream chat response from the server
@@ -14,15 +42,17 @@ export async function* getChatResponse(
14
42
  project,
15
43
  language,
16
44
  priorMessages,
45
+ siteTitle,
17
46
  }: {
18
47
  query: string;
19
48
  project: string;
20
49
  language: DocsLanguage;
21
50
  priorMessages: NonNullable<RequestBody['additionalContext']>['prior_messages'];
51
+ siteTitle: string | undefined;
22
52
  },
23
53
  abortSignal: AbortSignal,
24
54
  ) {
25
- const req = await fetch(CHAT_ENDPOINT, {
55
+ const res = await fetch(CHAT_ENDPOINT, {
26
56
  method: 'POST',
27
57
  headers: {
28
58
  'Content-Type': 'application/json',
@@ -31,17 +61,53 @@ export async function* getChatResponse(
31
61
  query,
32
62
  sdk: { project, language },
33
63
  stream: true,
34
- additionalContext: { prior_messages: priorMessages },
64
+ additionalContext: {
65
+ prior_messages: priorMessages,
66
+ intent: getPageContext({ siteTitle }),
67
+ },
68
+ browser_id: getClientId(),
35
69
  } satisfies RequestBody),
36
70
 
37
71
  signal: abortSignal,
38
72
  });
39
73
 
40
- if (!req.ok || !req.body) throw new Error(`Chat request failed with status ${req.status}`);
74
+ if (!res.ok || !res.body) throw new Error(`Chat request failed with status ${res.status}`);
41
75
 
42
76
  const parser = new JSONParser({ separator: '\n', paths: ['$'] });
43
- for await (const chunk of streamAsyncIterator(req.body.pipeThrough(parser))) {
77
+ for await (const chunk of streamAsyncIterator(res.body.pipeThrough(parser))) {
44
78
  const chunkParsed = responseChunk.safeParse(chunk.value);
45
79
  if (chunkParsed.success) yield chunkParsed.data;
46
80
  }
47
81
  }
82
+
83
+ /**
84
+ * Attach a score to a response
85
+ */
86
+ export async function submitResponseFeedback(spanId: string, score: 0 | 1) {
87
+ const res = await fetch(FEEDBACK_ENDPOINT(spanId), {
88
+ method: 'PUT',
89
+ headers: {
90
+ 'Content-Type': 'application/json',
91
+ },
92
+ body: JSON.stringify({ score } satisfies FeedbackRequestBody),
93
+ });
94
+
95
+ if (!res.ok) throw new Error(`Feedback request failed with status ${res.status}`);
96
+ return feedbackResponseBody.parse(await res.json());
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,6 +14,7 @@ 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
20
  prior_messages: z.array(
@@ -28,6 +29,7 @@ export const requestBody = z.object({
28
29
  errors: z.string().optional(),
29
30
  })
30
31
  .optional(),
32
+ browser_id: z.string().optional(),
31
33
  });
32
34
  export type RequestBody = z.input<typeof requestBody>;
33
35
 
@@ -48,6 +50,19 @@ export const responseChunk = z.discriminatedUnion('type', [
48
50
  }),
49
51
  z.object({
50
52
  type: z.literal('done'),
53
+ span_id: z.string(),
54
+ }),
55
+ z.object({
56
+ type: z.literal('start_session'),
57
+ session_id: z.string(),
51
58
  }),
52
59
  ]);
53
60
  export type ResponseChunk = z.infer<typeof responseChunk>;
61
+
62
+ export const feedbackRequestBody = z.object({ score: z.number().min(0).max(1) });
63
+ export type FeedbackRequestBody = z.infer<typeof feedbackRequestBody>;
64
+ export const feedbackResponseBody = z.object({ success: z.boolean() });
65
+
66
+ export const metadataRequestBody = z.object({ metadata: z.record(z.string(), z.string()) });
67
+ export type MetadataRequestBody = z.infer<typeof metadataRequestBody>;
68
+ export const metadataResponseBody = z.object({ success: z.boolean() });