diff --git a/admin_llm_routes.go b/admin_llm_routes.go index db476bd..0bcee7f 100644 --- a/admin_llm_routes.go +++ b/admin_llm_routes.go @@ -1,6 +1,7 @@ package main import ( + "errors" "net/http" "strconv" "time" @@ -12,12 +13,24 @@ import ( func registerAdminLLMRoutes(r *gin.RouterGroup, store *store) { group := r.Group("/llm") { + // LLM Message Queue group.GET("/messages", handleListLLMMessages(store)) group.GET("/messages/:id", handleGetLLMMessage(store)) group.PUT("/messages/:id/status", handleUpdateLLMMessageStatus(store)) group.DELETE("/messages/:id", handleDeleteLLMMessage(store)) group.DELETE("/bots/:bot_id/messages", handleDeleteLLMMessagesByBot(store)) group.POST("/messages/cleanup", handleCleanupDeletedLLMMessages(store)) + + // LLM Providers + group.GET("/providers", handleListLLMProviders(store)) + group.GET("/providers/:name", handleGetLLMProvider(store)) + group.POST("/providers", handleCreateLLMProvider(store)) + group.PUT("/providers/:name", handleUpdateLLMProvider(store)) + group.DELETE("/providers/:name", handleDeleteLLMProvider(store)) + + // LLM Tool Router + group.GET("/tool-router", handleGetLLMToolRouter(store)) + group.PUT("/tool-router", handleUpdateLLMToolRouter(store)) } } @@ -184,3 +197,301 @@ func handleCleanupDeletedLLMMessages(store *store) gin.HandlerFunc { c.JSON(http.StatusOK, gin.H{"status": "ok", "deleted_count": count}) } } + +// ============================================ +// LLM Provider Handlers +// ============================================ + +func handleListLLMProviders(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + includeInactive := c.Query("include_inactive") == "true" + + rows, err := store.ListLLMProviders(includeInactive) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + items := make([]map[string]any, 0, len(rows)) + for _, row := range rows { + items = append(items, llmProviderDTO(row)) + } + + c.JSON(http.StatusOK, gin.H{"items": items}) + } +} + +func handleGetLLMProvider(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + name := c.Param("name") + if name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "provider name is required"}) + return + } + + record, err := store.GetLLMProvider(name) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{"error": "provider not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"item": llmProviderDTO(*record)}) + } +} + +func handleCreateLLMProvider(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + var req struct { + Name string `json:"name"` + Active bool `json:"active"` + APIKey string `json:"api_key"` + BaseURL string `json:"base_url"` + Model string `json:"model"` + Timeout int `json:"timeout"` + ContextWindowTokens int `json:"context_window_tokens"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) + return + } + + if req.Name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"}) + return + } + + record := &llmProviderRecord{ + Name: req.Name, + Active: req.Active, + APIKey: req.APIKey, + BaseURL: req.BaseURL, + Model: req.Model, + Timeout: req.Timeout, + ContextWindowTokens: req.ContextWindowTokens, + } + + if err := store.CreateLLMProvider(record); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "ok", "item": llmProviderDTO(*record)}) + } +} + +func handleUpdateLLMProvider(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + name := c.Param("name") + if name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "provider name is required"}) + return + } + + var req struct { + Active *bool `json:"active"` + APIKey *string `json:"api_key"` + BaseURL *string `json:"base_url"` + Model *string `json:"model"` + Timeout *int `json:"timeout"` + ContextWindowTokens *int `json:"context_window_tokens"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) + return + } + + updates := make(map[string]any) + if req.Active != nil { + updates["active"] = *req.Active + } + if req.APIKey != nil { + updates["api_key"] = *req.APIKey + } + if req.BaseURL != nil { + updates["base_url"] = *req.BaseURL + } + if req.Model != nil { + updates["model"] = *req.Model + } + if req.Timeout != nil { + updates["timeout"] = *req.Timeout + } + if req.ContextWindowTokens != nil { + updates["context_window_tokens"] = *req.ContextWindowTokens + } + + if len(updates) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"}) + return + } + + if err := store.UpdateLLMProvider(name, updates); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{"error": "provider not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + record, err := store.GetLLMProvider(name) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "ok", "item": llmProviderDTO(*record)}) + } +} + +func handleDeleteLLMProvider(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + name := c.Param("name") + if name == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "provider name is required"}) + return + } + + if err := store.DeleteLLMProvider(name); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"status": "ok"}) + } +} + +func llmProviderDTO(row llmProviderRecord) map[string]any { + return map[string]any{ + "name": row.Name, + "active": row.Active, + "base_url": row.BaseURL, + "model": row.Model, + "timeout": row.Timeout, + "context_window_tokens": row.ContextWindowTokens, + "created_at": row.CreatedAt, + "updated_at": row.UpdatedAt, + } +} + +// ============================================ +// LLM Tool Router Handlers +// ============================================ + +func handleGetLLMToolRouter(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + record, err := store.GetLLMToolRouter() + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusNotFound, gin.H{"error": "tool router config not found"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"item": llmToolRouterDTO(*record)}) + } +} + +func handleUpdateLLMToolRouter(store *store) gin.HandlerFunc { + return func(c *gin.Context) { + record, err := store.GetLLMToolRouter() + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + var req struct { + Enabled *bool `json:"enabled"` + OpenAIName *string `json:"openai_name"` + Timeout *int `json:"timeout"` + MaxTokens *int `json:"max_tokens"` + SystemPrompt *string `json:"system_prompt"` + } + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"}) + return + } + + updates := make(map[string]any) + if req.Enabled != nil { + updates["enabled"] = *req.Enabled + } + if req.OpenAIName != nil { + updates["openai_name"] = *req.OpenAIName + } + if req.Timeout != nil { + updates["timeout"] = *req.Timeout + } + if req.MaxTokens != nil { + updates["max_tokens"] = *req.MaxTokens + } + if req.SystemPrompt != nil { + updates["system_prompt"] = *req.SystemPrompt + } + + if len(updates) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"}) + return + } + + if record == nil { + // 创建新配置 + newRecord := &llmToolRouterRecord{ + Enabled: req.Enabled != nil && *req.Enabled, + OpenAIName: "", + Timeout: 30, + MaxTokens: 512, + SystemPrompt: "", + } + if req.OpenAIName != nil { + newRecord.OpenAIName = *req.OpenAIName + } + if req.Timeout != nil { + newRecord.Timeout = *req.Timeout + } + if req.MaxTokens != nil { + newRecord.MaxTokens = *req.MaxTokens + } + if req.SystemPrompt != nil { + newRecord.SystemPrompt = *req.SystemPrompt + } + if err := store.CreateLLMToolRouter(newRecord); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + record = newRecord + } else { + // 更新现有配置 + if err := store.UpdateLLMToolRouter(record.ID, updates); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + record, err = store.GetLLMToolRouter() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + } + + c.JSON(http.StatusOK, gin.H{"status": "ok", "item": llmToolRouterDTO(*record)}) + } +} + +func llmToolRouterDTO(row llmToolRouterRecord) map[string]any { + return map[string]any{ + "id": row.ID, + "enabled": row.Enabled, + "openai_name": row.OpenAIName, + "timeout": row.Timeout, + "max_tokens": row.MaxTokens, + "system_prompt": row.SystemPrompt, + "created_at": row.CreatedAt, + "updated_at": row.UpdatedAt, + } +} diff --git a/db.go b/db.go index d4787e3..33d5c0b 100644 --- a/db.go +++ b/db.go @@ -460,6 +460,39 @@ func (textMessageRecord) TableName() string { return "text_message" } +// llmProviderRecord 保存 LLM API 配置,支持多个 AI 提供商 +type llmProviderRecord struct { + Name string `gorm:"column:name;primaryKey;size:64;not null"` // 配置名称,如 "default"、"openai"、"ark" 等 + Active bool `gorm:"column:active;not null;index"` // 是否启用此配置 + APIKey string `gorm:"column:api_key;type:text;not null"` // API 密钥 + BaseURL string `gorm:"column:base_url;type:text;not null"` // API 基础 URL + Model string `gorm:"column:model;not null"` // 模型名称 + Timeout int `gorm:"column:timeout;not null;default:120"` // 超时时间(秒) + ContextWindowTokens int `gorm:"column:context_window_tokens;not null;default:262144"` // 上下文窗口 token 数 + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` + UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;index"` +} + +func (llmProviderRecord) TableName() string { + return "llm_providers" +} + +// llmToolRouterRecord 保存工具路由的配置 +type llmToolRouterRecord struct { + ID uint64 `gorm:"column:id;primaryKey;autoIncrement"` + Enabled bool `gorm:"column:enabled;not null;index"` // 是否启用工具路由 + OpenAIName string `gorm:"column:openai_name;size:64;not null"` // 使用的 LLM 提供商名称(关联 llm_providers.name) + Timeout int `gorm:"column:timeout;not null;default:30"` // 工具调用超时时间(秒) + MaxTokens int `gorm:"column:max_tokens;not null;default:512"` // 工具调用最大 token 数 + SystemPrompt string `gorm:"column:system_prompt;type:text;not null"` // 系统提示词 + CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` + UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;index"` +} + +func (llmToolRouterRecord) TableName() string { + return "llm_tool_router" +} + type positionRecord struct { AppendPacketFields `gorm:"embedded"` MQTTClientRecordFields `gorm:"embedded"` @@ -591,6 +624,8 @@ func (s *store) migrate() error { {label: "bot_messages", model: &botMessageRecord{}}, {label: "bot_direct_messages", model: &botDirectMessageRecord{}}, {label: "llm_message_queue", model: &llmMessageQueueRecord{}}, + {label: "llm_providers", model: &llmProviderRecord{}}, + {label: "llm_tool_router", model: &llmToolRouterRecord{}}, {label: "nodeinfo", model: &nodeInfoRecord{}}, {label: "map_report", model: &mapReportRecord{}}, {label: "text_message", model: &textMessageRecord{}}, @@ -627,7 +662,17 @@ func (s *store) migrate() error { if err := migrateMapTileSourceHash(tx, migrator, s.driver); err != nil { return err } - return (&store{db: tx, driver: s.driver}).EnsureDefaultMapTileSource() + txStore := &store{db: tx, driver: s.driver} + if err := txStore.EnsureDefaultMapTileSource(); err != nil { + return err + } + if err := txStore.EnsureDefaultLLMProvider(); err != nil { + return err + } + if err := txStore.EnsureDefaultLLMToolRouter(); err != nil { + return err + } + return nil }) } diff --git a/llm_store.go b/llm_store.go index 96806ec..7c5ce00 100644 --- a/llm_store.go +++ b/llm_store.go @@ -9,6 +9,134 @@ import ( "gorm.io/gorm" ) +// ============================================ +// LLM Provider (llm_providers) - 多 AI API 配置 +// ============================================ + +// ListLLMProviders 列出所有 LLM Provider +func (s *store) ListLLMProviders(includeInactive bool) ([]llmProviderRecord, error) { + var rows []llmProviderRecord + query := s.db.Model(&llmProviderRecord{}) + if !includeInactive { + query = query.Where("active = ?", true) + } + if err := query.Order("created_at DESC").Find(&rows).Error; err != nil { + return nil, fmt.Errorf("list llm providers: %w", err) + } + return rows, nil +} + +// GetLLMProvider 获取单个 LLM Provider +func (s *store) GetLLMProvider(name string) (*llmProviderRecord, error) { + var record llmProviderRecord + if err := s.db.Where("name = ?", name).Take(&record).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return nil, fmt.Errorf("get llm provider %s: %w", name, err) + } + return &record, nil +} + +// CreateLLMProvider 创建 LLM Provider +func (s *store) CreateLLMProvider(record *llmProviderRecord) error { + if err := s.db.Create(record).Error; err != nil { + return fmt.Errorf("create llm provider %s: %w", record.Name, err) + } + return nil +} + +// UpdateLLMProvider 更新 LLM Provider +func (s *store) UpdateLLMProvider(name string, updates map[string]any) error { + if err := s.db.Model(&llmProviderRecord{}).Where("name = ?", name).Updates(updates).Error; err != nil { + return fmt.Errorf("update llm provider %s: %w", name, err) + } + return nil +} + +// DeleteLLMProvider 删除 LLM Provider +func (s *store) DeleteLLMProvider(name string) error { + if err := s.db.Where("name = ?", name).Delete(&llmProviderRecord{}).Error; err != nil { + return fmt.Errorf("delete llm provider %s: %w", name, err) + } + return nil +} + +// EnsureDefaultLLMProvider 确保存在默认 LLM Provider 配置 +func (s *store) EnsureDefaultLLMProvider() error { + _, err := s.GetLLMProvider("default") + if err == nil { + return nil // 已存在 + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + // 创建默认配置 + defaultConfig := &llmProviderRecord{ + Name: "default", + Active: true, + APIKey: "", + BaseURL: "https://ark.cn-beijing.volces.com/api/v3", + Model: "", + Timeout: 120, + ContextWindowTokens: 262144, + } + return s.CreateLLMProvider(defaultConfig) +} + +// ============================================ +// LLM Tool Router (llm_tool_router) - 工具路由配置 +// ============================================ + +// GetLLMToolRouter 获取当前激活的 Tool Router 配置 +func (s *store) GetLLMToolRouter() (*llmToolRouterRecord, error) { + var record llmToolRouterRecord + // 默认取第一条记录(ID 最小的),因为通常只需要一个配置 + if err := s.db.Order("id ASC").First(&record).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + return nil, fmt.Errorf("get llm tool router: %w", err) + } + return &record, nil +} + +// CreateLLMToolRouter 创建 Tool Router 配置 +func (s *store) CreateLLMToolRouter(record *llmToolRouterRecord) error { + if err := s.db.Create(record).Error; err != nil { + return fmt.Errorf("create llm tool router: %w", err) + } + return nil +} + +// UpdateLLMToolRouter 更新 Tool Router 配置 +func (s *store) UpdateLLMToolRouter(id uint64, updates map[string]any) error { + if err := s.db.Model(&llmToolRouterRecord{}).Where("id = ?", id).Updates(updates).Error; err != nil { + return fmt.Errorf("update llm tool router %d: %w", id, err) + } + return nil +} + +// EnsureDefaultLLMToolRouter 确保存在默认 Tool Router 配置 +func (s *store) EnsureDefaultLLMToolRouter() error { + _, err := s.GetLLMToolRouter() + if err == nil { + return nil // 已存在 + } + if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + // 创建默认配置 + defaultConfig := &llmToolRouterRecord{ + Enabled: true, + OpenAIName: "", + Timeout: 30, + MaxTokens: 512, + SystemPrompt: "你可以按需直接调用可用工具来回答用户问题。\n每个工具的 description 描述了它的适用场景和调用条件。\n工具结果优先于模型内置知识;工具失败时必须如实说明,不要编造结果。\n只调用确实必要的工具。", + } + return s.CreateLLMToolRouter(defaultConfig) +} + // LLMMessageQueueInput 是添加 LLM 队列消息的输入 type LLMMessageQueueInput struct { BotID uint64 // 0 表示频道消息 diff --git a/meshmap_frontend/src/App.vue b/meshmap_frontend/src/App.vue index bd89e24..7acc098 100644 --- a/meshmap_frontend/src/App.vue +++ b/meshmap_frontend/src/App.vue @@ -6,6 +6,7 @@ import AdminBot from './components/AdminBot.vue' import AdminBotDirect from './components/AdminBotDirect.vue' import AdminDashboard from './components/AdminDashboard.vue' import AdminLLM from './components/AdminLLM.vue' +import AdminLLMApi from './components/AdminLLMApi.vue' import AdminDiscardDetails from './components/AdminDiscardDetails.vue' import AdminHelpEdit from './components/AdminHelpEdit.vue' import AdminLogin from './components/AdminLogin.vue' @@ -31,6 +32,7 @@ const isMqttForwardAdminPage = adminPath === '/admin/mqtt_forward' || adminPath const isBotAdminPage = adminPath === '/admin/bot' || adminPath === '/admin/bot/' const isBotDirectAdminPage = adminPath === '/admin/bot/direct' || adminPath === '/admin/bot/direct/' const isLLMAdminPage = adminPath === '/admin/llm' || adminPath === '/admin/llm/' +const isLLMApiAdminPage = adminPath === '/admin/llm/api' || adminPath === '/admin/llm/api/' const isSignAdminPage = adminPath === '/admin/sign' || adminPath === '/admin/sign/' const detailMatch = currentPath.match(/^\/detailed\/(.+)$/) const detailedNodeId = detailMatch ? decodeURIComponent(detailMatch[1]) : '' @@ -553,6 +555,7 @@ onBeforeUnmount(() => { 机器人 机器人私聊 LLM消息队列 + LLM API配置 签到管理 地图图源 帮助编辑 @@ -602,6 +605,7 @@ onBeforeUnmount(() => { + diff --git a/meshmap_frontend/src/api.ts b/meshmap_frontend/src/api.ts index aa2dfde..e0a6730 100644 --- a/meshmap_frontend/src/api.ts +++ b/meshmap_frontend/src/api.ts @@ -49,6 +49,11 @@ import type { BotDirectConversationsResponse, LLMMessage, LLMMessageStatus, + LLMProvider, + LLMProviderPayload, + LLMProviderResponse, + LLMPlatformRouterPayload, + LLMPlatformRouterResponse, } from './types' async function requestJSON(path: string, init?: RequestInit): Promise { @@ -464,5 +469,40 @@ export function cleanupDeletedLLMMessages(days = 7): Promise<{ status: string; d return postJSON<{ status: string; deleted_count: number }>('/api/admin/llm/messages/cleanup', { days }) } +// LLM Provider API +export function getLLMProviders(includeInactive = false): Promise> { + const params = new URLSearchParams() + if (includeInactive) { + params.set('include_inactive', 'true') + } + const query = params.toString() ? `?${params.toString()}` : '' + return getJSON>(`/api/admin/llm/providers${query}`) +} + +export function getLLMProvider(name: string): Promise { + return getJSON(`/api/admin/llm/providers/${encodeURIComponent(name)}`) +} + +export function createLLMProvider(payload: LLMProviderPayload): Promise { + return postJSON('/api/admin/llm/providers', payload) +} + +export function updateLLMProvider(name: string, payload: Partial): Promise { + return putJSON(`/api/admin/llm/providers/${encodeURIComponent(name)}`, payload) +} + +export function deleteLLMProvider(name: string): Promise<{ status: string }> { + return deleteJSON<{ status: string }>(`/api/admin/llm/providers/${encodeURIComponent(name)}`) +} + +// LLM Tool Router API +export function getLLMToolRouter(): Promise { + return getJSON('/api/admin/llm/tool-router') +} + +export function updateLLMToolRouter(payload: Partial): Promise { + return putJSON('/api/admin/llm/tool-router', payload) +} + // 静默使用未导出类型,避免 TS6133(未使用的导入)。 -export type { LLMMessage, LLMMessageStatus } from './types' +export type { LLMMessage, LLMMessageStatus, LLMProvider, LLMPlatformRouter } from './types' diff --git a/meshmap_frontend/src/components/AdminLLMApi.vue b/meshmap_frontend/src/components/AdminLLMApi.vue new file mode 100644 index 0000000..264192a --- /dev/null +++ b/meshmap_frontend/src/components/AdminLLMApi.vue @@ -0,0 +1,827 @@ + + + + + LLM API 配置管理 + + {{ error }} + {{ success }} + + + + + + AI 提供商配置 + 管理多个 LLM API 提供商配置,支持不同模型和服务。 + + + 添加配置 + + + 加载中... + + + + + + + {{ provider.active ? '启用' : '停用' }} + + {{ provider.name }} + + + 编辑 + 删除 + + + + + API 地址 + {{ provider.base_url }} + + + 模型 + {{ provider.model || '-' }} + + + 超时 + {{ provider.timeout }} 秒 + + + 上下文窗口 + {{ provider.context_window_tokens.toLocaleString() }} tokens + + + API Key + {{ provider.api_key ? '••••••••••' : '-' }} + + + + + + 暂无配置,点击上方按钮添加第一个 AI 提供商配置。 + + + + + + + + + 工具路由配置 + 配置 LLM 工具调用的路由设置,用于实现函数调用功能。 + + 编辑配置 + + + + + + + 启用工具路由 + + + + + + 使用的 AI 配置 + + 请选择 + {{ p.name }} + + 选择用于工具调用的 AI 提供商配置 + + + + + + 超时时间(秒) + + + + 最大 Token 数 + + + + + + 系统提示词 + + 用于指导模型如何使用工具的系统提示词 + + + + 取消 + 保存 + + + + + + + {{ toolRouter.enabled ? '已启用' : '已停用' }} + + + + + 使用的 AI 配置 + {{ toolRouter.openai_name || '未设置' }} + + + 超时时间 + {{ toolRouter.timeout }} 秒 + + + 最大 Token 数 + {{ toolRouter.max_tokens }} + + + 系统提示词 + {{ toolRouter.system_prompt }} + + + + + + 暂无工具路由配置,点击上方按钮进行配置。 + + + + + + + + {{ isCreatingProvider ? '添加 AI 提供商配置' : '编辑 AI 提供商配置' }} + × + + + + 配置名称 * + + 唯一标识此配置的名称,创建后不可修改 + + + + + + 启用此配置 + + + + + API 地址 * + + + + + API Key * + + + + + 模型名称 + + + + + + 超时时间(秒) + + + + 上下文窗口(tokens) + + + + + + + + + + + diff --git a/meshmap_frontend/src/types.ts b/meshmap_frontend/src/types.ts index cbc0d4b..370b21e 100644 --- a/meshmap_frontend/src/types.ts +++ b/meshmap_frontend/src/types.ts @@ -617,3 +617,54 @@ export interface LLMMessageStatusPayload { status: LLMMessageStatus error?: string } + +// LLM Provider 相关类型 +export interface LLMProvider { + name: string + active: boolean + api_key?: string + base_url: string + model: string + timeout: number + context_window_tokens: number + created_at: string + updated_at: string +} + +export interface LLMProviderPayload { + name: string + active: boolean + api_key: string + base_url: string + model: string + timeout: number + context_window_tokens: number +} + +export interface LLMProviderResponse { + item: LLMProvider +} + +// LLM Tool Router 相关类型 +export interface LLMPlatformRouter { + id: number + enabled: boolean + openai_name: string + timeout: number + max_tokens: number + system_prompt: string + created_at: string + updated_at: string +} + +export interface LLMPlatformRouterPayload { + enabled: boolean + openai_name: string + timeout: number + max_tokens: number + system_prompt: string +} + +export interface LLMPlatformRouterResponse { + item: LLMPlatformRouter +}
{{ error }}
{{ success }}
管理多个 LLM API 提供商配置,支持不同模型和服务。
暂无配置,点击上方按钮添加第一个 AI 提供商配置。
配置 LLM 工具调用的路由设置,用于实现函数调用功能。
选择用于工具调用的 AI 提供商配置
用于指导模型如何使用工具的系统提示词
{{ toolRouter.system_prompt }}
暂无工具路由配置,点击上方按钮进行配置。
唯一标识此配置的名称,创建后不可修改