Bin29's picture
Sync from main: e764154 feat(plot-skill): add math-exam-diagram SKILL.md for exam-style math figures
abcf568
import type { StudioProcessorStreamEvent } from '../../domain/types'
import { throwIfStudioRunCancelled } from '../../runtime/execution/run-cancellation'
import { determineStudioAgentLoopAction } from './loop-policy'
import { appendStudioAssistantConversationTurn, emitStudioAssistantText } from './message-assembly'
import { createStudioLoopFinishStepEvent, logStudioLoopStepFinished } from './observability'
import {
buildStudioLoopStepRequest,
createStudioLoopRuntime,
persistStudioProviderSnapshot,
requestStudioLoopStep
} from './request-builder'
import { StudioLoopCheckpointManager } from './state'
import { executeStudioToolCallsForStep } from './tool-dispatch'
import type { StudioOpenAIToolLoopInput } from './types'
export async function* createStudioOpenAIToolLoop(
input: StudioOpenAIToolLoopInput
): AsyncGenerator<StudioProcessorStreamEvent> {
const runtime = await createStudioLoopRuntime(input)
const checkpoints = new StudioLoopCheckpointManager(input)
for (let step = 0; step < runtime.maxSteps; step += 1) {
throwIfStudioRunCancelled(input.abortSignal)
const stepStartedAt = Date.now()
if (step > 0) {
runtime.currentAssistantMessage = await input.createAssistantMessage()
yield {
type: 'assistant-message-start',
message: runtime.currentAssistantMessage
}
}
const autonomy = await checkpoints.beginStep()
throwIfStudioRunCancelled(input.abortSignal)
const request = buildStudioLoopStepRequest(runtime)
const result = await requestStudioLoopStep({
loopInput: input,
runtime,
request,
step,
stepStartedAt
})
await persistStudioProviderSnapshot(input, runtime.currentAssistantMessage, result.message)
yield* emitStudioAssistantText(result.assistantText)
const nextAction = determineStudioAgentLoopAction({
finishReason: result.completion.choices[0]?.finish_reason ?? null,
toolCallCount: result.toolCalls.length,
step,
maxSteps: runtime.maxSteps
})
if (nextAction.type === 'finish') {
await checkpoints.markSuccess()
yield createStudioLoopFinishStepEvent(result.completion)
return
}
if (nextAction.type === 'abort') {
yield* emitStudioAssistantText(nextAction.message)
await checkpoints.markFailure(nextAction.message)
yield createStudioLoopFinishStepEvent(result.completion)
return
}
appendStudioAssistantConversationTurn(runtime, result)
const toolIterator = executeStudioToolCallsForStep(input, runtime, result, autonomy)
let toolExecution: IteratorResult<StudioProcessorStreamEvent, { failureMessage: string | null }>
while (true) {
toolExecution = await toolIterator.next()
if (toolExecution.done) {
break
}
yield toolExecution.value
}
if (toolExecution.value.failureMessage) {
const failedAutonomy = await checkpoints.markFailure(toolExecution.value.failureMessage)
if (failedAutonomy.consecutiveFailures >= failedAutonomy.maxConsecutiveFailures) {
const stopMessage = `Stopped after ${failedAutonomy.consecutiveFailures} consecutive failures: ${toolExecution.value.failureMessage}`
yield* emitStudioAssistantText(stopMessage)
await checkpoints.markStopped(stopMessage)
yield createStudioLoopFinishStepEvent(result.completion)
return
}
} else {
await checkpoints.markSuccess()
}
logStudioLoopStepFinished({
loopInput: input,
step,
failed: Boolean(toolExecution.value.failureMessage),
stepStartedAt
})
yield createStudioLoopFinishStepEvent(result.completion)
}
}