2025-04-03 15:10:53 +00:00
import { bedrock } from '@ai-sdk/amazon-bedrock' ;
2025-04-04 01:26:16 +00:00
import { openai } from '@ai-sdk/openai' ;
import { google } from '@ai-sdk/google' ;
2025-08-31 12:54:14 +09:00
import { smoothStream , streamText , convertToModelMessages } from 'ai' ;
2025-05-22 00:44:24 +00:00
import { createOpenRouter } from '@openrouter/ai-sdk-provider' ;
2025-08-19 01:17:17 +00:00
import { createGoogleGenerativeAI } from '@ai-sdk/google' ;
import { createOpenAI } from '@ai-sdk/openai' ;
2025-04-04 02:10:24 +00:00
2025-08-31 12:54:14 +09:00
import { z } from "zod/v3" ;
2025-08-31 20:52:04 +09:00
import { replaceXMLParts } from "@/lib/utils" ;
2025-03-19 06:04:06 +00:00
2025-04-04 01:48:15 +00:00
export const maxDuration = 60
2025-05-22 00:44:24 +00:00
const openrouter = createOpenRouter ( { apiKey : process.env.OPENROUTER_API_KEY } ) ;
2025-03-19 11:03:37 +00:00
2025-11-10 00:00:02 +09:00
export async function POST ( req : Request ) {
try {
const { messages , xml } = await req . json ( ) ;
2025-03-19 11:03:37 +00:00
2025-11-10 00:00:02 +09:00
const systemMessage = `
You are an expert diagram creation assistant specializing in draw . io XML generation .
Your primary function is crafting clear , well - organized visual diagrams through precise XML specifications .
2025-03-23 11:03:25 +00:00
You can see the image that user uploaded .
2025-11-10 00:00:02 +09:00
When you need to generate diagram about aws architecture , use AWS 2025 icons .
2025-08-31 20:52:04 +09:00
You utilize the following tools :
2025-03-19 11:03:37 +00:00
-- - Tool1 -- -
2025-03-22 14:28:55 +00:00
tool name : display_diagram
description : Display a diagram on draw . io
2025-03-19 11:03:37 +00:00
parameters : {
xml : string
}
2025-08-31 20:52:04 +09:00
-- - Tool2 -- -
tool name : edit_diagram
description : Edit specific parts of the current diagram
parameters : {
edits : Array < { search : string , replace : string } >
}
2025-03-19 11:03:37 +00:00
-- - End of tools -- -
2025-03-23 11:03:25 +00:00
Core capabilities :
- Generate valid , well - formed XML strings for draw . io diagrams
- Create professional flowcharts , mind maps , entity diagrams , and technical illustrations
- Convert user descriptions into visually appealing diagrams using basic shapes and connectors
- Apply proper spacing , alignment and visual hierarchy in diagram layouts
- Adapt artistic concepts into abstract diagram representations using available shapes
- Optimize element positioning to prevent overlapping and maintain readability
- Structure complex systems into clear , organized visual components
Note that :
- Focus on producing clean , professional diagrams that effectively communicate the intended information through thoughtful layout and design choices .
- When artistic drawings are requested , creatively compose them using standard diagram shapes and connectors while maintaining visual clarity .
2025-11-10 09:12:30 +09:00
- Return XML only via tool calls , never in text responses .
2025-03-23 11:03:25 +00:00
- If user asks you to replicate a diagram based on an image , remember to match the diagram style and layout as closely as possible . Especially , pay attention to the lines and shapes , for example , if the lines are straight or curved , and if the shapes are rounded or square .
2025-05-22 00:44:24 +00:00
2025-08-31 20:52:04 +09:00
When using edit_diagram tool :
- Keep edits minimal - only include the specific line being changed plus 1 - 2 context lines
- Example GOOD edit : { "search" : " <mxCell id=\"2\" value=\"Old Text\">" , "replace" : " <mxCell id=\"2\" value=\"New Text\">" }
- Example BAD edit : Including 10 + unchanged lines just to change one attribute
- For multiple changes , use separate edits : [ { "search" : "line1" , "replace" : "new1" } , { "search" : "line2" , "replace" : "new2" } ]
2025-03-23 11:03:25 +00:00
` ;
2025-03-19 11:03:37 +00:00
2025-11-10 00:00:02 +09:00
const lastMessage = messages [ messages . length - 1 ] ;
2025-08-31 12:54:14 +09:00
2025-11-10 00:00:02 +09:00
// Extract text from the last message parts
const lastMessageText = lastMessage . parts ? . find ( ( part : any ) = > part . type === 'text' ) ? . text || '' ;
2025-08-31 12:54:14 +09:00
2025-11-10 00:00:02 +09:00
// Extract file parts (images) from the last message
const fileParts = lastMessage . parts ? . filter ( ( part : any ) = > part . type === 'file' ) || [ ] ;
const formattedTextContent = `
2025-03-24 02:38:27 +00:00
Current diagram XML :
"" " xml
2025-08-31 20:52:04 +09:00
$ { xml || '' }
2025-03-24 02:38:27 +00:00
"" "
User input :
"" " md
2025-08-31 12:54:14 +09:00
$ { lastMessageText }
2025-03-24 02:38:27 +00:00
"" " ` ;
2025-08-31 12:54:14 +09:00
2025-11-10 00:00:02 +09:00
// Convert UIMessages to ModelMessages and add system message
const modelMessages = convertToModelMessages ( messages ) ;
let enhancedMessages = [ . . . modelMessages ] ;
// Update the last message with formatted content if it's a user message
if ( enhancedMessages . length >= 1 ) {
const lastModelMessage = enhancedMessages [ enhancedMessages . length - 1 ] ;
if ( lastModelMessage . role === 'user' ) {
// Build content array with text and file parts
const contentParts : any [ ] = [
{ type : 'text' , text : formattedTextContent }
] ;
// Add image parts back
for ( const filePart of fileParts ) {
contentParts . push ( {
type : 'image' ,
image : filePart.url ,
mimeType : filePart.mediaType
} ) ;
}
enhancedMessages = [
. . . enhancedMessages . slice ( 0 , - 1 ) ,
{ . . . lastModelMessage , content : contentParts }
] ;
}
2025-08-31 12:54:14 +09:00
}
2025-11-10 00:00:02 +09:00
console . log ( "Enhanced messages:" , enhancedMessages ) ;
const result = streamText ( {
// model: google("gemini-2.5-flash-preview-05-20"),
// model: google("gemini-2.5-pro"),
// model: bedrock('anthropic.claude-sonnet-4-20250514-v1:0'),
system : systemMessage ,
model : bedrock ( 'global.anthropic.claude-sonnet-4-5-20250929-v1:0' ) ,
// model: openrouter('moonshotai/kimi-k2:free'),
// model: model,
// providerOptions: {
// google: {
// thinkingConfig: {
// thinkingBudget: 128,
// },
// }
// },
// providerOptions: {
// openai: {
// reasoningEffort: "minimal"
// },
// },
messages : enhancedMessages ,
tools : {
// Client-side tool that will be executed on the client
display_diagram : {
description : ` Display a diagram on draw.io. You only need to pass the nodes inside the <root> tag (including the <root> tag itself) in the XML string.
For example :
< root >
< mxCell id = "0" / >
< mxCell id = "1" parent = "0" / >
2025-03-25 08:56:24 +00:00
< mxGeometry x = "20" y = "20" width = "100" height = "100" as = "geometry" / >
2025-11-10 00:00:02 +09:00
< mxCell id = "2" value = "Hello, World!" style = "shape=rectangle" parent = "1" >
< mxGeometry x = "20" y = "20" width = "100" height = "100" as = "geometry" / >
< / mxCell >
< / root > ` ,
inputSchema : z.object ( {
xml : z.string ( ) . describe ( "XML string to be displayed on draw.io" )
} )
} ,
edit_diagram : {
description : ` Edit specific parts of the current diagram by replacing exact line matches. Use this tool to make targeted fixes without regenerating the entire XML.
2025-08-31 20:52:04 +09:00
IMPORTANT : Keep edits concise :
- Only include the lines that are changing , plus 1 - 2 surrounding lines for context if needed
- Break large changes into multiple smaller edits
- Each search must contain complete lines ( never truncate mid - line )
- First match only - be specific enough to target the right element ` ,
2025-11-10 00:00:02 +09:00
inputSchema : z.object ( {
edits : z.array ( z . object ( {
search : z.string ( ) . describe ( "Exact lines to search for (including whitespace and indentation)" ) ,
replace : z.string ( ) . describe ( "Replacement lines" )
} ) ) . describe ( "Array of search/replace pairs to apply sequentially" )
} )
} ,
2025-08-31 20:52:04 +09:00
} ,
2025-11-10 00:00:02 +09:00
temperature : 0 ,
} ) ;
2025-04-04 02:10:24 +00:00
2025-11-10 00:00:02 +09:00
// Error handler function to provide detailed error messages
function errorHandler ( error : unknown ) {
if ( error == null ) {
return 'unknown error' ;
}
2025-08-19 01:17:17 +00:00
2025-11-10 00:00:02 +09:00
if ( typeof error === 'string' ) {
return error ;
}
2025-08-19 01:17:17 +00:00
2025-11-10 00:00:02 +09:00
if ( error instanceof Error ) {
return error . message ;
}
2025-08-19 01:17:17 +00:00
2025-11-10 00:00:02 +09:00
return JSON . stringify ( error ) ;
2025-08-19 01:17:17 +00:00
}
2025-11-10 00:00:02 +09:00
return result . toUIMessageStreamResponse ( {
onError : errorHandler ,
} ) ;
} catch ( error ) {
console . error ( 'Error in chat route:' , error ) ;
return Response . json (
{ error : 'Internal server error' } ,
{ status : 500 }
) ;
2025-08-19 01:17:17 +00:00
}
2025-03-19 08:16:44 +00:00
}