1+ import type { FastifyRequest , FastifyReply } from "fastify" ;
2+ import type { OpenaiRequestType } from "./type"
3+ import { getModelInfo } from "../../../../utils/get-model-info" ;
4+ import { embeddings } from "../../../../utils/embeddings" ;
5+ import { Document } from "langchain/document" ;
6+ import { BaseRetriever } from "@langchain/core/retrievers" ;
7+ import { DialoqbaseHybridRetrival } from "../../../../utils/hybrid" ;
8+ import { DialoqbaseVectorStore } from "../../../../utils/store" ;
9+ import { createChatModel } from "../bot/playground/chat.service" ;
10+ import { createChain } from "../../../../chain" ;
11+ import { openaiNonStreamResponse , openaiStreamResponse } from "./openai-response" ;
12+ import { groupOpenAiMessages } from "./other" ;
13+ import { nextTick } from "../../../../utils/nextTick" ;
14+
15+
16+ export const createChatCompletionHandler = async (
17+ request : FastifyRequest < OpenaiRequestType > ,
18+ reply : FastifyReply
19+ ) => {
20+ try {
21+ const {
22+ model,
23+ messages
24+ } = request . body ;
25+
26+ const prisma = request . server . prisma ;
27+
28+ const bot = await prisma . bot . findFirst ( {
29+ where : {
30+ OR : [
31+ {
32+ id : model
33+ } ,
34+ {
35+ publicId : model
36+ }
37+ ] ,
38+ user_id : request . user . is_admin ? undefined : request . user . user_id ,
39+ } ,
40+ } )
41+
42+ if ( ! bot ) {
43+ return reply . status ( 404 ) . send ( {
44+ error : {
45+ message : "Bot not found" ,
46+ type : "not_found" ,
47+ param : "model" ,
48+ code : "bot_not_found"
49+ }
50+ } ) ;
51+ }
52+
53+
54+ const embeddingInfo = await getModelInfo ( {
55+ prisma,
56+ model : bot . embedding ,
57+ type : "embedding" ,
58+ } ) ;
59+
60+ if ( ! embeddingInfo ) {
61+ return reply . status ( 400 ) . send ( {
62+ error : {
63+ message : "Embedding not found" ,
64+ type : "not_found" ,
65+ param : "embedding" ,
66+ code : "embedding_not_found"
67+ }
68+ } ) ;
69+ }
70+
71+
72+ const embeddingModel = embeddings (
73+ embeddingInfo . model_provider ! . toLowerCase ( ) ,
74+ embeddingInfo . model_id ,
75+ embeddingInfo ?. config
76+ ) ;
77+
78+ const modelinfo = await getModelInfo ( {
79+ prisma,
80+ model : bot . model ,
81+ type : "chat" ,
82+ } ) ;
83+
84+ if ( ! modelinfo ) {
85+ return reply . status ( 400 ) . send ( {
86+ error : {
87+ message : "Model not found" ,
88+ type : "not_found" ,
89+ param : "model" ,
90+ code : "model_not_found"
91+ }
92+ } ) ;
93+ }
94+
95+ const botConfig = ( modelinfo . config as { } ) || { } ;
96+ let retriever : BaseRetriever ;
97+ let resolveWithDocuments : ( value : Document [ ] ) => void ;
98+
99+ if ( bot . use_hybrid_search ) {
100+ retriever = new DialoqbaseHybridRetrival ( embeddingModel , {
101+ botId : bot . id ,
102+ sourceId : null ,
103+ callbacks : [
104+ {
105+ handleRetrieverEnd ( documents ) {
106+ resolveWithDocuments ( documents ) ;
107+ } ,
108+ } ,
109+ ] ,
110+ } ) ;
111+ } else {
112+ const vectorstore = await DialoqbaseVectorStore . fromExistingIndex (
113+ embeddingModel ,
114+ {
115+ botId : bot . id ,
116+ sourceId : null ,
117+ }
118+ ) ;
119+
120+ retriever = vectorstore . asRetriever ( {
121+ } ) ;
122+ }
123+
124+ const streamedModel = createChatModel (
125+ bot ,
126+ bot . temperature ,
127+ botConfig ,
128+ true
129+ ) ;
130+ const nonStreamingModel = createChatModel ( bot , bot . temperature , botConfig ) ;
131+
132+ const chain = createChain ( {
133+ llm : streamedModel ,
134+ question_llm : nonStreamingModel ,
135+ question_template : bot . questionGeneratorPrompt ,
136+ response_template : bot . qaPrompt ,
137+ retriever,
138+ } ) ;
139+
140+ if ( ! request . body . stream ) {
141+ const res = await chain . invoke ( {
142+ question : messages [ messages . length - 1 ] . content ,
143+ chat_history : groupOpenAiMessages (
144+ messages
145+ ) ,
146+ } )
147+
148+
149+ return reply . status ( 200 ) . send ( openaiNonStreamResponse (
150+ res ,
151+ bot . name
152+ ) )
153+ }
154+
155+ const stream = await chain . stream ( {
156+ question : messages [ messages . length - 1 ] . content ,
157+ chat_history : groupOpenAiMessages (
158+ messages
159+ ) ,
160+ } )
161+ reply . raw . setHeader ( "Content-Type" , "text/event-stream" ) ;
162+
163+ for await ( const token of stream ) {
164+ reply . sse ( {
165+ data : openaiStreamResponse (
166+ token || "" ,
167+ bot . name
168+ )
169+ } ) ;
170+ }
171+ reply . sse ( {
172+ data : "[DONE]\n\n"
173+ } )
174+ await nextTick ( ) ;
175+ return reply . raw . end ( ) ;
176+ } catch ( error ) {
177+ console . log ( error )
178+ return reply . status ( 500 ) . send ( {
179+ error : {
180+ message : error . message ,
181+ type : "internal_server_error" ,
182+ param : null ,
183+ code : "internal_server_error"
184+ }
185+ } ) ;
186+ }
187+ }
0 commit comments