Files
AILab/vue2/apps/web-antd/src/api/llm/report-cube.ts
T
2026-07-17 09:53:02 +08:00

374 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useAppConfig } from '@vben/hooks';
import { useAccessStore } from '@vben/stores';
import { pyRequestClient } from '#/api/request';
export interface CubeReportScope {
canRefreshMetadata: boolean;
companies: Array<{ id: string; name: string }>;
}
export interface CubeReportMetaStatus {
available: boolean;
changed?: boolean;
cubeCount: number;
memberCount: number;
refreshedAt: null | string;
refreshing: boolean;
stale: boolean;
}
export interface CubeReportAppParameters {
fileUpload: Record<string, unknown>;
openingStatement: string;
suggestedQuestions: string[];
suggestedQuestionsAfterAnswer: { enabled: boolean };
systemParameters: Record<string, unknown>;
userInputForm: Array<Record<string, unknown>>;
}
export interface CubeReportSession {
createdAt: null | string;
hasData?: boolean;
id: string;
reportId?: null | string;
tenantId: string;
tenantName: string;
title: string;
updatedAt: null | string;
}
export interface CubeReportSessionPage {
hasMore: boolean;
items: CubeReportSession[];
nextCursor: null | string;
}
export interface CubeSavedReport {
conversationId: string;
createdAt: null | string;
id: string;
limitSource: 'system' | 'user';
message: string;
query: Record<string, unknown>;
requirement: string;
tenantId: string;
tenantName: string;
title: string;
updatedAt: null | string;
}
export interface CubeSavedReportPage {
items: CubeSavedReport[];
page: number;
pageSize: number;
total: number;
}
export interface CubeReportFile {
belongsTo?: 'assistant' | 'user';
id: string;
name: string;
type: string;
}
export interface CubeReportMessage {
content: string;
createdAt: null | string;
difyMessageId: null | string;
files: CubeReportFile[];
id: string;
role: 'assistant' | 'user';
status: 'completed' | 'failed' | 'streaming';
}
export interface CubeReportColumn {
key: string;
title: string;
type: string;
}
export interface CubeReportPage {
columns: CubeReportColumn[];
limitSource?: 'system' | 'user';
page: number;
pageSize: number;
rows: Array<Record<string, unknown>>;
sql?: string;
title?: string;
total: number;
}
export interface CubeReportExportResult {
bucketName: string;
filename: string;
objectName: string;
rowCount: number;
sheetCount: number;
url: string;
}
export type CubeReportStreamEvent =
| { content: string; type: 'message_delta' | 'message_replace' }
| {
conversationId?: string;
hasData: boolean;
type: 'complete';
}
| { message: string; type: 'error' }
| { taskId: string; type: 'task' }
| { text: string; type: 'status' };
export interface SendCubeReportMessage {
content: string;
conversationId?: null | string;
files: CubeReportFile[];
tenantId?: string;
tenantName?: string;
}
function getFetchConfig() {
const { pyApiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
const accessStore = useAccessStore();
return {
baseURL: pyApiURL.replace(/\/$/, ''),
authorization: accessStore.accessToken
? `Bearer ${accessStore.accessToken}`
: '',
};
}
async function authorizedFetch(path: string, init: RequestInit) {
const request = () => {
const { authorization, baseURL } = getFetchConfig();
const headers = new Headers(init.headers);
if (authorization) {
headers.set('Authorization', authorization);
} else {
headers.delete('Authorization');
}
return fetch(`${baseURL}${path}`, { ...init, headers });
};
let response = await request();
if (response.status !== 401) return response;
try {
// 标准请求客户端会使用项目现有的 refresh token 机制更新令牌。
await getCubeReportScope();
} catch {
return response;
}
await response.body?.cancel();
response = await request();
return response;
}
async function responseError(response: Response) {
const text = await response.text();
try {
const payload = JSON.parse(text) as { detail?: string; message?: string };
return payload.detail || payload.message || text;
} catch {
return text || `请求失败(${response.status}`;
}
}
export async function getCubeReportScope() {
return pyRequestClient.get<CubeReportScope>('/llm/cube-report/scope');
}
export async function getCubeReportMetaStatus() {
return pyRequestClient.get<CubeReportMetaStatus>(
'/llm/cube-report/meta/status',
);
}
export async function refreshCubeReportMeta() {
return pyRequestClient.post<CubeReportMetaStatus>(
'/llm/cube-report/meta/refresh',
);
}
export async function getCubeReportAppParameters() {
return pyRequestClient.get<CubeReportAppParameters>(
'/llm/cube-report/app-parameters',
);
}
export async function getCubeReportSessions(params: {
cursor?: string;
keyword?: string;
pageSize?: number;
tenantId: string;
}) {
return pyRequestClient.get<CubeReportSessionPage>(
'/llm/cube-report/sessions',
{ params },
);
}
export async function getCubeReports(params: {
keyword?: string;
page: number;
pageSize: number;
tenantId: string;
}) {
return pyRequestClient.get<CubeSavedReportPage>('/llm/cube-report/reports', {
params,
});
}
export async function getCubeReport(id: string) {
return pyRequestClient.get<CubeSavedReport>(`/llm/cube-report/reports/${id}`);
}
export async function getCubeReportSession(id: string) {
return pyRequestClient.get<{
messages: CubeReportMessage[];
session: CubeReportSession;
}>(`/llm/cube-report/sessions/${id}`);
}
export async function getCubeReportData(id: string, params: { page: number }) {
return pyRequestClient.get<CubeReportPage>(
`/llm/cube-report/sessions/${id}/data`,
{ params },
);
}
export async function getCubeSavedReportData(
id: string,
params: { page: number },
) {
return pyRequestClient.get<CubeReportPage>(
`/llm/cube-report/reports/${id}/data`,
{ params },
);
}
export async function exportCubeReportSession(id: string) {
return pyRequestClient.post<CubeReportExportResult>(
`/llm/cube-report/sessions/${id}/export`,
);
}
export async function exportCubeSavedReport(id: string) {
return pyRequestClient.post<CubeReportExportResult>(
`/llm/cube-report/reports/${id}/export`,
);
}
export async function favoriteCubeReportSession(id: string) {
return pyRequestClient.post<CubeSavedReport>(
`/llm/cube-report/sessions/${id}/favorite`,
);
}
export async function renameCubeReportSession(id: string, name: string) {
return pyRequestClient.post<CubeReportSession>(
`/llm/cube-report/sessions/${id}/name`,
{ name },
);
}
export async function deleteCubeReportSession(id: string) {
return pyRequestClient.delete<{ result: string }>(
`/llm/cube-report/sessions/${id}`,
);
}
export async function renameCubeSavedReport(id: string, name: string) {
return pyRequestClient.post<CubeSavedReport>(
`/llm/cube-report/reports/${id}/name`,
{ name },
);
}
export async function deleteCubeSavedReport(id: string) {
return pyRequestClient.delete<{ result: string }>(
`/llm/cube-report/reports/${id}`,
);
}
export async function stopCubeReportTask(taskId: string) {
return pyRequestClient.post<{ result: string }>(
`/llm/cube-report/tasks/${taskId}/stop`,
);
}
export async function uploadCubeReportFile(file: File) {
const form = new FormData();
form.append('file', file);
const response = await authorizedFetch('/llm/cube-report/files/upload', {
method: 'POST',
body: form,
});
if (!response.ok) throw new Error(await responseError(response));
const payload = (await response.json()) as {
data: CubeReportFile;
};
return payload.data;
}
export async function downloadCubeReportFile(
conversationId: string,
file: CubeReportFile,
) {
const params = new URLSearchParams({
conversationId,
asAttachment: 'true',
});
const response = await authorizedFetch(
`/llm/cube-report/files/${file.id}/preview?${params}`,
{},
);
if (!response.ok) throw new Error(await responseError(response));
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = file.name;
anchor.click();
URL.revokeObjectURL(url);
}
export async function streamCubeReportMessage(
payload: SendCubeReportMessage,
onEvent: (event: CubeReportStreamEvent) => void,
) {
const response = await authorizedFetch('/llm/cube-report/messages/stream', {
method: 'POST',
headers: {
Accept: 'text/event-stream',
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(await responseError(response));
if (!response.body) throw new Error('当前浏览器不支持流式响应');
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
const consumePacket = (packet: string) => {
const data = packet
.split(/\r?\n/)
.filter((line) => line.startsWith('data: '))
.map((line) => line.slice(6))
.join('\n');
if (data) onEvent(JSON.parse(data) as CubeReportStreamEvent);
};
while (true) {
const { done, value } = await reader.read();
buffer += decoder.decode(value, { stream: !done });
const packets = buffer.split(/\r?\n\r?\n/);
buffer = packets.pop() ?? '';
packets.forEach((packet) => consumePacket(packet));
if (done) break;
}
if (buffer.trim()) consumePacket(buffer);
}