Commit 626473e4 authored by yuguo's avatar yuguo

fix

parent ef9bb5d9
package internalagent package internalagent
import ( import (
"encoding/json"
"fmt"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"internet-hospital/internal/model"
"internet-hospital/pkg/agent"
"internet-hospital/pkg/database"
) )
// Handler Agent HTTP处理器 // Handler Agent HTTP处理器
...@@ -21,6 +27,7 @@ func (h *Handler) RegisterRoutes(r gin.IRouter) { ...@@ -21,6 +27,7 @@ func (h *Handler) RegisterRoutes(r gin.IRouter) {
g.GET("/sessions", h.ListSessions) g.GET("/sessions", h.ListSessions)
g.DELETE("/session/:session_id", h.DeleteSession) g.DELETE("/session/:session_id", h.DeleteSession)
g.GET("/list", h.ListAgents) g.GET("/list", h.ListAgents)
g.GET("/tools", h.ListTools)
} }
func (h *Handler) Chat(c *gin.Context) { func (h *Handler) Chat(c *gin.Context) {
...@@ -54,10 +61,96 @@ func (h *Handler) ListAgents(c *gin.Context) { ...@@ -54,10 +61,96 @@ func (h *Handler) ListAgents(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": h.svc.ListAgents()}) c.JSON(http.StatusOK, gin.H{"data": h.svc.ListAgents()})
} }
// ListSessions 获取用户的 Agent 会话列表
func (h *Handler) ListSessions(c *gin.Context) { func (h *Handler) ListSessions(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": []interface{}{}}) userID, _ := c.Get("user_id")
agentID := c.Query("agent_id")
var sessions []model.AgentSession
query := database.GetDB().Where("user_id = ?", userID).Order("updated_at DESC")
if agentID != "" {
query = query.Where("agent_id = ?", agentID)
}
query.Find(&sessions)
type SessionSummary struct {
model.AgentSession
LastMessage string `json:"last_message"`
}
result := make([]SessionSummary, 0, len(sessions))
for _, s := range sessions {
var history []map[string]string
json.Unmarshal([]byte(s.History), &history)
lastMsg := ""
if len(history) > 0 {
lastMsg = history[len(history)-1]["content"]
if len(lastMsg) > 60 {
lastMsg = lastMsg[:60] + "..."
}
}
result = append(result, SessionSummary{s, lastMsg})
}
c.JSON(http.StatusOK, gin.H{"data": result})
} }
// DeleteSession 删除会话
func (h *Handler) DeleteSession(c *gin.Context) { func (h *Handler) DeleteSession(c *gin.Context) {
userID, _ := c.Get("user_id")
sessionID := c.Param("session_id")
database.GetDB().Where("session_id = ? AND user_id = ?", sessionID, userID).
Delete(&model.AgentSession{})
c.JSON(http.StatusOK, gin.H{"message": "ok"}) c.JSON(http.StatusOK, gin.H{"message": "ok"})
} }
// ListTools 获取所有已注册工具列表
func (h *Handler) ListTools(c *gin.Context) {
registry := agent.GetRegistry()
allTools := registry.All()
type ToolInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Category string `json:"category"`
Parameters map[string]interface{} `json:"parameters"`
IsEnabled bool `json:"is_enabled"`
CreatedAt string `json:"created_at"`
}
categoryMap := map[string]string{
"query_symptom_knowledge": "knowledge",
"recommend_department": "recommendation",
"query_medical_record": "medical",
"search_medical_knowledge": "knowledge",
"query_drug": "pharmacy",
"check_drug_interaction": "safety",
"check_contraindication": "safety",
"calculate_dosage": "pharmacy",
"generate_follow_up_plan": "follow_up",
"send_notification": "notification",
}
result := make([]ToolInfo, 0, len(allTools))
i := 1
for name, tool := range allTools {
params := make(map[string]interface{})
for _, p := range tool.Parameters() {
params[p.Name] = p.Type
}
category := categoryMap[name]
if category == "" {
category = "other"
}
result = append(result, ToolInfo{
ID: fmt.Sprintf("%d", i),
Name: name,
Description: tool.Description(),
Category: category,
Parameters: params,
IsEnabled: true,
CreatedAt: "2026-01-01",
})
i++
}
c.JSON(http.StatusOK, gin.H{"data": result})
}
package admin
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"internet-hospital/internal/model"
"internet-hospital/pkg/database"
)
// GetAgentExecutionLogs 获取 Agent 执行日志(分页)
func (h *Handler) GetAgentExecutionLogs(c *gin.Context) {
agentID := c.Query("agent_id")
start := c.Query("start")
end := c.Query("end")
page := 1
pageSize := 20
if p := c.Query("page"); p != "" {
if v, err := strconv.Atoi(p); err == nil && v > 0 {
page = v
}
}
if ps := c.Query("page_size"); ps != "" {
if v, err := strconv.Atoi(ps); err == nil && v > 0 && v <= 100 {
pageSize = v
}
}
query := database.GetDB().Model(&model.AgentExecutionLog{})
if agentID != "" {
query = query.Where("agent_id = ?", agentID)
}
if start != "" {
query = query.Where("created_at >= ?", start)
}
if end != "" {
query = query.Where("created_at <= ?", end)
}
var total int64
query.Count(&total)
var logs []model.AgentExecutionLog
query.Order("created_at DESC").
Offset((page - 1) * pageSize).
Limit(pageSize).
Find(&logs)
c.JSON(http.StatusOK, gin.H{"data": gin.H{
"list": logs,
"total": total,
"page": page,
"page_size": pageSize,
}})
}
// GetAgentStats 获取 Agent 执行统计
func (h *Handler) GetAgentStats(c *gin.Context) {
start := c.Query("start")
end := c.Query("end")
type AgentStat struct {
AgentID string `json:"agent_id"`
Count int64 `json:"count"`
AvgIterations float64 `json:"avg_iterations"`
AvgTokens float64 `json:"avg_tokens"`
SuccessRate float64 `json:"success_rate"`
}
db := database.GetDB()
query := db.Model(&model.AgentExecutionLog{})
if start != "" {
query = query.Where("created_at >= ?", start)
}
if end != "" {
query = query.Where("created_at <= ?", end)
}
var stats []AgentStat
query.Select("agent_id, COUNT(*) as count, AVG(iterations) as avg_iterations, AVG(total_tokens) as avg_tokens, SUM(CASE WHEN success THEN 1 ELSE 0 END)::float / COUNT(*) as success_rate").
Group("agent_id").
Scan(&stats)
c.JSON(http.StatusOK, gin.H{"data": stats})
}
...@@ -102,7 +102,13 @@ func (h *Handler) RegisterRoutes(r *gin.RouterGroup) { ...@@ -102,7 +102,13 @@ func (h *Handler) RegisterRoutes(r *gin.RouterGroup) {
// 工作流管理 // 工作流管理
adm.GET("/workflows", h.ListWorkflows) adm.GET("/workflows", h.ListWorkflows)
adm.POST("/workflows", h.CreateWorkflow) adm.POST("/workflows", h.CreateWorkflow)
adm.PUT("/workflows/:id", h.UpdateWorkflow)
adm.PUT("/workflows/:id/publish", h.PublishWorkflow) adm.PUT("/workflows/:id/publish", h.PublishWorkflow)
adm.GET("/workflow/executions", h.ListWorkflowExecutions)
// Agent 执行监控
adm.GET("/agent/logs", h.GetAgentExecutionLogs)
adm.GET("/agent/stats", h.GetAgentStats)
} }
} }
......
...@@ -2,6 +2,7 @@ package admin ...@@ -2,6 +2,7 @@ package admin
import ( import (
"net/http" "net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
...@@ -42,6 +43,33 @@ func (h *Handler) CreateWorkflow(c *gin.Context) { ...@@ -42,6 +43,33 @@ func (h *Handler) CreateWorkflow(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": wf}) c.JSON(http.StatusOK, gin.H{"data": wf})
} }
// UpdateWorkflow 更新工作流(名称/描述/定义)
func (h *Handler) UpdateWorkflow(c *gin.Context) {
var req struct {
Name string `json:"name"`
Description string `json:"description"`
Definition string `json:"definition"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
updates := map[string]interface{}{}
if req.Definition != "" {
updates["definition"] = req.Definition
}
if req.Name != "" {
updates["name"] = req.Name
}
if req.Description != "" {
updates["description"] = req.Description
}
database.GetDB().Model(&model.WorkflowDefinition{}).
Where("id = ?", c.Param("id")).
Updates(updates)
c.JSON(http.StatusOK, gin.H{"message": "ok"})
}
// PublishWorkflow 发布工作流 // PublishWorkflow 发布工作流
func (h *Handler) PublishWorkflow(c *gin.Context) { func (h *Handler) PublishWorkflow(c *gin.Context) {
database.GetDB().Model(&model.WorkflowDefinition{}). database.GetDB().Model(&model.WorkflowDefinition{}).
...@@ -49,3 +77,40 @@ func (h *Handler) PublishWorkflow(c *gin.Context) { ...@@ -49,3 +77,40 @@ func (h *Handler) PublishWorkflow(c *gin.Context) {
Update("status", "active") Update("status", "active")
c.JSON(http.StatusOK, gin.H{"message": "ok"}) c.JSON(http.StatusOK, gin.H{"message": "ok"})
} }
// ListWorkflowExecutions 工作流执行记录列表
func (h *Handler) ListWorkflowExecutions(c *gin.Context) {
workflowID := c.Query("workflow_id")
page := 1
pageSize := 20
if p := c.Query("page"); p != "" {
if v, err := strconv.Atoi(p); err == nil && v > 0 {
page = v
}
}
if ps := c.Query("page_size"); ps != "" {
if v, err := strconv.Atoi(ps); err == nil && v > 0 && v <= 100 {
pageSize = v
}
}
var total int64
query := database.GetDB().Model(&model.WorkflowExecution{})
if workflowID != "" {
query = query.Where("workflow_id = ?", workflowID)
}
query.Count(&total)
var executions []model.WorkflowExecution
query.Order("created_at DESC").
Offset((page - 1) * pageSize).
Limit(pageSize).
Find(&executions)
c.JSON(http.StatusOK, gin.H{"data": gin.H{
"list": executions,
"total": total,
"page": page,
"page_size": pageSize,
}})
}
...@@ -10,8 +10,8 @@ import ( ...@@ -10,8 +10,8 @@ import (
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"gorm.io/gorm" "gorm.io/gorm"
internalagent "internet-hospital/internal/agent"
"internet-hospital/internal/model" "internet-hospital/internal/model"
"internet-hospital/pkg/ai"
"internet-hospital/pkg/database" "internet-hospital/pkg/database"
) )
...@@ -196,107 +196,53 @@ func (s *Service) CancelConsult(ctx context.Context, consultID string) error { ...@@ -196,107 +196,53 @@ func (s *Service) CancelConsult(ctx context.Context, consultID string) error {
return s.db.Model(&model.Consultation{}).Where("id = ?", consultID).Update("status", "cancelled").Error return s.db.Model(&model.Consultation{}).Where("id = ?", consultID).Update("status", "cancelled").Error
} }
// AIAssist AI辅助分析(鉴别诊断/用药建议) // AIAssist AI辅助分析(鉴别诊断/用药建议)—— 通过 Agent 调用
func (s *Service) AIAssist(ctx context.Context, consultID string, scene string) (map[string]interface{}, error) { func (s *Service) AIAssist(ctx context.Context, consultID string, scene string) (map[string]interface{}, error) {
// 获取问诊信息
var consult model.Consultation var consult model.Consultation
if err := s.db.Where("id = ?", consultID).First(&consult).Error; err != nil { if err := s.db.Where("id = ?", consultID).First(&consult).Error; err != nil {
return nil, fmt.Errorf("问诊不存在") return nil, fmt.Errorf("问诊不存在")
} }
// 获取对话消息 // 获取预问诊信息以提供上下文
var messages []model.ConsultMessage
s.db.Where("consult_id = ?", consultID).Order("created_at ASC").Find(&messages)
// 获取预问诊信息
var preConsult model.PreConsultation var preConsult model.PreConsultation
s.db.Where("consultation_id = ?", consultID).First(&preConsult) s.db.Where("consultation_id = ?", consultID).First(&preConsult)
// 构建对话上下文 agentCtx := map[string]interface{}{
var chatContext string "patient_id": consult.PatientID,
"consult_id": consultID,
"chief_complaint": consult.ChiefComplaint,
}
if preConsult.AIAnalysis != "" { if preConsult.AIAnalysis != "" {
chatContext += "【预问诊AI分析报告】\n" + preConsult.AIAnalysis + "\n\n" agentCtx["pre_consult_analysis"] = preConsult.AIAnalysis
}
if consult.ChiefComplaint != "" {
chatContext += "【主诉】" + consult.ChiefComplaint + "\n\n"
}
chatContext += "【问诊对话记录】\n"
for _, msg := range messages {
role := "患者"
if msg.SenderType == "doctor" {
role = "医生"
} else if msg.SenderType == "system" {
role = "系统"
}
chatContext += role + ":" + msg.Content + "\n"
} }
// 获取场景对应的Prompt模板 var agentID, message string
prompt := ai.GetActivePromptByScene(scene) switch scene {
if prompt == "" { case "consult_diagnosis":
switch scene { agentID = "diagnosis_agent"
case "consult_diagnosis": message = "请对患者当前情况进行诊断分析,提供鉴别诊断建议"
prompt = `你是一位资深的临床医学专家,请根据以下问诊信息进行鉴别诊断分析。 case "consult_medication":
agentID = "prescription_agent"
请以如下markdown格式输出: message = "请根据患者情况给出用药建议,包括推荐药物、用法用量和注意事项"
default:
## 初步诊断 return nil, fmt.Errorf("不支持的AI场景: %s", scene)
(最可能的诊断,附简要理由)
## 鉴别诊断
1. **疾病名称1** - 可能性:高/中/低,依据:...
2. **疾病名称2** - 可能性:高/中/低,依据:...
3. **疾病名称3** - 可能性:高/中/低,依据:...
## 建议检查
1. 检查项目1 - 目的:...
2. 检查项目2 - 目的:...
## 注意事项
(需要特别关注的情况)`
case "consult_medication":
prompt = `你是一位资深的临床药学专家,请根据以下问诊信息给出用药建议。
请以如下markdown格式输出:
## 推荐用药方案
1. **药品名称1** - 规格:...,用法用量:...,疗程:...
2. **药品名称2** - 规格:...,用法用量:...,疗程:...
## 用药注意事项
1. 注意事项1
2. 注意事项2
## 禁忌与过敏提示
(相关药物禁忌和需要询问的过敏史)
## 随访建议
(用药后的观察要点和复诊建议)`
default:
return nil, fmt.Errorf("不支持的AI场景: %s", scene)
}
} }
// 调用AI(使用统一接口,自动记录日志) agentSvc := internalagent.GetService()
aiMessages := []ai.ChatMessage{ output, err := agentSvc.Chat(ctx, agentID, consult.DoctorID, "", message, agentCtx)
{Role: "system", Content: prompt}, if err != nil {
{Role: "user", Content: chatContext}, return nil, fmt.Errorf("AI分析失败: %w", err)
} }
if output == nil {
result := ai.Call(ctx, ai.CallParams{ return nil, fmt.Errorf("agent不存在: %s", agentID)
Scene: scene,
UserID: consult.DoctorID,
Messages: aiMessages,
RequestSummary: chatContext,
})
if result.Error != nil {
return nil, fmt.Errorf("AI分析失败: %w", result.Error)
} }
return map[string]interface{}{ return map[string]interface{}{
"scene": scene, "scene": scene,
"content": result.Content, "response": output.Response,
"tool_calls": output.ToolCalls,
"iterations": output.Iterations,
"total_tokens": output.TotalTokens,
}, nil }, nil
} }
......
import { get, post, put, del } from './request';
// ==================== 类型定义 ====================
export interface ToolCall {
tool_name: string;
call_id: string;
arguments: string;
result: { success: boolean; data?: unknown; error?: string };
success: boolean;
}
export interface AgentOutput {
response: string;
session_id?: string;
tool_calls?: ToolCall[];
iterations?: number;
total_tokens?: number;
finish_reason?: string;
}
export interface AgentSession {
id: number;
session_id: string;
agent_id: string;
user_id: string;
history: string;
context: string;
status: string;
last_message?: string;
created_at: string;
updated_at: string;
}
export interface AgentExecutionLog {
id: number;
session_id: string;
agent_id: string;
user_id: string;
input: string;
output: string;
tool_calls: string;
iterations: number;
total_tokens: number;
duration_ms: number;
finish_reason: string;
success: boolean;
error_message: string;
created_at: string;
}
export interface WorkflowExecution {
id: number;
execution_id: string;
workflow_id: string;
trigger_type: string;
trigger_by: string;
input: string;
output: string;
status: string;
started_at: string;
completed_at: string;
}
export interface WorkflowCreateParams {
workflow_id: string;
name: string;
description?: string;
category?: string;
definition?: string;
}
export interface KnowledgeCollectionParams {
name: string;
description?: string;
category?: string;
}
export interface KnowledgeDocumentParams {
collection_id: string;
title: string;
content: string;
}
// ==================== Agent API ====================
export const agentApi = {
chat: (agentId: string, params: {
session_id?: string;
message: string;
context?: Record<string, unknown>;
}) => post<AgentOutput>(`/agent/${agentId}/chat`, params),
listAgents: () =>
get<{ id: string; name: string; description: string }[]>('/agent/list'),
listTools: () =>
get<{ id: string; name: string; description: string; category: string; parameters: Record<string, unknown>; is_enabled: boolean; created_at: string }[]>('/agent/tools'),
getSessions: (agentId?: string) =>
get<AgentSession[]>('/agent/sessions', { params: agentId ? { agent_id: agentId } : {} }),
deleteSession: (sessionId: string) =>
del<null>(`/agent/session/${sessionId}`),
getExecutionLogs: (params: {
agent_id?: string;
start?: string;
end?: string;
page?: number;
page_size?: number;
}) => get<{ list: AgentExecutionLog[]; total: number }>('/admin/agent/logs', { params }),
getStats: (params?: { start?: string; end?: string }) =>
get<{ agent_id: string; count: number; avg_iterations: number; avg_tokens: number; success_rate: number }[]>(
'/admin/agent/stats', { params }
),
};
// ==================== Workflow API ====================
export const workflowApi = {
list: () => get<unknown[]>('/admin/workflows'),
create: (data: WorkflowCreateParams) => post<unknown>('/admin/workflows', data),
update: (id: number, data: Partial<WorkflowCreateParams>) =>
put<unknown>(`/admin/workflows/${id}`, data),
publish: (id: number) => post<null>(`/admin/workflows/${id}/publish`),
execute: (workflowId: string, input?: Record<string, unknown>) =>
post<{ execution_id: string }>(`/workflow/${workflowId}/execute`, input || {}),
getExecution: (executionId: string) =>
get<WorkflowExecution>(`/workflow/execution/${executionId}`),
listExecutions: (params?: { workflow_id?: string; page?: number; page_size?: number }) =>
get<{ list: WorkflowExecution[]; total: number }>('/admin/workflow/executions', { params }),
getTasks: () => get<unknown[]>('/workflow/tasks'),
completeTask: (taskId: string, result: Record<string, unknown>) =>
post<null>(`/workflow/task/${taskId}/complete`, result),
};
// ==================== Knowledge API ====================
export const knowledgeApi = {
listCollections: () => get<unknown[]>('/knowledge/collections'),
createCollection: (data: KnowledgeCollectionParams) =>
post<unknown>('/knowledge/collections', data),
listDocuments: (collectionId?: string) =>
get<unknown[]>('/knowledge/documents', { params: collectionId ? { collection_id: collectionId } : {} }),
createDocument: (data: KnowledgeDocumentParams) =>
post<unknown>('/knowledge/documents', data),
search: (query: string, topK = 5, collectionId?: string) =>
post<unknown[]>('/knowledge/search', { query, top_k: topK, collection_id: collectionId }),
};
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
import { useState } from 'react'; import { useState } from 'react';
import { Card, Table, Tag, Button, Modal, Input, message, Space, Collapse, Timeline, Typography } from 'antd'; import { Card, Table, Tag, Button, Modal, Input, message, Space, Collapse, Timeline, Typography } from 'antd';
import { RobotOutlined, PlayCircleOutlined, ToolOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'; import { RobotOutlined, PlayCircleOutlined, ToolOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
import { useUserStore } from '@/store/userStore'; import { agentApi } from '@/api/agent';
import type { ToolCall } from '@/api/agent';
const { Text } = Typography; const { Text } = Typography;
const API = '';
const BUILTIN_AGENTS = [ const BUILTIN_AGENTS = [
{ id: 'pre_consult_agent', name: '预问诊智能助手', description: '通过多轮对话收集患者症状,生成预问诊报告', category: 'pre_consult', tools: ['query_symptom_knowledge', 'recommend_department'], max_iterations: 5 }, { id: 'pre_consult_agent', name: '预问诊智能助手', description: '通过多轮对话收集患者症状,生成预问诊报告', category: 'pre_consult', tools: ['query_symptom_knowledge', 'recommend_department'], max_iterations: 5 },
...@@ -29,14 +29,6 @@ const categoryLabel: Record<string, string> = { ...@@ -29,14 +29,6 @@ const categoryLabel: Record<string, string> = {
follow_up: '随访管理', follow_up: '随访管理',
}; };
interface ToolCall {
tool_name: string;
call_id: string;
arguments: string;
result: { success: boolean; data?: unknown; error?: string };
success: boolean;
}
interface AgentResponse { interface AgentResponse {
response: string; response: string;
tool_calls?: ToolCall[]; tool_calls?: ToolCall[];
...@@ -46,7 +38,6 @@ interface AgentResponse { ...@@ -46,7 +38,6 @@ interface AgentResponse {
} }
export default function AgentsPage() { export default function AgentsPage() {
const { accessToken: token } = useUserStore();
const [testModal, setTestModal] = useState<{ open: boolean; agentId: string; agentName: string }>({ open: false, agentId: '', agentName: '' }); const [testModal, setTestModal] = useState<{ open: boolean; agentId: string; agentName: string }>({ open: false, agentId: '', agentName: '' });
const [testMessages, setTestMessages] = useState<{ role: string; content: string; toolCalls?: ToolCall[]; meta?: { iterations?: number; tokens?: number } }[]>([]); const [testMessages, setTestMessages] = useState<{ role: string; content: string; toolCalls?: ToolCall[]; meta?: { iterations?: number; tokens?: number } }[]>([]);
const [inputMsg, setInputMsg] = useState(''); const [inputMsg, setInputMsg] = useState('');
...@@ -66,13 +57,8 @@ export default function AgentsPage() { ...@@ -66,13 +57,8 @@ export default function AgentsPage() {
setTestMessages(prev => [...prev, { role: 'user', content: userMsg }]); setTestMessages(prev => [...prev, { role: 'user', content: userMsg }]);
setLoading(true); setLoading(true);
try { try {
const res = await fetch(`${API}/api/v1/agent/${testModal.agentId}/chat`, { const res = await agentApi.chat(testModal.agentId, { session_id: sessionId, message: userMsg });
method: 'POST', const agentData = res.data as AgentResponse;
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ session_id: sessionId, message: userMsg }),
});
const data = await res.json();
const agentData = data.data as AgentResponse;
const reply = agentData?.response || '无响应'; const reply = agentData?.response || '无响应';
setTestMessages(prev => [...prev, { setTestMessages(prev => [...prev, {
role: 'assistant', role: 'assistant',
......
...@@ -3,10 +3,9 @@ ...@@ -3,10 +3,9 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Card, Table, Tag, Button, Modal, Form, Input, Select, message, Space, Tabs, Typography } from 'antd'; import { Card, Table, Tag, Button, Modal, Form, Input, Select, message, Space, Tabs, Typography } from 'antd';
import { BookOutlined, PlusOutlined, SearchOutlined, FileTextOutlined, ReloadOutlined } from '@ant-design/icons'; import { BookOutlined, PlusOutlined, SearchOutlined, FileTextOutlined, ReloadOutlined } from '@ant-design/icons';
import { useUserStore } from '@/store/userStore'; import { knowledgeApi } from '@/api/agent';
const { Text } = Typography; const { Text } = Typography;
const API = '';
const categoryLabel: Record<string, string> = { const categoryLabel: Record<string, string> = {
clinical_guideline: '临床指南', drug: '药品说明', disease: '疾病百科', paper: '医学论文', clinical_guideline: '临床指南', drug: '药品说明', disease: '疾病百科', paper: '医学论文',
...@@ -16,7 +15,6 @@ const categoryColor: Record<string, string> = { ...@@ -16,7 +15,6 @@ const categoryColor: Record<string, string> = {
}; };
export default function KnowledgePage() { export default function KnowledgePage() {
const { accessToken: token } = useUserStore();
const [collections, setCollections] = useState<any[]>([]); const [collections, setCollections] = useState<any[]>([]);
const [documents, setDocuments] = useState<any[]>([]); const [documents, setDocuments] = useState<any[]>([]);
const [colLoading, setColLoading] = useState(false); const [colLoading, setColLoading] = useState(false);
...@@ -30,23 +28,19 @@ export default function KnowledgePage() { ...@@ -30,23 +28,19 @@ export default function KnowledgePage() {
const [docForm] = Form.useForm(); const [docForm] = Form.useForm();
const [searchForm] = Form.useForm(); const [searchForm] = Form.useForm();
const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };
const fetchCollections = async () => { const fetchCollections = async () => {
setColLoading(true); setColLoading(true);
try { try {
const res = await fetch(`${API}/api/v1/knowledge/collections`, { headers }); const res = await knowledgeApi.listCollections();
const data = await res.json(); setCollections((res.data as any[]) || []);
setCollections(data.data || []);
} catch {} finally { setColLoading(false); } } catch {} finally { setColLoading(false); }
}; };
const fetchDocuments = async () => { const fetchDocuments = async () => {
setDocLoading(true); setDocLoading(true);
try { try {
const res = await fetch(`${API}/api/v1/knowledge/documents`, { headers }); const res = await knowledgeApi.listDocuments();
const data = await res.json(); setDocuments((res.data as any[]) || []);
setDocuments(data.data || []);
} catch {} finally { setDocLoading(false); } } catch {} finally { setDocLoading(false); }
}; };
...@@ -54,7 +48,7 @@ export default function KnowledgePage() { ...@@ -54,7 +48,7 @@ export default function KnowledgePage() {
const createCollection = async (values: any) => { const createCollection = async (values: any) => {
try { try {
await fetch(`${API}/api/v1/knowledge/collections`, { method: 'POST', headers, body: JSON.stringify(values) }); await knowledgeApi.createCollection(values);
message.success('集合创建成功'); message.success('集合创建成功');
setColModal(false); setColModal(false);
colForm.resetFields(); colForm.resetFields();
...@@ -64,7 +58,7 @@ export default function KnowledgePage() { ...@@ -64,7 +58,7 @@ export default function KnowledgePage() {
const createDocument = async (values: any) => { const createDocument = async (values: any) => {
try { try {
await fetch(`${API}/api/v1/knowledge/documents`, { method: 'POST', headers, body: JSON.stringify(values) }); await knowledgeApi.createDocument(values);
message.success('文档已添加'); message.success('文档已添加');
setDocModal(false); setDocModal(false);
docForm.resetFields(); docForm.resetFields();
...@@ -75,9 +69,8 @@ export default function KnowledgePage() { ...@@ -75,9 +69,8 @@ export default function KnowledgePage() {
const doSearch = async (values: any) => { const doSearch = async (values: any) => {
setSearching(true); setSearching(true);
try { try {
const res = await fetch(`${API}/api/v1/knowledge/search`, { method: 'POST', headers, body: JSON.stringify(values) }); const res = await knowledgeApi.search(values.query, values.top_k || 5);
const data = await res.json(); setSearchResults((res.data as any[]) || []);
setSearchResults(data.data || []);
} catch { message.error('检索失败'); } finally { setSearching(false); } } catch { message.error('检索失败'); } finally { setSearching(false); }
}; };
......
...@@ -6,10 +6,9 @@ import { ...@@ -6,10 +6,9 @@ import {
ToolOutlined, SearchOutlined, InfoCircleOutlined, ApiOutlined, ToolOutlined, SearchOutlined, InfoCircleOutlined, ApiOutlined,
CheckCircleOutlined, CodeOutlined, ReloadOutlined, CheckCircleOutlined, CodeOutlined, ReloadOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useUserStore } from '@/store/userStore'; import { agentApi } from '@/api/agent';
const { Text } = Typography; const { Text } = Typography;
const API = '';
interface AgentTool { interface AgentTool {
id: string; id: string;
...@@ -52,7 +51,6 @@ const statCards = [ ...@@ -52,7 +51,6 @@ const statCards = [
]; ];
export default function ToolsPage() { export default function ToolsPage() {
const { accessToken: token } = useUserStore();
const [tools, setTools] = useState<AgentTool[]>(BUILTIN_TOOLS); const [tools, setTools] = useState<AgentTool[]>(BUILTIN_TOOLS);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
...@@ -63,11 +61,8 @@ export default function ToolsPage() { ...@@ -63,11 +61,8 @@ export default function ToolsPage() {
const fetchTools = async () => { const fetchTools = async () => {
setLoading(true); setLoading(true);
try { try {
const res = await fetch(`${API}/api/v1/agent/tools`, { headers: { Authorization: `Bearer ${token}` } }); const res = await agentApi.listTools();
if (res.ok) { if (res.data?.length > 0) setTools(res.data as AgentTool[]);
const data = await res.json();
if (data.data?.length > 0) setTools(data.data);
}
} catch { /* 使用内置工具列表 */ } finally { } catch { /* 使用内置工具列表 */ } finally {
setLoading(false); setLoading(false);
} }
......
...@@ -3,12 +3,10 @@ ...@@ -3,12 +3,10 @@
import { useEffect, useState, useCallback } from 'react'; import { useEffect, useState, useCallback } from 'react';
import { Card, Table, Tag, Button, Modal, Form, Input, Select, message, Space, Badge } from 'antd'; import { Card, Table, Tag, Button, Modal, Form, Input, Select, message, Space, Badge } from 'antd';
import { DeploymentUnitOutlined, PlayCircleOutlined, PlusOutlined, EditOutlined } from '@ant-design/icons'; import { DeploymentUnitOutlined, PlayCircleOutlined, PlusOutlined, EditOutlined } from '@ant-design/icons';
import { useUserStore } from '@/store/userStore'; import { workflowApi } from '@/api/agent';
import VisualWorkflowEditor from '@/components/workflow/VisualWorkflowEditor'; import VisualWorkflowEditor from '@/components/workflow/VisualWorkflowEditor';
import type { Node, Edge } from '@xyflow/react'; import type { Node, Edge } from '@xyflow/react';
const API = '';
interface Workflow { interface Workflow {
id: number; id: number;
workflow_id: string; workflow_id: string;
...@@ -31,7 +29,6 @@ const categoryLabel: Record<string, string> = { ...@@ -31,7 +29,6 @@ const categoryLabel: Record<string, string> = {
}; };
export default function WorkflowsPage() { export default function WorkflowsPage() {
const { accessToken: token } = useUserStore();
const [workflows, setWorkflows] = useState<Workflow[]>([]); const [workflows, setWorkflows] = useState<Workflow[]>([]);
const [createModal, setCreateModal] = useState(false); const [createModal, setCreateModal] = useState(false);
const [editorModal, setEditorModal] = useState(false); const [editorModal, setEditorModal] = useState(false);
...@@ -43,9 +40,8 @@ export default function WorkflowsPage() { ...@@ -43,9 +40,8 @@ export default function WorkflowsPage() {
const fetchWorkflows = async () => { const fetchWorkflows = async () => {
setTableLoading(true); setTableLoading(true);
try { try {
const res = await fetch(`${API}/api/v1/admin/workflows`, { headers: { Authorization: `Bearer ${token}` } }); const res = await workflowApi.list();
const data = await res.json(); setWorkflows((res.data as Workflow[]) || []);
setWorkflows(data.data || []);
} catch {} finally { } catch {} finally {
setTableLoading(false); setTableLoading(false);
} }
...@@ -64,11 +60,7 @@ export default function WorkflowsPage() { ...@@ -64,11 +60,7 @@ export default function WorkflowsPage() {
}, },
edges: [{ id: 'e1', source_node: 'start', target_node: 'end' }], edges: [{ id: 'e1', source_node: 'start', target_node: 'end' }],
}; };
await fetch(`${API}/api/v1/admin/workflows`, { await workflowApi.create({ ...values, definition: JSON.stringify(definition) });
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ ...values, definition: JSON.stringify(definition) }),
});
message.success('创建成功'); message.success('创建成功');
setCreateModal(false); setCreateModal(false);
form.resetFields(); form.resetFields();
...@@ -83,38 +75,24 @@ export default function WorkflowsPage() { ...@@ -83,38 +75,24 @@ export default function WorkflowsPage() {
const handleSaveWorkflow = useCallback(async (nodes: Node[], edges: Edge[]) => { const handleSaveWorkflow = useCallback(async (nodes: Node[], edges: Edge[]) => {
if (!editingWorkflow) return; if (!editingWorkflow) return;
try { try {
await fetch(`${API}/api/v1/admin/workflows/${editingWorkflow.id}`, { await workflowApi.update(editingWorkflow.id, { definition: JSON.stringify({ nodes, edges }) });
method: 'PUT',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ definition: JSON.stringify({ nodes, edges }) }),
});
message.success('工作流已保存'); message.success('工作流已保存');
fetchWorkflows(); fetchWorkflows();
} catch { message.error('保存失败'); } } catch { message.error('保存失败'); }
}, [editingWorkflow, token]); }, [editingWorkflow]);
const handleExecuteFromEditor = useCallback(async (nodes: Node[], edges: Edge[]) => { const handleExecuteFromEditor = useCallback(async (nodes: Node[], edges: Edge[]) => {
if (!editingWorkflow) return; if (!editingWorkflow) return;
try { try {
const res = await fetch(`${API}/api/v1/workflow/${editingWorkflow.workflow_id}/execute`, { const result = await workflowApi.execute(editingWorkflow.workflow_id, { workflow_data: { nodes, edges } });
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({ workflow_data: { nodes, edges } }),
});
const result = await res.json();
message.success(`执行已启动: ${result.data?.execution_id}`); message.success(`执行已启动: ${result.data?.execution_id}`);
} catch { message.error('执行失败'); } } catch { message.error('执行失败'); }
}, [editingWorkflow, token]); }, [editingWorkflow]);
const handleExecute = async (workflowId: string) => { const handleExecute = async (workflowId: string) => {
try { try {
const res = await fetch(`${API}/api/v1/workflow/${workflowId}/execute`, { const result = await workflowApi.execute(workflowId);
method: 'POST', message.success(`执行已启动: ${result.data?.execution_id}`);
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
body: JSON.stringify({}),
});
const data = await res.json();
message.success(`执行已启动: ${data.data?.execution_id}`);
} catch { message.error('执行失败'); } } catch { message.error('执行失败'); }
}; };
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment