feat:完成dify知识库文档基础CRUD模块
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Dify Dataset 服务端 API 模块
|
||||
*
|
||||
* 提供 Node.js 服务端调用 FastAPI 后端的基础功能
|
||||
* Dify 的 DATASET_API_KEY 由 FastAPI 后端管理,前端只负责转发请求
|
||||
*
|
||||
* 调用链路:
|
||||
* Remix Server → FastAPI /dify_dataset/* → Dify Knowledge API
|
||||
*
|
||||
* @module api/dify-dataset/client.server
|
||||
*/
|
||||
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
|
||||
// ============================================================================
|
||||
// 配置
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Dify Dataset API 代理地址
|
||||
* 通过 FastAPI 后端的 /dify_dataset 路由代理访问 Dify Knowledge API
|
||||
* Dify 的认证(DATASET_API_KEY)由 FastAPI 后端处理
|
||||
*/
|
||||
const DIFY_DATASET_API_URL = `${API_BASE_URL}/dify_dataset`;
|
||||
|
||||
// ============================================================================
|
||||
// 基础请求函数
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Dify Dataset API 基础请求函数
|
||||
*
|
||||
* 使用用户 JWT 认证通过 FastAPI 代理访问 Dify Knowledge API
|
||||
* FastAPI 后端会验证 JWT 并添加 Dify DATASET_API_KEY
|
||||
*
|
||||
* @param endpoint - API 端点路径
|
||||
* @param options - fetch 请求选项
|
||||
* @param jwt - 用户 JWT 认证令牌
|
||||
* @returns Response 对象
|
||||
*/
|
||||
export async function difyDatasetFetch(
|
||||
endpoint: string,
|
||||
options: RequestInit = {},
|
||||
jwt?: string
|
||||
): Promise<Response> {
|
||||
const url = `${DIFY_DATASET_API_URL}/${endpoint.replace(/^\//, '')}`;
|
||||
|
||||
const headers: HeadersInit = {
|
||||
...options.headers,
|
||||
};
|
||||
|
||||
// 如果不是 FormData,设置 Content-Type 为 JSON
|
||||
if (!(options.body instanceof FormData)) {
|
||||
(headers as Record<string, string>)['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
if (jwt) {
|
||||
(headers as Record<string, string>)['Authorization'] = `Bearer ${jwt}`;
|
||||
} else {
|
||||
console.warn('[Dify Dataset] 没有提供 JWT,FastAPI 请求可能失败');
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('[Dify Dataset] API 转发错误:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: errorText
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new Error('JWT认证失败,请重新登录');
|
||||
}
|
||||
|
||||
throw new Error(`Dify Dataset API Error: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
@@ -9,11 +9,15 @@
|
||||
|
||||
import axios from 'axios';
|
||||
import type {
|
||||
Dataset,
|
||||
DatasetsResponse,
|
||||
DocumentsResponse,
|
||||
SegmentsResponse,
|
||||
Document,
|
||||
OperationResult,
|
||||
IndexingStatusResponse,
|
||||
UploadFileInfo,
|
||||
UpdateDatasetRequest,
|
||||
} from './types';
|
||||
|
||||
// ============================================================================
|
||||
@@ -59,14 +63,38 @@ export async function fetchDatasets(
|
||||
* @param datasetId - 知识库 ID
|
||||
* @returns 知识库详情
|
||||
*/
|
||||
export async function fetchDataset(datasetId: string): Promise<any> {
|
||||
const response = await axios.get(
|
||||
export async function fetchDataset(datasetId: string): Promise<Dataset> {
|
||||
const response = await axios.get<Dataset>(
|
||||
`${API_URL}/datasets/${datasetId}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新知识库详情
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param data - 更新数据
|
||||
* @returns 更新后的知识库详情
|
||||
*/
|
||||
export async function updateDataset(
|
||||
datasetId: string,
|
||||
data: UpdateDatasetRequest
|
||||
): Promise<Dataset> {
|
||||
console.log('[Dataset Client] 更新知识库:', { datasetId, data });
|
||||
|
||||
const response = await axios.patch<Dataset>(
|
||||
`${API_URL}/datasets/${datasetId}`,
|
||||
data,
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
withCredentials: true,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文档 API
|
||||
// ============================================================================
|
||||
@@ -144,6 +172,8 @@ export async function deleteDocument(
|
||||
|
||||
/**
|
||||
* 启用/禁用文档
|
||||
* Dify API: PATCH /datasets/{dataset_id}/documents/status/{action}
|
||||
* action: enable / disable / archive / un_archive
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
@@ -155,11 +185,12 @@ export async function toggleDocumentStatus(
|
||||
documentId: string,
|
||||
enabled: boolean
|
||||
): Promise<OperationResult> {
|
||||
console.log('[Dataset Client] 切换文档状态:', { datasetId, documentId, enabled });
|
||||
const action = enabled ? 'enable' : 'disable';
|
||||
console.log('[Dataset Client] 切换文档状态:', { datasetId, documentId, action });
|
||||
|
||||
const response = await axios.patch<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/status`,
|
||||
{ enabled },
|
||||
`${API_URL}/datasets/${datasetId}/documents/status/${action}`,
|
||||
{ document_ids: [documentId] },
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
withCredentials: true,
|
||||
@@ -231,6 +262,8 @@ export async function deleteSegment(
|
||||
|
||||
/**
|
||||
* 启用/禁用分段
|
||||
* Dify API: POST /datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}
|
||||
* 通过更新分段的方式来切换状态
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
@@ -244,9 +277,11 @@ export async function toggleSegmentStatus(
|
||||
segmentId: string,
|
||||
enabled: boolean
|
||||
): Promise<OperationResult> {
|
||||
const response = await axios.patch<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/status`,
|
||||
{ enabled },
|
||||
console.log('[Dataset Client] 切换分段状态:', { datasetId, documentId, segmentId, enabled });
|
||||
|
||||
const response = await axios.post<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`,
|
||||
{ segment: { enabled } },
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
withCredentials: true,
|
||||
@@ -284,7 +319,7 @@ export async function uploadDocument(
|
||||
console.log('[Dataset Client] 上传文档:', { datasetId, fileName: file.name });
|
||||
|
||||
const response = await axios.post(
|
||||
`${API_URL}/datasets/${datasetId}/documents/create-by-file`,
|
||||
`${API_URL}/datasets/${datasetId}/documents`,
|
||||
formData,
|
||||
{
|
||||
withCredentials: true,
|
||||
@@ -298,3 +333,43 @@ export async function uploadDocument(
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档嵌入状态(索引进度)
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param batch - 上传文档的批次号
|
||||
* @returns 索引状态列表
|
||||
*/
|
||||
export async function fetchIndexingStatus(
|
||||
datasetId: string,
|
||||
batch: string
|
||||
): Promise<IndexingStatusResponse> {
|
||||
console.log('[Dataset Client] 获取索引状态:', { datasetId, batch });
|
||||
|
||||
const response = await axios.get<IndexingStatusResponse>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${batch}/indexing-status`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档上传文件信息
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @returns 上传文件信息
|
||||
*/
|
||||
export async function fetchUploadFileInfo(
|
||||
datasetId: string,
|
||||
documentId: string
|
||||
): Promise<UploadFileInfo> {
|
||||
console.log('[Dataset Client] 获取上传文件信息:', { datasetId, documentId });
|
||||
|
||||
const response = await axios.get<UploadFileInfo>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/upload-file`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -16,21 +16,32 @@ export type {
|
||||
OperationResult,
|
||||
CreateDocumentResponse,
|
||||
UploadProgress,
|
||||
DocumentIndexingStatus,
|
||||
IndexingStatusResponse,
|
||||
UploadFileInfo,
|
||||
RetrievalModel,
|
||||
UpdateDatasetRequest,
|
||||
} from './types';
|
||||
|
||||
// 客户端 API 导出
|
||||
// 客户端 API 导出(浏览器端使用 axios)
|
||||
export {
|
||||
// 知识库
|
||||
fetchDatasets,
|
||||
fetchDataset,
|
||||
updateDataset,
|
||||
// 文档
|
||||
fetchDocuments,
|
||||
fetchDocument,
|
||||
deleteDocument,
|
||||
toggleDocumentStatus,
|
||||
uploadDocument,
|
||||
fetchIndexingStatus,
|
||||
fetchUploadFileInfo,
|
||||
// 分段
|
||||
fetchSegments,
|
||||
deleteSegment,
|
||||
toggleSegmentStatus,
|
||||
} from './client';
|
||||
|
||||
// 服务端 API 请直接从 client.server.ts 导入
|
||||
// import { difyDatasetFetch } from '~/api/dify-dataset/client.server';
|
||||
|
||||
@@ -165,3 +165,86 @@ export interface UploadProgress {
|
||||
total: number;
|
||||
percent: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 索引状态类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 单个文档的索引状态
|
||||
*/
|
||||
export interface DocumentIndexingStatus {
|
||||
id: string;
|
||||
indexing_status: IndexingStatus;
|
||||
processing_started_at: number | null;
|
||||
parsing_completed_at: number | null;
|
||||
cleaning_completed_at: number | null;
|
||||
splitting_completed_at: number | null;
|
||||
completed_at: number | null;
|
||||
paused_at: number | null;
|
||||
error: string | null;
|
||||
stopped_at: number | null;
|
||||
completed_segments: number;
|
||||
total_segments: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量文档索引状态响应
|
||||
*/
|
||||
export interface IndexingStatusResponse {
|
||||
data: DocumentIndexingStatus[];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 上传文件信息类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 上传文件信息
|
||||
*/
|
||||
export interface UploadFileInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
size: number;
|
||||
extension: string;
|
||||
url: string;
|
||||
download_url: string;
|
||||
mime_type: string;
|
||||
created_by: string;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 知识库更新类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 检索模型配置
|
||||
*/
|
||||
export interface RetrievalModel {
|
||||
search_method: 'keyword_search' | 'semantic_search' | 'full_text_search' | 'hybrid_search';
|
||||
reranking_enable?: boolean;
|
||||
reranking_mode?: string | null;
|
||||
reranking_model?: {
|
||||
reranking_provider_name: string;
|
||||
reranking_model_name: string;
|
||||
};
|
||||
weights?: number | null;
|
||||
top_k?: number;
|
||||
score_threshold_enabled?: boolean;
|
||||
score_threshold?: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新知识库请求参数
|
||||
*/
|
||||
export interface UpdateDatasetRequest {
|
||||
name?: string;
|
||||
description?: string;
|
||||
indexing_technique?: 'high_quality' | 'economy';
|
||||
permission?: 'only_me' | 'all_team_members' | 'partial_members';
|
||||
embedding_model_provider?: string;
|
||||
embedding_model?: string;
|
||||
retrieval_model?: RetrievalModel;
|
||||
partial_member_list?: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user