1
- import { langfuseHandler } from '@/libs/langfuse/langfuseHandler'
2
- import { } from '@langchain/core/messages'
3
- import { ChatPromptTemplate } from '@langchain/core/prompts'
4
- import { ChatOpenAI } from '@langchain/openai'
1
+ import { mastra } from '@/lib/mastra'
5
2
import type { Schema , TableGroup } from '@liam-hq/db-structure'
6
- import { Document } from 'langchain/document '
3
+ import * as Sentry from '@sentry/nextjs '
7
4
import { NextResponse } from 'next/server'
8
5
9
6
// Export TableGroupData type for compatibility
@@ -13,7 +10,7 @@ export type TableGroupData = TableGroup
13
10
const tableToDocument = (
14
11
tableName : string ,
15
12
tableData : Schema [ 'tables' ] [ string ] ,
16
- ) : Document => {
13
+ ) : string => {
17
14
// Table description
18
15
const tableDescription = `Table: ${ tableName } \nDescription: ${ tableData . comment || 'No description' } \n`
19
16
@@ -39,30 +36,20 @@ const tableToDocument = (
39
36
}
40
37
41
38
// Combine all information
42
- const tableText = `${ tableDescription } ${ columnsText } ${ primaryKeyText } `
43
-
44
- return new Document ( {
45
- pageContent : tableText ,
46
- metadata : { tableName } ,
47
- } )
39
+ return `${ tableDescription } ${ columnsText } ${ primaryKeyText } `
48
40
}
49
41
50
42
// Convert relationship data to text document
51
43
const relationshipToDocument = (
52
44
relationshipName : string ,
53
45
relationshipData : Schema [ 'relationships' ] [ string ] ,
54
- ) : Document => {
55
- const relationshipText = `Relationship: ${ relationshipName }
46
+ ) : string => {
47
+ return `Relationship: ${ relationshipName }
56
48
From Table: ${ relationshipData . primaryTableName }
57
49
From Column: ${ relationshipData . primaryColumnName }
58
50
To Table: ${ relationshipData . foreignTableName }
59
51
To Column: ${ relationshipData . foreignColumnName }
60
52
Type: ${ relationshipData . cardinality || 'unknown' } \n`
61
-
62
- return new Document ( {
63
- pageContent : relationshipText ,
64
- metadata : { relationshipName } ,
65
- } )
66
53
}
67
54
68
55
// Convert table groups to text document
@@ -99,7 +86,7 @@ const convertSchemaToText = (schema: Schema): string => {
99
86
schemaText += 'TABLES:\n\n'
100
87
for ( const [ tableName , tableData ] of Object . entries ( schema . tables ) ) {
101
88
const tableDoc = tableToDocument ( tableName , tableData )
102
- schemaText = `${ schemaText } ${ tableDoc . pageContent } \n\n`
89
+ schemaText = `${ schemaText } ${ tableDoc } \n\n`
103
90
}
104
91
}
105
92
@@ -113,7 +100,7 @@ const convertSchemaToText = (schema: Schema): string => {
113
100
relationshipName ,
114
101
relationshipData ,
115
102
)
116
- schemaText = `${ schemaText } ${ relationshipDoc . pageContent } \n\n`
103
+ schemaText = `${ schemaText } ${ relationshipDoc } \n\n`
117
104
}
118
105
}
119
106
@@ -141,7 +128,7 @@ export async function POST(request: Request) {
141
128
)
142
129
}
143
130
144
- // Format chat history for prompt template
131
+ // Format chat history for prompt
145
132
const formattedChatHistory =
146
133
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
147
134
history && history . length > 0
@@ -155,126 +142,41 @@ export async function POST(request: Request) {
155
142
// Convert schema to text
156
143
const schemaText = convertSchemaToText ( schemaData )
157
144
158
- // Create a streaming model
159
- const streamingModel = new ChatOpenAI ( {
160
- modelName : 'o4-mini-2025-04-16' ,
161
- streaming : true ,
162
- callbacks : [ langfuseHandler ] ,
163
- } )
164
-
165
- // Create a prompt template with full schema context and chat history
166
- const prompt = ChatPromptTemplate . fromTemplate ( `
167
- You are a database schema expert.
168
- Answer questions about the user's schema and provide advice on database design.
169
- Follow these guidelines:
170
-
171
- 1. Clearly explain the structure of the schema, tables, and relationships.
172
- 2. Provide advice based on good database design principles.
173
- 3. Share best practices for normalization, indexing, and performance.
174
- 4. When using technical terms, include brief explanations.
175
- 5. Provide only information directly related to the question, avoiding unnecessary details.
176
- 6. Format your responses using GitHub Flavored Markdown (GFM) for better readability.
177
-
178
- Your goal is to help users understand and optimize their database schemas.
145
+ try {
146
+ // Get the agent from Mastra
147
+ const agent = mastra . getAgent ( 'databaseSchemaAgent' )
148
+ if ( ! agent ) {
149
+ throw new Error ( 'databaseSchemaAgent not found in Mastra instance' )
150
+ }
179
151
152
+ // Create a response using the agent
153
+ const response = await agent . generate ( [
154
+ {
155
+ role : 'system' ,
156
+ content : `
180
157
Complete Schema Information:
181
158
${ schemaText }
182
159
183
160
Previous conversation:
184
- {chat_history}
185
-
186
- Question: {input}
187
-
188
- Based on the schema information provided and considering any previous conversation, answer the question thoroughly and accurately.
189
- ` )
190
-
191
- // Create streaming chain
192
- const streamingChain = prompt . pipe ( streamingModel )
193
-
194
- // Generate streaming response
195
- const stream = await streamingChain . stream (
196
- {
197
- input : message ,
198
- chat_history : formattedChatHistory ,
199
- } ,
200
- {
201
- callbacks : [ langfuseHandler ] ,
202
- metadata : {
203
- endpoint : '/api/chat' ,
204
-
205
- method : 'POST' ,
206
- messageLength : message . length ,
207
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
208
- hasHistory : history ? history . length > 0 : false ,
161
+ ${ formattedChatHistory }
162
+ ` ,
209
163
} ,
210
- } ,
211
- )
212
-
213
- // Create a TransformStream to convert the LangChain stream to a ReadableStream
214
- const encoder = new TextEncoder ( )
215
- const { readable, writable } = new TransformStream ( )
216
- const writer = writable . getWriter ( )
217
-
218
- // Define types for content processing
219
- type ContentItem = string | { type : string ; text : string } | unknown
220
-
221
- // Extract content processing to a separate function
222
- const extractTextContent = (
223
- content : string | ContentItem [ ] | unknown ,
224
- ) : string => {
225
- if ( typeof content === 'string' ) {
226
- return content
227
- }
228
-
229
- if ( ! Array . isArray ( content ) ) {
230
- return ''
231
- }
232
-
233
- // Process array content
234
- return content . reduce ( ( text , item ) => {
235
- if ( typeof item === 'string' ) {
236
- return text + item
237
- }
238
-
239
- if (
240
- item &&
241
- typeof item === 'object' &&
242
- 'type' in item &&
243
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
244
- item . type === 'text' &&
245
- 'text' in item &&
246
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
247
- typeof item . text === 'string'
248
- ) {
249
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
250
- return text + item . text
251
- }
252
-
253
- return text
254
- } , '' )
255
- }
164
+ {
165
+ role : 'user' ,
166
+ content : message ,
167
+ } ,
168
+ ] )
256
169
257
- // Main stream processing function - simplified
258
- const processStream = async ( ) => {
259
- try {
260
- for await ( const chunk of stream ) {
261
- const textContent = extractTextContent ( chunk . content )
262
- await writer . write ( encoder . encode ( textContent ) )
263
- }
264
- } catch ( error ) {
265
- console . error ( 'Error processing stream:' , error )
266
- } finally {
267
- await writer . close ( )
268
- }
170
+ return new Response ( response . text , {
171
+ headers : {
172
+ 'Content-Type' : 'text/plain; charset=utf-8' ,
173
+ } ,
174
+ } )
175
+ } catch ( error ) {
176
+ Sentry . captureException ( error )
177
+ return NextResponse . json (
178
+ { error : 'Failed to generate response' } ,
179
+ { status : 500 } ,
180
+ )
269
181
}
270
-
271
- // Execute the processing function
272
- processStream ( )
273
-
274
- // Return the streaming response
275
- return new Response ( readable , {
276
- headers : {
277
- 'Content-Type' : 'text/plain; charset=utf-8' ,
278
- } ,
279
- } )
280
182
}
0 commit comments