Files
dify/web/features/new-rag/create-knowledge-workflow.ts

148 lines
4.8 KiB
TypeScript

import type {
KnowledgeFsControlSpaceVisibility,
KnowledgeFsModelIntent,
KnowledgeFsSpaceCreatePayload,
KnowledgeFsSpaceCreateResponse,
} from '@dify/contracts/api/console/knowledge-fs/types.gen'
import { consoleClient } from '@/service/client'
import { KNOWLEDGE_DESCRIPTION_MAX_LENGTH, KNOWLEDGE_NAME_MAX_LENGTH } from './constants'
export const NAME_MAX_LENGTH = KNOWLEDGE_NAME_MAX_LENGTH
export const DESCRIPTION_MAX_LENGTH = KNOWLEDGE_DESCRIPTION_MAX_LENGTH
export type KnowledgeVisibility = Extract<
KnowledgeFsControlSpaceVisibility,
'all_team_members' | 'only_me'
>
type CreateKnowledgeValues = {
existingKnowledge?: KnowledgeFsSpaceCreateResponse
description: string
idempotencyKey: string
name: string
onCreated: (knowledgeSpace: KnowledgeFsSpaceCreateResponse) => void
visibility: KnowledgeVisibility
}
export type KnowledgeCreationResult = {
knowledgeSpace: KnowledgeFsSpaceCreateResponse
modelSetupRequired: boolean
}
export class KnowledgeCreationError extends Error {
readonly originalError: unknown
readonly stage: 'preflight' | 'request'
constructor(originalError: unknown, stage: 'preflight' | 'request') {
super('Knowledge creation failed')
this.name = 'KnowledgeCreationError'
this.originalError = originalError
this.stage = stage
}
}
function responseStatus(error: unknown) {
if (error instanceof Response) return error.status
if (error && typeof error === 'object' && 'status' in error) return error.status
if (error && typeof error === 'object' && 'data' in error) {
const data = error.data
if (data && typeof data === 'object' && 'status' in data) return data.status
}
}
export function isDefinitiveCreationRejection(error: unknown) {
const status = responseStatus(error)
return status === 400 || status === 401 || status === 403 || status === 422
}
function modelSelection(model: string, canonicalProvider: string): KnowledgeFsModelIntent {
const providerParts = canonicalProvider.split('/').filter(Boolean)
const provider = providerParts.pop()
const pluginId = providerParts.join('/')
if (!model.trim() || !pluginId || !provider)
throw new Error('The default model provider identity is incomplete')
return { model, plugin_id: pluginId, provider }
}
async function getDefaultModelSelection(
modelType: 'llm' | 'text-embedding',
): Promise<KnowledgeFsModelIntent | undefined> {
const response = await consoleClient.workspaces.current.defaultModel.get({
query: { model_type: modelType },
})
if (!response.data) return undefined
return modelSelection(response.data.model, response.data.provider.provider)
}
async function initialModelConfiguration(): Promise<
Partial<Pick<KnowledgeFsSpaceCreatePayload, 'embedding' | 'retrieval'>>
> {
const [embeddingResult, reasoningModelResult] = await Promise.allSettled([
getDefaultModelSelection('text-embedding'),
getDefaultModelSelection('llm'),
])
const embedding = embeddingResult.status === 'fulfilled' ? embeddingResult.value : undefined
const reasoningModel =
reasoningModelResult.status === 'fulfilled' ? reasoningModelResult.value : undefined
if (!embedding || !reasoningModel) return {}
return {
embedding,
retrieval: {
default_mode: 'fast',
reasoning_model: reasoningModel,
rerank: { enabled: false },
score_threshold: { enabled: false, stage: 'mode-final' },
top_k: 10,
},
}
}
function knowledgeSlug(name: string, idempotencyKey: string) {
const normalizedName = name
.normalize('NFKD')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '')
const suffix = idempotencyKey
.toLowerCase()
.replace(/[^a-z0-9]/g, '')
.slice(0, 12)
const base = normalizedName || 'knowledge'
return `${base.slice(0, 147 - suffix.length)}-${suffix}`
}
export async function createKnowledge(
values: CreateKnowledgeValues,
): Promise<KnowledgeCreationResult> {
let created = values.existingKnowledge
if (!created) {
let modelConfiguration: Partial<Pick<KnowledgeFsSpaceCreatePayload, 'embedding' | 'retrieval'>>
try {
modelConfiguration = await initialModelConfiguration()
} catch (error) {
throw new KnowledgeCreationError(error, 'preflight')
}
try {
created = await consoleClient.knowledgeFs.spaces.post({
body: {
description: values.description || undefined,
idempotency_key: values.idempotencyKey,
...modelConfiguration,
name: values.name,
slug: knowledgeSlug(values.name, values.idempotencyKey),
visibility: values.visibility,
},
})
} catch (error) {
throw new KnowledgeCreationError(error, 'request')
}
}
values.onCreated(created)
return {
knowledgeSpace: created,
modelSetupRequired: created.model_setup_required,
}
}