diff --git a/src/api/ai/base/aiApp/index.ts b/src/api/ai/base/aiApp/index.ts new file mode 100644 index 0000000..42e0fa9 --- /dev/null +++ b/src/api/ai/base/aiApp/index.ts @@ -0,0 +1,178 @@ +import request from '@/utils/request' + +// AI聊天消息接口 +export interface ChatMessage { + role: 'user' | 'assistant' | 'system' + content: string + timestamp?: number +} + +// AI聊天请求参数 +export interface ChatRequest { + messages: ChatMessage[] + model: string + temperature?: number + top_p?: number + max_tokens?: number + stream?: boolean + api_key?: string + api_endpoint?: string +} + +// AI聊天响应 +export interface ChatResponse { + id: string + object: string + created: number + model: string + choices: Array<{ + index: number + message: ChatMessage + finish_reason: string + }> + usage?: { + prompt_tokens: number + completion_tokens: number + total_tokens: number + } +} + +// 流式响应数据 +export interface StreamChunk { + id: string + object: string + created: number + model: string + choices: Array<{ + index: number + delta: { + content?: string + role?: string + } + finish_reason: string | null + }> +} + +// 发送聊天消息(非流式) +export function sendChatMessage(data: ChatRequest) { + return request({ + url: '/ai/chat', + method: 'post', + data + }) +} + +// 发送聊天消息(流式) +export function sendChatMessageStream(data: ChatRequest): ReadableStream { + return new ReadableStream({ + start(controller) { + const eventSource = new EventSource(`/ai/chat/stream?${new URLSearchParams({ + messages: JSON.stringify(data.messages), + model: data.model, + temperature: data.temperature?.toString() || '0.7', + top_p: data.top_p?.toString() || '1', + max_tokens: data.max_tokens?.toString() || '2048', + api_key: data.api_key || '', + api_endpoint: data.api_endpoint || '' + })}`) + + eventSource.onmessage = (event) => { + try { + if (event.data === '[DONE]') { + controller.close() + eventSource.close() + return + } + + const chunk: StreamChunk = JSON.parse(event.data) + controller.enqueue(chunk) + } catch (error) { + console.error('解析流式数据失败:', error) + } + } + + eventSource.onerror = (error) => { + console.error('流式请求错误:', error) + controller.error(error) + eventSource.close() + } + } + }) +} + +// 获取聊天历史 +export function getChatHistory(params?: { + page?: number + size?: number + chatId?: string +}) { + return request<{ + total: number + list: Array<{ + id: string + title: string + messages: ChatMessage[] + createdAt: string + updatedAt: string + }> + }>({ + url: '/ai/chat/history', + method: 'get', + params + }) +} + +// 保存聊天记录 +export function saveChat(data: { + title: string + messages: ChatMessage[] +}) { + return request<{ id: string }>({ + url: '/ai/chat/save', + method: 'post', + data + }) +} + +// 删除聊天记录 +export function deleteChat(chatId: string) { + return request({ + url: `/ai/chat/${chatId}`, + method: 'delete' + }) +} + +// 更新聊天标题 +export function updateChatTitle(chatId: string, title: string) { + return request({ + url: `/ai/chat/${chatId}/title`, + method: 'put', + data: { title } + }) +} + +// AI问答设置接口 +export interface AiChatSettings { + welcomeIcon: string + welcomeTitle: string + welcomeMessage: string + presetQuestions: string[] + historyCount: number +} + +// 获取AI问答设置 +export function getAiChatSettings() { + return request({ + url: '/ai/chat/settings', + method: 'get' + }) +} + +// 保存AI问答设置 +export function saveAiChatSettings(data: AiChatSettings) { + return request({ + url: '/ai/chat/settings', + method: 'post', + data + }) +} diff --git a/src/api/ai/skill/aiKnowledgeBase/index.ts b/src/api/ai/skill/aiKnowledgeBase/index.ts new file mode 100644 index 0000000..1bfcc0b --- /dev/null +++ b/src/api/ai/skill/aiKnowledgeBase/index.ts @@ -0,0 +1,143 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { AiKnowledgeBaseVO, AiKnowledgeBaseForm, AiKnowledgeBaseQuery } from '@/api/ai/skill/aiKnowledgeBase/types'; + +/** + * 查询AI知识库列表 + * @param query + * @returns {*} + */ + +export const listAiKnowledgeBase = (query?: AiKnowledgeBaseQuery): AxiosPromise => { + return request({ + url: '/ai/aiKnowledgeBase/list', + method: 'get', + params: query + }); +}; + +/** + * 查询AI知识库详细 + * @param knowledgeBaseId + */ +export const getAiKnowledgeBase = (knowledgeBaseId: string | number): AxiosPromise => { + return request({ + url: '/ai/aiKnowledgeBase/' + knowledgeBaseId, + method: 'get' + }); +}; + +/** + * 新增AI知识库 + * @param data + */ +export const addAiKnowledgeBase = (data: AiKnowledgeBaseForm) => { + return request({ + url: '/ai/aiKnowledgeBase', + method: 'post', + data: data + }); +}; + +/** + * 修改AI知识库 + * @param data + */ +export const updateAiKnowledgeBase = (data: AiKnowledgeBaseForm) => { + return request({ + url: '/ai/aiKnowledgeBase', + method: 'put', + data: data + }); +}; + +/** + * 删除AI知识库 + * @param knowledgeBaseId + */ +export const delAiKnowledgeBase = (knowledgeBaseId: string | number | Array) => { + return request({ + url: '/ai/aiKnowledgeBase/' + knowledgeBaseId, + method: 'delete' + }); +}; + + +/** + * 下拉框查询AI知识库列表 + * @param query + * @returns {*} + */ +export function getAiKnowledgeBaseList (query) { + return request({ + url: '/ai/aiKnowledgeBase/getAiKnowledgeBaseList', + method: 'get', + params: query + }); +}; + + + + +/** + * 知识库文件上传切分后向量化 + * @param data 知识库切片内容 + */ +export const vectorizeKnowledgeContent = (data: FormData) => { + return request({ + url: '/ai/aiKnowledgeBase/vectorizeKnowledgeContent', + method: 'post', + data: data + }); +}; + +/** + * 获取AI知识库内容列表 + * @param query + * @returns {*} + */ +export function listAiKnowledgeContent (query) { + return request({ + url: '/ai/aiKnowledgeBase/listAiKnowledgeContent', + method: 'get', + params: query + }); +}; + + +/** + * 删除AI知识库内容,连同向量库内容一同删除 + * @param knowledgeBaseId + * @param knowledgeContentId + */ +export const delAiKnowledgeContent = (knowledgeBaseId: string | number ,knowledgeContentId: string | number | Array) => { + return request({ + url: '/ai/aiKnowledgeBase/deleteKnowledgeContent/' + knowledgeBaseId + '/' + knowledgeContentId, + method: 'post' + }); +}; + + +/** + * 删除知识库内容文件 + * @param knowledgeContentId + */ +export const removeContentFile = (knowledgeContentId: string | number) => { + return request({ + url: '/ai/aiKnowledgeBase/removeContentFile/' + knowledgeContentId, + method: 'post' + }); +}; + +/** + * 获取AI知识库内容分段列表 + * @param query + * @returns {*} + */ +export function getKnowledgeContentFragmentList (query) { + return request({ + url: '/ai/aiKnowledgeBase/getKnowledgeContentFragmentList', + method: 'get', + params: query + }); +}; diff --git a/src/api/ai/skill/aiKnowledgeBase/types.ts b/src/api/ai/skill/aiKnowledgeBase/types.ts new file mode 100644 index 0000000..9210314 --- /dev/null +++ b/src/api/ai/skill/aiKnowledgeBase/types.ts @@ -0,0 +1,232 @@ +export interface AiKnowledgeBaseVO { + /** + * 主键 + */ + knowledgeBaseId: string | number; + + /** + * 知识库名称 + */ + knowledgeBaseName: string; + + /** + * 知识库图标 + */ + knowledgeBaseIcon: string; + + + /** + * AI模型ID,关联ai_model + */ + modelId: string | number; + + /** + * 知识库类型ID,关联ai_knowledge_base_type + */ + knowledgeBaseTypeId: string | number; + + /** + * 分隔符 + */ + knowledgeBaseSeparator: string; + + /** + * 知识库中检索的条数 + */ + retrieveLimit: number; + + /** + * 文本块大小 + */ + textBlockSize: number; + + /** + * 重叠字符数 + */ + overlapCharacter: number; + + /** + * 提问分隔符 + */ + questionSeparator: string; + + /** + * 知识库描述 + */ + knowledgeBaseDesc: string; + + /** + * 状态(1启用,0禁用) + */ + knowledgeBaseStatus: string; + + /** + * 向量库(milvus) + */ + vector: string; + + /** + * 是否公开(1是,0否) + */ + openFlag: string; + + /** + * AI模型名称 + */ + modelName: string; + + /** + * AI知识库类型名称 + */ + knowledgeBaseTypeName: string; + +} + +export interface AiKnowledgeBaseForm extends BaseEntity { + /** + * 主键 + */ + knowledgeBaseId?: string | number; + + /** + * 知识库名称 + */ + knowledgeBaseName?: string; + + /** + * 知识库图标 + */ + knowledgeBaseIcon?: string; + + /** + * AI模型ID,关联ai_model + */ + modelId?: string | number; + + /** + * 知识库类型ID,关联ai_knowledge_base_type + */ + knowledgeBaseTypeId?: string | number; + + /** + * 分隔符 + */ + knowledgeBaseSeparator?: string; + + /** + * 知识库中检索的条数 + */ + retrieveLimit?: number; + + /** + * 文本块大小 + */ + textBlockSize?: number; + + /** + * 重叠字符数 + */ + overlapCharacter?: number; + + /** + * 提问分隔符 + */ + questionSeparator?: string; + + /** + * 知识库描述 + */ + knowledgeBaseDesc?: string; + + /** + * 状态(1启用,0禁用) + */ + knowledgeBaseStatus?: string; + + /** + * 向量库(milvus) + */ + vector?: string; + + /** + * 是否公开(1是,0否) + */ + openFlag?: string; + +} + +export interface AiKnowledgeBaseQuery extends PageQuery { + + /** + * 主键 + */ + knowledgeBaseId?: string | number; + + /** + * 知识库名称 + */ + knowledgeBaseName?: string; + + /** + * AI模型ID,关联ai_model + */ + modelId?: string | number; + + /** + * 知识库类型ID,关联ai_knowledge_base_type + */ + knowledgeBaseTypeId?: string | number; + + /** + * 分隔符 + */ + knowledgeBaseSeparator?: string; + + /** + * 知识库中检索的条数 + */ + retrieveLimit?: number; + + /** + * 文本块大小 + */ + textBlockSize?: number; + + /** + * 重叠字符数 + */ + overlapCharacter?: number; + + /** + * 提问分隔符 + */ + questionSeparator?: string; + + /** + * 知识库描述 + */ + knowledgeBaseDesc?: string; + + /** + * 状态(1启用,0禁用) + */ + knowledgeBaseStatus?: string; + + /** + * 向量库(milvus) + */ + vector?: string; + + /** + * 是否公开(1是,0否) + */ + openFlag?: string; + + /** + * 日期范围参数 + */ + params?: any; +} + + + diff --git a/src/api/ai/skill/aiKnowledgeContent/index.ts b/src/api/ai/skill/aiKnowledgeContent/index.ts new file mode 100644 index 0000000..ecf6e0c --- /dev/null +++ b/src/api/ai/skill/aiKnowledgeContent/index.ts @@ -0,0 +1,77 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { AiKnowledgeContentVO, AiKnowledgeContentForm, AiKnowledgeContentQuery } from '@/api/ai/aiKnowledgeContent/types'; + +/** + * 查询AI知识库内容列表 + * @param query + * @returns {*} + */ + +export const listAiKnowledgeContent = (query?: AiKnowledgeContentQuery): AxiosPromise => { + return request({ + url: '/ai/aiKnowledgeContent/list', + method: 'get', + params: query + }); +}; + +/** + * 查询AI知识库内容详细 + * @param knowledgeContentId + */ +export const getAiKnowledgeContent = (knowledgeContentId: string | number): AxiosPromise => { + return request({ + url: '/ai/aiKnowledgeContent/' + knowledgeContentId, + method: 'get' + }); +}; + +/** + * 新增AI知识库内容 + * @param data + */ +export const addAiKnowledgeContent = (data: AiKnowledgeContentForm) => { + return request({ + url: '/ai/aiKnowledgeContent', + method: 'post', + data: data + }); +}; + +/** + * 修改AI知识库内容 + * @param data + */ +export const updateAiKnowledgeContent = (data: AiKnowledgeContentForm) => { + return request({ + url: '/ai/aiKnowledgeContent', + method: 'put', + data: data + }); +}; + +/** + * 删除AI知识库内容 + * @param knowledgeContentId + */ +export const delAiKnowledgeContent = (knowledgeContentId: string | number | Array) => { + return request({ + url: '/ai/aiKnowledgeContent/' + knowledgeContentId, + method: 'delete' + }); +}; + + +/** + * 下拉框查询AI知识库内容列表 + * @param query + * @returns {*} + */ +export function getAiKnowledgeContentList (query) { + return request({ + url: '/ai/aiKnowledgeContent/getAiKnowledgeContentList', + method: 'get', + params: query + }); +}; diff --git a/src/api/ai/skill/aiKnowledgeContent/types.ts b/src/api/ai/skill/aiKnowledgeContent/types.ts new file mode 100644 index 0000000..15827b3 --- /dev/null +++ b/src/api/ai/skill/aiKnowledgeContent/types.ts @@ -0,0 +1,206 @@ +export interface AiKnowledgeContentVO { + /** + * 主键 + */ + knowledgeContentId: string | number; + + /** + * 知识库ID,关联ai_knowledge_base + */ + knowledgeBaseId: string | number; + + /** + * 标题 + */ + contentTitle: string; + + /** + * 内容方式(1上传文件,2手动录入) + */ + contentWay: string; + + /** + * 内容(暂时不存,只存内容片段) + */ + description: string; + + /** + * 文件名称 + */ + fileName: string; + + /** + * 文件路径 + */ + filePath: string; + + /** + * 文件类型 + */ + fileType: string; + + /** + * 文件大小(byte) + */ + fileSize: number; + + /** + * 内容状态(1已完成,2失败(解析或向量失败等),3待解析) + */ + contentStatus: string; + + /** + * 重叠字符数 + */ + overlapCharacter: number; + + /** + * 总共片段 + */ + totalChunk: number; + + /** + * 创建时间 + */ + createTime: string; + + /** + * 更新时间 + */ + updateTime: string; + +} + +export interface AiKnowledgeContentForm extends BaseEntity { + /** + * 主键 + */ + knowledgeContentId?: string | number; + + /** + * 知识库ID,关联ai_knowledge_base + */ + knowledgeBaseId?: string | number; + + /** + * 标题 + */ + contentTitle?: string; + + /** + * 内容方式(1上传文件,2手动录入) + */ + contentWay?: string; + + /** + * 内容(暂时不存,只存内容片段) + */ + description?: string; + + /** + * 文件名称 + */ + fileName?: string; + + /** + * 文件路径 + */ + filePath?: string; + + /** + * 文件类型 + */ + fileType?: string; + + /** + * 文件大小(byte) + */ + fileSize?: number; + + /** + * 内容状态(1已完成,2失败(解析或向量失败等),3待解析) + */ + contentStatus?: string; + + /** + * 重叠字符数 + */ + overlapCharacter?: number; + + /** + * 总共片段 + */ + totalChunk?: number; + +} + +export interface AiKnowledgeContentQuery extends PageQuery { + + /** + * 主键 + */ + knowledgeContentId?: string | number; + + /** + * 知识库ID,关联ai_knowledge_base + */ + knowledgeBaseId?: string | number; + + /** + * 标题 + */ + contentTitle?: string; + + /** + * 内容方式(1上传文件,2手动录入) + */ + contentWay?: string; + + /** + * 内容(暂时不存,只存内容片段) + */ + description?: string; + + /** + * 文件名称 + */ + fileName?: string; + + /** + * 文件路径 + */ + filePath?: string; + + /** + * 文件类型 + */ + fileType?: string; + + /** + * 文件大小(byte) + */ + fileSize?: number; + + /** + * 内容状态(1已完成,2失败(解析或向量失败等),3待解析) + */ + contentStatus?: string; + + /** + * 重叠字符数 + */ + overlapCharacter?: number; + + /** + * 总共片段 + */ + totalChunk?: number; + + /** + * 日期范围参数 + */ + params?: any; +} + + + diff --git a/src/assets/PDF.png b/src/assets/PDF.png new file mode 100644 index 0000000..ee947e3 Binary files /dev/null and b/src/assets/PDF.png differ diff --git a/src/assets/document.png b/src/assets/document.png new file mode 100644 index 0000000..53cf297 Binary files /dev/null and b/src/assets/document.png differ diff --git a/src/assets/docx.png b/src/assets/docx.png new file mode 100644 index 0000000..23af3a1 Binary files /dev/null and b/src/assets/docx.png differ diff --git a/src/assets/icons/svg/knowledge-base.svg b/src/assets/icons/svg/knowledge-base.svg new file mode 100644 index 0000000..7e62cf0 --- /dev/null +++ b/src/assets/icons/svg/knowledge-base.svg @@ -0,0 +1 @@ + diff --git a/src/assets/knowledge-base.png b/src/assets/knowledge-base.png new file mode 100644 index 0000000..bb06082 Binary files /dev/null and b/src/assets/knowledge-base.png differ diff --git a/src/router/index.ts b/src/router/index.ts index b36eacd..22df47a 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -518,35 +518,35 @@ export const dynamicRoutes: RouteRecordRaw[] = [ ] }, - // { - // path: '/ai/skill/knowledgeBaseDocs', - // component: Layout, - // hidden: true, - // permissions: ['ai:aiKnowledgeBaseDocs:list'], - // children: [ - // { - // path: 'index', - // component: () => import('@/views/ai/skill/aiKnowledge/knowledgeBaseDocs.vue'), - // name: 'KnowledgeBaseDocs', - // meta: { title: '知识库详情', activeMenu: '/ai/skill/aiKnowledge', noCache: true } - // } - // ] - // }, - // - // { - // path: '/ai/skill/knowledgeBaseQA', - // component: Layout, - // hidden: true, - // permissions: ['ai:aiKnowledgeBaseDocs:qa'], - // children: [ - // { - // path: 'index', - // component: () => import('@/views/ai/skill/aiChat/index.vue'), - // name: 'KnowledgeBaseQA', - // meta: { title: '知识库问答', activeMenu: '/ai/skill/aiKnowledge', noCache: true } - // } - // ] - // }, + { + path: '/ai/skill/knowledgeContent', + component: Layout, + hidden: true, + permissions: ['ai:aiKnowledgeContent:list'], + children: [ + { + path: 'index/:knowledgeBaseId/:modelId/:knowledgeBaseName', + component: () => import('@/views/ai/skill/aiKnowledge/knowledgeContent.vue'), + name: 'KnowledgeContent', + meta: { title: '知识库内容', activeMenu: '/ai/skill/aiKnowledge', noCache: true } + } + ] + }, + + { + path: '/ai/skill/knowledgeBaseQA', + component: Layout, + hidden: true, + permissions: ['ai:aiKnowledgeBaseDocs:qa'], + children: [ + { + path: 'index/:knowledgeBaseId', + component: () => import('@/views/ai/skill/aiChat/index.vue'), + name: 'KnowledgeBaseQA', + meta: { title: '知识库问答', activeMenu: '/ai/skill/aiChat', noCache: true } + } + ] + }, // { // path: '/knowledge-base-preview', diff --git a/src/views/ai/base/aiApp/aiAppConfig.vue b/src/views/ai/base/aiApp/aiAppConfig.vue new file mode 100644 index 0000000..f661095 --- /dev/null +++ b/src/views/ai/base/aiApp/aiAppConfig.vue @@ -0,0 +1,522 @@ + + + + + diff --git a/src/views/ai/record/tokenUsage/index.vue b/src/views/ai/record/tokenUsage/index.vue new file mode 100644 index 0000000..ec78dba --- /dev/null +++ b/src/views/ai/record/tokenUsage/index.vue @@ -0,0 +1,621 @@ + + + + + diff --git a/src/views/ai/record/usageRecord/index.vue b/src/views/ai/record/usageRecord/index.vue new file mode 100644 index 0000000..973388a --- /dev/null +++ b/src/views/ai/record/usageRecord/index.vue @@ -0,0 +1,383 @@ + + + + + diff --git a/src/views/ai/skill/aiChat/index.vue b/src/views/ai/skill/aiChat/index.vue index c3fa5fa..3baf040 100644 --- a/src/views/ai/skill/aiChat/index.vue +++ b/src/views/ai/skill/aiChat/index.vue @@ -1,10 +1,10 @@