-
Notifications
You must be signed in to change notification settings - Fork 434
/
Copy pathconversation.ts
137 lines (127 loc) · 5.07 KB
/
conversation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import destr from 'destr'
import { getBotMetaById, getProviderById } from '@/stores/provider'
import { updateConversationById } from '@/stores/conversation'
import { clearMessagesByConversationId, getMessagesByConversationId, pushMessageByConversationId } from '@/stores/messages'
import { getGeneralSettings, getSettingsByProviderId } from '@/stores/settings'
import { setLoadingStateByConversationId, setStreamByConversationId } from '@/stores/streams'
import { currentErrorMessage } from '@/stores/ui'
import { generateRapidProviderPayload, promptHelper } from './helper'
import type { HandlerPayload, PromptResponse } from '@/types/provider'
import type { Conversation } from '@/types/conversation'
import type { ErrorMessage, Message } from '@/types/message'
export const handlePrompt = async(conversation: Conversation, prompt?: string, signal?: AbortSignal) => {
const generalSettings = getGeneralSettings()
const bot = getBotMetaById(conversation.bot)
const [providerId, botId] = conversation.bot.split(':')
const provider = getProviderById(providerId)
if (!provider) return
let callMethod = generalSettings.requestWithBackend ? 'backend' : 'frontend' as 'frontend' | 'backend'
if (provider.supportCallMethod === 'frontend' || provider.supportCallMethod === 'backend')
callMethod = provider.supportCallMethod
if (bot.type !== 'chat_continuous')
clearMessagesByConversationId(conversation.id)
if (prompt) {
pushMessageByConversationId(conversation.id, {
id: `${conversation.id}:user:${Date.now()}`,
role: 'user',
content: prompt,
dateTime: new Date().getTime(),
})
}
setLoadingStateByConversationId(conversation.id, true)
let providerResponse: PromptResponse
const handlerPayload: HandlerPayload = {
conversationId: conversation.id,
conversationType: bot.type,
botId,
globalSettings: getSettingsByProviderId(provider.id),
botSettings: {},
prompt,
messages: [
...(conversation.systemInfo ? [{ role: 'system', content: conversation.systemInfo }] : []) as Message[],
...(destr(conversation.mockMessages) || []) as Message[],
...getMessagesByConversationId(conversation.id).map(message => ({
role: message.role,
content: message.content,
})),
],
}
try {
providerResponse = await getProviderResponse(provider.id, handlerPayload, {
caller: callMethod,
signal,
})
} catch (e) {
const error = e as Error
const cause = error?.cause as ErrorMessage
setLoadingStateByConversationId(conversation.id, false)
if (error.name !== 'AbortError') {
currentErrorMessage.set({
code: cause?.code || 'provider_error',
message: cause?.message || error.message || 'Unknown error',
})
}
}
if (providerResponse) {
const messageId = `${conversation.id}:assistant:${Date.now()}`
if (providerResponse instanceof ReadableStream) {
setStreamByConversationId(conversation.id, {
messageId,
stream: providerResponse,
})
}
pushMessageByConversationId(conversation.id, {
id: messageId,
role: 'assistant',
content: typeof providerResponse === 'string' ? providerResponse : '',
stream: providerResponse instanceof ReadableStream,
dateTime: new Date().getTime(),
})
}
setLoadingStateByConversationId(conversation.id, false)
// Update conversation title
if (providerResponse && bot.type === 'chat_continuous' && !conversation.name) {
const inputText = conversation.systemInfo || prompt!
const rapidPayload = generateRapidProviderPayload(promptHelper.summarizeText(inputText), provider.id)
const generatedTitle = await getProviderResponse(provider.id, rapidPayload).catch(() => {}) as string || inputText
updateConversationById(conversation.id, {
name: generatedTitle.replace(/^['"\s]+|['"\s]+$/g, ''),
})
}
}
const getProviderResponse = async(providerId: string, payload: HandlerPayload, options?: {
caller: 'frontend' | 'backend'
signal?: AbortSignal
}) => {
if (options?.caller === 'frontend') {
return callProviderHandler(providerId, payload, options.signal)
} else {
const backendResponse = await fetch(`/api/handle/${providerId}`, {
method: 'POST',
body: JSON.stringify(payload),
signal: options?.signal,
})
if (!backendResponse.ok) {
const error = await backendResponse.json()
throw new Error('Request failed', {
cause: error?.error,
})
}
if (backendResponse.headers.get('content-type')?.includes('text/plain'))
return backendResponse.text()
else
return backendResponse.body
}
}
// Called by both client and server
export const callProviderHandler = async(providerId: string, payload: HandlerPayload, signal?: AbortSignal) => {
console.log('callProviderHandler', payload)
const provider = getProviderById(providerId)
if (!provider) return
let response: PromptResponse
if (payload.botId === 'temp')
response = await provider.handleRapidPrompt?.(payload.prompt!, payload.globalSettings)
else
response = await provider.handlePrompt?.(payload, signal)
return response
}