@surph_ai/sdk 0.0.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/auth/index.js +147 -0
- package/base/vinely-base/.nvmrc +1 -0
- package/base/vinely-base/README.md +30 -0
- package/base/vinely-base/app.js +5 -0
- package/base/vinely-base/declarations.d.ts +1 -0
- package/base/vinely-base/package.json +103 -0
- package/base/vinely-base/public/css/globals-2.css +3699 -0
- package/base/vinely-base/public/css/globals.css +3695 -0
- package/base/vinely-base/public/js/app.js +1 -0
- package/base/vinely-base/public/js/jquery.js +2 -0
- package/base/vinely-base/scratch.js +21 -0
- package/base/vinely-base/src/client/components/app-sidebar.tsx +59 -0
- package/base/vinely-base/src/client/components/assistant-ui/markdown-text.tsx +153 -0
- package/base/vinely-base/src/client/components/assistant-ui/thread-list.tsx +67 -0
- package/base/vinely-base/src/client/components/assistant-ui/thread.tsx +666 -0
- package/base/vinely-base/src/client/components/assistant-ui/tool-fallback.tsx +44 -0
- package/base/vinely-base/src/client/components/assistant-ui/tooltip-icon-button.tsx +44 -0
- package/base/vinely-base/src/client/components/ui/breadcrumb.tsx +109 -0
- package/base/vinely-base/src/client/components/ui/button.tsx +59 -0
- package/base/vinely-base/src/client/components/ui/input.tsx +21 -0
- package/base/vinely-base/src/client/components/ui/separator.tsx +28 -0
- package/base/vinely-base/src/client/components/ui/sheet.tsx +139 -0
- package/base/vinely-base/src/client/components/ui/sidebar.tsx +726 -0
- package/base/vinely-base/src/client/components/ui/skeleton.tsx +14 -0
- package/base/vinely-base/src/client/components/ui/tooltip.tsx +61 -0
- package/base/vinely-base/src/client/components/vines-sidebar.tsx +182 -0
- package/base/vinely-base/src/client/contexts/SessionContext.tsx +28 -0
- package/base/vinely-base/src/client/contexts/SessionReducer.tsx +32 -0
- package/base/vinely-base/src/client/hooks/use-mobile.ts +19 -0
- package/base/vinely-base/src/client/index.tsx +154 -0
- package/base/vinely-base/src/client/lib/API.ts +155 -0
- package/base/vinely-base/src/client/lib/RPC.ts +49 -0
- package/base/vinely-base/src/client/lib/utils.ts +96 -0
- package/base/vinely-base/src/server/index.ts +63 -0
- package/base/vinely-base/src/server/routes/api.ts +38 -0
- package/base/vinely-base/src/server/routes/chat.ts +133 -0
- package/base/vinely-base/src/server/routes/main.ts +36 -0
- package/base/vinely-base/src/server/routes/mcp.ts +52 -0
- package/base/vinely-base/src/server/routes/rpc.ts +64 -0
- package/base/vinely-base/src/server/routes/thread.ts +44 -0
- package/base/vinely-base/src/server/utils/APIMgr.ts +156 -0
- package/base/vinely-base/src/server/utils/Auth.ts +235 -0
- package/base/vinely-base/src/server/utils/fetchManifest.ts +15 -0
- package/base/vinely-base/src/server/utils/mcp-auth.ts +251 -0
- package/base/vinely-base/tsconfig.json +16 -0
- package/base/vinely-base/views/landing.html +187 -0
- package/base/vinely-base/views/partials/imports.dev.html +3 -0
- package/base/vinely-base/webpack.config.js +112 -0
- package/deploy/Dockerfile +9 -0
- package/deploy/index.js +244 -0
- package/index.js +266 -0
- package/package.json +28 -0
- package/scaffold/app/gulpfile.js +12 -0
- package/scaffold/app/package.json +24 -0
- package/scaffold/app/src/index.js +14 -0
- package/scaffold/index.js +171 -0
- package/scaffold/project/README.md +28 -0
- package/scaffold/project/app.js +20 -0
- package/scaffold/project/apps/README.md +1 -0
- package/scaffold/project/env.txt +4 -0
- package/scaffold/project/package-lock.json +2459 -0
- package/scaffold/project/package.json +21 -0
- package/scaffold/project/public/css/bootstrap.css +5926 -0
- package/scaffold/project/public/js/app.js +1 -0
- package/scaffold/project/public/js/jquery.js +2 -0
- package/scaffold/project/routes/index.js +8 -0
- package/scaffold/project/templates/index.mustache +20 -0
- package/utils/Realm.js +60 -0
- package/utils/http.js +22 -0
- package/utils/index.js +276 -0
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import React, { useMemo, useEffect } from "react"
|
|
2
|
+
import { useHistory } from 'react-router-dom'
|
|
3
|
+
import {
|
|
4
|
+
ThreadPrimitive,
|
|
5
|
+
ComposerPrimitive,
|
|
6
|
+
MessagePrimitive,
|
|
7
|
+
ActionBarPrimitive,
|
|
8
|
+
BranchPickerPrimitive,
|
|
9
|
+
ErrorPrimitive,
|
|
10
|
+
ToolCallMessagePartProps,
|
|
11
|
+
useMessage,
|
|
12
|
+
useThread,
|
|
13
|
+
useThreadListItem
|
|
14
|
+
} from "@assistant-ui/react";
|
|
15
|
+
import type { FC } from "react";
|
|
16
|
+
import {
|
|
17
|
+
ArrowDownIcon,
|
|
18
|
+
ArrowUpIcon,
|
|
19
|
+
PlusIcon,
|
|
20
|
+
CopyIcon,
|
|
21
|
+
CheckIcon,
|
|
22
|
+
PencilIcon,
|
|
23
|
+
RefreshCwIcon,
|
|
24
|
+
ChevronLeftIcon,
|
|
25
|
+
ChevronRightIcon,
|
|
26
|
+
Square,
|
|
27
|
+
} from "lucide-react";
|
|
28
|
+
import { motion } from "framer-motion";
|
|
29
|
+
import { TooltipIconButton } from "./tooltip-icon-button";
|
|
30
|
+
import { MarkdownText } from "./markdown-text";
|
|
31
|
+
import { Button } from "../ui/button";
|
|
32
|
+
import { cn, uploadFile, fetchSpace, updateSpace } from "../../lib/utils";
|
|
33
|
+
// import { ToolFallback } from "./tool-fallback";
|
|
34
|
+
// import { SessionContext } from '../../contexts/SessionContext'
|
|
35
|
+
|
|
36
|
+
const { vineyard } = window.__CONTEXT__
|
|
37
|
+
|
|
38
|
+
const removeClass = (selector: string, className: string) => {
|
|
39
|
+
Array.from(document.getElementsByClassName(selector)).forEach(el => {
|
|
40
|
+
el.classList.remove(className)
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const addClass = (event: any, selector: string, className: string) => {
|
|
45
|
+
const els = Array.from(event.target.querySelectorAll(`.${selector}`))
|
|
46
|
+
els.forEach((el: any) => {
|
|
47
|
+
el.classList.add(className)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parseDocname = (docPath: string): Record<string, string> => {
|
|
52
|
+
// console.log('DOC PATH: ' + docPath) // s3://vinely/sites/dans-test-12/storage/describe-econ-1012/econ1012.pdf
|
|
53
|
+
const parts = docPath.split('/')
|
|
54
|
+
const name = parts[parts.length-1]
|
|
55
|
+
const url = docPath.replace('s3://vinely/sites/', 'https://d24qxjs65cjbf1.cloudfront.net/')
|
|
56
|
+
return { name, url }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const parseVector = (vector: any, docs: any[]) => {
|
|
60
|
+
const { url, name } = parseDocname(vector.metadata['x-amz-bedrock-kb-source-uri'])
|
|
61
|
+
if (docs.find(doc => doc.url === url)) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
docs.push({ url, name })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const Thread: FC = () => {
|
|
69
|
+
const thread = useThread()
|
|
70
|
+
const threadList = useThreadListItem()
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!thread) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (thread.isRunning) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const msg = thread.messages.map(({ id, createdAt, role, content }: any) => {
|
|
82
|
+
const interactions = content.find((c: any) => c.type === 'text')
|
|
83
|
+
const text = interactions ? interactions.text : null
|
|
84
|
+
|
|
85
|
+
return ({
|
|
86
|
+
id,
|
|
87
|
+
createdAt,
|
|
88
|
+
role,
|
|
89
|
+
text
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
if (msg.length === 0) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// console.log(JSON.stringify({ id: threadList.id, messages: msg }))
|
|
98
|
+
uploadFile({
|
|
99
|
+
folder: `sites/${vineyard.slug}`,
|
|
100
|
+
filedata: JSON.stringify({ id: threadList.id, messages: msg }),
|
|
101
|
+
filename: `threads/${threadList.id}.json`,
|
|
102
|
+
filetype: 'application/json'
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const addThreadPreview = async (vyd: any) => {
|
|
106
|
+
// fetch space first because another session may be updating too:
|
|
107
|
+
const current = await fetchSpace(vyd)
|
|
108
|
+
|
|
109
|
+
const metadata = current.metadata || {}
|
|
110
|
+
const recentThreads = metadata.recentThreads || []
|
|
111
|
+
const found = recentThreads.find((t: any) => t.id === threadList.id)
|
|
112
|
+
|
|
113
|
+
// preview already found, this is a continuation of a thread:
|
|
114
|
+
if (found) {
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const preview = msg.length > 1 ? msg[1].text : ''
|
|
119
|
+
recentThreads.unshift({
|
|
120
|
+
id: threadList.id,
|
|
121
|
+
initial: msg[0], // just send first message, not entire thread
|
|
122
|
+
preview: `${preview.substring(0, 275).trim()}...`
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
if (recentThreads.length === 7) { // max of 6 recent threads
|
|
126
|
+
recentThreads.pop()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
await updateSpace(current, {
|
|
130
|
+
metadata: { ...metadata, recentThreads }
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
addThreadPreview(vineyard)
|
|
135
|
+
}, [thread])
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<ThreadPrimitive.Root
|
|
139
|
+
// aui-thread-root
|
|
140
|
+
className="bg-background flex h-full flex-col"
|
|
141
|
+
style={{
|
|
142
|
+
["--thread-max-width" as string]: "54rem",
|
|
143
|
+
["--thread-padding-x" as string]: "1rem",
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
{/* aui-thread-viewport */}
|
|
147
|
+
<ThreadPrimitive.Viewport
|
|
148
|
+
className="relative flex min-w-0 flex-1 flex-col gap-6 overflow-y-scroll"
|
|
149
|
+
style={{ paddingTop: 80 }}
|
|
150
|
+
onScroll={(e: any) => {
|
|
151
|
+
const header = document.getElementById('nav-header')
|
|
152
|
+
if (!header) {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const offset = e.target.scrollTop
|
|
157
|
+
if (offset < 50) {
|
|
158
|
+
if (header.style.borderBottomColor !== '#191a1a') {
|
|
159
|
+
header.style.borderBottomColor = '#191a1a'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (header.style.borderBottomColor !== '#ece6e70d') {
|
|
166
|
+
header.style.borderBottomColor = '#ece6e70d'
|
|
167
|
+
}
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
<ThreadPrimitive.Messages
|
|
171
|
+
components={{
|
|
172
|
+
UserMessage,
|
|
173
|
+
EditComposer,
|
|
174
|
+
AssistantMessage,
|
|
175
|
+
}}
|
|
176
|
+
/>
|
|
177
|
+
|
|
178
|
+
<ThreadPrimitive.If empty={false}>
|
|
179
|
+
{/* aui-thread-viewport-spacer */}
|
|
180
|
+
<motion.div className="min-h-6 min-w-6 shrink-0" />
|
|
181
|
+
</ThreadPrimitive.If>
|
|
182
|
+
</ThreadPrimitive.Viewport>
|
|
183
|
+
|
|
184
|
+
<Composer />
|
|
185
|
+
</ThreadPrimitive.Root>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const ThreadScrollToBottom: FC = () => {
|
|
190
|
+
return (
|
|
191
|
+
<ThreadPrimitive.ScrollToBottom asChild>
|
|
192
|
+
<TooltipIconButton
|
|
193
|
+
tooltip="Scroll to bottom"
|
|
194
|
+
variant="outline"
|
|
195
|
+
// aui-thread-scroll-to-bottom
|
|
196
|
+
className="dark:bg-background dark:hover:bg-accent absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible"
|
|
197
|
+
style={{ background: '#fff' }}
|
|
198
|
+
>
|
|
199
|
+
<ArrowDownIcon />
|
|
200
|
+
</TooltipIconButton>
|
|
201
|
+
</ThreadPrimitive.ScrollToBottom>
|
|
202
|
+
);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/*
|
|
206
|
+
const ThreadWelcomeSuggestions: FC = () => {
|
|
207
|
+
return (
|
|
208
|
+
// aui-thread-welcome-suggestions
|
|
209
|
+
<div className="grid w-full gap-2 sm:grid-cols-2">
|
|
210
|
+
{[
|
|
211
|
+
{
|
|
212
|
+
title: "What are the advantages",
|
|
213
|
+
label: "of using Assistant Cloud?",
|
|
214
|
+
action: "What are the advantages of using Assistant Cloud?",
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
title: "Write code to",
|
|
218
|
+
label: `demonstrate topological sorting`,
|
|
219
|
+
action: `Write code to demonstrate topological sorting`,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
title: "Help me write an essay",
|
|
223
|
+
label: `about AI chat applications`,
|
|
224
|
+
action: `Help me write an essay about AI chat applications`,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
title: "What is the weather",
|
|
228
|
+
label: "in San Francisco?",
|
|
229
|
+
action: "What is the weather in San Francisco?",
|
|
230
|
+
},
|
|
231
|
+
].map((suggestedAction, index) => (
|
|
232
|
+
<motion.div
|
|
233
|
+
initial={{ opacity: 0, y: 20 }}
|
|
234
|
+
animate={{ opacity: 1, y: 0 }}
|
|
235
|
+
exit={{ opacity: 0, y: 20 }}
|
|
236
|
+
transition={{ delay: 0.05 * index }}
|
|
237
|
+
key={`suggested-action-${suggestedAction.title}-${index}`}
|
|
238
|
+
// aui-thread-welcome-suggestion-display
|
|
239
|
+
className="[&:nth-child(n+3)]:hidden sm:[&:nth-child(n+3)]:block"
|
|
240
|
+
>
|
|
241
|
+
<ThreadPrimitive.Suggestion
|
|
242
|
+
prompt={suggestedAction.action}
|
|
243
|
+
method="replace"
|
|
244
|
+
autoSend
|
|
245
|
+
asChild
|
|
246
|
+
>
|
|
247
|
+
<Button
|
|
248
|
+
variant="ghost"
|
|
249
|
+
className="dark:hover:bg-accent/60 h-auto w-full flex-1 flex-wrap items-start justify-start gap-1 rounded-xl border px-4 py-3.5 text-left text-sm sm:flex-col"
|
|
250
|
+
aria-label={suggestedAction.action}
|
|
251
|
+
>
|
|
252
|
+
<span className="font-medium">{suggestedAction.title}</span>
|
|
253
|
+
<p className="text-muted-foreground">{suggestedAction.label}</p>
|
|
254
|
+
</Button>
|
|
255
|
+
</ThreadPrimitive.Suggestion>
|
|
256
|
+
</motion.div>
|
|
257
|
+
))}
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
const RecentThreads = () => {
|
|
264
|
+
const history = useHistory()
|
|
265
|
+
|
|
266
|
+
useEffect(() => {
|
|
267
|
+
const threadPreviews = Array.from(document.getElementsByClassName('thread-preview'))
|
|
268
|
+
threadPreviews.forEach((preview: any, index: number) => {
|
|
269
|
+
const delay = (index * 200)
|
|
270
|
+
setTimeout(() => {
|
|
271
|
+
preview.style.opacity = '1'
|
|
272
|
+
preview.style.marginTop = '0px'
|
|
273
|
+
}, delay)
|
|
274
|
+
})
|
|
275
|
+
}, [])
|
|
276
|
+
|
|
277
|
+
const metadata = vineyard.metadata || {}
|
|
278
|
+
const threads = metadata.recentThreads || []
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<>
|
|
282
|
+
<h4 className="color-accent m-auto">Recent</h4>
|
|
283
|
+
<div className="grid w-full gap-4 sm:grid-cols-3">
|
|
284
|
+
{threads.map((thread: any) => (
|
|
285
|
+
<div
|
|
286
|
+
key={thread.id}
|
|
287
|
+
data-thread={thread.id}
|
|
288
|
+
className="thread-preview"
|
|
289
|
+
style={{ opacity: 0, marginTop: 8 }}
|
|
290
|
+
onClick={() => history.push(`thread/${thread.id}`)}
|
|
291
|
+
>
|
|
292
|
+
<div
|
|
293
|
+
className="card-preview"
|
|
294
|
+
onMouseEnter={(e: any) => {
|
|
295
|
+
removeClass('shine', 'effect-shine')
|
|
296
|
+
addClass(e, 'shine', 'effect-shine')
|
|
297
|
+
}}
|
|
298
|
+
onMouseLeave={(e: any) => {
|
|
299
|
+
removeClass('shine', 'effect-shine')
|
|
300
|
+
}}
|
|
301
|
+
>
|
|
302
|
+
<small className="color-accent">job market</small>
|
|
303
|
+
|
|
304
|
+
<small>{thread.initial.createdAt}</small>
|
|
305
|
+
<h5 className="shine">{thread.initial.text}</h5>
|
|
306
|
+
<hr />
|
|
307
|
+
<p className="shine">{thread.preview}</p>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
))}
|
|
311
|
+
</div>
|
|
312
|
+
</>
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const Composer: FC = () => {
|
|
317
|
+
return (
|
|
318
|
+
// aui-composer-wrapper
|
|
319
|
+
<div className="bg-background relative mx-auto flex w-full max-w-[var(--thread-max-width)] flex-col gap-4 pb-4 md:pb-6">
|
|
320
|
+
<ThreadScrollToBottom />
|
|
321
|
+
<ThreadPrimitive.Empty>
|
|
322
|
+
{/* <ThreadWelcomeSuggestions /> */}
|
|
323
|
+
<RecentThreads />
|
|
324
|
+
</ThreadPrimitive.Empty>
|
|
325
|
+
|
|
326
|
+
{/* aui-composer-root */}
|
|
327
|
+
<ComposerPrimitive.Root className="focus-within::ring-offset-2 relative flex w-full flex-col rounded-2xl focus-within:ring-2 focus-within:ring-black dark:focus-within:ring-white">
|
|
328
|
+
{/* aui-composer-input */}
|
|
329
|
+
<ComposerPrimitive.Input
|
|
330
|
+
placeholder="Send a message..."
|
|
331
|
+
className={
|
|
332
|
+
"border-border dark:border-muted-foreground/15 focus:outline-primary placeholder:text-muted-foreground max-h-[calc(50dvh)] min-h-16 w-full resize-none rounded-t-2xl border-x border-t px-4 pt-2 pb-3 text-base outline-none"
|
|
333
|
+
}
|
|
334
|
+
rows={1}
|
|
335
|
+
autoFocus
|
|
336
|
+
aria-label="Message input"
|
|
337
|
+
/>
|
|
338
|
+
<ComposerAction />
|
|
339
|
+
</ComposerPrimitive.Root>
|
|
340
|
+
</div>
|
|
341
|
+
);
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const ComposerAction: FC = () => {
|
|
345
|
+
return (
|
|
346
|
+
// aui-composer-action-wrapper
|
|
347
|
+
<div className="border-border dark:border-muted-foreground/15 relative flex items-center justify-between rounded-b-2xl border-x border-b p-2" style={{ background: '#1f2121' }}>
|
|
348
|
+
<TooltipIconButton
|
|
349
|
+
tooltip="Attach file"
|
|
350
|
+
variant="ghost"
|
|
351
|
+
// aui-composer-attachment-button
|
|
352
|
+
className="hover:bg-foreground/15 dark:hover:bg-background/50 scale-115 p-3.5"
|
|
353
|
+
onClick={() => {
|
|
354
|
+
console.log("Attachment clicked - not implemented");
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
357
|
+
<PlusIcon style={{ color:'#e8e8e3'}} />
|
|
358
|
+
</TooltipIconButton>
|
|
359
|
+
|
|
360
|
+
<ThreadPrimitive.If running={false}>
|
|
361
|
+
<ComposerPrimitive.Send asChild>
|
|
362
|
+
<Button
|
|
363
|
+
type="submit"
|
|
364
|
+
variant="default"
|
|
365
|
+
// aui-composer-send
|
|
366
|
+
className="dark:border-muted-foreground/90 border-muted-foreground/60 hover:bg-primary/75 size-8 rounded-full border"
|
|
367
|
+
aria-label="Send message"
|
|
368
|
+
>
|
|
369
|
+
{/* aui-composer-send-icon */}
|
|
370
|
+
<ArrowUpIcon className="size-5" />
|
|
371
|
+
</Button>
|
|
372
|
+
</ComposerPrimitive.Send>
|
|
373
|
+
</ThreadPrimitive.If>
|
|
374
|
+
|
|
375
|
+
<ThreadPrimitive.If running>
|
|
376
|
+
<ComposerPrimitive.Cancel asChild>
|
|
377
|
+
<Button
|
|
378
|
+
type="button"
|
|
379
|
+
variant="default"
|
|
380
|
+
// aui-composer-cancel
|
|
381
|
+
className="dark:border-muted-foreground/90 border-muted-foreground/60 hover:bg-primary/75 size-8 rounded-full border"
|
|
382
|
+
aria-label="Stop generating"
|
|
383
|
+
>
|
|
384
|
+
{/* aui-composer-cancel-icon */}
|
|
385
|
+
<Square className="size-3.5 fill-white dark:size-4 dark:fill-black" />
|
|
386
|
+
</Button>
|
|
387
|
+
</ComposerPrimitive.Cancel>
|
|
388
|
+
</ThreadPrimitive.If>
|
|
389
|
+
</div>
|
|
390
|
+
);
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const MessageError: FC = () => {
|
|
394
|
+
return (
|
|
395
|
+
<MessagePrimitive.Error>
|
|
396
|
+
{/* aui-message-error-root */}
|
|
397
|
+
<ErrorPrimitive.Root className="border-destructive bg-destructive/10 dark:bg-destructive/5 text-destructive mt-2 rounded-md border p-3 text-sm dark:text-red-200">
|
|
398
|
+
{/* aui-message-error-message */}
|
|
399
|
+
<ErrorPrimitive.Message className="line-clamp-2" />
|
|
400
|
+
</ErrorPrimitive.Root>
|
|
401
|
+
</MessagePrimitive.Error>
|
|
402
|
+
);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const CustomToolFallback: FC<ToolCallMessagePartProps> = (toolInvocation: any) => {
|
|
406
|
+
const refs: any = useMemo(() => {
|
|
407
|
+
if (!toolInvocation.result) {
|
|
408
|
+
return null
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!toolInvocation.result.content) {
|
|
412
|
+
return null
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const { content } = toolInvocation.result
|
|
416
|
+
if (content.length === 0) {
|
|
417
|
+
return null
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const payload = content[0]
|
|
421
|
+
if (!payload.text) {
|
|
422
|
+
return null
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let parsed: any = {}
|
|
426
|
+
try {
|
|
427
|
+
parsed = JSON.parse(payload.text)
|
|
428
|
+
} catch (error) {
|
|
429
|
+
return null
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (parsed.vectors) {
|
|
433
|
+
const docs: any[] = []
|
|
434
|
+
parsed.vectors.forEach((vector: any) => parseVector(vector, docs))
|
|
435
|
+
return { vectors: parsed.vectors, docs }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (!parsed.response) {
|
|
439
|
+
return null
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const response = parsed.response
|
|
443
|
+
if (!response.vectors) {
|
|
444
|
+
return null
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const { vectors } = response
|
|
448
|
+
const docs: any[] = []
|
|
449
|
+
vectors.forEach((vector: any) => parseVector(vector, docs))
|
|
450
|
+
|
|
451
|
+
return { vectors, docs }
|
|
452
|
+
}, [toolInvocation.result])
|
|
453
|
+
|
|
454
|
+
if (!refs) {
|
|
455
|
+
return null
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<>
|
|
460
|
+
{/* <ToolFallback { ...toolInvocation } /> */}
|
|
461
|
+
<div
|
|
462
|
+
className="space-y-2 mb-5 reference-container"
|
|
463
|
+
style={{ opacity: refs?.vectors === null ? 0 : 1 }}
|
|
464
|
+
>
|
|
465
|
+
{refs?.vectors === null
|
|
466
|
+
? null
|
|
467
|
+
: <>
|
|
468
|
+
<small className="fw-600">Sources</small>
|
|
469
|
+
<ol style={{ marginTop: 6 }}>
|
|
470
|
+
{refs?.docs.map((doc: any) => (
|
|
471
|
+
<li key={doc.url}>
|
|
472
|
+
<a
|
|
473
|
+
target="_blank"
|
|
474
|
+
className="color-accent"
|
|
475
|
+
href={doc.url}
|
|
476
|
+
>
|
|
477
|
+
<small>{doc.name}</small>
|
|
478
|
+
</a>
|
|
479
|
+
</li>
|
|
480
|
+
))}
|
|
481
|
+
</ol>
|
|
482
|
+
</>
|
|
483
|
+
}
|
|
484
|
+
</div>
|
|
485
|
+
</>
|
|
486
|
+
);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const AssistantMessage: FC = () => {
|
|
490
|
+
return (
|
|
491
|
+
<MessagePrimitive.Root asChild>
|
|
492
|
+
<motion.div
|
|
493
|
+
// aui-assistant-message-root
|
|
494
|
+
className="relative mx-auto grid w-full max-w-[var(--thread-max-width)] grid-cols-[auto_auto_1fr] grid-rows-[auto_1fr] px-[var(--thread-padding-x)] py-4"
|
|
495
|
+
initial={{ y: 5, opacity: 0 }}
|
|
496
|
+
animate={{ y: 0, opacity: 1 }}
|
|
497
|
+
data-role="assistant"
|
|
498
|
+
>
|
|
499
|
+
{/* aui-assistant-message-avatar */}
|
|
500
|
+
{/* <div className="ring-border bg-background col-start-1 row-start-1 flex size-8 shrink-0 items-center justify-center rounded-full ring-1">
|
|
501
|
+
<StarIcon size={14} />
|
|
502
|
+
</div> */}
|
|
503
|
+
|
|
504
|
+
{/* aui-assistant-message-content */}
|
|
505
|
+
<div className="text-foreground col-span-2 col-start-2 row-start-1 ml-4 leading-7 break-words" style={{ color: '#e8e8e3' }}>
|
|
506
|
+
<MessagePrimitive.Content
|
|
507
|
+
components={{
|
|
508
|
+
Text: MarkdownText,
|
|
509
|
+
tools: { Fallback: CustomToolFallback }
|
|
510
|
+
}}
|
|
511
|
+
/>
|
|
512
|
+
<MessageError />
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
<AssistantActionBar />
|
|
516
|
+
|
|
517
|
+
{/* aui-assistant-branch-picker */}
|
|
518
|
+
<BranchPicker className="col-start-2 row-start-2 mr-2 -ml-2" />
|
|
519
|
+
</motion.div>
|
|
520
|
+
</MessagePrimitive.Root>
|
|
521
|
+
);
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const AssistantActionBar: FC = () => {
|
|
525
|
+
return (
|
|
526
|
+
<ActionBarPrimitive.Root
|
|
527
|
+
hideWhenRunning
|
|
528
|
+
autohide="not-last"
|
|
529
|
+
autohideFloat="single-branch"
|
|
530
|
+
// aui-assistant-action-bar-root
|
|
531
|
+
className="text-muted-foreground data-floating:bg-background col-start-3 row-start-2 mt-3 ml-3 flex gap-1 data-floating:absolute data-floating:mt-2 data-floating:rounded-md data-floating:border data-floating:p-1 data-floating:shadow-sm"
|
|
532
|
+
>
|
|
533
|
+
<ActionBarPrimitive.Copy asChild>
|
|
534
|
+
<TooltipIconButton tooltip="Copy">
|
|
535
|
+
<MessagePrimitive.If copied>
|
|
536
|
+
<CheckIcon />
|
|
537
|
+
</MessagePrimitive.If>
|
|
538
|
+
<MessagePrimitive.If copied={false}>
|
|
539
|
+
<CopyIcon />
|
|
540
|
+
</MessagePrimitive.If>
|
|
541
|
+
</TooltipIconButton>
|
|
542
|
+
</ActionBarPrimitive.Copy>
|
|
543
|
+
<ActionBarPrimitive.Reload asChild>
|
|
544
|
+
<TooltipIconButton tooltip="Refresh">
|
|
545
|
+
<RefreshCwIcon />
|
|
546
|
+
</TooltipIconButton>
|
|
547
|
+
</ActionBarPrimitive.Reload>
|
|
548
|
+
</ActionBarPrimitive.Root>
|
|
549
|
+
);
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
const UserMessage: FC = () => {
|
|
553
|
+
const { content } = useMessage()
|
|
554
|
+
const message = content[0]
|
|
555
|
+
|
|
556
|
+
if (message.type === 'text' && message.text === 'Please describe in plain english.') {
|
|
557
|
+
return null
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return (
|
|
561
|
+
<MessagePrimitive.Root asChild>
|
|
562
|
+
<motion.div
|
|
563
|
+
// aui-user-message-root
|
|
564
|
+
className="mx-auto grid w-full max-w-[var(--thread-max-width)] auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] gap-y-1 px-[var(--thread-padding-x)] py-4 [&:where(>*)]:col-start-2"
|
|
565
|
+
initial={{ y: 5, opacity: 0 }}
|
|
566
|
+
animate={{ y: 0, opacity: 1 }}
|
|
567
|
+
data-role="user"
|
|
568
|
+
>
|
|
569
|
+
<UserActionBar />
|
|
570
|
+
|
|
571
|
+
{/* aui-user-message-content */}
|
|
572
|
+
<div className="text-default col-start-2 rounded-3xl px-5 py-1 break-words" style={{ color: '#e8e8e3' }}>
|
|
573
|
+
<MessagePrimitive.Content
|
|
574
|
+
components={{ Text: MarkdownText }}
|
|
575
|
+
/>
|
|
576
|
+
<small style={{ float: 'right', color: '#8c8c8c', fontSize: 12, marginTop: 6 }}>{`${new Date().toLocaleString('en-US')}`}</small>
|
|
577
|
+
</div>
|
|
578
|
+
|
|
579
|
+
{/* aui-user-branch-picker */}
|
|
580
|
+
<BranchPicker className="col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
|
|
581
|
+
</motion.div>
|
|
582
|
+
</MessagePrimitive.Root>
|
|
583
|
+
);
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const UserActionBar: FC = () => {
|
|
587
|
+
return (
|
|
588
|
+
<ActionBarPrimitive.Root
|
|
589
|
+
hideWhenRunning
|
|
590
|
+
autohide="not-last"
|
|
591
|
+
// aui-user-action-bar-root
|
|
592
|
+
className="col-start-1 mt-2.5 mr-3 flex flex-col items-end"
|
|
593
|
+
>
|
|
594
|
+
<ActionBarPrimitive.Edit asChild>
|
|
595
|
+
<TooltipIconButton tooltip="Edit?">
|
|
596
|
+
<PencilIcon style={{ color: '#fff' }} />
|
|
597
|
+
</TooltipIconButton>
|
|
598
|
+
</ActionBarPrimitive.Edit>
|
|
599
|
+
</ActionBarPrimitive.Root>
|
|
600
|
+
);
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
const EditComposer: FC = () => {
|
|
604
|
+
return (
|
|
605
|
+
// aui-edit-composer-wrapper
|
|
606
|
+
<div className="mx-auto flex w-full max-w-[var(--thread-max-width)] flex-col gap-4 px-[var(--thread-padding-x)]">
|
|
607
|
+
{/* aui-edit-composer-root */}
|
|
608
|
+
<ComposerPrimitive.Root
|
|
609
|
+
className="bg-muted ml-auto flex w-full max-w-7/8 flex-col rounded-xl"
|
|
610
|
+
style={{ background: '#1f2121' }}
|
|
611
|
+
>
|
|
612
|
+
{/* aui-edit-composer-input */}
|
|
613
|
+
<ComposerPrimitive.Input
|
|
614
|
+
className="text-foreground flex min-h-[60px] w-full resize-none bg-transparent p-4 outline-none"
|
|
615
|
+
autoFocus
|
|
616
|
+
/>
|
|
617
|
+
|
|
618
|
+
{/* aui-edit-composer-footer */}
|
|
619
|
+
<div className="mx-3 mb-3 flex items-center justify-center gap-2 self-end">
|
|
620
|
+
<ComposerPrimitive.Cancel asChild>
|
|
621
|
+
<Button variant="ghost" size="sm" aria-label="Cancel edit">
|
|
622
|
+
Cancel
|
|
623
|
+
</Button>
|
|
624
|
+
</ComposerPrimitive.Cancel>
|
|
625
|
+
<ComposerPrimitive.Send asChild>
|
|
626
|
+
<Button size="sm" aria-label="Update message">
|
|
627
|
+
Update
|
|
628
|
+
</Button>
|
|
629
|
+
</ComposerPrimitive.Send>
|
|
630
|
+
</div>
|
|
631
|
+
</ComposerPrimitive.Root>
|
|
632
|
+
</div>
|
|
633
|
+
);
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
637
|
+
className,
|
|
638
|
+
...rest
|
|
639
|
+
}) => {
|
|
640
|
+
return (
|
|
641
|
+
<BranchPickerPrimitive.Root
|
|
642
|
+
hideWhenSingleBranch
|
|
643
|
+
// aui-branch-picker-root
|
|
644
|
+
className={cn(
|
|
645
|
+
"text-muted-foreground inline-flex items-center text-xs",
|
|
646
|
+
className,
|
|
647
|
+
)}
|
|
648
|
+
{...rest}
|
|
649
|
+
>
|
|
650
|
+
<BranchPickerPrimitive.Previous asChild>
|
|
651
|
+
<TooltipIconButton tooltip="Previous">
|
|
652
|
+
<ChevronLeftIcon />
|
|
653
|
+
</TooltipIconButton>
|
|
654
|
+
</BranchPickerPrimitive.Previous>
|
|
655
|
+
{/* aui-branch-picker-state */}
|
|
656
|
+
<span className="font-medium">
|
|
657
|
+
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
|
658
|
+
</span>
|
|
659
|
+
<BranchPickerPrimitive.Next asChild>
|
|
660
|
+
<TooltipIconButton tooltip="Next">
|
|
661
|
+
<ChevronRightIcon />
|
|
662
|
+
</TooltipIconButton>
|
|
663
|
+
</BranchPickerPrimitive.Next>
|
|
664
|
+
</BranchPickerPrimitive.Root>
|
|
665
|
+
);
|
|
666
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useMemo, useState, useEffect } from "react";
|
|
2
|
+
import { ToolCallContentPartComponent } from "@assistant-ui/react";
|
|
3
|
+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
4
|
+
import { Button } from "../ui/button";
|
|
5
|
+
|
|
6
|
+
export const ToolFallback: ToolCallContentPartComponent = ({
|
|
7
|
+
toolName,
|
|
8
|
+
argsText,
|
|
9
|
+
result,
|
|
10
|
+
}) => {
|
|
11
|
+
const [isCollapsed, setIsCollapsed] = useState(true)
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="mb-0 flex w-full flex-col gap-3 rounded-lg border py-3" style={{ cursor: 'pointer' }} onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
15
|
+
<div className="flex items-center gap-2 px-4">
|
|
16
|
+
<CheckIcon className="size-4" />
|
|
17
|
+
<p>
|
|
18
|
+
Using tool: <b>{toolName}</b>
|
|
19
|
+
</p>
|
|
20
|
+
<div className="flex-grow" />
|
|
21
|
+
<Button onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
22
|
+
{isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
23
|
+
</Button>
|
|
24
|
+
</div>
|
|
25
|
+
{!isCollapsed && (
|
|
26
|
+
<div className="flex flex-col gap-2 border-t pt-2">
|
|
27
|
+
<div className="px-4">
|
|
28
|
+
<pre className="whitespace-pre-wrap">{argsText}</pre>
|
|
29
|
+
</div>
|
|
30
|
+
{result !== undefined && (
|
|
31
|
+
<div className="border-t border-dashed px-4 pt-2">
|
|
32
|
+
<p className="font-semibold">Result:</p>
|
|
33
|
+
<pre className="whitespace-pre-wrap">
|
|
34
|
+
{typeof result === "string"
|
|
35
|
+
? result
|
|
36
|
+
: JSON.stringify(result, null, 2)}
|
|
37
|
+
</pre>
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|