ai服务状态更新
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"meshtastic_mqtt_server/internal/autoreply"
|
||||
"meshtastic_mqtt_server/internal/llm"
|
||||
storepkg "meshtastic_mqtt_server/internal/store"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AIServiceStatus reports the current state of the AI service
|
||||
type AIServiceStatus struct {
|
||||
Running bool `json:"running"`
|
||||
Enabled bool `json:"enabled"`
|
||||
ProviderCount int `json:"provider_count"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// AIManager manages the lifecycle of the AI service, supporting restart
|
||||
type AIManager struct {
|
||||
mu sync.Mutex
|
||||
service *Service
|
||||
cfg Config
|
||||
db *gorm.DB
|
||||
botSender autoreply.BotSender
|
||||
ctx context.Context
|
||||
store *storepkg.Store
|
||||
}
|
||||
|
||||
// NewAIManager creates a new AIManager
|
||||
func NewAIManager(cfg Config, db *gorm.DB, botSender autoreply.BotSender, ctx context.Context, store *storepkg.Store) *AIManager {
|
||||
return &AIManager{
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
botSender: botSender,
|
||||
ctx: ctx,
|
||||
store: store,
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfigEnabled sets the enabled flag on the config
|
||||
func (m *AIManager) SetConfigEnabled(enabled bool) {
|
||||
m.cfg.Enabled = enabled
|
||||
}
|
||||
|
||||
// SetProviderConfigs sets the LLM provider configs
|
||||
func (m *AIManager) SetProviderConfigs(configs []llm.ProviderConfig) {
|
||||
m.cfg.LLMProviders = configs
|
||||
}
|
||||
|
||||
// Init creates and starts the AI service
|
||||
func (m *AIManager) Init() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
svc, err := NewService(m.cfg, m.db, m.botSender)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create AI service: %w", err)
|
||||
}
|
||||
if err := svc.Start(m.ctx); err != nil {
|
||||
return fmt.Errorf("failed to start AI service: %w", err)
|
||||
}
|
||||
m.service = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the currently running AI service
|
||||
func (m *AIManager) Stop() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.service != nil {
|
||||
m.service.Stop()
|
||||
m.service = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Status returns the current AI service status
|
||||
func (m *AIManager) Status() AIServiceStatus {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
providerCount := len(m.cfg.LLMProviders)
|
||||
if m.service == nil {
|
||||
msg := "AI 服务未运行,可在配置提供商后点击重启"
|
||||
if providerCount == 0 {
|
||||
msg = "尚未配置 AI 提供商,请先添加提供商配置"
|
||||
}
|
||||
return AIServiceStatus{
|
||||
Running: false,
|
||||
Enabled: m.cfg.Enabled,
|
||||
ProviderCount: providerCount,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
enabled := m.service.Enabled()
|
||||
if !enabled {
|
||||
return AIServiceStatus{
|
||||
Running: false,
|
||||
Enabled: false,
|
||||
ProviderCount: providerCount,
|
||||
Message: "AI 服务未启用",
|
||||
}
|
||||
}
|
||||
return AIServiceStatus{
|
||||
Running: true,
|
||||
Enabled: true,
|
||||
ProviderCount: providerCount,
|
||||
}
|
||||
}
|
||||
|
||||
// Restart stops the current AI service and creates a new one from DB
|
||||
func (m *AIManager) Restart() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.service != nil {
|
||||
m.service.Stop()
|
||||
m.service = nil
|
||||
}
|
||||
|
||||
providers, err := m.store.ListLLMProviders(true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载 LLM 提供商列表失败: %w", err)
|
||||
}
|
||||
if len(providers) == 0 {
|
||||
return fmt.Errorf("没有配置任何 LLM 提供商,请先添加配置")
|
||||
}
|
||||
|
||||
providerConfigs := make([]llm.ProviderConfig, 0, len(providers))
|
||||
for _, p := range providers {
|
||||
providerConfigs = append(providerConfigs, llm.ProviderConfig{
|
||||
Name: p.Name,
|
||||
Active: p.Active,
|
||||
APIKey: p.APIKey,
|
||||
BaseURL: p.BaseURL,
|
||||
Model: p.Model,
|
||||
Timeout: p.Timeout,
|
||||
ContextWindowTokens: p.ContextWindowTokens,
|
||||
})
|
||||
}
|
||||
m.cfg.LLMProviders = providerConfigs
|
||||
|
||||
svc, err := NewService(m.cfg, m.db, m.botSender)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建 AI 服务失败: %w", err)
|
||||
}
|
||||
if err := svc.Start(m.ctx); err != nil {
|
||||
return fmt.Errorf("启动 AI 服务失败: %w", err)
|
||||
}
|
||||
|
||||
m.service = svc
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReloadLLMProvider delegates to the current service
|
||||
func (m *AIManager) ReloadLLMProvider(config interface{}) error {
|
||||
m.mu.Lock()
|
||||
svc := m.service
|
||||
m.mu.Unlock()
|
||||
|
||||
if svc == nil {
|
||||
return nil
|
||||
}
|
||||
return svc.ReloadLLMProvider(config)
|
||||
}
|
||||
|
||||
// AddLLMProvider delegates to the current service
|
||||
func (m *AIManager) AddLLMProvider(config interface{}) error {
|
||||
m.mu.Lock()
|
||||
svc := m.service
|
||||
m.mu.Unlock()
|
||||
|
||||
if svc == nil {
|
||||
return nil
|
||||
}
|
||||
return svc.AddLLMProvider(config)
|
||||
}
|
||||
|
||||
// RemoveLLMProvider delegates to the current service
|
||||
func (m *AIManager) RemoveLLMProvider(name string) error {
|
||||
m.mu.Lock()
|
||||
svc := m.service
|
||||
m.mu.Unlock()
|
||||
|
||||
if svc == nil {
|
||||
return nil
|
||||
}
|
||||
return svc.RemoveLLMProvider(name)
|
||||
}
|
||||
|
||||
// AIServiceStatus returns the AI service status for the web interface
|
||||
func (m *AIManager) AIServiceStatus() AIServiceStatus {
|
||||
return m.Status()
|
||||
}
|
||||
|
||||
// RestartAIService restarts the AI service for the web interface
|
||||
func (m *AIManager) RestartAIService() error {
|
||||
return m.Restart()
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
aipkg "meshtastic_mqtt_server/internal/ai"
|
||||
storepkg "meshtastic_mqtt_server/internal/store"
|
||||
"meshtastic_mqtt_server/internal/webutil"
|
||||
)
|
||||
@@ -18,6 +19,8 @@ type LLMProviderReloader interface {
|
||||
ReloadLLMProvider(config interface{}) error
|
||||
AddLLMProvider(config interface{}) error
|
||||
RemoveLLMProvider(name string) error
|
||||
AIServiceStatus() aipkg.AIServiceStatus
|
||||
RestartAIService() error
|
||||
}
|
||||
|
||||
func RegisterRoutes(r *gin.RouterGroup, store *storepkg.Store, aiService LLMProviderReloader) {
|
||||
@@ -49,6 +52,10 @@ func RegisterRoutes(r *gin.RouterGroup, store *storepkg.Store, aiService LLMProv
|
||||
// LLM Primary Config - 主 AI 回复配置
|
||||
group.GET("/primary-config", handleGetLLMPrimaryConfig(store))
|
||||
group.PUT("/primary-config", handleUpdateLLMPrimaryConfig(store))
|
||||
|
||||
// AI Service Status
|
||||
group.GET("/status", handleGetAIServiceStatus(aiService))
|
||||
group.POST("/restart", handleRestartAIService(aiService))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,3 +835,49 @@ func llmPrimaryConfigDTO(row storepkg.LLMPrimaryConfigRecord) map[string]any {
|
||||
"updated_at": row.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// AI Service Status & Restart Handlers
|
||||
// ============================================
|
||||
|
||||
func handleGetAIServiceStatus(aiService LLMProviderReloader) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if aiService == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"running": false,
|
||||
"enabled": false,
|
||||
"provider_count": 0,
|
||||
"message": "AI 服务未初始化",
|
||||
})
|
||||
return
|
||||
}
|
||||
status := aiService.AIServiceStatus()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"running": status.Running,
|
||||
"enabled": status.Enabled,
|
||||
"provider_count": status.ProviderCount,
|
||||
"message": status.Message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func handleRestartAIService(aiService LLMProviderReloader) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if aiService == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "AI 服务未初始化,无法重启"})
|
||||
return
|
||||
}
|
||||
if err := aiService.RestartAIService(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "重启 AI 服务失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
status := aiService.AIServiceStatus()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"running": status.Running,
|
||||
"enabled": status.Enabled,
|
||||
"provider_count": status.ProviderCount,
|
||||
"message": "AI 服务已重启",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
aipkg "meshtastic_mqtt_server/internal/ai"
|
||||
"meshtastic_mqtt_server/internal/auth"
|
||||
blockingpkg "meshtastic_mqtt_server/internal/blocking"
|
||||
botpkg "meshtastic_mqtt_server/internal/bot"
|
||||
@@ -32,6 +33,8 @@ type LLMProviderReloader interface {
|
||||
ReloadLLMProvider(config interface{}) error
|
||||
AddLLMProvider(config interface{}) error
|
||||
RemoveLLMProvider(name string) error
|
||||
AIServiceStatus() aipkg.AIServiceStatus
|
||||
RestartAIService() error
|
||||
}
|
||||
|
||||
func NewHTTPServer(cfg configpkg.WebConfig, consoleLog bool, store *storepkg.Store, sessions *auth.Manager, mqttStatus MQTTStatusProvider, blocking *blockingpkg.Cache, forwarder mqttforwardpkg.Reloader, settings *rspkg.Cache, botSender botpkg.TextSender, aiService LLMProviderReloader) *http.Server {
|
||||
|
||||
@@ -398,16 +398,48 @@ func run(cfg *configpkg.Config) error {
|
||||
defer forwardManager.StopAll()
|
||||
|
||||
// Initialize AI Service
|
||||
var aiService *ai.Service
|
||||
// Create bot sender adapter - 支持频道消息和私聊消息两种发送方式
|
||||
botSenderAdapter := autoreply.NewBotServiceAdapter(
|
||||
// SendDirectText: 发送私聊消息
|
||||
func(ctx context.Context, botID uint64, toNodeNum int64, text string) error {
|
||||
_, err := botSender.SendText(ctx, botpkg.SendTextRequest{
|
||||
BotID: botID,
|
||||
MessageType: "direct",
|
||||
ToNodeNum: &toNodeNum,
|
||||
Text: text,
|
||||
})
|
||||
return err
|
||||
},
|
||||
// SendChannelText: 发送频道消息
|
||||
func(ctx context.Context, botID uint64, channelID string, text string) error {
|
||||
_, err := botSender.SendText(ctx, botpkg.SendTextRequest{
|
||||
BotID: botID,
|
||||
MessageType: "channel",
|
||||
ChannelID: channelID,
|
||||
Text: text,
|
||||
})
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
aiManager := ai.NewAIManager(ai.Config{
|
||||
DataDir: cfg.AI.DataDir,
|
||||
Enabled: cfg.AI.Enabled,
|
||||
ConsoleLog: cfg.ConsoleLog.LLM,
|
||||
ToolConfigStore: store,
|
||||
ToolRouterStore: store,
|
||||
TopicRouterStore: store,
|
||||
Store: store,
|
||||
}, store.DB(), botSenderAdapter, botCtx, store)
|
||||
|
||||
if cfg.AI.Enabled {
|
||||
// Get LLM providers from database
|
||||
llmProviders, err := store.ListLLMProviders(true)
|
||||
aiManager.SetConfigEnabled(true)
|
||||
providers, err := store.ListLLMProviders(true)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to load LLM providers: %v\n", err)
|
||||
} else if len(llmProviders) > 0 {
|
||||
// Convert database records to provider configs
|
||||
providerConfigs := make([]llm.ProviderConfig, 0, len(llmProviders))
|
||||
for _, p := range llmProviders {
|
||||
} else if len(providers) > 0 {
|
||||
providerConfigs := make([]llm.ProviderConfig, 0, len(providers))
|
||||
for _, p := range providers {
|
||||
providerConfigs = append(providerConfigs, llm.ProviderConfig{
|
||||
Name: p.Name,
|
||||
Active: p.Active,
|
||||
@@ -418,48 +450,11 @@ func run(cfg *configpkg.Config) error {
|
||||
ContextWindowTokens: p.ContextWindowTokens,
|
||||
})
|
||||
}
|
||||
|
||||
// Create bot sender adapter - 支持频道消息和私聊消息两种发送方式
|
||||
botSenderAdapter := autoreply.NewBotServiceAdapter(
|
||||
// SendDirectText: 发送私聊消息
|
||||
func(ctx context.Context, botID uint64, toNodeNum int64, text string) error {
|
||||
_, err := botSender.SendText(ctx, botpkg.SendTextRequest{
|
||||
BotID: botID,
|
||||
MessageType: "direct",
|
||||
ToNodeNum: &toNodeNum,
|
||||
Text: text,
|
||||
})
|
||||
return err
|
||||
},
|
||||
// SendChannelText: 发送频道消息
|
||||
func(ctx context.Context, botID uint64, channelID string, text string) error {
|
||||
_, err := botSender.SendText(ctx, botpkg.SendTextRequest{
|
||||
BotID: botID,
|
||||
MessageType: "channel",
|
||||
ChannelID: channelID,
|
||||
Text: text,
|
||||
})
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
aiService, err = ai.NewService(ai.Config{
|
||||
LLMProviders: providerConfigs,
|
||||
DataDir: cfg.AI.DataDir,
|
||||
Enabled: cfg.AI.Enabled,
|
||||
ConsoleLog: cfg.ConsoleLog.LLM,
|
||||
ToolConfigStore: store,
|
||||
ToolRouterStore: store,
|
||||
TopicRouterStore: store,
|
||||
Store: store,
|
||||
}, store.DB(), botSenderAdapter)
|
||||
if err != nil {
|
||||
aiManager.SetProviderConfigs(providerConfigs)
|
||||
if err := aiManager.Init(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to initialize AI service: %v\n", err)
|
||||
} else {
|
||||
if err := aiService.Start(botCtx); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: failed to start AI service: %v\n", err)
|
||||
}
|
||||
defer aiService.Stop()
|
||||
defer aiManager.Stop()
|
||||
printJSON(map[string]any{"event": "ai_service_started", "providers": len(providerConfigs)})
|
||||
}
|
||||
} else {
|
||||
@@ -475,11 +470,7 @@ func run(cfg *configpkg.Config) error {
|
||||
return err
|
||||
}
|
||||
mqttStatus := webpkg.MQTTRuntimeStatus{Server: server, Address: mqttAddr, TLS: cfg.MQTT.TLS.Enabled, Stats: messageStats, ClientStats: clientStats, DBQueue: dbQueue, DedupQueue: mqttHook.dedupQueue}
|
||||
var llmReloader webpkg.LLMProviderReloader
|
||||
if aiService != nil {
|
||||
llmReloader = aiService
|
||||
}
|
||||
handler := webpkg.NewRouter(cfg.Web, cfg.ConsoleLog.Web, store, sessions, mqttStatus, blocking, forwardManager, settings, botSender, llmReloader)
|
||||
handler := webpkg.NewRouter(cfg.Web, cfg.ConsoleLog.Web, store, sessions, mqttStatus, blocking, forwardManager, settings, botSender, aiManager)
|
||||
webAddresses := []string{}
|
||||
if cfg.Web.PortEnabled {
|
||||
httpServer := &http.Server{
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
AdminRuntimeSettingsPayload,
|
||||
AdminRuntimeSettingsResponse,
|
||||
AdminUsersResponse,
|
||||
AIServiceStatus,
|
||||
BotMessage,
|
||||
BotMessageMutationResponse,
|
||||
BotNode,
|
||||
@@ -519,6 +520,15 @@ export function deleteLLMProvider(name: string): Promise<{ status: string; warni
|
||||
return deleteJSON<{ status: string; warning?: string }>(`/api/admin/llm/providers/${encodeURIComponent(name)}`)
|
||||
}
|
||||
|
||||
// AI Service Status API
|
||||
export function getAIServiceStatus(): Promise<AIServiceStatus> {
|
||||
return getJSON<AIServiceStatus>('/api/admin/llm/status')
|
||||
}
|
||||
|
||||
export function restartAIService(): Promise<AIServiceStatus & { status: string; message?: string }> {
|
||||
return postJSON<AIServiceStatus & { status: string; message?: string }>('/api/admin/llm/restart')
|
||||
}
|
||||
|
||||
// LLM Tool Router API
|
||||
export function getLLMToolRouter(): Promise<LLMPlatformRouterResponse> {
|
||||
return getJSON<LLMPlatformRouterResponse>('/api/admin/llm/tool-router')
|
||||
|
||||
@@ -11,14 +11,20 @@ import {
|
||||
updateLLMToolRouter,
|
||||
updateLLMTopicConfig,
|
||||
updateLLMPrimaryConfig,
|
||||
getAIServiceStatus,
|
||||
restartAIService,
|
||||
} from '../api'
|
||||
import type { LLMPlatformRouter, LLMTopicConfig, LLMProvider, LLMPrimaryConfig } from '../types'
|
||||
import type { LLMPlatformRouter, LLMTopicConfig, LLMProvider, LLMPrimaryConfig, AIServiceStatus } from '../types'
|
||||
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
const success = ref('')
|
||||
const showWarning = ref(false)
|
||||
|
||||
// AI Service Status
|
||||
const aiStatus = ref<AIServiceStatus>({ running: false, enabled: false, provider_count: 0 })
|
||||
const restarting = ref(false)
|
||||
|
||||
// LLM Provider 相关
|
||||
const providers = ref<LLMProvider[]>([])
|
||||
const editingProvider = ref<LLMProvider | null>(null)
|
||||
@@ -299,7 +305,31 @@ async function savePrimaryConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAIStatus() {
|
||||
try {
|
||||
aiStatus.value = await getAIServiceStatus()
|
||||
} catch (err) {
|
||||
console.warn('Failed to load AI service status', err)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRestartAI() {
|
||||
if (!confirm('确定要重启 AI 服务吗?')) return
|
||||
restarting.value = true
|
||||
try {
|
||||
const result = await restartAIService()
|
||||
aiStatus.value = result
|
||||
success.value = result.message || 'AI 服务已重启'
|
||||
clearSuccess()
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : String(err)
|
||||
} finally {
|
||||
restarting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadAIStatus()
|
||||
loadProviders()
|
||||
loadToolRouter()
|
||||
loadTopicConfig()
|
||||
@@ -311,6 +341,21 @@ onMounted(() => {
|
||||
<div class="admin-llm-api">
|
||||
<h2>LLM API 配置管理</h2>
|
||||
|
||||
<div class="ai-status-bar">
|
||||
<span class="status-indicator" :class="{ running: aiStatus.running, stopped: !aiStatus.running }"></span>
|
||||
<span class="status-text">
|
||||
<template v-if="aiStatus.running">AI 服务运行中,{{ aiStatus.provider_count }} 个提供商</template>
|
||||
<template v-else>{{ aiStatus.message || 'AI 服务未运行' }}</template>
|
||||
</span>
|
||||
<button
|
||||
class="admin-button admin-button-small"
|
||||
:disabled="restarting"
|
||||
@click="handleRestartAI"
|
||||
>
|
||||
{{ restarting ? '重启中...' : '重启 AI 服务' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p v-if="error" class="error">{{ error }}</p>
|
||||
<p v-if="success" :class="showWarning ? 'warning' : 'success'">{{ success }}</p>
|
||||
|
||||
@@ -703,13 +748,49 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.admin-llm-api h2 {
|
||||
margin: 0 0 2rem;
|
||||
margin: 0 0 1rem;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.ai-status-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-indicator.running {
|
||||
background: #22c55e;
|
||||
box-shadow: 0 0 6px rgba(34, 197, 94, 0.5);
|
||||
}
|
||||
|
||||
.status-indicator.stopped {
|
||||
background: #ef4444;
|
||||
box-shadow: 0 0 6px rgba(239, 68, 68, 0.4);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
flex: 1;
|
||||
font-size: 0.9rem;
|
||||
color: #475569;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.admin-section {
|
||||
background: white;
|
||||
padding: 1.75rem;
|
||||
|
||||
@@ -647,6 +647,13 @@ export interface LLMProviderResponse {
|
||||
warning?: string
|
||||
}
|
||||
|
||||
export interface AIServiceStatus {
|
||||
running: boolean
|
||||
enabled: boolean
|
||||
provider_count: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
// LLM Tool Router 相关类型
|
||||
export interface LLMPlatformRouter {
|
||||
id: number
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
__pycache__
|
||||
db_config.py
|
||||
@@ -26,27 +26,7 @@ from typing import Any
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
|
||||
# ============================================================
|
||||
# 硬编码数据库配置
|
||||
# ============================================================
|
||||
|
||||
SOURCE_CONFIG = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 3306,
|
||||
"user": "root",
|
||||
"password": "PLEASE_CHANGE_ME",
|
||||
"database": "meshtastic",
|
||||
"charset": "utf8mb4",
|
||||
}
|
||||
|
||||
TARGET_CONFIG = {
|
||||
"host": "127.0.0.1",
|
||||
"port": 3307,
|
||||
"user": "root",
|
||||
"password": "PLEASE_CHANGE_ME",
|
||||
"database": "meshtastic",
|
||||
"charset": "utf8mb4",
|
||||
}
|
||||
from db_config import SOURCE_CONFIG, TARGET_CONFIG
|
||||
|
||||
BATCH_SIZE = 5000
|
||||
|
||||
|
||||
Reference in New Issue
Block a user