205 lines
6.5 KiB
TypeScript
205 lines
6.5 KiB
TypeScript
import type {
|
|
AnswerFeedbackVerdict,
|
|
KnowledgeSearchResponse,
|
|
KnowledgeMediaSelectedFile,
|
|
KnowledgeConversationState,
|
|
UnifiedKnowledgeResponse,
|
|
KnowledgeSnippet,
|
|
PositionSopSummary,
|
|
SummaryCardResponse
|
|
} from '@/types/api';
|
|
import { apiRequest, apiUrl, authHeaders, readTextPayload } from './api';
|
|
import { getSelectedPosition } from './auth';
|
|
|
|
type RawKnowledgeSnippet = KnowledgeSnippet & {
|
|
title?: string;
|
|
text?: string;
|
|
};
|
|
|
|
const mediaExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.mp4', '.mov', '.avi', '.mkv', '.webm', '.m4v'];
|
|
const videoExtensions = new Set(['.mp4', '.mov', '.avi', '.mkv', '.webm', '.m4v']);
|
|
|
|
export const chooseKnowledgeMedia = () =>
|
|
new Promise<KnowledgeMediaSelectedFile>((resolve, reject) => {
|
|
uni.chooseFile({
|
|
count: 1,
|
|
type: 'all',
|
|
extension: mediaExtensions,
|
|
success: (result) => {
|
|
const tempFiles = Array.isArray(result.tempFiles) ? result.tempFiles : [];
|
|
const file = (tempFiles[0] || {}) as { path?: string; name?: string; size?: number };
|
|
const path = file.path || result.tempFilePaths?.[0] || '';
|
|
const name = file.name || path.split('/').pop() || '现场附件';
|
|
const extension = `.${name.split('.').pop()?.toLowerCase() || ''}`;
|
|
const kind = videoExtensions.has(extension) ? 'video' : 'image';
|
|
const size = Number(file.size || 0);
|
|
const maxSize = kind === 'video' ? 100 * 1024 * 1024 : 10 * 1024 * 1024;
|
|
if (!path) {
|
|
reject(new Error('未获取到附件文件'));
|
|
return;
|
|
}
|
|
if (size > maxSize) {
|
|
reject(new Error(kind === 'video' ? '视频不能超过 100MB' : '图片不能超过 10MB'));
|
|
return;
|
|
}
|
|
resolve({ path, name, size, kind });
|
|
},
|
|
fail: () => reject(new Error('未选择附件'))
|
|
});
|
|
});
|
|
|
|
const normalizeSnippet = (snippet: RawKnowledgeSnippet): KnowledgeSnippet => ({
|
|
...snippet,
|
|
docTitle: snippet.docTitle || snippet.title || '',
|
|
content: snippet.content || snippet.text || ''
|
|
});
|
|
|
|
export const searchKnowledge = async (
|
|
queryText: string,
|
|
position = getSelectedPosition(),
|
|
conversation?: KnowledgeConversationState
|
|
) => {
|
|
const response = await apiRequest<UnifiedKnowledgeResponse>({
|
|
url: '/api/knowledge/query',
|
|
method: 'POST',
|
|
data: {
|
|
queryText,
|
|
category: 'sop',
|
|
position,
|
|
limit: 5,
|
|
conversationId: conversation?.conversationId,
|
|
contextVersion: conversation?.contextVersion
|
|
},
|
|
timeout: 30000
|
|
});
|
|
return normalizeUnifiedResponse(response);
|
|
};
|
|
|
|
export const searchKnowledgeWithMedia = (
|
|
file: KnowledgeMediaSelectedFile,
|
|
queryText: string,
|
|
position = getSelectedPosition(),
|
|
onUploadTask?: (task: ReturnType<typeof uni.uploadFile> | null) => void,
|
|
conversation?: KnowledgeConversationState
|
|
) => new Promise<KnowledgeSearchResponse>((resolve, reject) => {
|
|
const task = uni.uploadFile({
|
|
url: apiUrl('/api/knowledge/query-media'),
|
|
filePath: file.path,
|
|
name: 'file',
|
|
header: authHeaders(false),
|
|
formData: {
|
|
queryText,
|
|
position,
|
|
...(conversation ? {
|
|
conversationId: conversation.conversationId,
|
|
contextVersion: String(conversation.contextVersion)
|
|
} : {})
|
|
},
|
|
timeout: 360000,
|
|
success: (response) => {
|
|
try {
|
|
const payload = readTextPayload<UnifiedKnowledgeResponse>(response.statusCode, response.data);
|
|
resolve(normalizeUnifiedResponse(payload));
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
},
|
|
fail: () => reject(new Error(file.kind === 'video' ? '视频上传或分析超时' : '图片上传失败')),
|
|
complete: () => onUploadTask?.(null)
|
|
});
|
|
onUploadTask?.(task);
|
|
});
|
|
|
|
export const queryKnowledgeTool = async (
|
|
toolCode: 'MY_PRACTICE_SUMMARY' | 'TEAM_PRACTICE_SUMMARY',
|
|
queryText: string,
|
|
conversation?: KnowledgeConversationState
|
|
) => {
|
|
const response = await apiRequest<UnifiedKnowledgeResponse>({
|
|
url: '/api/knowledge/query',
|
|
method: 'POST',
|
|
data: {
|
|
queryText,
|
|
source: 'mobile_uni_tool',
|
|
limit: 5,
|
|
toolCode,
|
|
conversationId: conversation?.conversationId,
|
|
contextVersion: conversation?.contextVersion
|
|
},
|
|
timeout: 30000
|
|
});
|
|
return normalizeUnifiedResponse(response);
|
|
};
|
|
|
|
const normalizeUnifiedResponse = (response: UnifiedKnowledgeResponse): KnowledgeSearchResponse => {
|
|
const legacy = response.legacy || ({} as KnowledgeSearchResponse);
|
|
const citationSnippets = (response.citations || []).map((item) => normalizeSnippet({
|
|
docTitle: item.title,
|
|
content: item.snippet,
|
|
fragmentId: item.fragmentId,
|
|
spaceCode: item.spaceCode,
|
|
sourceType: item.sourceType
|
|
}));
|
|
return {
|
|
...legacy,
|
|
requestId: response.requestId,
|
|
queryText: response.queryText,
|
|
category: legacy.category || 'sop',
|
|
answer: response.answer,
|
|
reference: legacy.reference || (response.citations || []).map((item) => item.title).join('、'),
|
|
docs: legacy.docs || [],
|
|
snippets: (legacy.snippets?.length ? legacy.snippets : citationSnippets).map((item) => normalizeSnippet(item as RawKnowledgeSnippet)),
|
|
training: legacy.training || [],
|
|
records: legacy.records || [],
|
|
citations: response.citations || [],
|
|
usedSpaceCodes: response.usedSpaceCodes || [],
|
|
noEvidence: response.noEvidence,
|
|
promptVersion: response.promptVersion,
|
|
data: response.data,
|
|
conversationId: response.conversationId,
|
|
contextVersion: response.contextVersion,
|
|
intent: response.intent,
|
|
rewrittenQuery: response.rewrittenQuery,
|
|
resources: response.resources || []
|
|
};
|
|
};
|
|
|
|
export const getPositionSopSummary = (position = getSelectedPosition()) =>
|
|
apiRequest<PositionSopSummary>({
|
|
url: `/api/knowledge/position-sop?position=${encodeURIComponent(position)}&projectType=住宅`,
|
|
method: 'GET',
|
|
timeout: 15000
|
|
});
|
|
|
|
export const submitAnswerFeedback = (params: {
|
|
queryText: string;
|
|
fragmentIds: number[];
|
|
verdict: AnswerFeedbackVerdict;
|
|
reviewId?: number | null;
|
|
}) =>
|
|
apiRequest<{ saved: number }>({
|
|
url: '/api/knowledge/answer-feedback',
|
|
method: 'POST',
|
|
data: {
|
|
queryText: params.queryText,
|
|
fragmentIds: params.fragmentIds,
|
|
verdict: params.verdict,
|
|
category: 'sop',
|
|
position: getSelectedPosition(),
|
|
source: 'mobile_uni_sop',
|
|
reviewId: params.reviewId ?? null
|
|
},
|
|
timeout: 15000
|
|
});
|
|
|
|
export const generateSummaryCard = (queryText: string) =>
|
|
apiRequest<SummaryCardResponse>({
|
|
url: '/api/knowledge/summary-card',
|
|
method: 'POST',
|
|
data: {
|
|
queryText,
|
|
category: 'sop'
|
|
},
|
|
timeout: 45000
|
|
});
|