325 lines
10 KiB
TypeScript
325 lines
10 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
|
|
import { apiFetch } from "@/core/api/fetch-client";
|
|
import { getBackendBaseURL } from "@/core/config";
|
|
|
|
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;
|
|
content_markdown: string;
|
|
summary: string;
|
|
current_revision_id?: string | null;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface AssistantWikiRevision {
|
|
id: string;
|
|
page_id: string;
|
|
revision_no: number;
|
|
summary: string;
|
|
content_markdown: string;
|
|
change_type: string;
|
|
created_by?: string | null;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface AssistantKnowledgeSource {
|
|
id: string;
|
|
base_id: string;
|
|
source_type: "weknora" | "skill" | "local_file";
|
|
source_name: string;
|
|
source_key: string;
|
|
status: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
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> {
|
|
if (!response.ok) {
|
|
const data = await response.json().catch(() => null);
|
|
const detail = data && typeof data === "object" && "detail" in data
|
|
? (data as { detail: unknown }).detail
|
|
: `请求失败 (${response.status})`;
|
|
throw new Error(typeof detail === "string" ? detail : JSON.stringify(detail));
|
|
}
|
|
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 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",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ name }),
|
|
}),
|
|
);
|
|
return data.base;
|
|
}
|
|
|
|
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(baseId?: string | null) {
|
|
const params = withBaseParam(baseId);
|
|
return (await readJson<{ sources: AssistantKnowledgeSource[] }>(await apiFetch(`${BASE()}/sources?${params}`))).sources;
|
|
}
|
|
|
|
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, baseId?: string | null) {
|
|
const params = withBaseParam(baseId);
|
|
return (await readJson<{ revisions: AssistantWikiRevision[] }>(
|
|
await apiFetch(`${BASE()}/wiki-pages/${encodeURIComponent(slug)}/revisions?${params}`),
|
|
)).revisions;
|
|
}
|
|
|
|
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?${params}`,
|
|
{ method: "POST" },
|
|
),
|
|
);
|
|
}
|
|
|
|
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 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 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) {
|
|
const response = await apiFetch(`${BASE()}/sources/${encodeURIComponent(sourceId)}`, {
|
|
method: "DELETE",
|
|
});
|
|
if (!response.ok) await readJson(response);
|
|
}
|
|
|
|
const KEY = "assistant-knowledge";
|
|
|
|
export function useAssistantKnowledgeBase(baseId?: string | null) {
|
|
return useQuery({
|
|
queryKey: [KEY, "base", baseId ?? "global"],
|
|
queryFn: () => (baseId ? getAssistantKnowledgeBaseById(baseId) : getAssistantKnowledgeBase()),
|
|
});
|
|
}
|
|
|
|
export function useAssistantKnowledgeBases() {
|
|
return useQuery({ queryKey: [KEY, "bases"], queryFn: listAssistantKnowledgeBases });
|
|
}
|
|
|
|
export function useAssistantWikiPages(query = "", baseId?: string | null) {
|
|
return useQuery({
|
|
queryKey: [KEY, "pages", baseId ?? "global", query],
|
|
queryFn: () => listAssistantWikiPages(query, baseId),
|
|
});
|
|
}
|
|
|
|
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() {
|
|
const client = useQueryClient();
|
|
const invalidate = () => client.invalidateQueries({ queryKey: [KEY] });
|
|
return {
|
|
initialize: useMutation({ mutationFn: initializeAssistantKnowledgeBase, 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, baseId }: { slug: string; revisionId: string; baseId?: string | null }) =>
|
|
rollbackAssistantWikiRevision(slug, revisionId, baseId),
|
|
onSuccess: invalidate,
|
|
}),
|
|
};
|
|
}
|