langwatch 0.2.0 → 0.3.0-prerelease.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.
Files changed (235) hide show
  1. package/.editorconfig +16 -0
  2. package/LICENSE +7 -0
  3. package/README.md +268 -1
  4. package/copy-types.sh +19 -8
  5. package/examples/langchain/.env.example +2 -0
  6. package/examples/langchain/README.md +42 -0
  7. package/examples/langchain/package-lock.json +2930 -0
  8. package/examples/langchain/package.json +27 -0
  9. package/examples/langchain/src/cli-markdown.d.ts +137 -0
  10. package/examples/langchain/src/index.ts +109 -0
  11. package/examples/langchain/tsconfig.json +25 -0
  12. package/examples/langgraph/.env.example +2 -0
  13. package/examples/langgraph/README.md +42 -0
  14. package/examples/langgraph/package-lock.json +3031 -0
  15. package/examples/langgraph/package.json +28 -0
  16. package/examples/langgraph/src/cli-markdown.d.ts +137 -0
  17. package/examples/langgraph/src/index.ts +196 -0
  18. package/examples/langgraph/tsconfig.json +25 -0
  19. package/examples/mastra/.env.example +2 -0
  20. package/examples/mastra/README.md +57 -0
  21. package/examples/mastra/package-lock.json +5296 -0
  22. package/examples/mastra/package.json +32 -0
  23. package/examples/mastra/src/cli-markdown.d.ts +137 -0
  24. package/examples/mastra/src/index.ts +120 -0
  25. package/examples/mastra/src/mastra/agents/weather-agent.ts +30 -0
  26. package/examples/mastra/src/mastra/index.ts +21 -0
  27. package/examples/mastra/src/mastra/tools/weather-tool.ts +102 -0
  28. package/examples/mastra/tsconfig.json +25 -0
  29. package/examples/vercel-ai/.env.example +2 -0
  30. package/examples/vercel-ai/README.md +38 -0
  31. package/examples/vercel-ai/package-lock.json +2571 -0
  32. package/examples/vercel-ai/package.json +27 -0
  33. package/examples/vercel-ai/src/cli-markdown.d.ts +137 -0
  34. package/examples/vercel-ai/src/index.ts +110 -0
  35. package/examples/vercel-ai/src/instrumentation.ts +9 -0
  36. package/examples/vercel-ai/tsconfig.json +25 -0
  37. package/package.json +78 -34
  38. package/src/__tests__/client-browser.test.ts +92 -0
  39. package/src/__tests__/client-node.test.ts +76 -0
  40. package/src/__tests__/client.test.ts +71 -0
  41. package/src/__tests__/integration/client-browser.test.ts +46 -0
  42. package/src/__tests__/integration/client-node.test.ts +46 -0
  43. package/src/client-browser.ts +70 -0
  44. package/src/client-node.ts +82 -0
  45. package/src/client-shared.ts +72 -0
  46. package/src/client.ts +119 -0
  47. package/src/evaluation/__tests__/record-evaluation.test.ts +112 -0
  48. package/src/evaluation/__tests__/run-evaluation.test.ts +171 -0
  49. package/src/evaluation/index.ts +2 -0
  50. package/src/evaluation/record-evaluation.ts +101 -0
  51. package/src/evaluation/run-evaluation.ts +133 -0
  52. package/src/evaluation/tracer.ts +3 -0
  53. package/src/evaluation/types.ts +23 -0
  54. package/src/index.ts +10 -593
  55. package/src/internal/api/__tests__/errors.test.ts +98 -0
  56. package/src/internal/api/client.ts +30 -0
  57. package/src/internal/api/errors.ts +32 -0
  58. package/src/internal/generated/types/.gitkeep +0 -0
  59. package/src/observability/__tests__/integration/base.test.ts +74 -0
  60. package/src/observability/__tests__/integration/browser-setup-ordering.test.ts +60 -0
  61. package/src/observability/__tests__/integration/complex-nested-spans.test.ts +29 -0
  62. package/src/observability/__tests__/integration/error-handling.test.ts +24 -0
  63. package/src/observability/__tests__/integration/langwatch-disabled-otel.test.ts +24 -0
  64. package/src/observability/__tests__/integration/langwatch-first-then-vercel.test.ts +24 -0
  65. package/src/observability/__tests__/integration/multiple-setup-attempts.test.ts +27 -0
  66. package/src/observability/__tests__/integration/otel-ordering.test.ts +27 -0
  67. package/src/observability/__tests__/integration/vercel-configurations.test.ts +20 -0
  68. package/src/observability/__tests__/integration/vercel-first-then-langwatch.test.ts +27 -0
  69. package/src/observability/__tests__/span.test.ts +214 -0
  70. package/src/observability/__tests__/trace.test.ts +180 -0
  71. package/src/observability/exporters/index.ts +1 -0
  72. package/src/observability/exporters/langwatch-exporter.ts +53 -0
  73. package/src/observability/index.ts +4 -0
  74. package/src/observability/instrumentation/langchain/__tests__/integration/langchain-chatbot.test.ts +112 -0
  75. package/src/observability/instrumentation/langchain/__tests__/langchain.test.ts +284 -0
  76. package/src/observability/instrumentation/langchain/index.ts +624 -0
  77. package/src/observability/processors/__tests__/filterable-batch-span-exporter.test.ts +98 -0
  78. package/src/observability/processors/filterable-batch-span-processor.ts +99 -0
  79. package/src/observability/processors/index.ts +1 -0
  80. package/src/observability/semconv/attributes.ts +185 -0
  81. package/src/observability/semconv/events.ts +42 -0
  82. package/src/observability/semconv/index.ts +16 -0
  83. package/src/observability/semconv/values.ts +159 -0
  84. package/src/observability/span.ts +728 -0
  85. package/src/observability/trace.ts +301 -0
  86. package/src/prompt/__tests__/prompt.test.ts +139 -0
  87. package/src/prompt/get-prompt-version.ts +49 -0
  88. package/src/prompt/get-prompt.ts +44 -0
  89. package/src/prompt/index.ts +3 -0
  90. package/src/prompt/prompt.ts +133 -0
  91. package/src/prompt/service.ts +221 -0
  92. package/src/prompt/tracer.ts +3 -0
  93. package/src/prompt/types.ts +0 -0
  94. package/ts-to-zod.config.js +11 -0
  95. package/tsconfig.json +3 -9
  96. package/tsup.config.ts +11 -1
  97. package/vitest.config.ts +1 -0
  98. package/dist/chunk-LKD2K67J.mjs +0 -717
  99. package/dist/chunk-LKD2K67J.mjs.map +0 -1
  100. package/dist/index.d.mts +0 -1030
  101. package/dist/index.d.ts +0 -1030
  102. package/dist/index.js +0 -27310
  103. package/dist/index.js.map +0 -1
  104. package/dist/index.mjs +0 -963
  105. package/dist/index.mjs.map +0 -1
  106. package/dist/utils-Cv-rUjJ1.d.mts +0 -313
  107. package/dist/utils-Cv-rUjJ1.d.ts +0 -313
  108. package/dist/utils.d.mts +0 -2
  109. package/dist/utils.d.ts +0 -2
  110. package/dist/utils.js +0 -709
  111. package/dist/utils.js.map +0 -1
  112. package/dist/utils.mjs +0 -11
  113. package/dist/utils.mjs.map +0 -1
  114. package/example/.env.example +0 -12
  115. package/example/.eslintrc.json +0 -26
  116. package/example/LICENSE +0 -13
  117. package/example/README.md +0 -12
  118. package/example/app/(chat)/chat/[id]/page.tsx +0 -60
  119. package/example/app/(chat)/layout.tsx +0 -14
  120. package/example/app/(chat)/page.tsx +0 -27
  121. package/example/app/actions.ts +0 -156
  122. package/example/app/globals.css +0 -76
  123. package/example/app/guardrails/page.tsx +0 -26
  124. package/example/app/langchain/page.tsx +0 -27
  125. package/example/app/langchain-rag/page.tsx +0 -28
  126. package/example/app/late-update/page.tsx +0 -27
  127. package/example/app/layout.tsx +0 -64
  128. package/example/app/login/actions.ts +0 -71
  129. package/example/app/login/page.tsx +0 -18
  130. package/example/app/manual/page.tsx +0 -27
  131. package/example/app/new/page.tsx +0 -5
  132. package/example/app/opengraph-image.png +0 -0
  133. package/example/app/share/[id]/page.tsx +0 -58
  134. package/example/app/signup/actions.ts +0 -111
  135. package/example/app/signup/page.tsx +0 -18
  136. package/example/app/twitter-image.png +0 -0
  137. package/example/auth.config.ts +0 -42
  138. package/example/auth.ts +0 -45
  139. package/example/components/button-scroll-to-bottom.tsx +0 -36
  140. package/example/components/chat-history.tsx +0 -49
  141. package/example/components/chat-list.tsx +0 -52
  142. package/example/components/chat-message-actions.tsx +0 -40
  143. package/example/components/chat-message.tsx +0 -80
  144. package/example/components/chat-panel.tsx +0 -139
  145. package/example/components/chat-share-dialog.tsx +0 -95
  146. package/example/components/chat.tsx +0 -84
  147. package/example/components/clear-history.tsx +0 -75
  148. package/example/components/empty-screen.tsx +0 -38
  149. package/example/components/external-link.tsx +0 -29
  150. package/example/components/footer.tsx +0 -19
  151. package/example/components/header.tsx +0 -114
  152. package/example/components/login-button.tsx +0 -42
  153. package/example/components/login-form.tsx +0 -97
  154. package/example/components/markdown.tsx +0 -9
  155. package/example/components/prompt-form.tsx +0 -115
  156. package/example/components/providers.tsx +0 -17
  157. package/example/components/sidebar-actions.tsx +0 -125
  158. package/example/components/sidebar-desktop.tsx +0 -19
  159. package/example/components/sidebar-footer.tsx +0 -16
  160. package/example/components/sidebar-item.tsx +0 -124
  161. package/example/components/sidebar-items.tsx +0 -42
  162. package/example/components/sidebar-list.tsx +0 -38
  163. package/example/components/sidebar-mobile.tsx +0 -31
  164. package/example/components/sidebar-toggle.tsx +0 -24
  165. package/example/components/sidebar.tsx +0 -21
  166. package/example/components/signup-form.tsx +0 -95
  167. package/example/components/stocks/events-skeleton.tsx +0 -31
  168. package/example/components/stocks/events.tsx +0 -30
  169. package/example/components/stocks/index.tsx +0 -36
  170. package/example/components/stocks/message.tsx +0 -134
  171. package/example/components/stocks/spinner.tsx +0 -16
  172. package/example/components/stocks/stock-purchase.tsx +0 -146
  173. package/example/components/stocks/stock-skeleton.tsx +0 -22
  174. package/example/components/stocks/stock.tsx +0 -210
  175. package/example/components/stocks/stocks-skeleton.tsx +0 -9
  176. package/example/components/stocks/stocks.tsx +0 -67
  177. package/example/components/tailwind-indicator.tsx +0 -14
  178. package/example/components/theme-toggle.tsx +0 -31
  179. package/example/components/ui/alert-dialog.tsx +0 -141
  180. package/example/components/ui/badge.tsx +0 -36
  181. package/example/components/ui/button.tsx +0 -57
  182. package/example/components/ui/codeblock.tsx +0 -148
  183. package/example/components/ui/dialog.tsx +0 -122
  184. package/example/components/ui/dropdown-menu.tsx +0 -205
  185. package/example/components/ui/icons.tsx +0 -507
  186. package/example/components/ui/input.tsx +0 -25
  187. package/example/components/ui/label.tsx +0 -26
  188. package/example/components/ui/select.tsx +0 -164
  189. package/example/components/ui/separator.tsx +0 -31
  190. package/example/components/ui/sheet.tsx +0 -140
  191. package/example/components/ui/sonner.tsx +0 -31
  192. package/example/components/ui/switch.tsx +0 -29
  193. package/example/components/ui/textarea.tsx +0 -24
  194. package/example/components/ui/tooltip.tsx +0 -30
  195. package/example/components/user-menu.tsx +0 -53
  196. package/example/components.json +0 -17
  197. package/example/instrumentation.ts +0 -11
  198. package/example/lib/chat/guardrails.tsx +0 -181
  199. package/example/lib/chat/langchain-rag.tsx +0 -191
  200. package/example/lib/chat/langchain.tsx +0 -112
  201. package/example/lib/chat/late-update.tsx +0 -208
  202. package/example/lib/chat/manual.tsx +0 -605
  203. package/example/lib/chat/vercel-ai.tsx +0 -576
  204. package/example/lib/hooks/use-copy-to-clipboard.tsx +0 -33
  205. package/example/lib/hooks/use-enter-submit.tsx +0 -23
  206. package/example/lib/hooks/use-local-storage.ts +0 -24
  207. package/example/lib/hooks/use-scroll-anchor.tsx +0 -86
  208. package/example/lib/hooks/use-sidebar.tsx +0 -60
  209. package/example/lib/hooks/use-streamable-text.ts +0 -25
  210. package/example/lib/types.ts +0 -41
  211. package/example/lib/utils.ts +0 -89
  212. package/example/middleware.ts +0 -8
  213. package/example/next-env.d.ts +0 -5
  214. package/example/next.config.js +0 -16
  215. package/example/package-lock.json +0 -10917
  216. package/example/package.json +0 -84
  217. package/example/pnpm-lock.yaml +0 -5712
  218. package/example/postcss.config.js +0 -6
  219. package/example/prettier.config.cjs +0 -34
  220. package/example/public/apple-touch-icon.png +0 -0
  221. package/example/public/favicon-16x16.png +0 -0
  222. package/example/public/favicon.ico +0 -0
  223. package/example/public/next.svg +0 -1
  224. package/example/public/thirteen.svg +0 -1
  225. package/example/public/vercel.svg +0 -1
  226. package/example/tailwind.config.ts +0 -81
  227. package/example/tsconfig.json +0 -35
  228. package/src/LangWatchExporter.ts +0 -96
  229. package/src/evaluations.ts +0 -219
  230. package/src/index.test.ts +0 -402
  231. package/src/langchain.ts +0 -557
  232. package/src/typeUtils.ts +0 -89
  233. package/src/types.ts +0 -82
  234. package/src/utils.ts +0 -205
  235. /package/src/{server/types → internal/generated/openapi}/.gitkeep +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
- }