refactor(runtime-fallback): decompose index.ts into focused modules
Split 1021-line index.ts into 10 focused modules per project conventions. New structure: - error-classifier.ts: error analysis with dynamic status code extraction - agent-resolver.ts: agent detection utilities - fallback-state.ts: state management and cooldown logic - fallback-models.ts: model resolution from config - auto-retry.ts: retry helpers with mutual recursion support - event-handler.ts: session lifecycle events - message-update-handler.ts: message.updated event handling - chat-message-handler.ts: chat message interception - hook.ts: main factory with proper cleanup - types.ts: updated with HookDeps interface - index.ts: 2-line barrel re-export Embedded fixes: - Fix setInterval leak with .unref() - Replace require() with ESM import - Add log warning on invalid model format - Update sessionLastAccess on normal traffic - Make extractStatusCode dynamic from config - Remove unused SessionErrorInfo type All 61 tests pass without modification. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
committed by
YeonGyu-Kim
parent
03c0e5acfc
commit
df990f1174
@@ -0,0 +1,74 @@
|
||||
import type { FallbackState, FallbackResult } from "./types"
|
||||
import { HOOK_NAME } from "./constants"
|
||||
import { log } from "../../shared/logger"
|
||||
import type { RuntimeFallbackConfig } from "../../config"
|
||||
|
||||
export function createFallbackState(originalModel: string): FallbackState {
|
||||
return {
|
||||
originalModel,
|
||||
currentModel: originalModel,
|
||||
fallbackIndex: -1,
|
||||
failedModels: new Map<string, number>(),
|
||||
attemptCount: 0,
|
||||
pendingFallbackModel: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export function isModelInCooldown(model: string, state: FallbackState, cooldownSeconds: number): boolean {
|
||||
const failedAt = state.failedModels.get(model)
|
||||
if (failedAt === undefined) return false
|
||||
const cooldownMs = cooldownSeconds * 1000
|
||||
return Date.now() - failedAt < cooldownMs
|
||||
}
|
||||
|
||||
export function findNextAvailableFallback(
|
||||
state: FallbackState,
|
||||
fallbackModels: string[],
|
||||
cooldownSeconds: number
|
||||
): string | undefined {
|
||||
for (let i = state.fallbackIndex + 1; i < fallbackModels.length; i++) {
|
||||
const candidate = fallbackModels[i]
|
||||
if (!isModelInCooldown(candidate, state, cooldownSeconds)) {
|
||||
return candidate
|
||||
}
|
||||
log(`[${HOOK_NAME}] Skipping fallback model in cooldown`, { model: candidate, index: i })
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function prepareFallback(
|
||||
sessionID: string,
|
||||
state: FallbackState,
|
||||
fallbackModels: string[],
|
||||
config: Required<RuntimeFallbackConfig>
|
||||
): FallbackResult {
|
||||
if (state.attemptCount >= config.max_fallback_attempts) {
|
||||
log(`[${HOOK_NAME}] Max fallback attempts reached`, { sessionID, attempts: state.attemptCount })
|
||||
return { success: false, error: "Max fallback attempts reached", maxAttemptsReached: true }
|
||||
}
|
||||
|
||||
const nextModel = findNextAvailableFallback(state, fallbackModels, config.cooldown_seconds)
|
||||
|
||||
if (!nextModel) {
|
||||
log(`[${HOOK_NAME}] No available fallback models`, { sessionID })
|
||||
return { success: false, error: "No available fallback models (all in cooldown or exhausted)" }
|
||||
}
|
||||
|
||||
log(`[${HOOK_NAME}] Preparing fallback`, {
|
||||
sessionID,
|
||||
from: state.currentModel,
|
||||
to: nextModel,
|
||||
attempt: state.attemptCount + 1,
|
||||
})
|
||||
|
||||
const failedModel = state.currentModel
|
||||
const now = Date.now()
|
||||
|
||||
state.fallbackIndex = fallbackModels.indexOf(nextModel)
|
||||
state.failedModels.set(failedModel, now)
|
||||
state.attemptCount++
|
||||
state.currentModel = nextModel
|
||||
state.pendingFallbackModel = nextModel
|
||||
|
||||
return { success: true, newModel: nextModel }
|
||||
}
|
||||
Reference in New Issue
Block a user