diff --git a/ai-service-admin/src/api/llm.ts b/ai-service-admin/src/api/llm.ts new file mode 100644 index 0000000..73b04f5 --- /dev/null +++ b/ai-service-admin/src/api/llm.ts @@ -0,0 +1,50 @@ +import request from '@/utils/request' +import type { + LLMProviderInfo, + LLMConfig, + LLMConfigUpdate, + LLMTestResult, + LLMTestRequest, + LLMProvidersResponse, + LLMConfigUpdateResponse +} from '@/types/llm' + +export function getLLMProviders(): Promise { + return request({ + url: '/llm/providers', + method: 'get' + }) +} + +export function getLLMConfig(): Promise { + return request({ + url: '/llm/config', + method: 'get' + }) +} + +export function saveLLMConfig(data: LLMConfigUpdate): Promise { + return request({ + url: '/llm/config', + method: 'put', + data + }) +} + +export function testLLM(data: LLMTestRequest): Promise { + return request({ + url: '/llm/test', + method: 'post', + data + }) +} + +export type { + LLMProviderInfo, + LLMConfig, + LLMConfigUpdate, + LLMTestResult, + LLMTestRequest, + LLMProvidersResponse, + LLMConfigUpdateResponse +} diff --git a/ai-service-admin/src/api/rag.ts b/ai-service-admin/src/api/rag.ts index 1e0147b..cd6c495 100644 --- a/ai-service-admin/src/api/rag.ts +++ b/ai-service-admin/src/api/rag.ts @@ -1,9 +1,131 @@ import request from '@/utils/request' -export function runRagExperiment(data: { query: string, kbIds?: string[], params?: any }) { +export interface AIResponse { + content: string + prompt_tokens?: number + completion_tokens?: number + total_tokens?: number + latency_ms?: number + model?: string +} + +export interface RetrievalResult { + content: string + score: number + source: string + metadata?: Record +} + +export interface RagExperimentRequest { + query: string + kb_ids?: string[] + top_k?: number + score_threshold?: number + llm_provider?: string + generate_response?: boolean +} + +export interface RagExperimentResult { + query: string + retrieval_results?: RetrievalResult[] + final_prompt?: string + ai_response?: AIResponse + total_latency_ms?: number +} + +export function runRagExperiment(data: RagExperimentRequest): Promise { return request({ url: '/admin/rag/experiments/run', method: 'post', data }) } + +export function runRagExperimentStream( + data: RagExperimentRequest, + onMessage: (event: MessageEvent) => void, + onError?: (error: Event) => void, + onComplete?: () => void +): EventSource { + const baseUrl = import.meta.env.VITE_APP_BASE_API || '/api' + const url = `${baseUrl}/admin/rag/experiments/stream` + + const eventSource = new EventSource(url, { + withCredentials: true + }) + + eventSource.onmessage = onMessage + eventSource.onerror = (error) => { + eventSource.close() + onError?.(error) + } + + return eventSource +} + +export function createSSEConnection( + url: string, + body: RagExperimentRequest, + onMessage: (data: string) => void, + onError?: (error: Error) => void, + onComplete?: () => void +): () => void { + const baseUrl = import.meta.env.VITE_APP_BASE_API || '/api' + const fullUrl = `${baseUrl}${url}` + + const controller = new AbortController() + + fetch(fullUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'text/event-stream', + }, + body: JSON.stringify(body), + signal: controller.signal + }) + .then(async (response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + const reader = response.body?.getReader() + if (!reader) { + throw new Error('No response body') + } + + const decoder = new TextDecoder() + let buffer = '' + + while (true) { + const { done, value } = await reader.read() + + if (done) { + onComplete?.() + break + } + + buffer += decoder.decode(value, { stream: true }) + const lines = buffer.split('\n') + buffer = lines.pop() || '' + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = line.slice(6) + if (data === '[DONE]') { + onComplete?.() + return + } + onMessage(data) + } + } + } + }) + .catch((error) => { + if (error.name !== 'AbortError') { + onError?.(error) + } + }) + + return () => controller.abort() +} diff --git a/ai-service-admin/src/types/llm.ts b/ai-service-admin/src/types/llm.ts new file mode 100644 index 0000000..50889df --- /dev/null +++ b/ai-service-admin/src/types/llm.ts @@ -0,0 +1,42 @@ +export interface LLMProviderInfo { + name: string + display_name: string + description?: string + config_schema: Record +} + +export interface LLMConfig { + provider: string + config: Record + updated_at?: string +} + +export interface LLMConfigUpdate { + provider: string + config?: Record +} + +export interface LLMTestResult { + success: boolean + response?: string + latency_ms?: number + prompt_tokens?: number + completion_tokens?: number + total_tokens?: number + message?: string + error?: string +} + +export interface LLMTestRequest { + test_prompt?: string + config?: LLMConfigUpdate +} + +export interface LLMProvidersResponse { + providers: LLMProviderInfo[] +} + +export interface LLMConfigUpdateResponse { + success: boolean + message: string +} diff --git a/docs/progress/ai-service-admin-progress.md b/docs/progress/ai-service-admin-progress.md index 43b074d..5b220d0 100644 --- a/docs/progress/ai-service-admin-progress.md +++ b/docs/progress/ai-service-admin-progress.md @@ -50,7 +50,7 @@ version: "0.3.0" - [x] (P5-01~P5-06) 后端管理接口实现 #### Phase 6: 嵌入模型管理(待处理) -- [ ] (P5-01) API 服务层与类型定义 [AC-ASA-08, AC-ASA-09] +- [x] (P5-01) API 服务层与类型定义 [AC-ASA-08, AC-ASA-09] - [ ] (P5-02) 提供者选择组件 [AC-ASA-09] - [ ] (P5-03) 动态配置表单 [AC-ASA-09, AC-ASA-10] - [ ] (P5-04) 测试连接组件 [AC-ASA-11, AC-ASA-12] @@ -60,7 +60,7 @@ version: "0.3.0" - [ ] (P5-08) 组件整合与测试 [AC-ASA-08~AC-ASA-13] #### Phase 7: LLM 配置与 RAG 调试输出(当前) -- [ ] (P6-01) LLM API 服务层与类型定义:创建 src/api/llm.ts 和 src/types/llm.ts [AC-ASA-14, AC-ASA-15] +- [x] (P6-01) LLM API 服务层与类型定义:创建 src/api/llm.ts 和 src/types/llm.ts [AC-ASA-14, AC-ASA-15] - [ ] (P6-02) LLM 提供者选择组件:创建 LLMProviderSelect.vue [AC-ASA-15] - [ ] (P6-03) LLM 动态配置表单:创建 LLMConfigForm.vue [AC-ASA-15, AC-ASA-16] - [ ] (P6-04) LLM 测试连接组件:创建 LLMTestPanel.vue [AC-ASA-17, AC-ASA-18] diff --git a/spec/ai-service-admin/tasks.md b/spec/ai-service-admin/tasks.md index d274e43..f7791cb 100644 --- a/spec/ai-service-admin/tasks.md +++ b/spec/ai-service-admin/tasks.md @@ -123,7 +123,7 @@ principles: > 页面导向:嵌入模型配置页面,支持提供者切换、参数配置、连接测试。 -- [ ] (P5-01) API 服务层与类型定义:创建 src/api/embedding.ts 和 src/types/embedding.ts +- [x] (P5-01) API 服务层与类型定义:创建 src/api/embedding.ts 和 src/types/embedding.ts - AC: [AC-ASA-08, AC-ASA-09] - [ ] (P5-02) 提供者选择组件:实现 `EmbeddingProviderSelect` 下拉组件,对接 `/admin/embedding/providers` @@ -153,7 +153,7 @@ principles: | 任务 | 描述 | 状态 | |------|------|------| -| P5-01 | API 服务层与类型定义 | ⏳ 待处理 | +| P5-01 | API 服务层与类型定义 | ✅ 已完成 | | P5-02 | 提供者选择组件 | ⏳ 待处理 | | P5-03 | 动态配置表单 | ⏳ 待处理 | | P5-04 | 测试连接组件 | ⏳ 待处理 | @@ -170,7 +170,7 @@ principles: ### 6.1 LLM 模型配置 -- [ ] (P6-01) LLM API 服务层与类型定义:创建 src/api/llm.ts 和 src/types/llm.ts +- [x] (P6-01) LLM API 服务层与类型定义:创建 src/api/llm.ts 和 src/types/llm.ts - AC: [AC-ASA-14, AC-ASA-15] - [ ] (P6-02) LLM 提供者选择组件:实现 `LLMProviderSelect` 下拉组件 @@ -208,7 +208,7 @@ principles: | 任务 | 描述 | 状态 | |------|------|------| -| P6-01 | LLM API 服务层与类型定义 | ⏳ 待处理 | +| P6-01 | LLM API 服务层与类型定义 | ✅ 已完成 | | P6-02 | LLM 提供者选择组件 | ⏳ 待处理 | | P6-03 | LLM 动态配置表单 | ⏳ 待处理 | | P6-04 | LLM 测试连接组件 | ⏳ 待处理 |