fix: continue Pascal agents until a scene write
This commit is contained in:
parent
cc6c4b547c
commit
4f5dbb4589
@ -1,5 +1,13 @@
|
||||
import { createOpenAI } from '@ai-sdk/openai'
|
||||
import { convertToModelMessages, stepCountIs, streamText, type UIMessage } from 'ai'
|
||||
import {
|
||||
convertToModelMessages,
|
||||
createUIMessageStream,
|
||||
createUIMessageStreamResponse,
|
||||
type ModelMessage,
|
||||
stepCountIs,
|
||||
streamText,
|
||||
type UIMessage,
|
||||
} from 'ai'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { validatePascalAgentAccess } from '@/lib/pascal-agent-access'
|
||||
@ -24,7 +32,9 @@ const requestSchema = z.object({
|
||||
})
|
||||
|
||||
const AGENT_ERROR_MESSAGE = 'AI design agent is temporarily unavailable. Please try again.'
|
||||
const AGENT_NO_MUTATION_MESSAGE = '模型未执行任何建模动作,请重试或换用支持工具调用的多模态模型。'
|
||||
const SCENE_ERROR_MESSAGE = 'The current scene could not be loaded.'
|
||||
const MAX_MUTATION_CONTINUATIONS = 2
|
||||
|
||||
function accessErrorResponse(result: ReturnType<typeof validatePascalAgentAccess>) {
|
||||
if (result === 'missing_proxy_secret') {
|
||||
@ -114,6 +124,11 @@ export async function POST(request: Request) {
|
||||
baseURL: config.baseUrl,
|
||||
name: 'pascal-agent',
|
||||
})
|
||||
const initialMessages = convertToModelMessages(parsed.data.messages as UIMessage[])
|
||||
const systemPrompt = buildPascalAgentPrompt({
|
||||
sceneId: parsed.data.sceneId,
|
||||
sceneName: session.sceneName,
|
||||
})
|
||||
|
||||
let finalized = false
|
||||
const finalize = async (save: boolean) => {
|
||||
@ -134,36 +149,91 @@ export async function POST(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = streamText({
|
||||
const runAgent = (messages: ModelMessage[], mutationOnly: boolean) =>
|
||||
streamText({
|
||||
abortSignal,
|
||||
...(mutationOnly
|
||||
? {
|
||||
activeTools: session.mutationToolNames,
|
||||
toolChoice: 'required' as const,
|
||||
}
|
||||
: {
|
||||
prepareStep: ({ stepNumber }) =>
|
||||
pascalAgentStepPolicy({
|
||||
hasMutation: session.hasMutation(),
|
||||
mutationRequired,
|
||||
mutationToolNames: session.mutationToolNames,
|
||||
stepNumber,
|
||||
}),
|
||||
}),
|
||||
maxOutputTokens: 1600,
|
||||
messages: convertToModelMessages(parsed.data.messages as UIMessage[]),
|
||||
messages,
|
||||
model: provider.chat(config.model),
|
||||
onAbort: async () => finalize(false),
|
||||
onError: async () => finalize(false),
|
||||
onFinish: async () => finalize(session.hasMutation()),
|
||||
prepareStep: ({ stepNumber }) =>
|
||||
pascalAgentStepPolicy({
|
||||
hasMutation: session.hasMutation(),
|
||||
mutationRequired,
|
||||
mutationToolNames: session.mutationToolNames,
|
||||
stepNumber,
|
||||
}),
|
||||
stopWhen: stepCountIs(config.maxSteps),
|
||||
system: buildPascalAgentPrompt({
|
||||
sceneId: parsed.data.sceneId,
|
||||
sceneName: session.sceneName,
|
||||
}),
|
||||
system: systemPrompt,
|
||||
temperature: 0.2,
|
||||
tools: session.tools,
|
||||
})
|
||||
|
||||
return result.toUIMessageStreamResponse({
|
||||
onError: (error) => {
|
||||
console.error('Pascal agent stream failed.', error)
|
||||
return AGENT_ERROR_MESSAGE
|
||||
},
|
||||
const streamErrorText = (error: unknown) => {
|
||||
console.error('Pascal agent stream failed.', error)
|
||||
return AGENT_ERROR_MESSAGE
|
||||
}
|
||||
|
||||
const stream = createUIMessageStream<UIMessage>({
|
||||
execute: async ({ writer }) => {
|
||||
let messages = initialMessages
|
||||
let sendStart = true
|
||||
let finishReason: Awaited<ReturnType<typeof runAgent>['finishReason']> | undefined
|
||||
|
||||
try {
|
||||
for (let attempt = 0; attempt <= MAX_MUTATION_CONTINUATIONS; attempt++) {
|
||||
const mutationOnly = attempt > 0
|
||||
const result = runAgent(messages, mutationOnly)
|
||||
for await (const chunk of result.toUIMessageStream<UIMessage>({
|
||||
onError: streamErrorText,
|
||||
sendFinish: false,
|
||||
sendStart,
|
||||
})) {
|
||||
writer.write(chunk)
|
||||
}
|
||||
|
||||
finishReason = await result.finishReason
|
||||
const response = await result.response
|
||||
if (!mutationRequired || session.hasMutation()) break
|
||||
|
||||
messages = [
|
||||
...messages,
|
||||
...response.messages,
|
||||
{
|
||||
role: 'user',
|
||||
content:
|
||||
'You only inspected the scene and did not change it. Continue now. Do not explain or finish. Call one of the available scene mutation tools with valid arguments, then verify the persisted result.',
|
||||
},
|
||||
]
|
||||
sendStart = false
|
||||
}
|
||||
|
||||
if (mutationRequired && !session.hasMutation()) {
|
||||
console.error(
|
||||
'Pascal agent exhausted mutation continuations without changing the scene.',
|
||||
{
|
||||
sceneId: parsed.data.sceneId,
|
||||
},
|
||||
)
|
||||
writer.write({ type: 'error', errorText: AGENT_NO_MUTATION_MESSAGE })
|
||||
}
|
||||
writer.write({ type: 'finish', finishReason })
|
||||
} finally {
|
||||
await finalize(session.hasMutation())
|
||||
}
|
||||
},
|
||||
onError: streamErrorText,
|
||||
})
|
||||
|
||||
try {
|
||||
return createUIMessageStreamResponse({
|
||||
stream,
|
||||
})
|
||||
} catch (error) {
|
||||
await finalize(false)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user