Files
dify/web/features/deployments/components/runtime-credential-bindings-utils.ts

119 lines
3.5 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 ''
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()
if (!providerId)
return rawLabel
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 ?? [])
.filter(candidate => candidate.credentialId)
.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 (candidates.length === 1 && candidates[0])
next[slotKey] = candidates[0].value
}
return next
}
export function selectedDeploymentRuntimeCredentials(
slots: CredentialSlot[],
selections: RuntimeCredentialBindingSelections,
): CredentialSelectionInput[] {
return slots
.map((slot): CredentialSelectionInput | undefined => {
const slotKey = runtimeCredentialSlotKey(slot)
const selectedValue = selections[slotKey]
if (!slotKey || !selectedValue)
return undefined
return {
providerId: slot.providerId,
category: slot.category,
credentialId: selectedValue,
}
})
.filter((binding): binding is CredentialSelectionInput => Boolean(binding))
}