langwatch 0.1.4 → 0.1.5
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-2I4YLOQY.mjs → chunk-I5AU3P36.mjs} +19 -1
- package/dist/chunk-I5AU3P36.mjs.map +1 -0
- package/dist/index.d.mts +735 -154
- package/dist/index.d.ts +735 -154
- package/dist/index.js +20177 -220
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +74 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-CFtM8VVg.d.mts → utils-DJoZVcOA.d.mts} +11 -1
- package/dist/{utils-CFtM8VVg.d.ts → utils-DJoZVcOA.d.ts} +11 -1
- package/dist/utils.d.mts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +18 -0
- package/dist/utils.js.map +1 -1
- package/dist/utils.mjs +1 -1
- package/example/app/manual/page.tsx +27 -0
- package/example/components/header.tsx +4 -0
- package/example/instrumentation.ts +9 -0
- package/example/lib/chat/manual.tsx +605 -0
- package/example/lib/chat/vercel-ai.tsx +151 -179
- package/example/next.config.js +3 -0
- package/example/package-lock.json +509 -498
- package/example/package.json +8 -2
- package/package.json +4 -1
- package/src/LangWatchExporter.ts +91 -0
- package/src/index.ts +8 -2
- package/dist/chunk-2I4YLOQY.mjs.map +0 -1
|
@@ -1,122 +1,42 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
|
|
3
|
+
import { openai } from '@ai-sdk/openai'
|
|
3
4
|
import {
|
|
4
5
|
createAI,
|
|
5
6
|
createStreamableUI,
|
|
6
|
-
getMutableAIState,
|
|
7
7
|
getAIState,
|
|
8
|
-
|
|
9
|
-
createStreamableValue
|
|
8
|
+
getMutableAIState
|
|
10
9
|
} from 'ai/rsc'
|
|
11
|
-
import { openai } from '@ai-sdk/openai'
|
|
12
10
|
|
|
13
11
|
import {
|
|
14
|
-
spinner,
|
|
15
12
|
BotCard,
|
|
16
13
|
BotMessage,
|
|
17
|
-
|
|
14
|
+
Purchase,
|
|
15
|
+
spinner,
|
|
18
16
|
Stock,
|
|
19
|
-
|
|
17
|
+
SystemMessage
|
|
20
18
|
} from '@/components/stocks'
|
|
21
19
|
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
20
|
+
import { saveChat } from '@/app/actions'
|
|
21
|
+
import { auth } from '@/auth'
|
|
24
22
|
import { Events } from '@/components/stocks/events'
|
|
25
|
-
import {
|
|
23
|
+
import { UserMessage } from '@/components/stocks/message'
|
|
26
24
|
import { Stocks } from '@/components/stocks/stocks'
|
|
27
|
-
import {
|
|
25
|
+
import { Chat, Message } from '@/lib/types'
|
|
28
26
|
import {
|
|
29
27
|
formatNumber,
|
|
28
|
+
nanoid,
|
|
30
29
|
runAsyncFnWithoutBlocking,
|
|
31
|
-
sleep
|
|
32
|
-
nanoid
|
|
30
|
+
sleep
|
|
33
31
|
} from '@/lib/utils'
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import { LangWatch, convertFromVercelAIMessages } from 'langwatch'
|
|
39
|
-
|
|
40
|
-
async function confirmPurchase(symbol: string, price: number, amount: number) {
|
|
41
|
-
'use server'
|
|
42
|
-
|
|
43
|
-
const aiState = getMutableAIState<typeof AI>()
|
|
44
|
-
|
|
45
|
-
const purchasing = createStreamableUI(
|
|
46
|
-
<div className="inline-flex items-start gap-1 md:items-center">
|
|
47
|
-
{spinner}
|
|
48
|
-
<p className="mb-2">
|
|
49
|
-
Purchasing {amount} ${symbol}...
|
|
50
|
-
</p>
|
|
51
|
-
</div>
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
const systemMessage = createStreamableUI(null)
|
|
55
|
-
|
|
56
|
-
runAsyncFnWithoutBlocking(async () => {
|
|
57
|
-
await sleep(1000)
|
|
58
|
-
|
|
59
|
-
purchasing.update(
|
|
60
|
-
<div className="inline-flex items-start gap-1 md:items-center">
|
|
61
|
-
{spinner}
|
|
62
|
-
<p className="mb-2">
|
|
63
|
-
Purchasing {amount} ${symbol}... working on it...
|
|
64
|
-
</p>
|
|
65
|
-
</div>
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
await sleep(1000)
|
|
69
|
-
|
|
70
|
-
purchasing.done(
|
|
71
|
-
<div>
|
|
72
|
-
<p className="mb-2">
|
|
73
|
-
You have successfully purchased {amount} ${symbol}. Total cost:{' '}
|
|
74
|
-
{formatNumber(amount * price)}
|
|
75
|
-
</p>
|
|
76
|
-
</div>
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
systemMessage.done(
|
|
80
|
-
<SystemMessage>
|
|
81
|
-
You have purchased {amount} shares of {symbol} at ${price}. Total cost ={' '}
|
|
82
|
-
{formatNumber(amount * price)}.
|
|
83
|
-
</SystemMessage>
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
aiState.done({
|
|
87
|
-
...aiState.get(),
|
|
88
|
-
messages: [
|
|
89
|
-
...aiState.get().messages,
|
|
90
|
-
{
|
|
91
|
-
id: nanoid(),
|
|
92
|
-
role: 'system',
|
|
93
|
-
content: `[User has purchased ${amount} shares of ${symbol} at ${price}. Total cost = ${
|
|
94
|
-
amount * price
|
|
95
|
-
}]`
|
|
96
|
-
}
|
|
97
|
-
]
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
purchasingUI: purchasing.value,
|
|
103
|
-
newMessage: {
|
|
104
|
-
id: nanoid(),
|
|
105
|
-
display: systemMessage.value
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
32
|
+
import { generateText, streamText, tool } from 'ai'
|
|
33
|
+
import { z } from 'zod'
|
|
34
|
+
import { StockSkeleton } from '../../components/stocks/stock-skeleton'
|
|
35
|
+
import { EventsSkeleton } from '../../components/stocks/events-skeleton'
|
|
109
36
|
|
|
110
37
|
async function submitUserMessage(content: string) {
|
|
111
38
|
'use server'
|
|
112
39
|
|
|
113
|
-
const langwatch = new LangWatch()
|
|
114
|
-
langwatch.on('error', e => {
|
|
115
|
-
console.log('Error from LangWatch:', e)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
const trace = langwatch.getTrace()
|
|
119
|
-
|
|
120
40
|
const aiState = getMutableAIState<typeof AI>()
|
|
121
41
|
|
|
122
42
|
aiState.update({
|
|
@@ -135,84 +55,42 @@ async function submitUserMessage(content: string) {
|
|
|
135
55
|
You are a stock trading conversation bot and you can help users buy stocks, step by step.
|
|
136
56
|
You and the user can discuss stock prices and the user can adjust the amount of stocks they want to buy, or place an order, in the UI.
|
|
137
57
|
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
58
|
+
To use tools, use the following format:
|
|
59
|
+
- For stock price: show_stock_price(SYMBOL, PRICE, DELTA)
|
|
60
|
+
- For listing stocks: list_stocks([{"symbol": "AAPL", "price": 150.5, "delta": 2.3}, ...])
|
|
61
|
+
- For purchase UI: show_stock_purchase(SYMBOL, PRICE, NUMBER_OF_SHARES)
|
|
62
|
+
- For events: get_events([{"date": "2024-01-01", "headline": "...", "description": "..."}, ...])
|
|
141
63
|
|
|
142
|
-
|
|
143
|
-
If the user just wants the price, call \`show_stock_price\` to show the price.
|
|
144
|
-
If you want to show trending stocks, call \`list_stocks\`.
|
|
145
|
-
If you want to show events, call \`get_events\`.
|
|
146
|
-
If the user wants to sell stock, or complete another impossible task, respond that you are a demo and cannot do that.
|
|
64
|
+
Messages inside [] means that it's a UI element or a user event.`
|
|
147
65
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
model: 'gpt-4o-mini',
|
|
152
|
-
input: {
|
|
153
|
-
type: 'chat_messages',
|
|
154
|
-
value: [
|
|
155
|
-
{
|
|
156
|
-
role: 'system',
|
|
157
|
-
content: system
|
|
158
|
-
},
|
|
159
|
-
...convertFromVercelAIMessages(aiState.get().messages)
|
|
160
|
-
]
|
|
161
|
-
}
|
|
162
|
-
})
|
|
66
|
+
const ui = createStreamableUI(<BotMessage content="" />)
|
|
67
|
+
// let textNode = <BotMessage content={textStream.value} />
|
|
68
|
+
let fullContent = ''
|
|
163
69
|
|
|
164
70
|
const onFinish = (output: Message[]) => {
|
|
165
71
|
aiState.done({
|
|
166
72
|
...aiState.get(),
|
|
167
73
|
messages: [...aiState.get().messages, ...output]
|
|
168
74
|
})
|
|
169
|
-
|
|
170
|
-
span.end({
|
|
171
|
-
output: {
|
|
172
|
-
type: 'chat_messages',
|
|
173
|
-
value: convertFromVercelAIMessages(output)
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
75
|
}
|
|
177
76
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const result = await streamUI({
|
|
182
|
-
model: openai('gpt-3.5-turbo'),
|
|
183
|
-
initial: <SpinnerMessage />,
|
|
184
|
-
system,
|
|
77
|
+
const stream = streamText({
|
|
78
|
+
model: openai('gpt-4o-mini'),
|
|
185
79
|
messages: [
|
|
186
|
-
|
|
187
|
-
role:
|
|
188
|
-
content:
|
|
189
|
-
|
|
190
|
-
|
|
80
|
+
{
|
|
81
|
+
role: 'system',
|
|
82
|
+
content: system
|
|
83
|
+
},
|
|
84
|
+
...aiState.get().messages
|
|
191
85
|
],
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (done) {
|
|
199
|
-
textStream.done()
|
|
200
|
-
|
|
201
|
-
onFinish([
|
|
202
|
-
{
|
|
203
|
-
id: nanoid(),
|
|
204
|
-
role: 'assistant',
|
|
205
|
-
content
|
|
206
|
-
}
|
|
207
|
-
])
|
|
208
|
-
} else {
|
|
209
|
-
textStream.update(delta)
|
|
86
|
+
experimental_telemetry: {
|
|
87
|
+
isEnabled: true,
|
|
88
|
+
metadata: {
|
|
89
|
+
threadId: aiState.get().chatId
|
|
210
90
|
}
|
|
211
|
-
|
|
212
|
-
return textNode
|
|
213
91
|
},
|
|
214
92
|
tools: {
|
|
215
|
-
listStocks: {
|
|
93
|
+
listStocks: tool({
|
|
216
94
|
description: 'List three imaginary stocks that are trending.',
|
|
217
95
|
parameters: z.object({
|
|
218
96
|
stocks: z.array(
|
|
@@ -223,10 +101,10 @@ async function submitUserMessage(content: string) {
|
|
|
223
101
|
})
|
|
224
102
|
)
|
|
225
103
|
}),
|
|
226
|
-
|
|
227
|
-
|
|
104
|
+
execute: async ({ stocks }) => {
|
|
105
|
+
ui.update(
|
|
228
106
|
<BotCard>
|
|
229
|
-
<
|
|
107
|
+
<StockSkeleton />
|
|
230
108
|
</BotCard>
|
|
231
109
|
)
|
|
232
110
|
|
|
@@ -261,14 +139,14 @@ async function submitUserMessage(content: string) {
|
|
|
261
139
|
}
|
|
262
140
|
])
|
|
263
141
|
|
|
264
|
-
|
|
142
|
+
ui.update(
|
|
265
143
|
<BotCard>
|
|
266
144
|
<Stocks props={stocks} />
|
|
267
145
|
</BotCard>
|
|
268
146
|
)
|
|
269
147
|
}
|
|
270
|
-
},
|
|
271
|
-
showStockPrice: {
|
|
148
|
+
}),
|
|
149
|
+
showStockPrice: tool({
|
|
272
150
|
description:
|
|
273
151
|
'Get the current stock price of a given stock or currency. Use this to show the price to the user.',
|
|
274
152
|
parameters: z.object({
|
|
@@ -280,8 +158,8 @@ async function submitUserMessage(content: string) {
|
|
|
280
158
|
price: z.number().describe('The price of the stock.'),
|
|
281
159
|
delta: z.number().describe('The change in price of the stock')
|
|
282
160
|
}),
|
|
283
|
-
|
|
284
|
-
|
|
161
|
+
execute: async ({ symbol, price, delta }) => {
|
|
162
|
+
ui.update(
|
|
285
163
|
<BotCard>
|
|
286
164
|
<StockSkeleton />
|
|
287
165
|
</BotCard>
|
|
@@ -318,14 +196,14 @@ async function submitUserMessage(content: string) {
|
|
|
318
196
|
}
|
|
319
197
|
])
|
|
320
198
|
|
|
321
|
-
|
|
199
|
+
ui.update(
|
|
322
200
|
<BotCard>
|
|
323
201
|
<Stock props={{ symbol, price, delta }} />
|
|
324
202
|
</BotCard>
|
|
325
203
|
)
|
|
326
204
|
}
|
|
327
|
-
},
|
|
328
|
-
showStockPurchase: {
|
|
205
|
+
}),
|
|
206
|
+
showStockPurchase: tool({
|
|
329
207
|
description:
|
|
330
208
|
'Show price and the UI to purchase a stock or currency. Use this if the user wants to purchase a stock or currency.',
|
|
331
209
|
parameters: z.object({
|
|
@@ -341,7 +219,7 @@ async function submitUserMessage(content: string) {
|
|
|
341
219
|
'The **number of shares** for a stock or currency to purchase. Can be optional if the user did not specify it.'
|
|
342
220
|
)
|
|
343
221
|
}),
|
|
344
|
-
|
|
222
|
+
execute: async ({ symbol, price, numberOfShares = 100 }) => {
|
|
345
223
|
const toolCallId = nanoid()
|
|
346
224
|
|
|
347
225
|
if (numberOfShares <= 0 || numberOfShares > 1000) {
|
|
@@ -382,7 +260,7 @@ async function submitUserMessage(content: string) {
|
|
|
382
260
|
}
|
|
383
261
|
])
|
|
384
262
|
|
|
385
|
-
|
|
263
|
+
ui.update(<BotMessage content={'Invalid amount'} />)
|
|
386
264
|
} else {
|
|
387
265
|
onFinish([
|
|
388
266
|
{
|
|
@@ -415,7 +293,7 @@ async function submitUserMessage(content: string) {
|
|
|
415
293
|
}
|
|
416
294
|
])
|
|
417
295
|
|
|
418
|
-
|
|
296
|
+
ui.update(
|
|
419
297
|
<BotCard>
|
|
420
298
|
<Purchase
|
|
421
299
|
props={{
|
|
@@ -429,8 +307,8 @@ async function submitUserMessage(content: string) {
|
|
|
429
307
|
)
|
|
430
308
|
}
|
|
431
309
|
}
|
|
432
|
-
},
|
|
433
|
-
getEvents: {
|
|
310
|
+
}),
|
|
311
|
+
getEvents: tool({
|
|
434
312
|
description:
|
|
435
313
|
'List funny imaginary events between user highlighted dates that describe stock activity.',
|
|
436
314
|
parameters: z.object({
|
|
@@ -444,8 +322,8 @@ async function submitUserMessage(content: string) {
|
|
|
444
322
|
})
|
|
445
323
|
)
|
|
446
324
|
}),
|
|
447
|
-
|
|
448
|
-
|
|
325
|
+
execute: async ({ events }) => {
|
|
326
|
+
ui.update(
|
|
449
327
|
<BotCard>
|
|
450
328
|
<EventsSkeleton />
|
|
451
329
|
</BotCard>
|
|
@@ -482,19 +360,113 @@ async function submitUserMessage(content: string) {
|
|
|
482
360
|
}
|
|
483
361
|
])
|
|
484
362
|
|
|
485
|
-
|
|
363
|
+
ui.update(
|
|
486
364
|
<BotCard>
|
|
487
365
|
<Events props={events} />
|
|
488
366
|
</BotCard>
|
|
489
367
|
)
|
|
490
368
|
}
|
|
491
|
-
}
|
|
369
|
+
})
|
|
492
370
|
}
|
|
493
371
|
})
|
|
494
372
|
|
|
373
|
+
setTimeout(async () => {
|
|
374
|
+
// First, stream all text chunks
|
|
375
|
+
for await (const chunk of stream.textStream) {
|
|
376
|
+
ui.update(<BotMessage content={fullContent} />)
|
|
377
|
+
fullContent += chunk
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
ui.done()
|
|
381
|
+
const toolCalls = await stream.toolCalls
|
|
382
|
+
if (!toolCalls || toolCalls.length == 0) {
|
|
383
|
+
aiState.done({
|
|
384
|
+
...aiState.get(),
|
|
385
|
+
messages: [
|
|
386
|
+
...aiState.get().messages,
|
|
387
|
+
{
|
|
388
|
+
id: nanoid(),
|
|
389
|
+
role: 'assistant',
|
|
390
|
+
content: fullContent
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
}, 0)
|
|
396
|
+
|
|
495
397
|
return {
|
|
496
398
|
id: nanoid(),
|
|
497
|
-
display:
|
|
399
|
+
display: ui.value
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async function confirmPurchase(symbol: string, price: number, amount: number) {
|
|
404
|
+
'use server'
|
|
405
|
+
|
|
406
|
+
const aiState = getMutableAIState<typeof AI>()
|
|
407
|
+
|
|
408
|
+
const purchasing = createStreamableUI(
|
|
409
|
+
<div className="inline-flex items-start gap-1 md:items-center">
|
|
410
|
+
{spinner}
|
|
411
|
+
<p className="mb-2">
|
|
412
|
+
Purchasing {amount} ${symbol}...
|
|
413
|
+
</p>
|
|
414
|
+
</div>
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
const systemMessage = createStreamableUI(null)
|
|
418
|
+
|
|
419
|
+
runAsyncFnWithoutBlocking(async () => {
|
|
420
|
+
await sleep(1000)
|
|
421
|
+
|
|
422
|
+
purchasing.update(
|
|
423
|
+
<div className="inline-flex items-start gap-1 md:items-center">
|
|
424
|
+
{spinner}
|
|
425
|
+
<p className="mb-2">
|
|
426
|
+
Purchasing {amount} ${symbol}... working on it...
|
|
427
|
+
</p>
|
|
428
|
+
</div>
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
await sleep(1000)
|
|
432
|
+
|
|
433
|
+
purchasing.done(
|
|
434
|
+
<div>
|
|
435
|
+
<p className="mb-2">
|
|
436
|
+
You have successfully purchased {amount} ${symbol}. Total cost:{' '}
|
|
437
|
+
{formatNumber(amount * price)}
|
|
438
|
+
</p>
|
|
439
|
+
</div>
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
systemMessage.done(
|
|
443
|
+
<SystemMessage>
|
|
444
|
+
You have purchased {amount} shares of {symbol} at ${price}. Total cost ={' '}
|
|
445
|
+
{formatNumber(amount * price)}.
|
|
446
|
+
</SystemMessage>
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
aiState.done({
|
|
450
|
+
...aiState.get(),
|
|
451
|
+
messages: [
|
|
452
|
+
...aiState.get().messages,
|
|
453
|
+
{
|
|
454
|
+
id: nanoid(),
|
|
455
|
+
role: 'system',
|
|
456
|
+
content: `[User has purchased ${amount} shares of ${symbol} at ${price}. Total cost = ${
|
|
457
|
+
amount * price
|
|
458
|
+
}]`
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
})
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
purchasingUI: purchasing.value,
|
|
466
|
+
newMessage: {
|
|
467
|
+
id: nanoid(),
|
|
468
|
+
display: systemMessage.value
|
|
469
|
+
}
|
|
498
470
|
}
|
|
499
471
|
}
|
|
500
472
|
|