735 lines
22 KiB
JavaScript
735 lines
22 KiB
JavaScript
const API_BASE = '/api/v1/knowledge'
|
||
|
||
// 用户认证信息(实际应用中应该从登录获取)
|
||
const USER_ID = 'user1'
|
||
const USER_NAME = '42tr'
|
||
const ROLE = 'admin'
|
||
|
||
const getHeaders = (contentType = true) => {
|
||
const headers = {
|
||
'x-user-id': USER_ID,
|
||
'x-user-name': USER_NAME,
|
||
'x-role': ROLE,
|
||
}
|
||
if (contentType) {
|
||
headers['Content-Type'] = 'application/json'
|
||
}
|
||
return headers
|
||
}
|
||
|
||
const readErrorMessage = async (response, fallback) => {
|
||
try {
|
||
const payload = await response.json()
|
||
if (payload?.error) return payload.error
|
||
if (payload?.message) return payload.message
|
||
} catch (_) {
|
||
// ignore json parse error
|
||
}
|
||
return fallback
|
||
}
|
||
|
||
export const api = {
|
||
// 搜索
|
||
async search(query, kbId = null, fileId = null, options = {}) {
|
||
const { advanced = false } = options
|
||
let url = `${API_BASE}/search/?query=${encodeURIComponent(query)}`
|
||
if (kbId) {
|
||
url += `&kb_id=${kbId}`
|
||
}
|
||
if (fileId) {
|
||
url += `&file_id=${fileId}`
|
||
}
|
||
if (advanced) {
|
||
url += '&advanced=true'
|
||
}
|
||
const response = await fetch(url, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('搜索失败')
|
||
const data = await response.json()
|
||
return data.results || []
|
||
},
|
||
|
||
advancedSearchStream(params, handlers = {}) {
|
||
const {
|
||
query,
|
||
kbId = null,
|
||
fileId = null,
|
||
maxSubQueries = 3,
|
||
perQueryLimit = 10,
|
||
contextChars = 2000,
|
||
debug = false,
|
||
} = params
|
||
|
||
const searchParams = new URLSearchParams()
|
||
searchParams.set('query', query)
|
||
searchParams.set('max_sub_queries', maxSubQueries)
|
||
searchParams.set('per_query_limit', perQueryLimit)
|
||
searchParams.set('context_chars', contextChars)
|
||
if (debug) searchParams.set('debug', 'true')
|
||
if (kbId) searchParams.set('kb_id', kbId)
|
||
if (fileId) searchParams.set('file_id', fileId)
|
||
|
||
const controller = new AbortController()
|
||
const url = `${API_BASE}/search/advanced/stream?${searchParams.toString()}`
|
||
const headers = getHeaders(false)
|
||
const decoder = new TextDecoder('utf-8')
|
||
|
||
const parseEvent = (chunk) => {
|
||
const lines = chunk.split('\n')
|
||
let eventType = 'message'
|
||
let dataLines = []
|
||
for (const line of lines) {
|
||
const trimmed = line.trim()
|
||
if (trimmed.startsWith('event:')) {
|
||
eventType = trimmed.slice(6).trim()
|
||
} else if (trimmed.startsWith('data:')) {
|
||
dataLines.push(trimmed.slice(5).trim())
|
||
}
|
||
}
|
||
const dataStr = dataLines.join('\n')
|
||
let parsedData = null
|
||
if (dataStr) {
|
||
try {
|
||
parsedData = JSON.parse(dataStr)
|
||
} catch (err) {
|
||
parsedData = dataStr
|
||
}
|
||
}
|
||
return { eventType, data: parsedData }
|
||
}
|
||
|
||
const streamPromise = (async () => {
|
||
try {
|
||
const response = await fetch(url, {
|
||
headers,
|
||
signal: controller.signal,
|
||
})
|
||
if (!response.ok) {
|
||
throw new Error('高级搜索连接失败')
|
||
}
|
||
const reader = response.body.getReader()
|
||
let buffer = ''
|
||
while (true) {
|
||
const { value, done } = await reader.read()
|
||
if (done) break
|
||
buffer += decoder.decode(value, { stream: true })
|
||
let boundary
|
||
while ((boundary = buffer.indexOf('\n\n')) !== -1) {
|
||
const rawEvent = buffer.slice(0, boundary).trim()
|
||
buffer = buffer.slice(boundary + 2)
|
||
if (!rawEvent) continue
|
||
const { eventType, data } = parseEvent(rawEvent)
|
||
switch (eventType) {
|
||
case 'status':
|
||
handlers.onStatus?.(data)
|
||
break
|
||
case 'plan':
|
||
handlers.onPlan?.(data)
|
||
break
|
||
case 'step':
|
||
handlers.onStep?.(data)
|
||
break
|
||
case 'candidate':
|
||
handlers.onCandidate?.(data)
|
||
break
|
||
case 'filtered':
|
||
handlers.onFiltered?.(data)
|
||
break
|
||
case 'result':
|
||
handlers.onResult?.(data)
|
||
break
|
||
case 'error':
|
||
handlers.onErrorEvent?.(data)
|
||
break
|
||
case 'done':
|
||
handlers.onDone?.()
|
||
break
|
||
default:
|
||
handlers.onMessage?.({ eventType, data })
|
||
}
|
||
}
|
||
}
|
||
handlers.onComplete?.()
|
||
} catch (err) {
|
||
if (controller.signal.aborted) {
|
||
handlers.onAbort?.()
|
||
} else {
|
||
handlers.onError?.(err)
|
||
}
|
||
} finally {
|
||
handlers.onFinally?.()
|
||
}
|
||
})()
|
||
|
||
return {
|
||
cancel: () => controller.abort(),
|
||
finished: streamPromise,
|
||
}
|
||
},
|
||
async searchFull(query, kbId = null, fileId = null) {
|
||
let url = `${API_BASE}/search/full?query=${encodeURIComponent(query)}`
|
||
if (kbId) {
|
||
url += `&kb_id=${kbId}`
|
||
}
|
||
if (fileId) {
|
||
url += `&file_id=${fileId}`
|
||
}
|
||
const response = await fetch(url, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('全文搜索失败')
|
||
const data = await response.json()
|
||
return data.results || []
|
||
},
|
||
async searchImage(file, text = '', kbId = null, fileId = null) {
|
||
const formData = new FormData()
|
||
formData.append('file', file)
|
||
formData.append('text', text)
|
||
let url = `${API_BASE}/search/image`
|
||
const params = []
|
||
if (kbId) {
|
||
params.push(`kb_id=${kbId}`)
|
||
}
|
||
if (fileId) {
|
||
params.push(`file_id=${fileId}`)
|
||
}
|
||
if (params.length > 0) {
|
||
url += `?${params.join('&')}`
|
||
}
|
||
const response = await fetch(url, {
|
||
method: 'POST',
|
||
headers: getHeaders(false),
|
||
body: formData,
|
||
})
|
||
if (!response.ok) throw new Error('图片搜索失败')
|
||
const data = await response.json()
|
||
return data.results || []
|
||
},
|
||
|
||
// 词表
|
||
async listLexicons(params = {}) {
|
||
const searchParams = new URLSearchParams()
|
||
if (params.q) searchParams.set('q', params.q)
|
||
if (params.enabled === true) searchParams.set('enabled', 'true')
|
||
if (params.enabled === false) searchParams.set('enabled', 'false')
|
||
if (params.limit) searchParams.set('limit', String(params.limit))
|
||
if (params.offset) searchParams.set('offset', String(params.offset))
|
||
const query = searchParams.toString()
|
||
const url = `${API_BASE}/search/lexicons${query ? `?${query}` : ''}`
|
||
const response = await fetch(url, { headers: getHeaders() })
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '获取词表失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async createLexicon(data) {
|
||
const response = await fetch(`${API_BASE}/search/lexicons`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '创建词条失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async updateLexicon(id, data) {
|
||
const response = await fetch(`${API_BASE}/search/lexicons/${id}`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '更新词条失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async deleteLexicon(id) {
|
||
const response = await fetch(`${API_BASE}/search/lexicons/${id}`, {
|
||
method: 'DELETE',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '删除词条失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async toggleLexiconEnabled(id, enabled) {
|
||
const response = await fetch(`${API_BASE}/search/lexicons/${id}/enabled`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({ enabled }),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '更新词条状态失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async reloadLexicon() {
|
||
const response = await fetch(`${API_BASE}/search/lexicons/reload`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '重载词表失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async publishLexicon() {
|
||
const response = await fetch(`${API_BASE}/search/lexicons/publish`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '发布词表失败'))
|
||
return response.json()
|
||
},
|
||
|
||
// 同义词
|
||
async listSynonyms(params = {}) {
|
||
const searchParams = new URLSearchParams()
|
||
if (params.q) searchParams.set('q', params.q)
|
||
if (params.enabled === true) searchParams.set('enabled', 'true')
|
||
if (params.enabled === false) searchParams.set('enabled', 'false')
|
||
if (params.limit) searchParams.set('limit', String(params.limit))
|
||
if (params.offset) searchParams.set('offset', String(params.offset))
|
||
const query = searchParams.toString()
|
||
const url = `${API_BASE}/search/synonyms${query ? `?${query}` : ''}`
|
||
const response = await fetch(url, { headers: getHeaders() })
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '获取同义词失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async createSynonym(data) {
|
||
const response = await fetch(`${API_BASE}/search/synonyms`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '创建同义词失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async updateSynonym(id, data) {
|
||
const response = await fetch(`${API_BASE}/search/synonyms/${id}`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '更新同义词失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async deleteSynonym(id) {
|
||
const response = await fetch(`${API_BASE}/search/synonyms/${id}`, {
|
||
method: 'DELETE',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '删除同义词失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async toggleSynonymEnabled(id, enabled) {
|
||
const response = await fetch(`${API_BASE}/search/synonyms/${id}/enabled`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({ enabled }),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '更新同义词状态失败'))
|
||
return response.json()
|
||
},
|
||
|
||
// 系统
|
||
async getIndexRebuildStatus() {
|
||
const response = await fetch(`${API_BASE}/system/index/rebuild/status`, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '获取重建状态失败'))
|
||
return response.json()
|
||
},
|
||
|
||
// 知识库
|
||
async getKnowledgeBases(parentId = null) {
|
||
let url = `${API_BASE}/knowledge_base/`;
|
||
const params = new URLSearchParams();
|
||
// A null parentId fetches top-level KBs by default on the backend.
|
||
if (parentId) {
|
||
params.append('parent_id', parentId);
|
||
}
|
||
const queryString = params.toString();
|
||
if (queryString) {
|
||
url += `?${queryString}`;
|
||
}
|
||
const response = await fetch(url, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取知识库列表失败')
|
||
return response.json()
|
||
},
|
||
|
||
async getKnowledgeBase(id) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${id}`, {
|
||
headers: getHeaders(),
|
||
});
|
||
if (!response.ok) throw new Error('获取知识库详情失败');
|
||
return response.json();
|
||
},
|
||
|
||
async getKnowledgeBaseFiles(id, params = {}) {
|
||
const searchParams = new URLSearchParams()
|
||
if (params.page) searchParams.set('page', String(params.page))
|
||
if (params.size) searchParams.set('size', String(params.size))
|
||
if (params.filename) searchParams.set('filename', params.filename)
|
||
if (params.tag) searchParams.set('tag', params.tag)
|
||
const query = searchParams.toString()
|
||
const response = await fetch(
|
||
`${API_BASE}/knowledge_base/${id}/files${query ? `?${query}` : ''}`,
|
||
{ headers: getHeaders() }
|
||
)
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '获取知识库文件失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async createKnowledgeBase(data) { // data may include { name, description, parent_id }
|
||
const response = await fetch(`${API_BASE}/knowledge_base/`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error('创建知识库失败')
|
||
return response.json()
|
||
},
|
||
|
||
async updateKnowledgeBase(id, data) { // data may include { name, description, parent_id, is_public }
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${id}`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error('更新知识库失败')
|
||
return response.json()
|
||
},
|
||
|
||
async deleteKnowledgeBase(id) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${id}`, {
|
||
method: 'DELETE',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('删除知识库失败')
|
||
return
|
||
},
|
||
|
||
async reparseKnowledgeBases() {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/reparse`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '重新解析失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async reparseKnowledgeBase(id) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${id}/reparse`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '重新解析指定知识库失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async exportKnowledgeBases(kbIds, includeChildren = false) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/export`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({ kb_ids: kbIds, include_children: includeChildren }),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '导出知识库失败'))
|
||
return response.json()
|
||
},
|
||
|
||
// 知识库权限管理
|
||
async getKbPermissions(kbId) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${kbId}/permissions`, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取权限列表失败')
|
||
return response.json()
|
||
},
|
||
|
||
async addKbPermission(kbId, userId, permission) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${kbId}/permissions`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({ user_id: userId, permission }),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '添加权限失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async removeKbPermission(kbId, userId) {
|
||
const response = await fetch(`${API_BASE}/knowledge_base/${kbId}/permissions/${userId}`, {
|
||
method: 'DELETE',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '删除权限失败'))
|
||
return response.json()
|
||
},
|
||
|
||
// 文件
|
||
async uploadFiles(knowledgeBaseId, files, tags = [], isPublic = false, sliceType = 'smart') {
|
||
const formData = new FormData()
|
||
if (knowledgeBaseId) {
|
||
formData.append('kb_id', knowledgeBaseId)
|
||
}
|
||
if (tags.length > 0) {
|
||
formData.append('tags', JSON.stringify(tags))
|
||
}
|
||
formData.append('is_public', isPublic ? 'true' : 'false')
|
||
formData.append('slice_type', sliceType)
|
||
for (const file of files) {
|
||
formData.append('file', file)
|
||
}
|
||
|
||
const response = await fetch(`${API_BASE}/files/`, {
|
||
method: 'POST',
|
||
headers: getHeaders(false),
|
||
body: formData,
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '上传文件失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async deleteFile(id) {
|
||
const response = await fetch(`${API_BASE}/files/${id}`, {
|
||
method: 'DELETE',
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('删除文件失败')
|
||
return
|
||
},
|
||
|
||
async batchDeleteFiles(ids, options = {}) {
|
||
const {
|
||
strict = false,
|
||
allowProcessing = false,
|
||
} = options
|
||
|
||
const response = await fetch(`${API_BASE}/files/batch-delete`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({
|
||
ids,
|
||
strict,
|
||
allow_processing: allowProcessing,
|
||
}),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '批量删除文件失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async getFiles(kbId, tag = null, pagination = {}) {
|
||
let url = `${API_BASE}/files/`
|
||
const params = []
|
||
if (kbId === null) {
|
||
// 明确传递null时,获取未分配知识库的文件
|
||
params.push('kb_id=null')
|
||
} else if (kbId !== undefined) {
|
||
// 传递具体的知识库ID
|
||
params.push(`kb_id=${kbId}`)
|
||
}
|
||
if (tag) {
|
||
params.push(`tag=${encodeURIComponent(tag)}`)
|
||
}
|
||
if (pagination.page) params.push(`page=${pagination.page}`)
|
||
if (pagination.size) params.push(`size=${pagination.size}`)
|
||
if (params.length > 0) {
|
||
url += `?${params.join('&')}`
|
||
}
|
||
const response = await fetch(url, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取文件列表失败')
|
||
return response.json()
|
||
},
|
||
|
||
async getFileStats(options = {}) {
|
||
const {
|
||
kbId = undefined,
|
||
includeDescendants = true,
|
||
includeUnassigned = true,
|
||
} = options
|
||
const params = new URLSearchParams()
|
||
if (kbId === null) {
|
||
params.append('kb_id', 'null')
|
||
} else if (kbId !== undefined) {
|
||
params.append('kb_id', kbId)
|
||
}
|
||
if (!includeDescendants) {
|
||
params.append('include_descendants', 'false')
|
||
}
|
||
if (!includeUnassigned) {
|
||
params.append('include_unassigned', 'false')
|
||
}
|
||
const query = params.toString()
|
||
const response = await fetch(
|
||
`${API_BASE}/files/stats${query ? `?${query}` : ''}`,
|
||
{ headers: getHeaders() }
|
||
)
|
||
if (!response.ok) throw new Error('获取文件状态统计失败')
|
||
return response.json()
|
||
},
|
||
|
||
async reparseFailedFiles(options = {}) {
|
||
const {
|
||
kbId = undefined,
|
||
includeDescendants = true,
|
||
includeUnassigned = true,
|
||
unassignedOnly = false,
|
||
} = options
|
||
|
||
const payload = {
|
||
include_descendants: includeDescendants,
|
||
include_unassigned: includeUnassigned,
|
||
unassigned_only: unassignedOnly,
|
||
}
|
||
if (kbId !== undefined) {
|
||
payload.kb_id = kbId ?? null
|
||
}
|
||
|
||
const response = await fetch(`${API_BASE}/files/reparse-failed`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(payload),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '重新解析失败文件失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async getFile(id) {
|
||
const response = await fetch(`${API_BASE}/files/${id}`, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取文件失败')
|
||
return response.json()
|
||
},
|
||
|
||
async downloadFile(id) {
|
||
const response = await fetch(`${API_BASE}/files/${id}/download`, {
|
||
headers: getHeaders(false),
|
||
})
|
||
if (!response.ok) throw new Error('下载文件失败')
|
||
return response.blob()
|
||
},
|
||
|
||
async updateFile(id, data) {
|
||
const response = await fetch(`${API_BASE}/files/${id}`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify(data),
|
||
})
|
||
if (!response.ok) throw new Error('更新文件失败')
|
||
return response.json()
|
||
},
|
||
|
||
async moveFile(id, targetKbId) {
|
||
const response = await fetch(`${API_BASE}/files/${id}/move`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({
|
||
target_kb_id: targetKbId ?? null,
|
||
}),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '移动文件失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async getFileSlices(fileId) {
|
||
const response = await fetch(`${API_BASE}/files/${fileId}/slices`, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取切片失败')
|
||
return response.json()
|
||
},
|
||
|
||
async updateSlices(fileId, slices) {
|
||
const response = await fetch(`${API_BASE}/files/${fileId}/slices`, {
|
||
method: 'PUT',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({ slices }),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '更新切片失败'))
|
||
return response.json()
|
||
},
|
||
|
||
// 压缩文件
|
||
async getArchiveEntries(id) {
|
||
const response = await fetch(`${API_BASE}/files/${id}/archive-entries`, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取压缩文件列表失败')
|
||
return response.json()
|
||
},
|
||
|
||
async extractArchive(id, password = null) {
|
||
const response = await fetch(`${API_BASE}/files/${id}/archive-extract`, {
|
||
method: 'POST',
|
||
headers: getHeaders(),
|
||
body: JSON.stringify({ password }),
|
||
})
|
||
if (!response.ok) throw new Error(await readErrorMessage(response, '解压失败'))
|
||
return response.json()
|
||
},
|
||
|
||
async downloadArchiveEntry(id, path) {
|
||
const encodedPath = encodeURIComponent(path)
|
||
const response = await fetch(`${API_BASE}/files/${id}/archive-download?path=${encodedPath}`, {
|
||
headers: getHeaders(false),
|
||
})
|
||
if (!response.ok) throw new Error('下载文件失败')
|
||
return response.blob()
|
||
},
|
||
|
||
// 知识图谱
|
||
async searchEntities(query = null, entityType = null, kbId = null, limit = 100, fileId = null) {
|
||
let url = `${API_BASE}/graph/entities?limit=${limit}`
|
||
if (query) {
|
||
url += `&q=${encodeURIComponent(query)}`
|
||
}
|
||
if (entityType) {
|
||
url += `&entity_type=${encodeURIComponent(entityType)}`
|
||
}
|
||
if (kbId) {
|
||
url += `&kb_id=${kbId}`
|
||
}
|
||
if (fileId) {
|
||
url += `&file_id=${fileId}`
|
||
}
|
||
const response = await fetch(url, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('搜索实体失败')
|
||
return response.json()
|
||
},
|
||
|
||
async getEntity(id) {
|
||
const response = await fetch(`${API_BASE}/graph/entities/${id}`, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取实体详情失败')
|
||
return response.json()
|
||
},
|
||
|
||
async getGraphStats(kbId = null, fileId = null) {
|
||
let url = `${API_BASE}/graph/stats`
|
||
const params = []
|
||
if (kbId) {
|
||
params.push(`kb_id=${kbId}`)
|
||
}
|
||
if (fileId) {
|
||
params.push(`file_id=${fileId}`)
|
||
}
|
||
if (params.length > 0) {
|
||
url += '?' + params.join('&')
|
||
}
|
||
const response = await fetch(url, {
|
||
headers: getHeaders(),
|
||
})
|
||
if (!response.ok) throw new Error('获取图谱统计失败')
|
||
return response.json()
|
||
},
|
||
}
|