fix: align knowledge base CRUD API contract with kb_name, canonical payload fields, and matching OpenAPI types (#9000)

Warning: Potential breaking changes in /kb related openapi.

* fix(kb): align CRUD API contract end-to-end

Unify knowledge base create/update/list with kb_name, canonical_payload,
optional list pagination with total, and matching OpenAPI plus dashboard types.

* fix: restore _model_dict references, add KnowledgeBaseCreateRequest, fix OpenAPI allOf validation

- Replace 3 remaining _model_dict() calls with payload.model_dump(exclude_none=True)
- Add KnowledgeBaseCreateRequest schema with required kb_name + embedding_provider_id
- Remove additionalProperties: false from OpenAPI allOf sub-schema to avoid validation failures
- Use Pydantic Field(alias="name") + populate_by_name=True for legacy name migration
- Delegate _canonical_kb_payload to KnowledgeBaseRequest to eliminate duplicated logic
- Add getattr default None for update_kb to prevent AttributeError
- Add validation error tests: missing kb_name, missing embedding_provider_id

* fix: address knowledge base contract follow-ups

* fix: preserve knowledge base create error shape
This commit is contained in:
lxfight
2026-07-03 22:18:56 +08:00
committed by GitHub
parent e2f3b00088
commit ea9e3421d9
9 changed files with 419 additions and 60 deletions

View File

@@ -259,13 +259,22 @@ export type JsonSchema = {
[key: string]: unknown;
};
export type KnowledgeBaseCreateRequest = KnowledgeBaseRequest & {
kb_name: string;
embedding_provider_id: string;
};
export type KnowledgeBaseRequest = {
name: string;
kb_name?: string;
description?: string;
embedding_provider_id?: string;
rerank_provider_id?: string;
chunking?: DynamicConfig;
metadata?: DynamicConfig;
emoji?: string;
embedding_provider_id?: (string) | null;
rerank_provider_id?: (string) | null;
chunk_size?: number;
chunk_overlap?: number;
top_k_dense?: number;
top_k_sparse?: number;
top_m_final?: number;
};
export type KnowledgeDocumentImportRequest = {
@@ -275,7 +284,6 @@ export type KnowledgeDocumentImportRequest = {
export type KnowledgeDocumentUploadRequest = {
file: (Blob | File);
parser?: string;
};
export type KnowledgeDocumentUrlImportRequest = {
@@ -2610,7 +2618,7 @@ export type ListKnowledgeBasesResponse = (SuccessEnvelope);
export type ListKnowledgeBasesError = unknown;
export type CreateKnowledgeBaseData = {
body: KnowledgeBaseRequest;
body: KnowledgeBaseCreateRequest;
};
export type CreateKnowledgeBaseResponse = (SuccessEnvelope);

View File

@@ -32,6 +32,8 @@ import {
type DynamicConfig,
type EnabledPatch,
type GhproxyTestRequest,
type KnowledgeBaseCreateRequest,
type KnowledgeBaseRequest,
type LoginRequest,
type ListConversationsData,
type McpServerConfig,
@@ -1366,16 +1368,16 @@ export const knowledgeApi = {
openApiV1.getKnowledgeBase({ path: { kb_id: kbId } }),
);
},
create(config: OpenConfig) {
create(config: KnowledgeBaseCreateRequest) {
return typed<OpenConfig>(
openApiV1.createKnowledgeBase({ body: config as any }),
openApiV1.createKnowledgeBase({ body: config }),
);
},
update(kbId: string, config: OpenConfig) {
update(kbId: string, config: KnowledgeBaseRequest) {
return typed<OpenConfig>(
openApiV1.updateKnowledgeBase({
path: { kb_id: kbId },
body: config as any,
body: config,
}),
);
},

View File

@@ -161,7 +161,9 @@
<v-select v-model="formData.embedding_provider_id" :items="embeddingProviders"
:item-title="item => item.embedding_model || item.id" :item-value="'id'"
:label="t('create.embeddingModelLabel')" variant="outlined" class="mb-4" :disabled="editingKB !== null" hint="嵌入模型选择后无法修改如需更换请创建新的知识库" persistent-hint>
:label="t('create.embeddingModelLabel')" variant="outlined" class="mb-4" :disabled="editingKB !== null"
:rules="[v => editingKB !== null || !!v || t('create.embeddingModelRequired')]" required
hint="嵌入模型选择后无法修改如需更换请创建新的知识库" persistent-hint>
<template #item="{ props, item }">
<v-list-item v-bind="props">
<template #subtitle>
@@ -455,7 +457,10 @@ const submitForm = async () => {
if (editingKB.value) {
response = await knowledgeApi.update(editingKB.value.kb_id, payload)
} else {
response = await knowledgeApi.create(payload)
response = await knowledgeApi.create({
...payload,
embedding_provider_id: formData.value.embedding_provider_id!
})
}
if (response.data.status === 'ok') {