Files
ragflow/web/src/utils/api.ts
euvre f4b8f53b6d Fix: restore embedding model switching for datasets with existing chunks (#14732)
### What problem does this PR solve?

## Problem

During the REST API refactoring (#13690), the
`/api/v2/kb/check_embedding` endpoint was removed and never migrated to
the new RESTful structure. The frontend was pointed to the
`/api/v1/datasets/{id}/embedding` endpoint (which is `run_embedding` — a
completely different function). Additionally, a hard guard was
introduced that rejects any `embd_id` change when `chunk_num > 0`,
making it impossible to switch embedding models on datasets with
existing chunks.

## Root Cause

1. **Missing endpoint**: The old `check_embedding` logic (sample random
chunks, re-embed with the new model, compare cosine similarity) was not
carried over to the new REST API service layer.
2. **Wrong frontend URL**: `checkEmbedding` in `api.ts` pointed to
`/datasets/{id}/embedding` (`run_embedding`) instead of a dedicated
check endpoint.
3. **Overly restrictive guard**: `dataset_api_service.py` line 310
blocked all `embd_id` updates when `chunk_num > 0`. This check did not
exist in the pre-refactor code — it was incorrectly introduced during
the refactor.

## Changes

### Backend

- **`api/apps/services/dataset_api_service.py`**
  - Remove the `chunk_num > 0` hard guard on `embd_id` updates
- Add `check_embedding()` service function: samples random chunks,
re-embeds them with the candidate model, computes cosine similarity,
returns compatibility result (avg ≥ 0.9 = compatible)
  - Add `import re` for the `_clean()` helper

- **`api/apps/restful_apis/dataset_api.py`**
- Add `POST /datasets/<dataset_id>/embedding/check` endpoint following
the new REST API conventions
  - Clean up unused top-level imports (`random`, `re`, `numpy`)

### Frontend

- **`web/src/utils/api.ts`**
- Fix `checkEmbedding` URL from `/datasets/${datasetId}/embedding` →
`/datasets/${datasetId}/embedding/check`

### Tests

-
**`test/testcases/test_http_api/test_dataset_management/test_update_dataset.py`**
- Update `test_embedding_model_with_existing_chunks` to assert success
(`code == 0`) instead of expecting the old `102` error

-
**`test/testcases/test_web_api/test_dataset_management/test_dataset_sdk_routes_unit.py`**
- Update `test_update_route_branch_matrix_unit` to assert
`RetCode.SUCCESS` when updating `embd_id` on a chunked dataset,
replacing the old `chunk_num` error assertion

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

---------

Signed-off-by: noob <yixiao121314@outlook.com>
2026-05-09 18:48:57 +08:00

345 lines
16 KiB
TypeScript

const webAPI = `/v1`;
const restAPIv1 = `/api/v1`;
export { restAPIv1, webAPI };
export default {
// user
login: `${restAPIv1}/auth/login`,
logout: `${restAPIv1}/auth/logout`,
register: `${restAPIv1}/users`,
setting: `${restAPIv1}/users/me`,
userInfo: `${restAPIv1}/users/me`,
tenantInfo: `${restAPIv1}/users/me/models`,
setTenantInfo: `${restAPIv1}/users/me/models`,
loginChannels: `${restAPIv1}/auth/login/channels`,
loginChannel: (channel: string) => `${restAPIv1}/auth/login/${channel}`,
// team
addTenantUser: (tenantId: string) => `${restAPIv1}/tenants/${tenantId}/users`,
listTenantUser: (tenantId: string) =>
`${restAPIv1}/tenants/${tenantId}/users`,
deleteTenantUser: (tenantId: string) =>
`${restAPIv1}/tenants/${tenantId}/users`,
listTenant: `${restAPIv1}/tenants`,
agreeTenant: (tenantId: string) => `${restAPIv1}/tenants/${tenantId}`,
// llm model
factoriesList: `${webAPI}/llm/factories`,
llmList: `${webAPI}/llm/list`,
myLlm: `${webAPI}/llm/my_llms`,
setApiKey: `${webAPI}/llm/set_api_key`,
addLlm: `${webAPI}/llm/add_llm`,
deleteLlm: `${webAPI}/llm/delete_llm`,
enableLlm: `${webAPI}/llm/enable_llm`,
deleteFactory: `${webAPI}/llm/delete_factory`,
// data source
dataSourceUpdate: (id: string) => `${restAPIv1}/connectors/${id}`,
dataSourceSet: `${restAPIv1}/connectors`,
dataSourceList: `${restAPIv1}/connectors`,
dataSourceDel: (id: string) => `${restAPIv1}/connectors/${id}`,
dataSourceResume: (id: string) => `${restAPIv1}/connectors/${id}/resume`,
dataSourceRebuild: (id: string) => `${restAPIv1}/connectors/${id}/rebuild`,
dataSourceLogs: (id: string) => `${restAPIv1}/connectors/${id}/logs`,
dataSourceDetail: (id: string) => `${restAPIv1}/connectors/${id}`,
googleWebAuthStart: (type: 'google-drive' | 'gmail') =>
`${restAPIv1}/connectors/google/oauth/web/start?type=${type}`,
googleWebAuthResult: (type: 'google-drive' | 'gmail') =>
`${restAPIv1}/connectors/google/oauth/web/result?type=${type}`,
boxWebAuthStart: () => `${restAPIv1}/connectors/box/oauth/web/start`,
boxWebAuthResult: () => `${restAPIv1}/connectors/box/oauth/web/result`,
// plugin
llmTools: `${restAPIv1}/plugin/tools`,
chatsTranscriptions: `${restAPIv1}/chat/audio/transcription`,
// knowledge base
checkEmbedding: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/embedding/check`,
kbList: `${restAPIv1}/datasets`,
createKb: `${restAPIv1}/datasets`,
updateKb: (datasetId: string) => `${restAPIv1}/datasets/${datasetId}`,
rmKb: `${restAPIv1}/datasets`,
getKbDetail: (datasetId: string) => `${restAPIv1}/datasets/${datasetId}`,
getKnowledgeGraph: (knowledgeId: string) =>
`${restAPIv1}/datasets/${knowledgeId}/graph`,
knowledgeGraph: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/graph`,
deleteKnowledgeGraph: (knowledgeId: string) =>
`${restAPIv1}/datasets/${knowledgeId}/graph`,
getMeta: `${restAPIv1}/datasets/metadata/flattened`,
getKnowledgeBasicInfo: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/ingestions/summary`,
// data pipeline log
fetchDataPipelineLog: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/ingestions`,
getPipelineDetail: (datasetId: string, logId: string) =>
`${restAPIv1}/datasets/${datasetId}/ingestions/${logId}`,
fetchPipelineDatasetLogs: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/ingestions`,
runIndex: (datasetId: string, indexType: string) =>
`${restAPIv1}/datasets/${datasetId}/index?type=${indexType.toLowerCase()}`,
traceIndex: (datasetId: string, indexType: string) =>
`${restAPIv1}/datasets/${datasetId}/index?type=${indexType.toLowerCase()}`,
unbindPipelineTask: (datasetId: string, indexType: string, wipe?: boolean) =>
`${restAPIv1}/datasets/${datasetId}/${indexType.toLowerCase()}${wipe === false ? '?wipe=false' : ''}`,
pipelineRerun: `${webAPI}/canvas/rerun`,
getMetaData: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/metadata/summary`,
updateDocumentsMetadata: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/metadatas`,
kbUpdateMetaData: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/metadata/config`,
documentUpdateMetaDataConfig: (datasetId: string, documentId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/${documentId}/metadata/config`,
// tags
listTag: (knowledgeId: string) => `${restAPIv1}/datasets/${knowledgeId}/tags`,
listTagByKnowledgeIds: `${restAPIv1}/datasets/tags/aggregation`,
removeTag: (knowledgeId: string) =>
`${restAPIv1}/datasets/${knowledgeId}/tags`,
renameTag: (knowledgeId: string) =>
`${restAPIv1}/datasets/${knowledgeId}/tags`,
// chunk
chunkList: (datasetId: string, documentId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/${documentId}/chunks`,
chunkDetail: (datasetId: string, documentId: string, chunkId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/${documentId}/chunks/${chunkId}`,
retrievalTest: `${restAPIv1}/datasets/search`,
// document
getDocumentList: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents`,
documentChangeStatus: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/batch-update-status`,
documentDelete: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents`,
documentRename: (datasetId: string, documentId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/${documentId}`,
documentIngest: `${restAPIv1}/documents/ingest`,
documentCreate: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents?type=empty`,
documentChangeParser: (datasetId: string, documentId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents/${documentId}`,
documentThumbnails: `${restAPIv1}/thumbnails`,
getDocumentFile: `${restAPIv1}/documents`,
getDocumentFileDownload: (docId: string) =>
`${restAPIv1}/documents/${docId}/download`,
documentUpload: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents`,
webCrawl: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents?type=web`,
documentInfoUpload: `${restAPIv1}/documents/upload`,
setMeta: `${webAPI}/document/set_meta`,
getDatasetFilter: (datasetId: string) =>
`${restAPIv1}/datasets/${datasetId}/documents?type=filter`,
// chat
createChat: `${restAPIv1}/chats`,
listChats: `${restAPIv1}/chats`,
getChat: (chatId: string) => `${restAPIv1}/chats/${chatId}`,
updateChat: (chatId: string) => `${restAPIv1}/chats/${chatId}`,
patchChat: (chatId: string) => `${restAPIv1}/chats/${chatId}`,
deleteChat: (chatId: string) => `${restAPIv1}/chats/${chatId}`,
bulkDeleteChats: `${restAPIv1}/chats`,
createSession: (chatId: string) => `${restAPIv1}/chats/${chatId}/sessions`,
listSessions: (chatId: string) => `${restAPIv1}/chats/${chatId}/sessions`,
getSession: (chatId: string, sessionId: string) =>
`${restAPIv1}/chats/${chatId}/sessions/${sessionId}`,
updateSession: (chatId: string, sessionId: string) =>
`${restAPIv1}/chats/${chatId}/sessions/${sessionId}`,
removeSessions: (chatId: string) => `${restAPIv1}/chats/${chatId}/sessions`,
deleteMessage: (chatId: string, sessionId: string, msgId: string) =>
`${restAPIv1}/chats/${chatId}/sessions/${sessionId}/messages/${msgId}`,
thumbup: (chatId: string, sessionId: string, msgId: string) =>
`${restAPIv1}/chats/${chatId}/sessions/${sessionId}/messages/${msgId}/feedback`,
completionUrl: `${restAPIv1}/chat/completions`,
chatsTts: `${restAPIv1}/chat/audio/speech`,
searchCompletion: (searchId: string) =>
`${restAPIv1}/searches/${searchId}/completions`,
chatsMindmap: `${restAPIv1}/chat/mindmap`,
chatsRelatedQuestions: `${restAPIv1}/chat/recommendation`,
// next chat
fetchExternalChatInfo: (id: string) => `${restAPIv1}/chatbots/${id}/info`,
// file manager
listFile: `${restAPIv1}/files`,
uploadFile: `${restAPIv1}/files`,
removeFile: `${restAPIv1}/files`,
getAllParentFolder: `${restAPIv1}/files`,
createFolder: `${restAPIv1}/files`,
connectFileToKnowledge: `${restAPIv1}/files/link-to-datasets`,
getFile: `${restAPIv1}/files`,
moveFile: `${restAPIv1}/files/move`,
// system
getSystemVersion: `${restAPIv1}/system/version`,
getSystemTokenList: `${restAPIv1}/system/tokens`,
createSystemToken: `${restAPIv1}/system/tokens`,
removeSystemToken: `${restAPIv1}/system/tokens`,
getSystemConfig: `${restAPIv1}/system/config`,
setLangfuseConfig: `${restAPIv1}/langfuse/api-key`,
// flow
listAgentTemplate: `${restAPIv1}/agents/templates`,
listAgents: `${restAPIv1}/agents`,
createAgent: `${restAPIv1}/agents`,
updateAgent: (agentId: string) => `${restAPIv1}/agents/${agentId}`,
deleteAgent: (agentId: string) => `${restAPIv1}/agents/${agentId}`,
agentChatCompletion: `${restAPIv1}/agents/chat/completions`,
resetAgent: (agentId: string) => `${restAPIv1}/agents/${agentId}/reset`,
testDbConnect: `${restAPIv1}/agents/test_db_connection`,
getInputElements: `${webAPI}/canvas/input_elements`,
debug: (agentId: string, componentId: string) =>
`${restAPIv1}/agents/${agentId}/components/${componentId}/debug`,
trace: (agentId: string, messageId: string) =>
`${restAPIv1}/agents/${agentId}/logs/${messageId}`,
cancelCanvas: (taskId: string) => `${restAPIv1}/tasks/${taskId}/cancel`,
// agent
inputForm: (agentId: string, componentId: string) =>
`${restAPIv1}/agents/${agentId}/components/${componentId}/input-form`,
fetchVersionList: (id: string) => `${restAPIv1}/agents/${id}/versions`,
fetchVersion: (agentId: string, versionId: string) =>
`${restAPIv1}/agents/${agentId}/versions/${versionId}`,
getAgent: (id: string) => `${restAPIv1}/agents/${id}`,
uploadAgentFile: (id?: string) => `${restAPIv1}/agents/${id}/upload`,
createAgentSession: (agentId: string) =>
`${restAPIv1}/agents/${agentId}/sessions`,
fetchAgentLogs: (canvasId: string) => `${webAPI}/canvas/${canvasId}/sessions`,
fetchAgentSessions: (agentId: string) =>
`${restAPIv1}/agents/${agentId}/sessions`,
fetchAgentSessionById: (agentId: string, sessionId: string) =>
`${restAPIv1}/agents/${agentId}/sessions/${sessionId}`,
fetchExternalAgentInputs: (canvasId: string) =>
`${restAPIv1}/agentbots/${canvasId}/inputs`,
prompt: `${restAPIv1}/agents/prompts`,
cancelDataflow: (id: string) => `${restAPIv1}/tasks/${id}/cancel`,
downloadFile: `${restAPIv1}/agents/download`,
testWebhook: (id: string) => `${restAPIv1}/agents/${id}/webhook/test`,
fetchWebhookTrace: (id: string) => `${restAPIv1}/agents/${id}/webhook/logs`,
// explore
// mcp server
listMcpServer: `${restAPIv1}/mcp/servers`,
getMcpServer: (id: string) => `${restAPIv1}/mcp/servers/${id}`,
createMcpServer: `${restAPIv1}/mcp/servers`,
updateMcpServer: (id: string) => `${restAPIv1}/mcp/servers/${id}`,
deleteMcpServer: (id: string) => `${restAPIv1}/mcp/servers/${id}`,
importMcpServer: `${restAPIv1}/mcp/servers/import`,
exportMcpServer: (id: string) =>
`${restAPIv1}/mcp/servers/${id}?mode=download`,
testMcpServer: (id: string) => `${restAPIv1}/mcp/servers/${id}/test`,
// next-search
createSearch: `${restAPIv1}/searches`,
getSearchList: `${restAPIv1}/searches`,
deleteSearch: (params: { search_id: string }) =>
`${restAPIv1}/searches/${params.search_id}`,
getSearchDetail: (params: { search_id: string }) =>
`${restAPIv1}/searches/${params.search_id}`,
getSearchDetailShare: `${restAPIv1}/searchbots/detail`,
updateSearchSetting: (params: { search_id: string }) =>
`${restAPIv1}/searches/${params.search_id}`,
askShare: `${restAPIv1}/searchbots/ask`,
mindmapShare: `${restAPIv1}/searchbots/mindmap`,
getRelatedQuestionsShare: `${restAPIv1}/searchbots/related_questions`,
retrievalTestShare: `${restAPIv1}/searchbots/retrieval_test`,
// memory
createMemory: `${restAPIv1}/memories`,
getMemoryList: `${restAPIv1}/memories`,
getMemoryConfig: (id: string) => `${restAPIv1}/memories/${id}/config`,
deleteMemory: (id: string) => `${restAPIv1}/memories/${id}`,
getMemoryDetail: (id: string) => `${restAPIv1}/memories/${id}`,
updateMemorySetting: (id: string) => `${restAPIv1}/memories/${id}`,
deleteMemoryMessage: (data: { memory_id: string; message_id: string }) =>
`${restAPIv1}/messages/${data.memory_id}:${data.message_id}`,
getMessageContent: (data: { memory_id: string; message_id: string }) =>
`${restAPIv1}/messages/${data.memory_id}:${data.message_id}/content`,
updateMessageState: (data: { memory_id: string; message_id: string }) =>
`${restAPIv1}/messages/${data.memory_id}:${data.message_id}`,
// data pipeline
fetchDataflow: (id: string) => `${webAPI}/dataflow/get/${id}`,
setDataflow: `${webAPI}/dataflow/set`,
removeDataflow: `${webAPI}/dataflow/rm`,
listDataflow: `${webAPI}/dataflow/list`,
runDataflow: `${webAPI}/dataflow/run`,
// admin
adminLogin: `${restAPIv1}/admin/login`,
adminLogout: `${restAPIv1}/admin/logout`,
adminListUsers: `${restAPIv1}/admin/users`,
adminCreateUser: `${restAPIv1}/admin/users`,
adminSetSuperuser: (username: string) =>
`${restAPIv1}/admin/users/${username}/admin`,
adminGetUserDetails: (username: string) =>
`${restAPIv1}/admin/users/${username}`,
adminUpdateUserStatus: (username: string) =>
`${restAPIv1}/admin/users/${username}/activate`,
adminUpdateUserPassword: (username: string) =>
`${restAPIv1}/admin/users/${username}/password`,
adminDeleteUser: (username: string) => `${restAPIv1}/admin/users/${username}`,
adminListUserDatasets: (username: string) =>
`${restAPIv1}/admin/users/${username}/datasets`,
adminListUserAgents: (username: string) =>
`${restAPIv1}/admin/users/${username}/agents`,
adminListServices: `${restAPIv1}/admin/services`,
adminShowServiceDetails: (serviceId: string) =>
`${restAPIv1}/admin/services/${serviceId}`,
adminListRoles: `${restAPIv1}/admin/roles`,
adminListRolesWithPermission: `${restAPIv1}/admin/roles_with_permission`,
adminGetRolePermissions: (roleName: string) =>
`${restAPIv1}/admin/roles/${roleName}/permissions`,
adminAssignRolePermissions: (roleName: string) =>
`${restAPIv1}/admin/roles/${roleName}/permission`,
adminRevokeRolePermissions: (roleName: string) =>
`${restAPIv1}/admin/roles/${roleName}/permission`,
adminCreateRole: `${restAPIv1}/admin/roles`,
adminDeleteRole: (roleName: string) => `${restAPIv1}/admin/roles/${roleName}`,
adminUpdateRoleDescription: (roleName: string) =>
`${restAPIv1}/admin/roles/${roleName}`,
adminUpdateUserRole: (username: string) =>
`${restAPIv1}/admin/users/${username}/role`,
adminGetUserPermissions: (username: string) =>
`${restAPIv1}/admin/users/${username}/permissions`,
adminListResources: `${restAPIv1}/admin/roles/resource`,
adminListWhitelist: `${restAPIv1}/admin/whitelist`,
adminCreateWhitelistEntry: `${restAPIv1}/admin/whitelist/add`,
adminUpdateWhitelistEntry: (id: number) =>
`${restAPIv1}/admin/whitelist/${id}`,
adminDeleteWhitelistEntry: (email: string) =>
`${restAPIv1}/admin/whitelist/${email}`,
adminImportWhitelist: `${restAPIv1}/admin/whitelist/batch`,
adminGetSystemVersion: `${restAPIv1}/admin/version`,
// Sandbox settings
adminListSandboxProviders: `${restAPIv1}/admin/sandbox/providers`,
adminGetSandboxProviderSchema: (providerId: string) =>
`${restAPIv1}/admin/sandbox/providers/${providerId}/schema`,
adminGetSandboxConfig: `${restAPIv1}/admin/sandbox/config`,
adminSetSandboxConfig: `${restAPIv1}/admin/sandbox/config`,
adminTestSandboxConnection: `${restAPIv1}/admin/sandbox/test`,
// Skill spaces
skillSpaces: `${restAPIv1}/skills/spaces`,
skillSpace: (spaceId: string) => `${restAPIv1}/skills/spaces/${spaceId}`,
skillSpaceByFolder: `${restAPIv1}/skills/space/by-folder`,
skillConfig: `${restAPIv1}/skills/config`,
skillSearch: `${restAPIv1}/skills/search`,
skillIndex: `${restAPIv1}/skills/index`,
skillReindex: `${restAPIv1}/skills/reindex`,
};