langwatch 0.2.0 → 0.3.0-prerelease.2

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.
Files changed (229) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +268 -1
  3. package/dist/chunk-4BZATFKJ.mjs +181 -0
  4. package/dist/chunk-4BZATFKJ.mjs.map +1 -0
  5. package/dist/chunk-CSC3CMIT.mjs +118 -0
  6. package/dist/chunk-CSC3CMIT.mjs.map +1 -0
  7. package/dist/chunk-F63YKTXA.mjs +47 -0
  8. package/dist/chunk-F63YKTXA.mjs.map +1 -0
  9. package/dist/chunk-G3AUABT7.js +4 -0
  10. package/dist/chunk-G3AUABT7.js.map +1 -0
  11. package/dist/chunk-HPC6Z7J4.js +118 -0
  12. package/dist/chunk-HPC6Z7J4.js.map +1 -0
  13. package/dist/chunk-KGDAENGD.js +50 -0
  14. package/dist/chunk-KGDAENGD.js.map +1 -0
  15. package/dist/chunk-LD74LVRU.js +47 -0
  16. package/dist/chunk-LD74LVRU.js.map +1 -0
  17. package/dist/chunk-OM7VY3XT.mjs +4 -0
  18. package/dist/chunk-PCQVQ7SB.js +45 -0
  19. package/dist/chunk-PCQVQ7SB.js.map +1 -0
  20. package/dist/chunk-PMBEK6YE.mjs +424 -0
  21. package/dist/chunk-PMBEK6YE.mjs.map +1 -0
  22. package/dist/chunk-PR3JDWC3.mjs +50 -0
  23. package/dist/chunk-PR3JDWC3.mjs.map +1 -0
  24. package/dist/chunk-PTJ6AAI7.js +360 -0
  25. package/dist/chunk-PTJ6AAI7.js.map +1 -0
  26. package/dist/chunk-QEWDG5QE.mjs +45 -0
  27. package/dist/chunk-QEWDG5QE.mjs.map +1 -0
  28. package/dist/chunk-REUCVT7A.mjs +39 -0
  29. package/dist/chunk-REUCVT7A.mjs.map +1 -0
  30. package/dist/chunk-SVJ7SCGB.js +424 -0
  31. package/dist/chunk-SVJ7SCGB.js.map +1 -0
  32. package/dist/chunk-VJSOCNPA.js +181 -0
  33. package/dist/chunk-VJSOCNPA.js.map +1 -0
  34. package/dist/chunk-WM2GRSRW.js +39 -0
  35. package/dist/chunk-WM2GRSRW.js.map +1 -0
  36. package/dist/chunk-Z5J5UI5E.mjs +360 -0
  37. package/dist/chunk-Z5J5UI5E.mjs.map +1 -0
  38. package/dist/client-B2HqIKg6.d.ts +51 -0
  39. package/dist/client-XyCqclCi.d.mts +51 -0
  40. package/dist/client-browser.d.mts +8 -0
  41. package/dist/client-browser.d.ts +8 -0
  42. package/dist/client-browser.js +83 -0
  43. package/dist/client-browser.js.map +1 -0
  44. package/dist/client-browser.mjs +83 -0
  45. package/dist/client-browser.mjs.map +1 -0
  46. package/dist/client-node.d.mts +8 -0
  47. package/dist/client-node.d.ts +8 -0
  48. package/dist/client-node.js +90 -0
  49. package/dist/client-node.js.map +1 -0
  50. package/dist/client-node.mjs +90 -0
  51. package/dist/client-node.mjs.map +1 -0
  52. package/dist/evaluation/index.d.mts +897 -0
  53. package/dist/evaluation/index.d.ts +897 -0
  54. package/dist/evaluation/index.js +13 -0
  55. package/dist/evaluation/index.js.map +1 -0
  56. package/dist/evaluation/index.mjs +13 -0
  57. package/dist/evaluation/index.mjs.map +1 -0
  58. package/dist/filterable-batch-span-processor-zO5kcjBY.d.mts +64 -0
  59. package/dist/filterable-batch-span-processor-zO5kcjBY.d.ts +64 -0
  60. package/dist/index.d.mts +45 -1027
  61. package/dist/index.d.ts +45 -1027
  62. package/dist/index.js +11 -27291
  63. package/dist/index.js.map +1 -1
  64. package/dist/index.mjs +23 -956
  65. package/dist/index.mjs.map +1 -1
  66. package/dist/observability/index.d.mts +260 -0
  67. package/dist/observability/index.d.ts +260 -0
  68. package/dist/observability/index.js +20 -0
  69. package/dist/observability/index.js.map +1 -0
  70. package/dist/observability/index.mjs +20 -0
  71. package/dist/observability/index.mjs.map +1 -0
  72. package/dist/observability/instrumentation/langchain/index.d.mts +40 -0
  73. package/dist/observability/instrumentation/langchain/index.d.ts +40 -0
  74. package/dist/observability/instrumentation/langchain/index.js +666 -0
  75. package/dist/observability/instrumentation/langchain/index.js.map +1 -0
  76. package/dist/observability/instrumentation/langchain/index.mjs +666 -0
  77. package/dist/observability/instrumentation/langchain/index.mjs.map +1 -0
  78. package/dist/prompt/index.d.mts +10 -0
  79. package/dist/prompt/index.d.ts +10 -0
  80. package/dist/prompt/index.js +18 -0
  81. package/dist/prompt/index.js.map +1 -0
  82. package/dist/prompt/index.mjs +18 -0
  83. package/dist/prompt/index.mjs.map +1 -0
  84. package/dist/prompt-BXJWdbQp.d.mts +1967 -0
  85. package/dist/prompt-BXJWdbQp.d.ts +1967 -0
  86. package/dist/record-evaluation-CmxMXa-3.d.mts +25 -0
  87. package/dist/record-evaluation-CmxMXa-3.d.ts +25 -0
  88. package/dist/trace-D-bZOuqb.d.mts +622 -0
  89. package/dist/trace-G2312klE.d.ts +622 -0
  90. package/package.json +86 -37
  91. package/.eslintrc.cjs +0 -37
  92. package/copy-types.sh +0 -17
  93. package/dist/chunk-LKD2K67J.mjs +0 -717
  94. package/dist/chunk-LKD2K67J.mjs.map +0 -1
  95. package/dist/utils-Cv-rUjJ1.d.mts +0 -313
  96. package/dist/utils-Cv-rUjJ1.d.ts +0 -313
  97. package/dist/utils.d.mts +0 -2
  98. package/dist/utils.d.ts +0 -2
  99. package/dist/utils.js +0 -709
  100. package/dist/utils.js.map +0 -1
  101. package/dist/utils.mjs +0 -11
  102. package/example/.env.example +0 -12
  103. package/example/.eslintrc.json +0 -26
  104. package/example/LICENSE +0 -13
  105. package/example/README.md +0 -12
  106. package/example/app/(chat)/chat/[id]/page.tsx +0 -60
  107. package/example/app/(chat)/layout.tsx +0 -14
  108. package/example/app/(chat)/page.tsx +0 -27
  109. package/example/app/actions.ts +0 -156
  110. package/example/app/globals.css +0 -76
  111. package/example/app/guardrails/page.tsx +0 -26
  112. package/example/app/langchain/page.tsx +0 -27
  113. package/example/app/langchain-rag/page.tsx +0 -28
  114. package/example/app/late-update/page.tsx +0 -27
  115. package/example/app/layout.tsx +0 -64
  116. package/example/app/login/actions.ts +0 -71
  117. package/example/app/login/page.tsx +0 -18
  118. package/example/app/manual/page.tsx +0 -27
  119. package/example/app/new/page.tsx +0 -5
  120. package/example/app/opengraph-image.png +0 -0
  121. package/example/app/share/[id]/page.tsx +0 -58
  122. package/example/app/signup/actions.ts +0 -111
  123. package/example/app/signup/page.tsx +0 -18
  124. package/example/app/twitter-image.png +0 -0
  125. package/example/auth.config.ts +0 -42
  126. package/example/auth.ts +0 -45
  127. package/example/components/button-scroll-to-bottom.tsx +0 -36
  128. package/example/components/chat-history.tsx +0 -49
  129. package/example/components/chat-list.tsx +0 -52
  130. package/example/components/chat-message-actions.tsx +0 -40
  131. package/example/components/chat-message.tsx +0 -80
  132. package/example/components/chat-panel.tsx +0 -139
  133. package/example/components/chat-share-dialog.tsx +0 -95
  134. package/example/components/chat.tsx +0 -84
  135. package/example/components/clear-history.tsx +0 -75
  136. package/example/components/empty-screen.tsx +0 -38
  137. package/example/components/external-link.tsx +0 -29
  138. package/example/components/footer.tsx +0 -19
  139. package/example/components/header.tsx +0 -114
  140. package/example/components/login-button.tsx +0 -42
  141. package/example/components/login-form.tsx +0 -97
  142. package/example/components/markdown.tsx +0 -9
  143. package/example/components/prompt-form.tsx +0 -115
  144. package/example/components/providers.tsx +0 -17
  145. package/example/components/sidebar-actions.tsx +0 -125
  146. package/example/components/sidebar-desktop.tsx +0 -19
  147. package/example/components/sidebar-footer.tsx +0 -16
  148. package/example/components/sidebar-item.tsx +0 -124
  149. package/example/components/sidebar-items.tsx +0 -42
  150. package/example/components/sidebar-list.tsx +0 -38
  151. package/example/components/sidebar-mobile.tsx +0 -31
  152. package/example/components/sidebar-toggle.tsx +0 -24
  153. package/example/components/sidebar.tsx +0 -21
  154. package/example/components/signup-form.tsx +0 -95
  155. package/example/components/stocks/events-skeleton.tsx +0 -31
  156. package/example/components/stocks/events.tsx +0 -30
  157. package/example/components/stocks/index.tsx +0 -36
  158. package/example/components/stocks/message.tsx +0 -134
  159. package/example/components/stocks/spinner.tsx +0 -16
  160. package/example/components/stocks/stock-purchase.tsx +0 -146
  161. package/example/components/stocks/stock-skeleton.tsx +0 -22
  162. package/example/components/stocks/stock.tsx +0 -210
  163. package/example/components/stocks/stocks-skeleton.tsx +0 -9
  164. package/example/components/stocks/stocks.tsx +0 -67
  165. package/example/components/tailwind-indicator.tsx +0 -14
  166. package/example/components/theme-toggle.tsx +0 -31
  167. package/example/components/ui/alert-dialog.tsx +0 -141
  168. package/example/components/ui/badge.tsx +0 -36
  169. package/example/components/ui/button.tsx +0 -57
  170. package/example/components/ui/codeblock.tsx +0 -148
  171. package/example/components/ui/dialog.tsx +0 -122
  172. package/example/components/ui/dropdown-menu.tsx +0 -205
  173. package/example/components/ui/icons.tsx +0 -507
  174. package/example/components/ui/input.tsx +0 -25
  175. package/example/components/ui/label.tsx +0 -26
  176. package/example/components/ui/select.tsx +0 -164
  177. package/example/components/ui/separator.tsx +0 -31
  178. package/example/components/ui/sheet.tsx +0 -140
  179. package/example/components/ui/sonner.tsx +0 -31
  180. package/example/components/ui/switch.tsx +0 -29
  181. package/example/components/ui/textarea.tsx +0 -24
  182. package/example/components/ui/tooltip.tsx +0 -30
  183. package/example/components/user-menu.tsx +0 -53
  184. package/example/components.json +0 -17
  185. package/example/instrumentation.ts +0 -11
  186. package/example/lib/chat/guardrails.tsx +0 -181
  187. package/example/lib/chat/langchain-rag.tsx +0 -191
  188. package/example/lib/chat/langchain.tsx +0 -112
  189. package/example/lib/chat/late-update.tsx +0 -208
  190. package/example/lib/chat/manual.tsx +0 -605
  191. package/example/lib/chat/vercel-ai.tsx +0 -576
  192. package/example/lib/hooks/use-copy-to-clipboard.tsx +0 -33
  193. package/example/lib/hooks/use-enter-submit.tsx +0 -23
  194. package/example/lib/hooks/use-local-storage.ts +0 -24
  195. package/example/lib/hooks/use-scroll-anchor.tsx +0 -86
  196. package/example/lib/hooks/use-sidebar.tsx +0 -60
  197. package/example/lib/hooks/use-streamable-text.ts +0 -25
  198. package/example/lib/types.ts +0 -41
  199. package/example/lib/utils.ts +0 -89
  200. package/example/middleware.ts +0 -8
  201. package/example/next-env.d.ts +0 -5
  202. package/example/next.config.js +0 -16
  203. package/example/package-lock.json +0 -10917
  204. package/example/package.json +0 -84
  205. package/example/pnpm-lock.yaml +0 -5712
  206. package/example/postcss.config.js +0 -6
  207. package/example/prettier.config.cjs +0 -34
  208. package/example/public/apple-touch-icon.png +0 -0
  209. package/example/public/favicon-16x16.png +0 -0
  210. package/example/public/favicon.ico +0 -0
  211. package/example/public/next.svg +0 -1
  212. package/example/public/thirteen.svg +0 -1
  213. package/example/public/vercel.svg +0 -1
  214. package/example/tailwind.config.ts +0 -81
  215. package/example/tsconfig.json +0 -35
  216. package/src/LangWatchExporter.ts +0 -96
  217. package/src/evaluations.ts +0 -219
  218. package/src/index.test.ts +0 -402
  219. package/src/index.ts +0 -596
  220. package/src/langchain.ts +0 -557
  221. package/src/server/types/.gitkeep +0 -0
  222. package/src/typeUtils.ts +0 -89
  223. package/src/types.ts +0 -82
  224. package/src/utils.ts +0 -205
  225. package/ts-to-zod.config.js +0 -24
  226. package/tsconfig.json +0 -32
  227. package/tsup.config.ts +0 -10
  228. package/vitest.config.ts +0 -8
  229. /package/dist/{utils.mjs.map → chunk-OM7VY3XT.mjs.map} +0 -0
@@ -1,80 +0,0 @@
1
- // Inspired by Chatbot-UI and modified to fit the needs of this project
2
- // @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Chat/ChatMessage.tsx
3
-
4
- import { Message } from 'ai'
5
- import remarkGfm from 'remark-gfm'
6
- import remarkMath from 'remark-math'
7
-
8
- import { cn } from '@/lib/utils'
9
- import { CodeBlock } from '@/components/ui/codeblock'
10
- import { MemoizedReactMarkdown } from '@/components/markdown'
11
- import { IconOpenAI, IconUser } from '@/components/ui/icons'
12
- import { ChatMessageActions } from '@/components/chat-message-actions'
13
-
14
- export interface ChatMessageProps {
15
- message: Message
16
- }
17
-
18
- export function ChatMessage({ message, ...props }: ChatMessageProps) {
19
- return (
20
- <div
21
- className={cn('group relative mb-4 flex items-start md:-ml-12')}
22
- {...props}
23
- >
24
- <div
25
- className={cn(
26
- 'flex size-8 shrink-0 select-none items-center justify-center rounded-md border shadow',
27
- message.role === 'user'
28
- ? 'bg-background'
29
- : 'bg-primary text-primary-foreground'
30
- )}
31
- >
32
- {message.role === 'user' ? <IconUser /> : <IconOpenAI />}
33
- </div>
34
- <div className="flex-1 px-1 ml-4 space-y-2 overflow-hidden">
35
- <MemoizedReactMarkdown
36
- className="prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0"
37
- remarkPlugins={[remarkGfm, remarkMath]}
38
- components={{
39
- p({ children }) {
40
- return <p className="mb-2 last:mb-0">{children}</p>
41
- },
42
- code({ node, inline, className, children, ...props }) {
43
- if (children.length) {
44
- if (children[0] == '▍') {
45
- return (
46
- <span className="mt-1 cursor-default animate-pulse">▍</span>
47
- )
48
- }
49
-
50
- children[0] = (children[0] as string).replace('`▍`', '▍')
51
- }
52
-
53
- const match = /language-(\w+)/.exec(className || '')
54
-
55
- if (inline) {
56
- return (
57
- <code className={className} {...props}>
58
- {children}
59
- </code>
60
- )
61
- }
62
-
63
- return (
64
- <CodeBlock
65
- key={Math.random()}
66
- language={(match && match[1]) || ''}
67
- value={String(children).replace(/\n$/, '')}
68
- {...props}
69
- />
70
- )
71
- }
72
- }}
73
- >
74
- {message.content}
75
- </MemoizedReactMarkdown>
76
- <ChatMessageActions message={message} />
77
- </div>
78
- </div>
79
- )
80
- }
@@ -1,139 +0,0 @@
1
- import * as React from 'react'
2
-
3
- import { shareChat } from '@/app/actions'
4
- import { Button } from '@/components/ui/button'
5
- import { PromptForm } from '@/components/prompt-form'
6
- import { ButtonScrollToBottom } from '@/components/button-scroll-to-bottom'
7
- import { IconShare } from '@/components/ui/icons'
8
- import { FooterText } from '@/components/footer'
9
- import { ChatShareDialog } from '@/components/chat-share-dialog'
10
- import { useAIState, useActions, useUIState } from 'ai/rsc'
11
- import type { AI } from '@/lib/chat/vercel-ai'
12
- import { nanoid } from 'nanoid'
13
- import { UserMessage } from './stocks/message'
14
-
15
- export interface ChatPanelProps {
16
- id?: string
17
- title?: string
18
- input: string
19
- setInput: (value: string) => void
20
- isAtBottom: boolean
21
- scrollToBottom: () => void
22
- }
23
-
24
- export function ChatPanel({
25
- id,
26
- title,
27
- input,
28
- setInput,
29
- isAtBottom,
30
- scrollToBottom
31
- }: ChatPanelProps) {
32
- const [aiState] = useAIState()
33
- const [messages, setMessages] = useUIState<typeof AI>()
34
- const { submitUserMessage } = useActions()
35
- const [shareDialogOpen, setShareDialogOpen] = React.useState(false)
36
-
37
- const exampleMessages = [
38
- {
39
- heading: 'What are the',
40
- subheading: 'trending memecoins today?',
41
- message: `What are the trending memecoins today?`
42
- },
43
- {
44
- heading: 'What is the price of',
45
- subheading: '$DOGE right now?',
46
- message: 'What is the price of $DOGE right now?'
47
- },
48
- {
49
- heading: 'I would like to buy',
50
- subheading: '42 $DOGE',
51
- message: `I would like to buy 42 $DOGE`
52
- },
53
- {
54
- heading: 'What are some',
55
- subheading: `recent events about $DOGE?`,
56
- message: `What are some recent events about $DOGE?`
57
- }
58
- ]
59
-
60
- return (
61
- <div className="fixed inset-x-0 bottom-0 w-full bg-gradient-to-b from-muted/30 from-0% to-muted/30 to-50% duration-300 ease-in-out animate-in dark:from-background/10 dark:from-10% dark:to-background/80 peer-[[data-state=open]]:group-[]:lg:pl-[250px] peer-[[data-state=open]]:group-[]:xl:pl-[300px]">
62
- <ButtonScrollToBottom
63
- isAtBottom={isAtBottom}
64
- scrollToBottom={scrollToBottom}
65
- />
66
-
67
- <div className="mx-auto sm:max-w-2xl sm:px-4">
68
- <div className="mb-4 grid grid-cols-2 gap-2 px-4 sm:px-0">
69
- {messages.length === 0 &&
70
- exampleMessages.map((example, index) => (
71
- <div
72
- key={example.heading}
73
- className={`cursor-pointer rounded-lg border bg-white p-4 hover:bg-zinc-50 dark:bg-zinc-950 dark:hover:bg-zinc-900 ${
74
- index > 1 && 'hidden md:block'
75
- }`}
76
- onClick={async () => {
77
- setMessages(currentMessages => [
78
- ...currentMessages,
79
- {
80
- id: nanoid(),
81
- display: <UserMessage>{example.message}</UserMessage>
82
- }
83
- ])
84
-
85
- const responseMessage = await submitUserMessage(
86
- example.message
87
- )
88
-
89
- setMessages(currentMessages => [
90
- ...currentMessages,
91
- responseMessage
92
- ])
93
- }}
94
- >
95
- <div className="text-sm font-semibold">{example.heading}</div>
96
- <div className="text-sm text-zinc-600">
97
- {example.subheading}
98
- </div>
99
- </div>
100
- ))}
101
- </div>
102
-
103
- {messages?.length >= 2 ? (
104
- <div className="flex h-12 items-center justify-center">
105
- <div className="flex space-x-2">
106
- {id && title ? (
107
- <>
108
- <Button
109
- variant="outline"
110
- onClick={() => setShareDialogOpen(true)}
111
- >
112
- <IconShare className="mr-2" />
113
- Share
114
- </Button>
115
- <ChatShareDialog
116
- open={shareDialogOpen}
117
- onOpenChange={setShareDialogOpen}
118
- onCopy={() => setShareDialogOpen(false)}
119
- shareChat={shareChat}
120
- chat={{
121
- id,
122
- title,
123
- messages: aiState.messages
124
- }}
125
- />
126
- </>
127
- ) : null}
128
- </div>
129
- </div>
130
- ) : null}
131
-
132
- <div className="space-y-4 border-t bg-background px-4 py-2 shadow-lg sm:rounded-t-xl sm:border md:py-4">
133
- <PromptForm input={input} setInput={setInput} />
134
- <FooterText className="hidden sm:block" />
135
- </div>
136
- </div>
137
- </div>
138
- )
139
- }
@@ -1,95 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { type DialogProps } from '@radix-ui/react-dialog'
5
- import { toast } from 'sonner'
6
-
7
- import { ServerActionResult, type Chat } from '@/lib/types'
8
- import { Button } from '@/components/ui/button'
9
- import {
10
- Dialog,
11
- DialogContent,
12
- DialogDescription,
13
- DialogFooter,
14
- DialogHeader,
15
- DialogTitle
16
- } from '@/components/ui/dialog'
17
- import { IconSpinner } from '@/components/ui/icons'
18
- import { useCopyToClipboard } from '@/lib/hooks/use-copy-to-clipboard'
19
-
20
- interface ChatShareDialogProps extends DialogProps {
21
- chat: Pick<Chat, 'id' | 'title' | 'messages'>
22
- shareChat: (id: string) => ServerActionResult<Chat>
23
- onCopy: () => void
24
- }
25
-
26
- export function ChatShareDialog({
27
- chat,
28
- shareChat,
29
- onCopy,
30
- ...props
31
- }: ChatShareDialogProps) {
32
- const { copyToClipboard } = useCopyToClipboard({ timeout: 1000 })
33
- const [isSharePending, startShareTransition] = React.useTransition()
34
-
35
- const copyShareLink = React.useCallback(
36
- async (chat: Chat) => {
37
- if (!chat.sharePath) {
38
- return toast.error('Could not copy share link to clipboard')
39
- }
40
-
41
- const url = new URL(window.location.href)
42
- url.pathname = chat.sharePath
43
- copyToClipboard(url.toString())
44
- onCopy()
45
- toast.success('Share link copied to clipboard')
46
- },
47
- [copyToClipboard, onCopy]
48
- )
49
-
50
- return (
51
- <Dialog {...props}>
52
- <DialogContent>
53
- <DialogHeader>
54
- <DialogTitle>Share link to chat</DialogTitle>
55
- <DialogDescription>
56
- Anyone with the URL will be able to view the shared chat.
57
- </DialogDescription>
58
- </DialogHeader>
59
- <div className="p-4 space-y-1 text-sm border rounded-md">
60
- <div className="font-medium">{chat.title}</div>
61
- <div className="text-muted-foreground">
62
- {chat.messages.length} messages
63
- </div>
64
- </div>
65
- <DialogFooter className="items-center">
66
- <Button
67
- disabled={isSharePending}
68
- onClick={() => {
69
- // @ts-ignore
70
- startShareTransition(async () => {
71
- const result = await shareChat(chat.id)
72
-
73
- if (result && 'error' in result) {
74
- toast.error(result.error)
75
- return
76
- }
77
-
78
- copyShareLink(result)
79
- })
80
- }}
81
- >
82
- {isSharePending ? (
83
- <>
84
- <IconSpinner className="mr-2 animate-spin" />
85
- Copying...
86
- </>
87
- ) : (
88
- <>Copy link</>
89
- )}
90
- </Button>
91
- </DialogFooter>
92
- </DialogContent>
93
- </Dialog>
94
- )
95
- }
@@ -1,84 +0,0 @@
1
- 'use client'
2
-
3
- import { cn } from '@/lib/utils'
4
- import { ChatList } from '@/components/chat-list'
5
- import { ChatPanel } from '@/components/chat-panel'
6
- import { EmptyScreen } from '@/components/empty-screen'
7
- import { useLocalStorage } from '@/lib/hooks/use-local-storage'
8
- import { useEffect, useState } from 'react'
9
- import { useUIState, useAIState } from 'ai/rsc'
10
- import { Message, Session } from '@/lib/types'
11
- import { usePathname, useRouter } from 'next/navigation'
12
- import { useScrollAnchor } from '@/lib/hooks/use-scroll-anchor'
13
- import { toast } from 'sonner'
14
-
15
- export interface ChatProps extends React.ComponentProps<'div'> {
16
- initialMessages?: Message[]
17
- id?: string
18
- session?: Session
19
- missingKeys: string[]
20
- }
21
-
22
- export function Chat({ id, className, session, missingKeys }: ChatProps) {
23
- const router = useRouter()
24
- const path = usePathname()
25
- const [input, setInput] = useState('')
26
- const [messages] = useUIState()
27
- const [aiState] = useAIState()
28
-
29
- const [_, setNewChatId] = useLocalStorage('newChatId', id)
30
-
31
- useEffect(() => {
32
- if (session?.user) {
33
- if (!path.includes('chat') && messages.length === 1) {
34
- window.history.replaceState({}, '', `/chat/${id}`)
35
- }
36
- }
37
- }, [id, path, session?.user, messages])
38
-
39
- useEffect(() => {
40
- const messagesLength = aiState.messages?.length
41
- if (messagesLength === 2) {
42
- router.refresh()
43
- }
44
- }, [aiState.messages, router])
45
-
46
- useEffect(() => {
47
- setNewChatId(id)
48
- })
49
-
50
- useEffect(() => {
51
- missingKeys.map(key => {
52
- toast.error(`Missing ${key} environment variable!`)
53
- })
54
- }, [missingKeys])
55
-
56
- const { messagesRef, scrollRef, visibilityRef, isAtBottom, scrollToBottom } =
57
- useScrollAnchor()
58
-
59
- return (
60
- <div
61
- className="group w-full overflow-auto pl-0 peer-[[data-state=open]]:lg:pl-[250px] peer-[[data-state=open]]:xl:pl-[300px]"
62
- ref={scrollRef}
63
- >
64
- <div
65
- className={cn('pb-[200px] pt-4 md:pt-10', className)}
66
- ref={messagesRef}
67
- >
68
- {messages.length ? (
69
- <ChatList messages={messages} isShared={false} session={session} />
70
- ) : (
71
- <EmptyScreen />
72
- )}
73
- <div className="w-full h-px" ref={visibilityRef} />
74
- </div>
75
- <ChatPanel
76
- id={id}
77
- input={input}
78
- setInput={setInput}
79
- isAtBottom={isAtBottom}
80
- scrollToBottom={scrollToBottom}
81
- />
82
- </div>
83
- )
84
- }
@@ -1,75 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { useRouter } from 'next/navigation'
5
- import { toast } from 'sonner'
6
-
7
- import { ServerActionResult } from '@/lib/types'
8
- import { Button } from '@/components/ui/button'
9
- import {
10
- AlertDialog,
11
- AlertDialogAction,
12
- AlertDialogCancel,
13
- AlertDialogContent,
14
- AlertDialogDescription,
15
- AlertDialogFooter,
16
- AlertDialogHeader,
17
- AlertDialogTitle,
18
- AlertDialogTrigger
19
- } from '@/components/ui/alert-dialog'
20
- import { IconSpinner } from '@/components/ui/icons'
21
-
22
- interface ClearHistoryProps {
23
- isEnabled: boolean
24
- clearChats: () => ServerActionResult<void>
25
- }
26
-
27
- export function ClearHistory({
28
- isEnabled = false,
29
- clearChats
30
- }: ClearHistoryProps) {
31
- const [open, setOpen] = React.useState(false)
32
- const [isPending, startTransition] = React.useTransition()
33
- const router = useRouter()
34
-
35
- return (
36
- <AlertDialog open={open} onOpenChange={setOpen}>
37
- <AlertDialogTrigger asChild>
38
- <Button variant="ghost" disabled={!isEnabled || isPending}>
39
- {isPending && <IconSpinner className="mr-2" />}
40
- Clear history
41
- </Button>
42
- </AlertDialogTrigger>
43
- <AlertDialogContent>
44
- <AlertDialogHeader>
45
- <AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
46
- <AlertDialogDescription>
47
- This will permanently delete your chat history and remove your data
48
- from our servers.
49
- </AlertDialogDescription>
50
- </AlertDialogHeader>
51
- <AlertDialogFooter>
52
- <AlertDialogCancel disabled={isPending}>Cancel</AlertDialogCancel>
53
- <AlertDialogAction
54
- disabled={isPending}
55
- onClick={event => {
56
- event.preventDefault()
57
- startTransition(async () => {
58
- const result = await clearChats()
59
- if (result && 'error' in result) {
60
- toast.error(result.error)
61
- return
62
- }
63
-
64
- setOpen(false)
65
- })
66
- }}
67
- >
68
- {isPending && <IconSpinner className="mr-2 animate-spin" />}
69
- Delete
70
- </AlertDialogAction>
71
- </AlertDialogFooter>
72
- </AlertDialogContent>
73
- </AlertDialog>
74
- )
75
- }
@@ -1,38 +0,0 @@
1
- import { UseChatHelpers } from 'ai/react'
2
-
3
- import { Button } from '@/components/ui/button'
4
- import { ExternalLink } from '@/components/external-link'
5
- import { IconArrowRight } from '@/components/ui/icons'
6
-
7
- export function EmptyScreen() {
8
- return (
9
- <div className="mx-auto max-w-2xl px-4">
10
- <div className="flex flex-col gap-2 rounded-lg border bg-background p-8">
11
- <h1 className="text-lg font-semibold">
12
- Welcome to Next.js AI Chatbot!
13
- </h1>
14
- <p className="leading-normal text-muted-foreground">
15
- This is an open source AI chatbot app template built with{' '}
16
- <ExternalLink href="https://nextjs.org">Next.js</ExternalLink>, the{' '}
17
- <ExternalLink href="https://sdk.vercel.ai">
18
- Vercel AI SDK
19
- </ExternalLink>
20
- , and{' '}
21
- <ExternalLink href="https://vercel.com/storage/kv">
22
- Vercel KV
23
- </ExternalLink>
24
- .
25
- </p>
26
- <p className="leading-normal text-muted-foreground">
27
- It uses{' '}
28
- <ExternalLink href="https://vercel.com/blog/ai-sdk-3-generative-ui">
29
- React Server Components
30
- </ExternalLink>{' '}
31
- to combine text with generative UI as output of the LLM. The UI state
32
- is synced through the SDK so the model is aware of your interactions
33
- as they happen.
34
- </p>
35
- </div>
36
- </div>
37
- )
38
- }
@@ -1,29 +0,0 @@
1
- export function ExternalLink({
2
- href,
3
- children
4
- }: {
5
- href: string
6
- children: React.ReactNode
7
- }) {
8
- return (
9
- <a
10
- href={href}
11
- target="_blank"
12
- className="inline-flex flex-1 justify-center gap-1 leading-4 hover:underline"
13
- >
14
- <span>{children}</span>
15
- <svg
16
- aria-hidden="true"
17
- height="7"
18
- viewBox="0 0 6 6"
19
- width="7"
20
- className="opacity-70"
21
- >
22
- <path
23
- d="M1.25215 5.54731L0.622742 4.9179L3.78169 1.75597H1.3834L1.38936 0.890915H5.27615V4.78069H4.40513L4.41109 2.38538L1.25215 5.54731Z"
24
- fill="currentColor"
25
- ></path>
26
- </svg>
27
- </a>
28
- )
29
- }
@@ -1,19 +0,0 @@
1
- import React from 'react'
2
-
3
- import { cn } from '@/lib/utils'
4
- import { ExternalLink } from '@/components/external-link'
5
-
6
- export function FooterText({ className, ...props }: React.ComponentProps<'p'>) {
7
- return (
8
- <p
9
- className={cn(
10
- 'px-2 text-center text-xs leading-normal text-muted-foreground',
11
- className
12
- )}
13
- {...props}
14
- >
15
- TypeScript SDK demo chatbot for{' '}
16
- <ExternalLink href="https://langwatch.ai">LangWatch</ExternalLink>
17
- </p>
18
- )
19
- }
@@ -1,114 +0,0 @@
1
- import * as React from 'react'
2
- import Link from 'next/link'
3
-
4
- import { cn } from '@/lib/utils'
5
- import { auth } from '@/auth'
6
- import { Button, buttonVariants } from '@/components/ui/button'
7
- import {
8
- IconGitHub,
9
- IconNextChat,
10
- IconSeparator,
11
- IconVercel
12
- } from '@/components/ui/icons'
13
- import { UserMenu } from '@/components/user-menu'
14
- import { SidebarMobile } from './sidebar-mobile'
15
- import { SidebarToggle } from './sidebar-toggle'
16
- import { ChatHistory } from './chat-history'
17
- import { Session } from '@/lib/types'
18
-
19
- async function UserOrLogin() {
20
- const session = (await auth()) as Session
21
- return (
22
- <>
23
- {session?.user ? (
24
- <>
25
- <SidebarMobile>
26
- <ChatHistory userId={session.user.id} />
27
- </SidebarMobile>
28
- <SidebarToggle />
29
- </>
30
- ) : (
31
- <Link href="/new" rel="nofollow">
32
- <IconNextChat className="size-6 mr-2 dark:hidden" inverted />
33
- <IconNextChat className="hidden size-6 mr-2 dark:block" />
34
- </Link>
35
- )}
36
- <div className="flex items-center">
37
- <IconSeparator className="size-6 text-muted-foreground/50" />
38
- {session?.user ? (
39
- <UserMenu user={session.user} />
40
- ) : (
41
- <Button variant="plain" asChild className="-ml-2">
42
- <Link href="/login">Login</Link>
43
- </Button>
44
- )}
45
- </div>
46
- </>
47
- )
48
- }
49
-
50
- function LogoIcon({ width, height }: { width: number; height: number }) {
51
- return (
52
- <svg
53
- xmlns="http://www.w3.org/2000/svg"
54
- width={width}
55
- height={height}
56
- fill="none"
57
- viewBox="0 0 38 52"
58
- >
59
- <path
60
- fill="#fff"
61
- d="M0 12.383v28.652c0 .357.19.688.5.866l16.595 9.58a.993.993 0 001 0l19.184-11.072a1 1 0 00.5-.866V10.887a.998.998 0 00-.5-.866l-6.111-3.526a.999.999 0 00-.999 0l-2.874 1.659V4.837a.998.998 0 00-.5-.866L20.684.442a1.003 1.003 0 00-1 0l-5.903 3.409a1 1 0 00-.5.866v7.44l-.36.208v-.493a1 1 0 00-.5-.866L7.405 8.107a1.005 1.005 0 00-1 0l-5.904 3.41a.998.998 0 00-.501.866z"
62
- ></path>
63
- <path
64
- fill="#213B41"
65
- d="M0 12.383v28.652c0 .357.19.688.5.866l16.595 9.58a.993.993 0 001 0l19.184-11.072a1 1 0 00.5-.866V10.887a.998.998 0 00-.5-.866l-6.111-3.526a.999.999 0 00-.999 0l-2.874 1.659V4.837a.998.998 0 00-.5-.866L20.684.442a1.003 1.003 0 00-1 0l-5.903 3.409a1 1 0 00-.5.866v7.44l-.36.208v-.493a1 1 0 00-.5-.866L7.405 8.107a1.005 1.005 0 00-1 0l-5.904 3.41a.998.998 0 00-.501.866zm1.5.865l4.019 2.318v7.728c0 .01.005.019.006.029a.363.363 0 00.009.065.46.46 0 00.043.128c.005.009.004.019.01.028.004.007.013.01.017.017a.464.464 0 00.12.125c.017.012.027.03.046.041l5.466 3.159c.007.004.016.002.024.006.068.035.142.06.224.06a.49.49 0 00.225-.059c.019-.01.034-.023.052-.035a.503.503 0 00.129-.127c.008-.012.021-.016.029-.029.005-.009.005-.02.01-.028.015-.03.022-.061.031-.094.009-.033.018-.065.02-.099 0-.01.006-.019.006-.029v-7.15l5.11 2.949v27.498L1.5 40.747V13.248zm34.278-2.361l-4.899 2.831-5.111-2.952.776-.449 4.124-2.38 5.11 2.95zM25.293 4.836l-4.902 2.829-5.11-2.949 4.902-2.832 5.11 2.952zM10.92 11.872l-4.901 2.829-4.018-2.318 4.903-2.832 4.016 2.321zm10.036 4.638l3.312-1.909v4.187c0 .021.01.039.012.06a.384.384 0 00.062.186c.016.027.031.054.053.078.022.026.049.047.076.068.018.013.028.03.047.041l5.36 3.093-5.88 3.394v-7.151c0-.01-.005-.019-.006-.029a.48.48 0 00-.051-.192c-.005-.009-.004-.02-.01-.028-.006-.009-.014-.014-.02-.022a.512.512 0 00-.142-.142c-.009-.006-.013-.015-.022-.02l-2.791-1.614zm4.312-4.877l5.111 2.952v6.863l-5.111-2.949v-6.866zm-12.782 6.804l4.903-2.833 5.109 2.952-4.903 2.829-5.109-2.948zm-1.501 7.15l-3.966-2.292 3.966-2.29v4.582zm1.435-11.202l1.86-1.074 2.542 1.466-4.402 2.543v-2.935zm2.36-8.803l5.111 2.949v6.863l-5.111-2.949V5.582z"
66
- ></path>
67
- </svg>
68
- )
69
- }
70
-
71
- export function Header() {
72
- return (
73
- <header className="sticky top-0 z-50 flex items-center justify-between w-full h-16 px-4 border-b shrink-0 bg-gradient-to-b from-background/10 via-background/50 to-background/80 backdrop-blur-xl">
74
- <div className="flex items-center">
75
- <LogoIcon width={19} height={26} />
76
- <IconSeparator className="size-6 text-muted-foreground/50 ml-3" />
77
- <Button variant="plain" asChild className="-ml-2">
78
- <Link href="/">Vercel AI SDK</Link>
79
- </Button>
80
- <IconSeparator className="size-6 text-muted-foreground/50" />
81
- <Button variant="plain" asChild className="-ml-2">
82
- <Link href="/langchain">LangChain.js</Link>
83
- </Button>
84
- <IconSeparator className="size-6 text-muted-foreground/50" />
85
- <Button variant="plain" asChild className="-ml-2">
86
- <Link href="/langchain-rag">LangChain.js RAG</Link>
87
- </Button>
88
- <IconSeparator className="size-6 text-muted-foreground/50" />
89
- <Button variant="plain" asChild className="-ml-2">
90
- <Link href="/guardrails">Guardrails</Link>
91
- </Button>
92
- <IconSeparator className="size-6 text-muted-foreground/50" />
93
- <Button variant="plain" asChild className="-ml-2">
94
- <Link href="/manual">Manual Tracing</Link>
95
- </Button>
96
- <IconSeparator className="size-6 text-muted-foreground/50" />
97
- <Button variant="plain" asChild className="-ml-2">
98
- <Link href="/late-update">Late Update Tracing</Link>
99
- </Button>
100
- </div>
101
- <div className="flex items-center justify-end space-x-2">
102
- <a
103
- target="_blank"
104
- href="https://github.com/langwatch/langwatch"
105
- rel="noopener noreferrer"
106
- className={cn(buttonVariants({ variant: 'outline' }))}
107
- >
108
- <IconGitHub />
109
- <span className="hidden ml-2 md:flex">GitHub</span>
110
- </a>
111
- </div>
112
- </header>
113
- )
114
- }