langwatch 0.0.3 → 0.1.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/dist/{chunk-AP23NJ57.mjs → chunk-OVS4NSDE.mjs} +373 -2
- package/dist/chunk-OVS4NSDE.mjs.map +1 -0
- package/dist/index.d.mts +47 -5
- package/dist/index.d.ts +47 -5
- package/dist/index.js +6275 -485
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +329 -349
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-Dg5eWsAz.d.mts → utils-K-jSEpnZ.d.mts} +11 -7
- package/dist/{utils-Dg5eWsAz.d.ts → utils-K-jSEpnZ.d.ts} +11 -7
- package/dist/utils.d.mts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +370 -0
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +3 -1
- package/example/README.md +3 -1
- package/example/app/(chat)/chat/[id]/page.tsx +1 -1
- package/example/app/(chat)/page.tsx +10 -5
- package/example/app/langchain/page.tsx +27 -0
- package/example/app/langchain-rag/page.tsx +28 -0
- package/example/app/share/[id]/page.tsx +1 -1
- package/example/components/chat-list.tsx +1 -1
- package/example/components/chat-panel.tsx +1 -1
- package/example/components/header.tsx +35 -13
- package/example/components/prompt-form.tsx +1 -1
- package/example/components/stocks/stock-purchase.tsx +1 -1
- package/example/components/stocks/stocks.tsx +1 -1
- package/example/lib/chat/langchain-rag.tsx +191 -0
- package/example/lib/chat/langchain.tsx +112 -0
- package/example/lib/chat/{actions.tsx → vercel-ai.tsx} +4 -6
- package/example/package-lock.json +287 -4
- package/example/package.json +1 -0
- package/package.json +12 -2
- package/src/index.test.ts +96 -28
- package/src/index.ts +18 -9
- package/src/langchain.ts +557 -0
- package/src/types.ts +4 -4
- package/src/utils.ts +28 -1
- package/dist/chunk-AP23NJ57.mjs.map +0 -1
- /package/src/{helpers.ts → typeUtils.ts} +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { nanoid } from '@/lib/utils'
|
|
2
2
|
import { Chat } from '@/components/chat'
|
|
3
|
-
import { AI } from '@/lib/chat/
|
|
3
|
+
import { AI } from '@/lib/chat/vercel-ai'
|
|
4
4
|
import { auth } from '@/auth'
|
|
5
5
|
import { Session } from '@/lib/types'
|
|
6
6
|
import { getMissingKeys } from '@/app/actions'
|
|
7
7
|
|
|
8
8
|
export const metadata = {
|
|
9
|
-
title: '
|
|
9
|
+
title: 'Vercel AI SDK Example'
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export default async function IndexPage() {
|
|
@@ -15,8 +15,13 @@ export default async function IndexPage() {
|
|
|
15
15
|
const missingKeys = await getMissingKeys()
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
18
|
+
<>
|
|
19
|
+
<div className="text-center w-full absolute pt-1">
|
|
20
|
+
Vercel AI SDK example
|
|
21
|
+
</div>
|
|
22
|
+
<AI initialAIState={{ chatId: id, messages: [] }}>
|
|
23
|
+
<Chat id={id} session={session} missingKeys={missingKeys} />
|
|
24
|
+
</AI>
|
|
25
|
+
</>
|
|
21
26
|
)
|
|
22
27
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { nanoid } from '@/lib/utils'
|
|
2
|
+
import { Chat } from '@/components/chat'
|
|
3
|
+
import { auth } from '@/auth'
|
|
4
|
+
import { Session } from '@/lib/types'
|
|
5
|
+
import { getMissingKeys } from '@/app/actions'
|
|
6
|
+
import { LangChainAI } from '../../lib/chat/langchain'
|
|
7
|
+
|
|
8
|
+
export const metadata = {
|
|
9
|
+
title: 'LangChain.js Example'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default async function IndexPage() {
|
|
13
|
+
const id = nanoid()
|
|
14
|
+
const session = (await auth()) as Session
|
|
15
|
+
const missingKeys = await getMissingKeys()
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<div className="text-center w-full absolute pt-1">
|
|
20
|
+
LangChain.js Example
|
|
21
|
+
</div>
|
|
22
|
+
<LangChainAI initialAIState={{ chatId: id, messages: [] }}>
|
|
23
|
+
<Chat id={id} session={session} missingKeys={missingKeys} />
|
|
24
|
+
</LangChainAI>
|
|
25
|
+
</>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { nanoid } from '@/lib/utils'
|
|
2
|
+
import { Chat } from '@/components/chat'
|
|
3
|
+
import { AI } from '@/lib/chat/vercel-ai'
|
|
4
|
+
import { auth } from '@/auth'
|
|
5
|
+
import { Session } from '@/lib/types'
|
|
6
|
+
import { getMissingKeys } from '@/app/actions'
|
|
7
|
+
import { LangChainRAGAI } from '../../lib/chat/langchain-rag'
|
|
8
|
+
|
|
9
|
+
export const metadata = {
|
|
10
|
+
title: 'LangChain.js Example'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default async function IndexPage() {
|
|
14
|
+
const id = nanoid()
|
|
15
|
+
const session = (await auth()) as Session
|
|
16
|
+
const missingKeys = await getMissingKeys()
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<div className="text-center w-full absolute pt-1">
|
|
21
|
+
LangChain.js RAG Example
|
|
22
|
+
</div>
|
|
23
|
+
<LangChainRAGAI initialAIState={{ chatId: id, messages: [] }}>
|
|
24
|
+
<Chat id={id} session={session} missingKeys={missingKeys} />
|
|
25
|
+
</LangChainRAGAI>
|
|
26
|
+
</>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -5,7 +5,7 @@ import { formatDate } from '@/lib/utils'
|
|
|
5
5
|
import { getSharedChat } from '@/app/actions'
|
|
6
6
|
import { ChatList } from '@/components/chat-list'
|
|
7
7
|
import { FooterText } from '@/components/footer'
|
|
8
|
-
import { AI, UIState, getUIStateFromAIState } from '@/lib/chat/
|
|
8
|
+
import { AI, UIState, getUIStateFromAIState } from '@/lib/chat/vercel-ai'
|
|
9
9
|
|
|
10
10
|
export const runtime = 'edge'
|
|
11
11
|
export const preferredRegion = 'home'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Separator } from '@/components/ui/separator'
|
|
2
|
-
import { UIState } from '@/lib/chat/
|
|
2
|
+
import { UIState } from '@/lib/chat/vercel-ai'
|
|
3
3
|
import { Session } from '@/lib/types'
|
|
4
4
|
import Link from 'next/link'
|
|
5
5
|
import { ExclamationTriangleIcon } from '@radix-ui/react-icons'
|
|
@@ -8,7 +8,7 @@ import { IconShare } from '@/components/ui/icons'
|
|
|
8
8
|
import { FooterText } from '@/components/footer'
|
|
9
9
|
import { ChatShareDialog } from '@/components/chat-share-dialog'
|
|
10
10
|
import { useAIState, useActions, useUIState } from 'ai/rsc'
|
|
11
|
-
import type { AI } from '@/lib/chat/
|
|
11
|
+
import type { AI } from '@/lib/chat/vercel-ai'
|
|
12
12
|
import { nanoid } from 'nanoid'
|
|
13
13
|
import { UserMessage } from './stocks/message'
|
|
14
14
|
|
|
@@ -47,33 +47,55 @@ async function UserOrLogin() {
|
|
|
47
47
|
)
|
|
48
48
|
}
|
|
49
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
|
+
|
|
50
71
|
export function Header() {
|
|
51
72
|
return (
|
|
52
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">
|
|
53
74
|
<div className="flex items-center">
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
<LogoIcon width={19} height={26} />
|
|
76
|
+
<IconSeparator className="size-6 text-muted-foreground/50 ml-3" />
|
|
77
|
+
<Button variant="link" 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="link" 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="link" asChild className="-ml-2">
|
|
86
|
+
<Link href="/langchain-rag">LangChain.js RAG</Link>
|
|
87
|
+
</Button>
|
|
57
88
|
</div>
|
|
58
89
|
<div className="flex items-center justify-end space-x-2">
|
|
59
90
|
<a
|
|
60
91
|
target="_blank"
|
|
61
|
-
href="https://github.com/
|
|
92
|
+
href="https://github.com/langwatch/langwatch"
|
|
62
93
|
rel="noopener noreferrer"
|
|
63
94
|
className={cn(buttonVariants({ variant: 'outline' }))}
|
|
64
95
|
>
|
|
65
96
|
<IconGitHub />
|
|
66
97
|
<span className="hidden ml-2 md:flex">GitHub</span>
|
|
67
98
|
</a>
|
|
68
|
-
<a
|
|
69
|
-
href="https://vercel.com/templates/Next.js/nextjs-ai-chatbot"
|
|
70
|
-
target="_blank"
|
|
71
|
-
className={cn(buttonVariants())}
|
|
72
|
-
>
|
|
73
|
-
<IconVercel className="mr-2" />
|
|
74
|
-
<span className="hidden sm:block">Deploy to Vercel</span>
|
|
75
|
-
<span className="sm:hidden">Deploy</span>
|
|
76
|
-
</a>
|
|
77
99
|
</div>
|
|
78
100
|
</header>
|
|
79
101
|
)
|
|
@@ -6,7 +6,7 @@ import Textarea from 'react-textarea-autosize'
|
|
|
6
6
|
import { useActions, useUIState } from 'ai/rsc'
|
|
7
7
|
|
|
8
8
|
import { UserMessage } from './stocks/message'
|
|
9
|
-
import { type AI } from '@/lib/chat/
|
|
9
|
+
import { type AI } from '@/lib/chat/vercel-ai'
|
|
10
10
|
import { Button } from '@/components/ui/button'
|
|
11
11
|
import { IconArrowElbow, IconPlus } from '@/components/ui/icons'
|
|
12
12
|
import {
|
|
@@ -4,7 +4,7 @@ import { useId, useState } from 'react'
|
|
|
4
4
|
import { useActions, useAIState, useUIState } from 'ai/rsc'
|
|
5
5
|
import { formatNumber } from '@/lib/utils'
|
|
6
6
|
|
|
7
|
-
import type { AI } from '@/lib/chat/
|
|
7
|
+
import type { AI } from '@/lib/chat/vercel-ai'
|
|
8
8
|
|
|
9
9
|
interface Purchase {
|
|
10
10
|
numberOfShares?: number
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import 'server-only'
|
|
2
|
+
|
|
3
|
+
import { openai } from '@ai-sdk/openai'
|
|
4
|
+
import {
|
|
5
|
+
createAI,
|
|
6
|
+
createStreamableUI,
|
|
7
|
+
createStreamableValue,
|
|
8
|
+
getMutableAIState,
|
|
9
|
+
streamUI
|
|
10
|
+
} from 'ai/rsc'
|
|
11
|
+
|
|
12
|
+
import { BotCard, BotMessage, Purchase, Stock } from '@/components/stocks'
|
|
13
|
+
|
|
14
|
+
import { Events } from '@/components/stocks/events'
|
|
15
|
+
import { SpinnerMessage, UserMessage } from '@/components/stocks/message'
|
|
16
|
+
import { Stocks } from '@/components/stocks/stocks'
|
|
17
|
+
import { Chat, Message } from '@/lib/types'
|
|
18
|
+
import { nanoid } from '@/lib/utils'
|
|
19
|
+
import { LangWatch, convertFromVercelAIMessages } from 'langwatch'
|
|
20
|
+
import { ChatOpenAI } from '@langchain/openai'
|
|
21
|
+
import {
|
|
22
|
+
ChatPromptTemplate,
|
|
23
|
+
PromptTemplateInput
|
|
24
|
+
} from '@langchain/core/prompts'
|
|
25
|
+
import {
|
|
26
|
+
HumanMessage,
|
|
27
|
+
SystemMessage,
|
|
28
|
+
AIMessage,
|
|
29
|
+
ToolMessage,
|
|
30
|
+
BaseMessageLike
|
|
31
|
+
} from '@langchain/core/messages'
|
|
32
|
+
import { StringOutputParser } from '@langchain/core/output_parsers'
|
|
33
|
+
import { CallbackManagerForRetrieverRun } from '@langchain/core/callbacks/manager'
|
|
34
|
+
import {
|
|
35
|
+
BaseRetriever,
|
|
36
|
+
type BaseRetrieverInput
|
|
37
|
+
} from '@langchain/core/retrievers'
|
|
38
|
+
import { Document } from '@langchain/core/documents'
|
|
39
|
+
import {
|
|
40
|
+
RunnableLambda,
|
|
41
|
+
RunnableMap,
|
|
42
|
+
RunnablePassthrough
|
|
43
|
+
} from '@langchain/core/runnables'
|
|
44
|
+
|
|
45
|
+
async function submitUserMessage(message: string) {
|
|
46
|
+
'use server'
|
|
47
|
+
|
|
48
|
+
const langwatch = new LangWatch()
|
|
49
|
+
langwatch.on('error', e => {
|
|
50
|
+
console.log('Error from LangWatch:', e)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const trace = langwatch.getTrace()
|
|
54
|
+
|
|
55
|
+
const aiState = getMutableAIState<typeof LangChainRAGAI>()
|
|
56
|
+
|
|
57
|
+
const messages: BaseMessageLike[] = [
|
|
58
|
+
['system', 'Answer based on the retrieved context'],
|
|
59
|
+
...(aiState.get().messages.map(message => {
|
|
60
|
+
if (message.role === 'system') {
|
|
61
|
+
return ['system', message.content.toString()]
|
|
62
|
+
}
|
|
63
|
+
if (message.role === 'user') {
|
|
64
|
+
return ['human', message.content.toString()]
|
|
65
|
+
}
|
|
66
|
+
if (message.role === 'tool') {
|
|
67
|
+
return ['tool', message.content.toString()]
|
|
68
|
+
}
|
|
69
|
+
return ['ai', message.content.toString()]
|
|
70
|
+
}) as BaseMessageLike[]),
|
|
71
|
+
['ai', 'Retrieved the following context: {context}'],
|
|
72
|
+
['human', '{question}']
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
aiState.update({
|
|
76
|
+
...aiState.get(),
|
|
77
|
+
messages: [
|
|
78
|
+
...aiState.get().messages,
|
|
79
|
+
{
|
|
80
|
+
id: nanoid(),
|
|
81
|
+
role: 'user',
|
|
82
|
+
content: message
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const prompt = ChatPromptTemplate.fromMessages(messages)
|
|
88
|
+
const model = new ChatOpenAI({ model: 'gpt-3.5-turbo' })
|
|
89
|
+
const retriever = new CustomRetriever()
|
|
90
|
+
const outputParser = new StringOutputParser()
|
|
91
|
+
|
|
92
|
+
const setupAndRetrieval = RunnableMap.from({
|
|
93
|
+
context: new RunnableLambda({
|
|
94
|
+
func: (input: string) =>
|
|
95
|
+
retriever
|
|
96
|
+
.invoke(input, {
|
|
97
|
+
callbacks: [trace.getLangChainCallback()]
|
|
98
|
+
})
|
|
99
|
+
.then(response => response[0].pageContent)
|
|
100
|
+
}).withConfig({ runName: 'contextRetriever' }),
|
|
101
|
+
question: new RunnablePassthrough()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const chain = setupAndRetrieval.pipe(prompt).pipe(model).pipe(outputParser)
|
|
105
|
+
|
|
106
|
+
const stream = await chain.stream(message, {
|
|
107
|
+
callbacks: [trace.getLangChainCallback()]
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
let textStream = createStreamableValue('')
|
|
111
|
+
let textNode = <BotMessage content={textStream.value} />
|
|
112
|
+
let content = ''
|
|
113
|
+
|
|
114
|
+
setTimeout(async () => {
|
|
115
|
+
for await (const chunk of stream) {
|
|
116
|
+
textStream.update(chunk)
|
|
117
|
+
content += chunk
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
textStream?.done()
|
|
121
|
+
aiState.done({
|
|
122
|
+
...aiState.get(),
|
|
123
|
+
messages: [
|
|
124
|
+
...aiState.get().messages,
|
|
125
|
+
{
|
|
126
|
+
id: nanoid(),
|
|
127
|
+
role: 'assistant',
|
|
128
|
+
content
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
})
|
|
132
|
+
}, 0)
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
id: nanoid(),
|
|
136
|
+
display: textNode
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export type AIState = {
|
|
141
|
+
chatId: string
|
|
142
|
+
messages: Message[]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export type UIState = {
|
|
146
|
+
id: string
|
|
147
|
+
display: React.ReactNode
|
|
148
|
+
}[]
|
|
149
|
+
|
|
150
|
+
export const LangChainRAGAI = createAI<AIState, UIState>({
|
|
151
|
+
actions: {
|
|
152
|
+
submitUserMessage
|
|
153
|
+
},
|
|
154
|
+
initialUIState: [],
|
|
155
|
+
initialAIState: { chatId: nanoid(), messages: [] },
|
|
156
|
+
onGetUIState: async () => {
|
|
157
|
+
'use server'
|
|
158
|
+
|
|
159
|
+
return undefined
|
|
160
|
+
},
|
|
161
|
+
onSetAIState: async ({ state }) => {
|
|
162
|
+
'use server'
|
|
163
|
+
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
export class CustomRetriever extends BaseRetriever {
|
|
169
|
+
lc_namespace = ['langchain', 'retrievers']
|
|
170
|
+
|
|
171
|
+
constructor(fields?: BaseRetrieverInput) {
|
|
172
|
+
super(fields)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async _getRelevantDocuments(
|
|
176
|
+
query: string,
|
|
177
|
+
_runManager?: CallbackManagerForRetrieverRun
|
|
178
|
+
): Promise<Document[]> {
|
|
179
|
+
console.log('query', query)
|
|
180
|
+
return [
|
|
181
|
+
new Document({
|
|
182
|
+
pageContent: `Some document pertaining to ${query}`,
|
|
183
|
+
metadata: {}
|
|
184
|
+
}),
|
|
185
|
+
new Document({
|
|
186
|
+
pageContent: `Some other document pertaining to ${query}`,
|
|
187
|
+
metadata: {}
|
|
188
|
+
})
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import 'server-only'
|
|
2
|
+
|
|
3
|
+
import { createAI, createStreamableValue, getMutableAIState } from 'ai/rsc'
|
|
4
|
+
|
|
5
|
+
import { BotMessage } from '@/components/stocks'
|
|
6
|
+
|
|
7
|
+
import { Message } from '@/lib/types'
|
|
8
|
+
import { nanoid } from '@/lib/utils'
|
|
9
|
+
import { StringOutputParser } from '@langchain/core/output_parsers'
|
|
10
|
+
import { ChatPromptTemplate } from '@langchain/core/prompts'
|
|
11
|
+
import { ChatOpenAI } from '@langchain/openai'
|
|
12
|
+
import { LangWatch } from 'langwatch'
|
|
13
|
+
|
|
14
|
+
async function submitUserMessage(message: string) {
|
|
15
|
+
'use server'
|
|
16
|
+
|
|
17
|
+
const langwatch = new LangWatch()
|
|
18
|
+
langwatch.on('error', e => {
|
|
19
|
+
console.log('Error from LangWatch:', e)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const trace = langwatch.getTrace()
|
|
23
|
+
|
|
24
|
+
const aiState = getMutableAIState<typeof LangChainAI>()
|
|
25
|
+
|
|
26
|
+
aiState.update({
|
|
27
|
+
...aiState.get(),
|
|
28
|
+
messages: [
|
|
29
|
+
{
|
|
30
|
+
id: nanoid(),
|
|
31
|
+
role: 'system',
|
|
32
|
+
content: 'Translate the following from English into Italian'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: nanoid(),
|
|
36
|
+
role: 'user',
|
|
37
|
+
content: message
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const prompt = ChatPromptTemplate.fromMessages([
|
|
43
|
+
['system', 'Translate the following from English into Italian'],
|
|
44
|
+
['human', '{input}']
|
|
45
|
+
])
|
|
46
|
+
const model = new ChatOpenAI({ model: 'gpt-3.5-turbo' })
|
|
47
|
+
const outputParser = new StringOutputParser()
|
|
48
|
+
|
|
49
|
+
const chain = prompt.pipe(model).pipe(outputParser)
|
|
50
|
+
|
|
51
|
+
const stream = await chain.stream(
|
|
52
|
+
{ input: message },
|
|
53
|
+
{ callbacks: [trace.getLangChainCallback()] }
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
let textStream = createStreamableValue('')
|
|
57
|
+
let textNode = <BotMessage content={textStream.value} />
|
|
58
|
+
let content = ''
|
|
59
|
+
|
|
60
|
+
setTimeout(async () => {
|
|
61
|
+
for await (const chunk of stream) {
|
|
62
|
+
textStream.update(chunk)
|
|
63
|
+
content += chunk
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
textStream?.done()
|
|
67
|
+
aiState.done({
|
|
68
|
+
...aiState.get(),
|
|
69
|
+
messages: [
|
|
70
|
+
...aiState.get().messages,
|
|
71
|
+
{
|
|
72
|
+
id: nanoid(),
|
|
73
|
+
role: 'assistant',
|
|
74
|
+
content
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
})
|
|
78
|
+
}, 0)
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
id: nanoid(),
|
|
82
|
+
display: textNode
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type AIState = {
|
|
87
|
+
chatId: string
|
|
88
|
+
messages: Message[]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type UIState = {
|
|
92
|
+
id: string
|
|
93
|
+
display: React.ReactNode
|
|
94
|
+
}[]
|
|
95
|
+
|
|
96
|
+
export const LangChainAI = createAI<AIState, UIState>({
|
|
97
|
+
actions: {
|
|
98
|
+
submitUserMessage
|
|
99
|
+
},
|
|
100
|
+
initialUIState: [],
|
|
101
|
+
initialAIState: { chatId: nanoid(), messages: [] },
|
|
102
|
+
onGetUIState: async () => {
|
|
103
|
+
'use server'
|
|
104
|
+
|
|
105
|
+
return undefined
|
|
106
|
+
},
|
|
107
|
+
onSetAIState: async ({ state }) => {
|
|
108
|
+
'use server'
|
|
109
|
+
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
})
|
|
@@ -168,12 +168,10 @@ async function submitUserMessage(content: string) {
|
|
|
168
168
|
})
|
|
169
169
|
|
|
170
170
|
span.end({
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
]
|
|
171
|
+
output: {
|
|
172
|
+
type: 'chat_messages',
|
|
173
|
+
value: convertFromVercelAIMessages(output)
|
|
174
|
+
}
|
|
177
175
|
})
|
|
178
176
|
}
|
|
179
177
|
|