Files
orca/src/shared/source-control-ai-model-selection.ts
T

173 lines
5.7 KiB
TypeScript

import type {
CommitMessageAiSettings,
CommitMessageAiModelCapability
} from './commit-message-ai-types'
import { LOCAL_COMMIT_MESSAGE_HOST_KEY } from './commit-message-host-key'
import type { TuiAgent } from './tui-agent'
import type {
RepoSourceControlAiOverrides,
SourceControlAiModelChoice,
SourceControlAiOperation,
SourceControlAiSettings
} from './source-control-ai-types'
export function readSourceControlAiModelChoiceForHost(
choice: SourceControlAiModelChoice | null | undefined,
hostKey: string,
agentId: TuiAgent
): string | undefined {
return (
choice?.selectedModelByAgentByHost?.[hostKey]?.[agentId] ??
(hostKey === LOCAL_COMMIT_MESSAGE_HOST_KEY
? choice?.selectedModelByAgent?.[agentId]
: undefined)
)
}
export function selectSourceControlAiModelChoiceForHost(
choice: SourceControlAiModelChoice | undefined,
hostKey: string,
agentId: TuiAgent,
modelId: string
): SourceControlAiModelChoice {
return {
...choice,
selectedModelByAgent:
hostKey === LOCAL_COMMIT_MESSAGE_HOST_KEY
? { ...choice?.selectedModelByAgent, [agentId]: modelId }
: choice?.selectedModelByAgent,
selectedModelByAgentByHost: {
...choice?.selectedModelByAgentByHost,
[hostKey]: {
...choice?.selectedModelByAgentByHost?.[hostKey],
[agentId]: modelId
}
}
}
}
export function clearSourceControlAiModelChoiceForHost(
choice: SourceControlAiModelChoice | undefined,
hostKey: string,
agentId: TuiAgent
): SourceControlAiModelChoice | undefined {
if (!choice) {
return undefined
}
const selectedModelByAgent = { ...choice.selectedModelByAgent }
if (hostKey === LOCAL_COMMIT_MESSAGE_HOST_KEY) {
delete selectedModelByAgent[agentId]
}
const selectedModelByAgentByHost = { ...choice.selectedModelByAgentByHost }
const hostModels = { ...selectedModelByAgentByHost[hostKey] }
delete hostModels[agentId]
if (Object.keys(hostModels).length > 0) {
selectedModelByAgentByHost[hostKey] = hostModels
} else {
delete selectedModelByAgentByHost[hostKey]
}
const nextChoice: SourceControlAiModelChoice = {}
if (Object.keys(selectedModelByAgent).length > 0) {
nextChoice.selectedModelByAgent = selectedModelByAgent
}
if (Object.keys(selectedModelByAgentByHost).length > 0) {
nextChoice.selectedModelByAgentByHost = selectedModelByAgentByHost
}
const hasModelSelection =
nextChoice.selectedModelByAgent !== undefined ||
nextChoice.selectedModelByAgentByHost !== undefined
if (hasModelSelection && Object.keys(choice.selectedThinkingByModel ?? {}).length > 0) {
nextChoice.selectedThinkingByModel = choice.selectedThinkingByModel
}
return hasModelSelection ? nextChoice : undefined
}
export function mergeSelectedModelByAgentByHost(
base: Partial<Record<string, Partial<Record<TuiAgent, string>>>> | undefined,
override: Partial<Record<string, Partial<Record<TuiAgent, string>>>> | undefined
): Partial<Record<string, Partial<Record<TuiAgent, string>>>> {
const merged = base === undefined ? {} : structuredClone(base)
for (const [hostKey, hostModels] of Object.entries(override ?? {})) {
merged[hostKey] = { ...merged[hostKey], ...hostModels }
}
return merged
}
export function getDiscoveredModels(
source: SourceControlAiSettings,
legacy: CommitMessageAiSettings | null | undefined,
hostKey: string,
agentId: TuiAgent
): CommitMessageAiModelCapability[] {
return (
source.discoveredModelsByAgentByHost?.[hostKey]?.[agentId] ??
(hostKey === LOCAL_COMMIT_MESSAGE_HOST_KEY
? (source.discoveredModelsByAgent?.[agentId] ??
legacy?.discoveredModelsByAgentByHost?.[hostKey]?.[agentId] ??
legacy?.discoveredModelsByAgent?.[agentId] ??
[])
: (legacy?.discoveredModelsByAgentByHost?.[hostKey]?.[agentId] ?? []))
)
}
export function selectPersistedModelId(args: {
source: SourceControlAiSettings
legacy: CommitMessageAiSettings | null | undefined
repoOverrides: RepoSourceControlAiOverrides | null | undefined
operation: SourceControlAiOperation
hostKey: string
agentId: TuiAgent
defaultModelId: string
}): string {
const { source, legacy, repoOverrides, operation, hostKey, agentId, defaultModelId } = args
return (
readSourceControlAiModelChoiceForHost(
repoOverrides?.modelOverridesByOperation?.[operation],
hostKey,
agentId
) ??
readSourceControlAiModelChoiceForHost(
source.modelOverridesByOperation?.[operation],
hostKey,
agentId
) ??
readSourceControlAiModelChoiceForHost(
{
selectedModelByAgent: source.selectedModelByAgent,
selectedModelByAgentByHost: source.selectedModelByAgentByHost
},
hostKey,
agentId
) ??
legacy?.selectedModelByAgentByHost?.[hostKey]?.[agentId] ??
(hostKey === LOCAL_COMMIT_MESSAGE_HOST_KEY
? legacy?.selectedModelByAgent?.[agentId]
: undefined) ??
defaultModelId
)
}
export function resolveThinkingLevel(args: {
model: CommitMessageAiModelCapability
source: SourceControlAiSettings
legacy: CommitMessageAiSettings | null | undefined
repoOverrides: RepoSourceControlAiOverrides | null | undefined
operation: SourceControlAiOperation
}): string | undefined {
if (!args.model.thinkingLevels?.length) {
return undefined
}
const persisted =
args.repoOverrides?.modelOverridesByOperation?.[args.operation]?.selectedThinkingByModel?.[
args.model.id
] ??
args.source.modelOverridesByOperation?.[args.operation]?.selectedThinkingByModel?.[
args.model.id
] ??
args.source.selectedThinkingByModel[args.model.id] ??
args.legacy?.selectedThinkingByModel?.[args.model.id]
return args.model.thinkingLevels.some((level) => level.id === persisted)
? persisted
: args.model.defaultThinkingLevel
}