Files
dify/web/features/deployments/shared/components/runtime-credential-bindings-utils.ts
Stephen Zhou a84c2d36a3 style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

116 lines
3.4 KiB
TypeScript

import type {
CredentialCandidate,
CredentialSelectionInput,
CredentialSlot,
} from '@dify/contracts/enterprise/types.gen'
export type RuntimeCredentialBindingSelections = Record<string, string>
export type RuntimeCredentialSelectOption = {
value: string
label: string
}
const PROVIDER_DISPLAY_NAMES: Record<string, string> = {
azure_openai: 'Azure OpenAI',
bedrock: 'Amazon Bedrock',
gemini: 'Gemini',
google: 'Google',
openai: 'OpenAI',
vertex_ai: 'Vertex AI',
volcengine_maas: 'Volcengine',
}
function providerSlug(providerId: string) {
const parts = providerId.split('/').filter(Boolean)
return parts[parts.length - 1]
}
function titleCaseProviderName(value: string) {
return value
.split(/[-_]/)
.filter(Boolean)
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
.join(' ')
}
export function runtimeCredentialSlotKey(slot: CredentialSlot) {
return [slot.providerId, slot.category].join(':')
}
export function runtimeCredentialProviderName(providerId: string) {
const slug = providerSlug(providerId)
if (!slug) return undefined
return PROVIDER_DISPLAY_NAMES[slug.toLowerCase()] ?? titleCaseProviderName(slug)
}
function runtimeCredentialCandidateLabel(candidate: CredentialCandidate) {
const fallback = candidate.credentialId
const rawLabel = candidate.displayName.trim() || fallback
const providerId = candidate.providerId.trim()
const providerSuffixes = [` · ${providerId}`, ` - ${providerId}`, ` (${providerId})`]
const label = providerSuffixes.reduce((nextLabel, suffix) => {
return nextLabel.endsWith(suffix) ? nextLabel.slice(0, -suffix.length).trim() : nextLabel
}, rawLabel)
return label || fallback
}
export function runtimeCredentialCandidateOptions(
slot: CredentialSlot,
): RuntimeCredentialSelectOption[] {
return slot.candidates.map((candidate) => ({
value: candidate.credentialId,
label: runtimeCredentialCandidateLabel(candidate),
}))
}
export function hasMissingRequiredRuntimeCredentialBinding(
_slot: CredentialSlot,
selectedValue?: string,
) {
return !selectedValue
}
export function selectedRuntimeCredentialSelections(
slots: CredentialSlot[],
manualBindings: RuntimeCredentialBindingSelections,
): RuntimeCredentialBindingSelections {
const next: RuntimeCredentialBindingSelections = {}
for (const slot of slots) {
const slotKey = runtimeCredentialSlotKey(slot)
const candidates = runtimeCredentialCandidateOptions(slot)
const existing = manualBindings[slotKey]
if (existing && candidates.some((candidate) => candidate.value === existing))
next[slotKey] = existing
else if (
slot.lastCredentialId &&
candidates.some((candidate) => candidate.value === slot.lastCredentialId)
)
next[slotKey] = slot.lastCredentialId
else if (candidates.length === 1 && candidates[0]) next[slotKey] = candidates[0].value
}
return next
}
export function selectedDeploymentRuntimeCredentials(
slots: CredentialSlot[],
selections: RuntimeCredentialBindingSelections,
): CredentialSelectionInput[] {
return slots.flatMap((slot): CredentialSelectionInput[] => {
const slotKey = runtimeCredentialSlotKey(slot)
const selectedValue = selections[slotKey]
if (!selectedValue) return []
return [
{
providerId: slot.providerId,
category: slot.category,
credentialId: selectedValue,
},
]
})
}