generative-ui/app/api/generate_ui/route.ts (117 lines of code) (raw):
import OpenAI from 'openai'
import { MODEL, SYSTEM_PROMPT } from '@/lib/constants'
import { generateUITool } from '@/lib/generate-ui-tool'
import {
ChatCompletionMessageParam,
ChatCompletionTool
} from 'openai/resources/chat/completions'
const openai = new OpenAI()
const toolsDefinition = [generateUITool]
const tools = toolsDefinition.map(tool => {
return {
type: 'function',
function: {
...tool,
parameters: tool.parameters
}
}
})
export async function POST(request: Request) {
const { user_input } = await request.json()
try {
const stream = new ReadableStream({
async start(controller) {
try {
console.log('Starting OpenAI stream', user_input)
const messages = [
{
role: 'system',
content: SYSTEM_PROMPT
},
{
role: 'user',
content: user_input
}
] as ChatCompletionMessageParam[]
const openaiStream = openai.beta.chat.completions.stream({
model: MODEL,
messages,
temperature: 0,
tools: tools as ChatCompletionTool[],
//Forcing tool call to generate UI
tool_choice: {
type: 'function',
function: { name: 'generate_ui' }
},
parallel_tool_calls: false
})
let functionArguments = ''
let callId = ''
let functionName = ''
let isCollectingFunctionArgs = false
for await (const part of openaiStream) {
const delta = part.choices[0].delta
const finishReason = part.choices[0].finish_reason
if (delta.content) {
const data = JSON.stringify({
event: 'assistant_delta',
data: delta
})
controller.enqueue(`data: ${data}\n\n`)
}
if (delta.tool_calls) {
isCollectingFunctionArgs = true
if (delta.tool_calls[0].id) {
callId = delta.tool_calls[0].id
}
if (delta.tool_calls[0].function?.name) {
functionName = delta.tool_calls[0].function.name
}
functionArguments += delta.tool_calls[0].function?.arguments || ''
const data = JSON.stringify({
event: 'function_arguments_delta',
data: {
callId: callId,
name: functionName,
arguments: delta.tool_calls[0].function?.arguments
}
})
controller.enqueue(`data: ${data}\n\n`)
}
if (finishReason === 'tool_calls' && isCollectingFunctionArgs) {
console.log(`tool call ${functionName} is complete`)
const data = JSON.stringify({
event: 'function_arguments_done',
data: {
callId: callId,
name: functionName,
arguments: functionArguments
}
})
controller.enqueue(`data: ${data}\n\n`)
functionArguments = ''
functionName = ''
isCollectingFunctionArgs = false
}
}
console.log('OpenAI stream done')
controller.close()
} catch (error) {
console.error('Error in stream start:', error)
controller.error(error)
}
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
}
})
} catch (error: any) {
console.error('Error in POST handler:', error)
return new Response(JSON.stringify({ error: error.message }), {
status: 500
})
}
}