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' ;
import { readFileSync } from 'fs' ;
import { resolve } from 'path' ;
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
// Read the XML guide from file
2025-03-19 06:04:06 +00:00
export async function POST ( req : Request ) {
2025-03-24 02:38:27 +00:00
const body = await req . json ( ) ;
2025-03-19 11:03:37 +00:00
2025-08-31 20:52:04 +09:00
// Extract messages and xml directly from the body
const { messages , xml } = body ;
2025-05-22 00:44:24 +00:00
const guide = readFileSync ( resolve ( './app/api/chat/xml_guide.md' ) , 'utf8' ) ;
2025-03-19 11:03:37 +00:00
// Read and escape the guide content
2025-03-19 08:40:08 +00:00
const systemMessage = `
2025-03-23 11:03:25 +00:00
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 .
You can see the image that user uploaded .
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 .
- * * Don ' t * * write out the XML string . Just return the XML string in the tool call .
- 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-05-22 00:44:24 +00:00
here is a guide for the XML format : $ { guide }
2025-03-23 11:03:25 +00:00
` ;
2025-03-19 11:03:37 +00:00
2025-03-24 02:38:27 +00:00
const lastMessage = messages [ messages . length - 1 ] ;
2025-08-31 12:54:14 +09:00
// Extract text from the last message parts
const lastMessageText = lastMessage . parts ? . find ( ( part : any ) = > part . type === 'text' ) ? . text || '' ;
2025-03-24 02:38:27 +00:00
const formattedContent = `
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
// Convert UIMessages to ModelMessages and add system message
const modelMessages = convertToModelMessages ( messages ) ;
2025-08-31 20:52:04 +09:00
let enhancedMessages = [ . . . modelMessages ] ;
2025-08-31 12:54:14 +09:00
// Update the last message with formatted content if it's a user message
2025-08-31 20:52:04 +09:00
if ( enhancedMessages . length >= 1 ) {
2025-08-31 12:54:14 +09:00
const lastModelMessage = enhancedMessages [ enhancedMessages . length - 1 ] ;
if ( lastModelMessage . role === 'user' ) {
enhancedMessages = [
. . . enhancedMessages . slice ( 0 , - 1 ) ,
{ . . . lastModelMessage , content : formattedContent }
] ;
}
}
2025-08-31 20:52:04 +09:00
console . log ( "Enhanced messages:" , enhancedMessages ) ;
2025-04-03 15:10:53 +00:00
2025-03-19 11:03:37 +00:00
const result = streamText ( {
2025-05-22 00:44:24 +00:00
// model: google("gemini-2.5-flash-preview-05-20"),
2025-08-31 12:59:29 +09:00
// model: google("gemini-2.5-pro"),
2025-07-31 22:01:24 +00:00
// model: bedrock('anthropic.claude-sonnet-4-20250514-v1:0'),
2025-08-31 20:52:04 +09:00
system : systemMessage ,
2025-11-09 23:24:15 +09:00
model : bedrock ( 'global.anthropic.claude-sonnet-4-5-20250929-v1:0' ) ,
2025-07-31 22:01:24 +00:00
// model: openrouter('moonshotai/kimi-k2:free'),
2025-05-22 00:44:24 +00:00
// model: model,
2025-08-31 12:54:14 +09:00
// providerOptions: {
2025-08-31 12:59:29 +09:00
// google: {
// thinkingConfig: {
// thinkingBudget: 128,
// },
// }
2025-08-31 12:54:14 +09:00
// },
2025-11-09 23:24:15 +09:00
// providerOptions: {
// openai: {
// reasoningEffort: "minimal"
// },
// },
2025-03-19 11:03:37 +00:00
messages : enhancedMessages ,
2025-03-19 08:16:44 +00:00
tools : {
2025-03-19 11:03:37 +00:00
// Client-side tool that will be executed on the client
2025-03-22 14:28:55 +00:00
display_diagram : {
2025-03-25 08:56:24 +00:00
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" / >
< mxGeometry x = "20" y = "20" width = "100" height = "100" as = "geometry" / >
< mxCell id = "2" value = "Hello, World!" style = "shape=rectangle" parent = "1" >
< mxGeometry x = "20" y = "20" width = "100" height = "100" as = "geometry" / >
< / mxCell >
< / root > ` ,
2025-08-31 12:54:14 +09:00
inputSchema : z.object ( {
2025-03-19 11:03:37 +00:00
xml : z.string ( ) . describe ( "XML string to be displayed on draw.io" )
} )
2025-03-19 08:16:44 +00:00
} ,
2025-08-31 20:52:04 +09:00
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.
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 ` ,
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-03-19 08:16:44 +00:00
} ,
2025-08-31 12:54:14 +09:00
temperature : 0 ,
2025-03-19 06:04:06 +00:00
} ) ;
2025-04-04 02:10:24 +00:00
2025-08-19 01:17:17 +00:00
// Error handler function to provide detailed error messages
function errorHandler ( error : unknown ) {
if ( error == null ) {
return 'unknown error' ;
}
if ( typeof error === 'string' ) {
return error ;
}
if ( error instanceof Error ) {
return error . message ;
}
return JSON . stringify ( error ) ;
}
2025-08-31 12:54:14 +09:00
return result . toUIMessageStreamResponse ( {
onError : errorHandler ,
2025-04-04 02:10:24 +00:00
} ) ;
2025-03-19 08:16:44 +00:00
}