File size: 3,679 Bytes
abcf568 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 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)
}
}
|