refactor: share streamed output evaluation
This commit is contained in:
131
src/streamed-output-evaluator.ts
Normal file
131
src/streamed-output-evaluator.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
isClaudeAuthError,
|
||||
isClaudeAuthExpiredMessage,
|
||||
isClaudeUsageExhaustedMessage,
|
||||
} from './agent-error-detection.js';
|
||||
import type { AgentOutput } from './agent-runner.js';
|
||||
import { detectCodexRotationTrigger } from './codex-token-rotation.js';
|
||||
import { detectFallbackTrigger } from './provider-fallback.js';
|
||||
|
||||
export interface StreamedTriggerReason {
|
||||
reason: string;
|
||||
retryAfterMs?: number;
|
||||
}
|
||||
|
||||
export interface StreamedOutputState {
|
||||
sawOutput: boolean;
|
||||
sawSuccessNullResultWithoutOutput: boolean;
|
||||
streamedTriggerReason?: StreamedTriggerReason;
|
||||
}
|
||||
|
||||
export interface EvaluateStreamedOutputOptions {
|
||||
agentType: 'claude' | 'codex';
|
||||
provider: string;
|
||||
suppressClaudeAuthErrorOutput?: boolean;
|
||||
trackSuccessNullResult?: boolean;
|
||||
shortCircuitTriggeredErrors?: boolean;
|
||||
}
|
||||
|
||||
export interface EvaluateStreamedOutputResult {
|
||||
state: StreamedOutputState;
|
||||
shouldForwardOutput: boolean;
|
||||
newTrigger?: StreamedTriggerReason;
|
||||
suppressedAuthError?: boolean;
|
||||
}
|
||||
|
||||
export function evaluateStreamedOutput(
|
||||
output: AgentOutput,
|
||||
state: StreamedOutputState,
|
||||
options: EvaluateStreamedOutputOptions,
|
||||
): EvaluateStreamedOutputResult {
|
||||
const nextState: StreamedOutputState = { ...state };
|
||||
const isPrimaryClaude =
|
||||
options.agentType === 'claude' && options.provider === 'claude';
|
||||
const isPrimaryCodex =
|
||||
options.agentType === 'codex' && options.provider === 'codex';
|
||||
|
||||
if (
|
||||
isPrimaryClaude &&
|
||||
output.status === 'success' &&
|
||||
!state.sawOutput &&
|
||||
typeof output.result === 'string'
|
||||
) {
|
||||
const triggerReason = isClaudeUsageExhaustedMessage(output.result)
|
||||
? 'usage-exhausted'
|
||||
: isClaudeAuthExpiredMessage(output.result)
|
||||
? 'auth-expired'
|
||||
: undefined;
|
||||
|
||||
if (triggerReason) {
|
||||
const newTrigger = nextState.streamedTriggerReason
|
||||
? undefined
|
||||
: { reason: triggerReason };
|
||||
nextState.streamedTriggerReason =
|
||||
nextState.streamedTriggerReason ?? newTrigger;
|
||||
return {
|
||||
state: nextState,
|
||||
shouldForwardOutput: false,
|
||||
newTrigger,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
options.suppressClaudeAuthErrorOutput &&
|
||||
isClaudeAuthError(output.result)
|
||||
) {
|
||||
return {
|
||||
state: nextState,
|
||||
shouldForwardOutput: false,
|
||||
suppressedAuthError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (output.result !== null && output.result !== undefined) {
|
||||
nextState.sawOutput = true;
|
||||
} else if (
|
||||
options.trackSuccessNullResult &&
|
||||
isPrimaryClaude &&
|
||||
output.status === 'success' &&
|
||||
!state.sawOutput
|
||||
) {
|
||||
nextState.sawSuccessNullResultWithoutOutput = true;
|
||||
}
|
||||
|
||||
if (
|
||||
output.status === 'error' &&
|
||||
!nextState.sawOutput &&
|
||||
!nextState.streamedTriggerReason
|
||||
) {
|
||||
let newTrigger: StreamedTriggerReason | undefined;
|
||||
|
||||
if (isPrimaryClaude) {
|
||||
const trigger = detectFallbackTrigger(output.error);
|
||||
if (trigger.shouldFallback) {
|
||||
newTrigger = {
|
||||
reason: trigger.reason,
|
||||
retryAfterMs: trigger.retryAfterMs,
|
||||
};
|
||||
}
|
||||
} else if (isPrimaryCodex) {
|
||||
const trigger = detectCodexRotationTrigger(output.error);
|
||||
if (trigger.shouldRotate) {
|
||||
newTrigger = { reason: trigger.reason };
|
||||
}
|
||||
}
|
||||
|
||||
if (newTrigger) {
|
||||
nextState.streamedTriggerReason = newTrigger;
|
||||
return {
|
||||
state: nextState,
|
||||
shouldForwardOutput: !options.shortCircuitTriggeredErrors,
|
||||
newTrigger,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state: nextState,
|
||||
shouldForwardOutput: true,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user