Use core messages spec (#337)

This commit is contained in:
Jeremy 2024-05-15 09:39:16 -04:00 committed by GitHub
parent 095550d4dc
commit 901e4ccec1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 231 additions and 107 deletions

View file

@ -33,7 +33,7 @@ import {
} from '@/lib/utils'
import { saveChat } from '@/app/actions'
import { SpinnerMessage, UserMessage } from '@/components/stocks/message'
import { Chat } from '@/lib/types'
import { Chat, Message } from '@/lib/types'
import { auth } from '@/auth'
async function confirmPurchase(symbol: string, price: number, amount: number) {
@ -85,18 +85,7 @@ async function confirmPurchase(symbol: string, price: number, amount: number) {
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages.slice(0, -1),
{
id: nanoid(),
role: 'function',
name: 'showStockPurchase',
content: JSON.stringify({
symbol,
price,
defaultAmount: amount,
status: 'completed'
})
},
...aiState.get().messages,
{
id: nanoid(),
role: 'system',
@ -208,15 +197,35 @@ async function submitUserMessage(content: string) {
await sleep(1000)
const toolCallId = nanoid()
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'function',
name: 'listStocks',
content: JSON.stringify(stocks)
role: 'assistant',
content: [
{
type: 'tool-call',
toolName: 'listStocks',
toolCallId,
args: { stocks }
}
]
},
{
id: nanoid(),
role: 'tool',
content: [
{
type: 'tool-result',
toolName: 'listStocks',
toolCallId,
result: stocks
}
]
}
]
})
@ -249,15 +258,35 @@ async function submitUserMessage(content: string) {
await sleep(1000)
const toolCallId = nanoid()
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'function',
name: 'showStockPrice',
content: JSON.stringify({ symbol, price, delta })
role: 'assistant',
content: [
{
type: 'tool-call',
toolName: 'showStockPrice',
toolCallId,
args: { symbol, price, delta }
}
]
},
{
id: nanoid(),
role: 'tool',
content: [
{
type: 'tool-result',
toolName: 'showStockPrice',
toolCallId,
result: { symbol, price, delta }
}
]
}
]
})
@ -286,11 +315,42 @@ async function submitUserMessage(content: string) {
)
}),
generate: async function* ({ symbol, price, numberOfShares = 100 }) {
const toolCallId = nanoid()
if (numberOfShares <= 0 || numberOfShares > 1000) {
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'assistant',
content: [
{
type: 'tool-call',
toolName: 'showStockPurchase',
toolCallId,
args: { symbol, price, numberOfShares }
}
]
},
{
id: nanoid(),
role: 'tool',
content: [
{
type: 'tool-result',
toolName: 'showStockPurchase',
toolCallId,
result: {
symbol,
price,
numberOfShares,
status: 'expired'
}
}
]
},
{
id: nanoid(),
role: 'system',
@ -300,37 +360,55 @@ async function submitUserMessage(content: string) {
})
return <BotMessage content={'Invalid amount'} />
} else {
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'assistant',
content: [
{
type: 'tool-call',
toolName: 'showStockPurchase',
toolCallId,
args: { symbol, price, numberOfShares }
}
]
},
{
id: nanoid(),
role: 'tool',
content: [
{
type: 'tool-result',
toolName: 'showStockPurchase',
toolCallId,
result: {
symbol,
price,
numberOfShares
}
}
]
}
]
})
return (
<BotCard>
<Purchase
props={{
numberOfShares,
symbol,
price: +price,
status: 'requires_action'
}}
/>
</BotCard>
)
}
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'function',
name: 'showStockPurchase',
content: JSON.stringify({
symbol,
price,
numberOfShares
})
}
]
})
return (
<BotCard>
<Purchase
props={{
numberOfShares,
symbol,
price: +price,
status: 'requires_action'
}}
/>
</BotCard>
)
}
},
getEvents: {
@ -356,15 +434,35 @@ async function submitUserMessage(content: string) {
await sleep(1000)
const toolCallId = nanoid()
aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
id: nanoid(),
role: 'function',
name: 'getEvents',
content: JSON.stringify(events)
role: 'assistant',
content: [
{
type: 'tool-call',
toolName: 'getEvents',
toolCallId,
args: { events }
}
]
},
{
id: nanoid(),
role: 'tool',
content: [
{
type: 'tool-result',
toolName: 'getEvents',
toolCallId,
result: events
}
]
}
]
})
@ -385,13 +483,6 @@ async function submitUserMessage(content: string) {
}
}
export type Message = {
role: 'user' | 'assistant' | 'system' | 'function' | 'data' | 'tool'
content: string
id: string
name?: string
}
export type AIState = {
chatId: string
messages: Message[]
@ -425,7 +516,7 @@ export const AI = createAI<AIState, UIState>({
return
}
},
onSetAIState: async ({ state, done }) => {
onSetAIState: async ({ state }) => {
'use server'
const session = await auth()
@ -436,7 +527,9 @@ export const AI = createAI<AIState, UIState>({
const createdAt = new Date()
const userId = session.user.id as string
const path = `/chat/${chatId}`
const title = messages[0].content.substring(0, 100)
const firstMessageContent = messages[0].content as string
const title = firstMessageContent.substring(0, 100)
const chat: Chat = {
id: chatId,
@ -460,28 +553,36 @@ export const getUIStateFromAIState = (aiState: Chat) => {
.map((message, index) => ({
id: `${aiState.chatId}-${index}`,
display:
message.role === 'function' ? (
message.name === 'listStocks' ? (
<BotCard>
<Stocks props={JSON.parse(message.content)} />
</BotCard>
) : message.name === 'showStockPrice' ? (
<BotCard>
<Stock props={JSON.parse(message.content)} />
</BotCard>
) : message.name === 'showStockPurchase' ? (
<BotCard>
<Purchase props={JSON.parse(message.content)} />
</BotCard>
) : message.name === 'getEvents' ? (
<BotCard>
<Events props={JSON.parse(message.content)} />
</BotCard>
) : null
message.role === 'tool' ? (
message.content.map(tool => {
return tool.toolName === 'listStocks' ? (
<BotCard>
{/* TODO: Infer types based on the tool result*/}
{/* @ts-expect-error */}
<Stocks props={tool.result} />
</BotCard>
) : tool.toolName === 'showStockPrice' ? (
<BotCard>
{/* @ts-expect-error */}
<Stock props={tool.result} />
</BotCard>
) : tool.toolName === 'showStockPurchase' ? (
<BotCard>
{/* @ts-expect-error */}
<Purchase props={tool.result} />
</BotCard>
) : tool.toolName === 'getEvents' ? (
<BotCard>
{/* @ts-expect-error */}
<Events props={tool.result} />
</BotCard>
) : null
})
) : message.role === 'user' ? (
<UserMessage>{message.content}</UserMessage>
) : (
<UserMessage>{message.content as string}</UserMessage>
) : message.role === 'assistant' &&
typeof message.content === 'string' ? (
<BotMessage content={message.content} />
)
) : null
}))
}

View file

@ -1,4 +1,8 @@
import { Message } from 'ai'
import { CoreMessage } from 'ai'
export type Message = CoreMessage & {
id: string
}
export interface Chat extends Record<string, any> {
id: string