mirror of
https://github.com/langgenius/dify.git
synced 2026-06-21 10:01:08 +08:00
Co-authored-by: zhangx1n <zhangxin@dify.ai> Co-authored-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
116 lines
3.4 KiB
TypeScript
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,
|
|
}]
|
|
})
|
|
}
|