feat: restore learning feedback and knowledge capabilities
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import type {
|
||||
AnswerFeedbackVerdict,
|
||||
KnowledgeSearchResponse,
|
||||
KnowledgeMediaSelectedFile,
|
||||
UnifiedKnowledgeResponse,
|
||||
KnowledgeSnippet,
|
||||
PositionSopSummary,
|
||||
SummaryCardResponse
|
||||
} from '@/types/api';
|
||||
import { apiRequest } from './api';
|
||||
import { apiRequest, apiUrl, authHeaders, readTextPayload } from './api';
|
||||
import { getSelectedPosition } from './auth';
|
||||
|
||||
type RawKnowledgeSnippet = KnowledgeSnippet & {
|
||||
@@ -13,6 +15,38 @@ type RawKnowledgeSnippet = KnowledgeSnippet & {
|
||||
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 || '',
|
||||
@@ -20,8 +54,8 @@ const normalizeSnippet = (snippet: RawKnowledgeSnippet): KnowledgeSnippet => ({
|
||||
});
|
||||
|
||||
export const searchKnowledge = async (queryText: string, position = getSelectedPosition()) => {
|
||||
const response = await apiRequest<KnowledgeSearchResponse>({
|
||||
url: '/api/knowledge/search',
|
||||
const response = await apiRequest<UnifiedKnowledgeResponse>({
|
||||
url: '/api/knowledge/query',
|
||||
method: 'POST',
|
||||
data: {
|
||||
queryText,
|
||||
@@ -31,9 +65,74 @@ export const searchKnowledge = async (queryText: string, position = getSelectedP
|
||||
},
|
||||
timeout: 30000
|
||||
});
|
||||
return normalizeUnifiedResponse(response);
|
||||
};
|
||||
|
||||
export const searchKnowledgeWithMedia = (
|
||||
file: KnowledgeMediaSelectedFile,
|
||||
queryText: string,
|
||||
position = getSelectedPosition(),
|
||||
onUploadTask?: (task: ReturnType<typeof uni.uploadFile> | null) => void
|
||||
) => 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 },
|
||||
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
|
||||
) => {
|
||||
const response = await apiRequest<UnifiedKnowledgeResponse>({
|
||||
url: '/api/knowledge/query',
|
||||
method: 'POST',
|
||||
data: { queryText, source: 'mobile_uni_tool', limit: 5, toolCode },
|
||||
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 {
|
||||
...response,
|
||||
snippets: (response.snippets || []).map((item) => normalizeSnippet(item as RawKnowledgeSnippet))
|
||||
...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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user