mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-09 07:29:51 +00:00
fix(codex): support dynamic reasoning efforts (#1012)
* fix(codex): support model-reported reasoning efforts * fix(web): prevent service worker edge caching * ci: retrigger stuck Actions run * fix(codex): accept dynamic reasoning effort values * fix(web): restore reasoning effort on model switch failure
This commit is contained in:
@@ -21,6 +21,10 @@ import { buildConversationOutline } from '@/chat/outline'
|
||||
import { buildVisibleChatBlocks, isToolGroupBlock, type ToolGroupBlock } from '@/chat/toolGroups'
|
||||
import { isQueuedForInvocation, mergeMessages } from '@/lib/messages'
|
||||
import { inactiveSessionCanResume } from '@/lib/sessionResume'
|
||||
import {
|
||||
getCodexModelReasoningEfforts,
|
||||
supportsCodexReasoningEffort
|
||||
} from '@/lib/codexModelCapabilities'
|
||||
import { HappyComposer, type ComposerSendError } from '@/components/AssistantChat/HappyComposer'
|
||||
import { codexModelAdvertisesFastTier } from '@/components/AssistantChat/codexFastMode'
|
||||
import type { PendingSchedule } from '@/components/AssistantChat/ScheduleTimePicker'
|
||||
@@ -64,6 +68,33 @@ import { useVoiceOptional } from '@/lib/voice-context'
|
||||
import { VoiceBackendSession, registerSessionStore, registerVoiceHooksStore, voiceHooks } from '@/realtime'
|
||||
import { isRemoteTerminalSupported } from '@/utils/terminalSupport'
|
||||
|
||||
type SessionModelSelection = { provider: string; modelId: string } | string | null
|
||||
|
||||
export async function applyModelChangeWithReasoningRollback(args: {
|
||||
model: SessionModelSelection
|
||||
previousModelReasoningEffort: string | null
|
||||
shouldClearReasoningEffort: boolean
|
||||
setModel: (model: SessionModelSelection) => Promise<void>
|
||||
setModelReasoningEffort: (effort: string | null) => Promise<void>
|
||||
}): Promise<void> {
|
||||
let clearedReasoningEffort = false
|
||||
|
||||
try {
|
||||
if (args.shouldClearReasoningEffort) {
|
||||
await args.setModelReasoningEffort(null)
|
||||
clearedReasoningEffort = true
|
||||
}
|
||||
await args.setModel(args.model)
|
||||
} catch (error) {
|
||||
if (clearedReasoningEffort && args.previousModelReasoningEffort) {
|
||||
await args.setModelReasoningEffort(args.previousModelReasoningEffort).catch((restoreError) => {
|
||||
console.error('Failed to restore model reasoning effort:', restoreError)
|
||||
})
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a PendingSchedule should trigger an auto-clear timer.
|
||||
*
|
||||
@@ -516,6 +547,16 @@ function SessionChatInner(props: SessionChatProps) {
|
||||
}
|
||||
return options
|
||||
}, [agentFlavor, codexModelsState.models])
|
||||
const codexSupportedReasoningEfforts = useMemo(
|
||||
() => agentFlavor === 'codex'
|
||||
? getCodexModelReasoningEfforts(codexModelsState.models, props.session.model)
|
||||
: undefined,
|
||||
[agentFlavor, codexModelsState.models, props.session.model]
|
||||
)
|
||||
const codexReasoningEffortOptions = useMemo(
|
||||
() => codexSupportedReasoningEfforts?.map((value) => ({ value })),
|
||||
[codexSupportedReasoningEfforts]
|
||||
)
|
||||
const opencodeModelsState = useOpencodeModels({
|
||||
api: props.api,
|
||||
sessionId: props.session.id,
|
||||
@@ -909,16 +950,39 @@ function SessionChatInner(props: SessionChatProps) {
|
||||
}, [setCollaborationMode, props.onRefresh, haptic])
|
||||
|
||||
// Model mode change handler
|
||||
const handleModelChange = useCallback(async (model: { provider: string; modelId: string } | string | null) => {
|
||||
const handleModelChange = useCallback(async (model: SessionModelSelection) => {
|
||||
const previousModelReasoningEffort = props.session.modelReasoningEffort
|
||||
const shouldClearReasoningEffort = agentFlavor === 'codex'
|
||||
&& Boolean(previousModelReasoningEffort)
|
||||
&& supportsCodexReasoningEffort(
|
||||
codexModelsState.models,
|
||||
model,
|
||||
previousModelReasoningEffort
|
||||
) === false
|
||||
|
||||
try {
|
||||
await setModel(model)
|
||||
await applyModelChangeWithReasoningRollback({
|
||||
model,
|
||||
previousModelReasoningEffort,
|
||||
shouldClearReasoningEffort,
|
||||
setModel,
|
||||
setModelReasoningEffort
|
||||
})
|
||||
haptic.notification('success')
|
||||
props.onRefresh()
|
||||
} catch (e) {
|
||||
haptic.notification('error')
|
||||
console.error('Failed to set model:', e)
|
||||
}
|
||||
}, [setModel, props.onRefresh, haptic])
|
||||
}, [
|
||||
agentFlavor,
|
||||
codexModelsState.models,
|
||||
props.session.modelReasoningEffort,
|
||||
setModelReasoningEffort,
|
||||
setModel,
|
||||
props.onRefresh,
|
||||
haptic
|
||||
])
|
||||
|
||||
const handleCursorBaseModelChange = useCallback(async (baseKey: string | null) => {
|
||||
if (!cursorPicker) {
|
||||
@@ -1243,9 +1307,11 @@ function SessionChatInner(props: SessionChatProps) {
|
||||
piModels={agentFlavor === 'pi' ? (piModelsState.availableModels.length > 0 ? piModelsState.availableModels : piCachedModels) : undefined}
|
||||
piSelectedModel={agentFlavor === 'pi' ? piSelectedModel : undefined}
|
||||
availableModelReasoningEffortOptions={
|
||||
agentFlavor === 'opencode' && opencodeReasoningEffortState.options.length > 0
|
||||
? opencodeReasoningEffortState.options
|
||||
: undefined
|
||||
agentFlavor === 'codex'
|
||||
? codexReasoningEffortOptions
|
||||
: agentFlavor === 'opencode' && opencodeReasoningEffortState.options.length > 0
|
||||
? opencodeReasoningEffortState.options
|
||||
: undefined
|
||||
}
|
||||
active={props.session.active}
|
||||
allowSendWhenInactive
|
||||
|
||||
Reference in New Issue
Block a user