feat: improve assistant knowledge import and database compatibility

This commit is contained in:
吉鑫升 committed 2026-09-02 17:55:17 +08:00
1 parent fbd8dec48f
commit 6b724bd00d
51 files changed
+4393 -1021

No files matched your search

@@ -8,13 +8,21 @@ const BASE = () => `${getBackendBaseURL()}/api/assistant-knowledge`;
export interface AssistantKnowledgeBase {
id: string;
name: string;
description?: string;
base_kind?: "global" | "custom";
is_global?: boolean;
status: string;
source_count?: number;
page_count?: number;
entity_count?: number;
relation_count?: number;
created_at: string;
updated_at: string;
}
export interface AssistantWikiPage {
id: string;
base_id: string;
slug: string;
title: string;
page_type: string;
@@ -37,6 +45,7 @@ export interface AssistantWikiRevision {
export interface AssistantKnowledgeSource {
id: string;
base_id: string;
source_type: "weknora" | "skill" | "local_file";
source_name: string;
source_key: string;
@@ -46,12 +55,34 @@ export interface AssistantKnowledgeSource {
export interface AssistantImportJob {
id: string;
base_id: string;
source_id: string;
mirrored_global_base_id?: string;
mirrored_global_job_id?: string;
package_download_url?: string;
trigger: string;
status: string;
phase: string;
counts: Record<string, number>;
last_error_message?: string | null;
created_at: string;
completed_at?: string | null;
}
export interface LlmWikiExportPackageJob {
id: string;
mapping_id: string;
status: string;
phase: string;
counts: Record<string, number>;
package_download_url?: string;
status_url?: string;
error?: string | null;
created_at?: string | null;
updated_at?: string | null;
completed_at?: string | null;
file_exists?: boolean;
file_size_bytes?: number;
}
async function readJson<T>(response: Response): Promise<T> {
@@ -65,12 +96,41 @@ async function readJson<T>(response: Response): Promise<T> {
return (await response.json()) as T;
}
function normalizeImportJob(job: AssistantImportJob): AssistantImportJob {
const url = normalizeBackendUrl(job.package_download_url);
return url ? { ...job, package_download_url: url } : job;
}
function normalizeBackendUrl(url: string | undefined | null): string | undefined {
if (!url || /^https?:\/\//i.test(url)) return url || undefined;
return `${getBackendBaseURL()}${url.startsWith("/") ? url : `/${url}`}`;
}
function normalizeExportPackageJob(job: LlmWikiExportPackageJob): LlmWikiExportPackageJob {
return {
...job,
package_download_url: normalizeBackendUrl(job.package_download_url),
status_url: normalizeBackendUrl(job.status_url),
};
}
export async function getAssistantKnowledgeBase() {
const data = await readJson<{ base: AssistantKnowledgeBase | null }>(await apiFetch(`${BASE()}/base`));
return data.base;
}
export async function initializeAssistantKnowledgeBase(name = "知识梳理") {
export async function getAssistantKnowledgeBaseById(baseId: string) {
const data = await readJson<{ base: AssistantKnowledgeBase }>(
await apiFetch(`${BASE()}/bases/${encodeURIComponent(baseId)}`),
);
return data.base;
}
export async function listAssistantKnowledgeBases() {
return (await readJson<{ bases: AssistantKnowledgeBase[] }>(await apiFetch(`${BASE()}/bases`))).bases;
}
export async function initializeAssistantKnowledgeBase(name = "知识梳理总库") {
const data = await readJson<{ base: AssistantKnowledgeBase }>(
await apiFetch(`${BASE()}/base/initialize`, {
method: "POST",
@@ -81,58 +141,124 @@ export async function initializeAssistantKnowledgeBase(name = "知识梳理") {
return data.base;
}
export async function listAssistantWikiPages(q = "") {
export async function createAssistantKnowledgeBase(input: { name: string; description?: string }) {
const data = await readJson<{ base: AssistantKnowledgeBase }>(
await apiFetch(`${BASE()}/bases`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(input),
}),
);
return data.base;
}
function withBaseParam(baseId?: string | null) {
const params = new URLSearchParams();
if (baseId) params.set("base_id", baseId);
return params;
}
export async function listAssistantWikiPages(q = "", baseId?: string | null) {
const params = withBaseParam(baseId);
if (q.trim()) params.set("q", q.trim());
return (await readJson<{ pages: AssistantWikiPage[] }>(await apiFetch(`${BASE()}/wiki-pages?${params}`))).pages;
}
export async function listAssistantKnowledgeSources() {
return (await readJson<{ sources: AssistantKnowledgeSource[] }>(await apiFetch(`${BASE()}/sources`))).sources;
export async function listAssistantKnowledgeSources(baseId?: string | null) {
const params = withBaseParam(baseId);
return (await readJson<{ sources: AssistantKnowledgeSource[] }>(await apiFetch(`${BASE()}/sources?${params}`))).sources;
}
export async function listAssistantImportJobs() {
return (await readJson<{ jobs: AssistantImportJob[] }>(await apiFetch(`${BASE()}/import-jobs`))).jobs;
export async function listAssistantImportJobs(baseId?: string | null) {
const params = withBaseParam(baseId);
return (
await readJson<{ jobs: AssistantImportJob[] }>(await apiFetch(`${BASE()}/import-jobs?${params}`))
).jobs.map(normalizeImportJob);
}
export async function listAssistantWikiRevisions(slug: string) {
export async function listAssistantWikiRevisions(slug: string, baseId?: string | null) {
const params = withBaseParam(baseId);
return (await readJson<{ revisions: AssistantWikiRevision[] }>(
await apiFetch(`${BASE()}/wiki-pages/${encodeURIComponent(slug)}/revisions`),
await apiFetch(`${BASE()}/wiki-pages/${encodeURIComponent(slug)}/revisions?${params}`),
)).revisions;
}
export async function rollbackAssistantWikiRevision(slug: string, revisionId: string) {
export async function rollbackAssistantWikiRevision(slug: string, revisionId: string, baseId?: string | null) {
const params = withBaseParam(baseId);
return readJson<AssistantWikiRevision>(
await apiFetch(
`${BASE()}/wiki-pages/${encodeURIComponent(slug)}/revisions/${encodeURIComponent(revisionId)}/rollback`,
`${BASE()}/wiki-pages/${encodeURIComponent(slug)}/revisions/${encodeURIComponent(revisionId)}/rollback?${params}`,
{ method: "POST" },
),
);
}
export async function uploadAssistantKnowledgeFiles(files: File[]) {
const form = new FormData();
files.forEach((file) => form.append("files", file));
return readJson<{ batch_id: string; jobs: AssistantImportJob[] }>(
await apiFetch(`${BASE()}/imports/files`, { method: "POST", body: form }),
export async function exportLlmWikiToAssistant(mappingId: string, assistantBaseId?: string | null) {
return normalizeImportJob(await readJson<AssistantImportJob>(
await apiFetch(
`${getBackendBaseURL()}/api/llmwiki/knowledge-bases/${encodeURIComponent(mappingId)}/export-to-assistant`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ assistant_base_id: assistantBaseId || null }),
},
),
));
}
export async function createLlmWikiExportPackage(mappingId: string) {
return normalizeExportPackageJob(
await readJson<LlmWikiExportPackageJob>(
await apiFetch(
`${getBackendBaseURL()}/api/llmwiki/knowledge-bases/${encodeURIComponent(mappingId)}/export-package`,
{ method: "POST" },
),
),
);
}
export async function exportLlmWikiToAssistant(mappingId: string) {
return readJson<AssistantImportJob>(
await apiFetch(
`${getBackendBaseURL()}/api/llmwiki/knowledge-bases/${encodeURIComponent(mappingId)}/export-to-assistant`,
{ method: "POST" },
export async function listLlmWikiExportPackages(mappingId: string) {
return (
await readJson<{ jobs: LlmWikiExportPackageJob[] }>(
await apiFetch(
`${getBackendBaseURL()}/api/llmwiki/knowledge-bases/${encodeURIComponent(mappingId)}/export-package`,
),
)
).jobs.map(normalizeExportPackageJob);
}
export async function getLlmWikiExportPackageStatus(mappingId: string, jobId: string) {
return normalizeExportPackageJob(
await readJson<LlmWikiExportPackageJob>(
await apiFetch(
`${getBackendBaseURL()}/api/llmwiki/knowledge-bases/${encodeURIComponent(mappingId)}/export-package/${encodeURIComponent(jobId)}`,
),
),
);
}
export async function reimportAssistantKnowledgeSource(sourceId: string) {
return readJson<AssistantImportJob>(
return normalizeImportJob(await readJson<AssistantImportJob>(
await apiFetch(`${BASE()}/sources/${encodeURIComponent(sourceId)}/reimport`, {
method: "POST",
}),
);
));
}
export function getAssistantImportPackageUrl(jobId: string) {
return `${BASE()}/import-jobs/${encodeURIComponent(jobId)}/package`;
}
export async function uploadAssistantWeKnoraPackage(input: { file: File; assistantBaseId?: string | null }) {
const body = new FormData();
body.set("file", input.file);
if (input.assistantBaseId) body.set("assistant_base_id", input.assistantBaseId);
return normalizeImportJob(await readJson<AssistantImportJob>(
await apiFetch(`${BASE()}/imports/weknora-package`, {
method: "POST",
body,
}),
));
}
export async function deleteAssistantKnowledgeSource(sourceId: string) {
@@ -144,20 +270,40 @@ export async function deleteAssistantKnowledgeSource(sourceId: string) {
const KEY = "assistant-knowledge";
export function useAssistantKnowledgeBase() {
return useQuery({ queryKey: [KEY, "base"], queryFn: getAssistantKnowledgeBase });
export function useAssistantKnowledgeBase(baseId?: string | null) {
return useQuery({
queryKey: [KEY, "base", baseId ?? "global"],
queryFn: () => (baseId ? getAssistantKnowledgeBaseById(baseId) : getAssistantKnowledgeBase()),
});
}
export function useAssistantWikiPages(query = "") {
return useQuery({ queryKey: [KEY, "pages", query], queryFn: () => listAssistantWikiPages(query) });
export function useAssistantKnowledgeBases() {
return useQuery({ queryKey: [KEY, "bases"], queryFn: listAssistantKnowledgeBases });
}
export function useAssistantKnowledgeSources() {
return useQuery({ queryKey: [KEY, "sources"], queryFn: listAssistantKnowledgeSources });
export function useAssistantWikiPages(query = "", baseId?: string | null) {
return useQuery({
queryKey: [KEY, "pages", baseId ?? "global", query],
queryFn: () => listAssistantWikiPages(query, baseId),
});
}
export function useAssistantImportJobs() {
return useQuery({ queryKey: [KEY, "jobs"], queryFn: listAssistantImportJobs });
export function useAssistantKnowledgeSources(baseId?: string | null) {
return useQuery({
queryKey: [KEY, "sources", baseId ?? "global"],
queryFn: () => listAssistantKnowledgeSources(baseId),
});
}
export function useAssistantImportJobs(baseId?: string | null) {
return useQuery({
queryKey: [KEY, "jobs", baseId ?? "global"],
queryFn: () => listAssistantImportJobs(baseId),
refetchInterval: (query) =>
(query.state.data ?? []).some((job) => ["queued", "running"].includes(job.status))
? 3000
: false,
});
}
export function useAssistantKnowledgeActions() {
@@ -165,12 +311,13 @@ export function useAssistantKnowledgeActions() {
const invalidate = () => client.invalidateQueries({ queryKey: [KEY] });
return {
initialize: useMutation({ mutationFn: initializeAssistantKnowledgeBase, onSuccess: invalidate }),
upload: useMutation({ mutationFn: uploadAssistantKnowledgeFiles, onSuccess: invalidate }),
createBase: useMutation({ mutationFn: createAssistantKnowledgeBase, onSuccess: invalidate }),
uploadWeKnoraPackage: useMutation({ mutationFn: uploadAssistantWeKnoraPackage, onSuccess: invalidate }),
reimportSource: useMutation({ mutationFn: reimportAssistantKnowledgeSource, onSuccess: invalidate }),
deleteSource: useMutation({ mutationFn: deleteAssistantKnowledgeSource, onSuccess: invalidate }),
rollback: useMutation({
mutationFn: ({ slug, revisionId }: { slug: string; revisionId: string }) =>
rollbackAssistantWikiRevision(slug, revisionId),
mutationFn: ({ slug, revisionId, baseId }: { slug: string; revisionId: string; baseId?: string | null }) =>
rollbackAssistantWikiRevision(slug, revisionId, baseId),
onSuccess: invalidate,
}),
};