1
1
'use client'
2
2
3
- import { CreateMessage , Message , useChat } from 'ai/react'
3
+ import { CreateMessage , Message , useChat , UseChatHelpers } from 'ai/react'
4
4
import { createContext , useCallback , useContext , useMemo } from 'react'
5
5
import { useMessageCreateMutation } from '~/data/messages/message-create-mutation'
6
6
import { useMessagesQuery } from '~/data/messages/messages-query'
7
7
import { useTablesQuery } from '~/data/tables/tables-query'
8
8
import { useOnToolCall } from '~/lib/hooks'
9
9
import { useBreakpoint } from '~/lib/use-breakpoint'
10
10
import { ensureMessageId , ensureToolResult } from '~/lib/util'
11
- import { useApp } from './app-provider'
12
11
import Chat , { getInitialMessages } from './chat'
13
12
import IDE from './ide'
14
13
@@ -27,13 +26,31 @@ export type WorkspaceProps = {
27
26
visibility : Visibility
28
27
29
28
/**
30
- * Callback called when the conversation has started .
29
+ * Callback called after the user sends a message .
31
30
*/
32
- onStart ?: ( ) => void | Promise < void >
31
+ onMessage ?: (
32
+ message : Message | CreateMessage ,
33
+ append : UseChatHelpers [ 'append' ]
34
+ ) => void | Promise < void >
35
+
36
+ /**
37
+ * Callback called after the LLM finishes a reply.
38
+ */
39
+ onReply ?: ( message : Message , append : UseChatHelpers [ 'append' ] ) => void | Promise < void >
40
+
41
+ /**
42
+ * Callback called when the user cancels the reply.
43
+ */
44
+ onCancelReply ?: ( append : UseChatHelpers [ 'append' ] ) => void | Promise < void >
33
45
}
34
46
35
- export default function Workspace ( { databaseId, visibility, onStart } : WorkspaceProps ) {
36
- const { dbManager } = useApp ( )
47
+ export default function Workspace ( {
48
+ databaseId,
49
+ visibility,
50
+ onMessage,
51
+ onReply,
52
+ onCancelReply,
53
+ } : WorkspaceProps ) {
37
54
const isSmallBreakpoint = useBreakpoint ( 'lg' )
38
55
const onToolCall = useOnToolCall ( databaseId )
39
56
const { mutateAsync : saveMessage } = useMessageCreateMutation ( databaseId )
@@ -46,40 +63,18 @@ export default function Workspace({ databaseId, visibility, onStart }: Workspace
46
63
47
64
const initialMessages = useMemo ( ( ) => ( tables ? getInitialMessages ( tables ) : undefined ) , [ tables ] )
48
65
49
- const {
50
- messages,
51
- setMessages,
52
- append,
53
- stop : stopReply ,
54
- } = useChat ( {
66
+ const { messages, setMessages, append, stop } = useChat ( {
55
67
id : databaseId ,
56
68
api : '/api/chat' ,
57
69
maxToolRoundtrips : 10 ,
70
+ keepLastMessageOnError : true ,
58
71
onToolCall : onToolCall as any , // our `OnToolCall` type is more specific than `ai` SDK's
59
72
initialMessages :
60
73
existingMessages && existingMessages . length > 0 ? existingMessages : initialMessages ,
61
74
async onFinish ( message ) {
62
- if ( ! dbManager ) {
63
- throw new Error ( 'dbManager is not available' )
64
- }
65
-
75
+ // Order is important here
76
+ await onReply ?.( message , append )
66
77
await saveMessage ( { message } )
67
-
68
- const database = await dbManager . getDatabase ( databaseId )
69
- const isStartOfConversation = database . isHidden && ! message . toolInvocations
70
-
71
- if ( isStartOfConversation ) {
72
- await onStart ?.( )
73
-
74
- // Intentionally using `append` vs `appendMessage` so that this message isn't persisted in the meta DB
75
- await append ( {
76
- role : 'user' ,
77
- content : 'Name this conversation. No need to reply.' ,
78
- data : {
79
- automated : true ,
80
- } ,
81
- } )
82
- }
83
78
} ,
84
79
} )
85
80
@@ -90,12 +85,20 @@ export default function Workspace({ databaseId, visibility, onStart }: Workspace
90
85
return isModified ? [ ...messages ] : messages
91
86
} )
92
87
ensureMessageId ( message )
88
+
89
+ // Intentionally don't await so that framer animations aren't affected
93
90
append ( message )
94
91
saveMessage ( { message } )
92
+ onMessage ?.( message , append )
95
93
} ,
96
- [ setMessages , saveMessage , append ]
94
+ [ onMessage , setMessages , saveMessage , append ]
97
95
)
98
96
97
+ const stopReply = useCallback ( async ( ) => {
98
+ stop ( )
99
+ onCancelReply ?.( append )
100
+ } , [ onCancelReply , stop , append ] )
101
+
99
102
const isConversationStarted =
100
103
initialMessages !== undefined && messages . length > initialMessages . length
101
104
@@ -134,7 +137,7 @@ export type WorkspaceContextValues = {
134
137
messages : Message [ ]
135
138
visibility : Visibility
136
139
appendMessage ( message : Message | CreateMessage ) : Promise < void >
137
- stopReply ( ) : void
140
+ stopReply ( ) : Promise < void >
138
141
}
139
142
140
143
export const WorkspaceContext = createContext < WorkspaceContextValues | undefined > ( undefined )
0 commit comments