71f0a1abdb
- 所有Go模块路径从 github.com/yourname/cyrene-ai 迁移到 git.yeij.top/AskaEth/Cyrene - 5个Go Dockerfile添加 GOPROXY=https://goproxy.cn,direct 解决国内构建问题 - ai-core go.mod 添加 pkg/plugins replace 指令 - Caddyfile 简化为 http:// 通配 + handle 保留 /api 前缀 - ethend Dockerfile 适配 (npm install + 仅 COPY package.json) - ethend 新增 RUNNING_IN_DOCKER 环境变量,健康检查改用Docker服务名 - ethend 数据库状态检查支持Docker hostname (postgres/redis/qdrant/minio) - process-manager 新增 CONTAINER_SVC_MAP + Docker模式自动检测 - 统一 docker-compose.dev.db.yml 卷名 (pg_data/redis_data/qdrant_data/minio_data) - docker-compose.yml ethend服务挂载docker.sock + 端口变量化 - 清理 .env 统一后的残留文件与提示信息 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
205 lines
5.3 KiB
Go
205 lines
5.3 KiB
Go
package subsession
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"git.yeij.top/AskaEth/Cyrene/pkg/logger"
|
|
"sync"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene/ai-core/internal/bus"
|
|
"git.yeij.top/AskaEth/Cyrene/ai-core/internal/llm"
|
|
"git.yeij.top/AskaEth/Cyrene/ai-core/internal/model"
|
|
)
|
|
|
|
// Manager 子会话管理器
|
|
// 负责注册 Provider、分派任务、并行执行、超时控制、结果收集
|
|
type Manager struct {
|
|
mu sync.RWMutex
|
|
providers map[model.SubSessionType]Provider
|
|
llmClient LLMClient
|
|
eventBus bus.Bus
|
|
}
|
|
|
|
// NewManager 创建子会话管理器
|
|
func NewManager(llmClient LLMClient) *Manager {
|
|
return &Manager{
|
|
providers: make(map[model.SubSessionType]Provider),
|
|
llmClient: llmClient,
|
|
}
|
|
}
|
|
|
|
// SetBus sets the event bus (optional, for Phase 1).
|
|
func (m *Manager) SetBus(b bus.Bus) {
|
|
m.eventBus = b
|
|
}
|
|
|
|
func (m *Manager) getBus() bus.Bus {
|
|
if m.eventBus == nil {
|
|
return &bus.NopBus{}
|
|
}
|
|
return m.eventBus
|
|
}
|
|
|
|
// Register 注册子会话提供者
|
|
func (m *Manager) Register(provider Provider) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.providers[provider.Type()] = provider
|
|
logger.Printf("[subsession] 注册子会话提供者: %s (优先级=%d, 超时=%v)", provider.Type(), provider.Priority(), provider.Timeout())
|
|
}
|
|
|
|
// RegisterWithOverride 注册或覆盖子会话提供者
|
|
func (m *Manager) RegisterWithOverride(provider Provider) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.providers[provider.Type()] = provider
|
|
logger.Printf("[subsession] 注册(覆盖)子会话提供者: %s (优先级=%d, 超时=%v)", provider.Type(), provider.Priority(), provider.Timeout())
|
|
}
|
|
|
|
// GetProvider 获取指定类型的 Provider
|
|
func (m *Manager) GetProvider(t model.SubSessionType) (Provider, bool) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
p, ok := m.providers[t]
|
|
return p, ok
|
|
}
|
|
|
|
// ListProviders 列出所有已注册的 Provider 类型
|
|
func (m *Manager) ListProviders() []model.SubSessionType {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
types := make([]model.SubSessionType, 0, len(m.providers))
|
|
for t := range m.providers {
|
|
types = append(types, t)
|
|
}
|
|
return types
|
|
}
|
|
|
|
// Dispatch 分派任务到子会话,并行执行,返回结果通道
|
|
func (m *Manager) Dispatch(
|
|
ctx context.Context,
|
|
intent *model.IntentResult,
|
|
userMessage string,
|
|
params CreateContextParams,
|
|
) <-chan model.SubSessionResult {
|
|
|
|
m.mu.RLock()
|
|
providers := make([]Provider, 0, len(m.providers))
|
|
for _, p := range m.providers {
|
|
providers = append(providers, p)
|
|
}
|
|
m.mu.RUnlock()
|
|
|
|
resultCh := make(chan model.SubSessionResult, len(providers))
|
|
var wg sync.WaitGroup
|
|
|
|
for _, provider := range providers {
|
|
if !provider.CanHandle(ctx, intent, userMessage) {
|
|
logger.Printf("[subsession] 跳过子会话 %s: CanHandle 返回 false", provider.Type())
|
|
continue
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func(p Provider) {
|
|
defer wg.Done()
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
logger.Printf("[subsession] dispatch goroutine panic 恢复 (type=%s): %v", p.Type(), r)
|
|
}
|
|
}()
|
|
|
|
result := model.SubSessionResult{Type: p.Type()}
|
|
m.getBus().Publish(bus.BusEvent{
|
|
Type: bus.EventSubSessionStarted,
|
|
Payload: bus.SubSessionPayload{SubType: p.Type(), Status: "started"},
|
|
})
|
|
|
|
|
|
// 创建带超时的 context
|
|
subCtx, cancel := context.WithTimeout(ctx, p.Timeout())
|
|
defer cancel()
|
|
|
|
// 构建 LLM 上下文
|
|
llmMessages, err := p.CreateContext(subCtx, params)
|
|
if err != nil {
|
|
result.Error = fmt.Sprintf("创建上下文失败: %v", err)
|
|
logger.Printf("[subsession] %s 创建上下文失败: %v", p.Type(), err)
|
|
resultCh <- result
|
|
return
|
|
}
|
|
|
|
logger.Printf("[subsession] %s 开始执行 (上下文 %d 条消息)", p.Type(), len(llmMessages))
|
|
|
|
// 执行子会话
|
|
subResult, execErr := p.Execute(subCtx, llmMessages)
|
|
if execErr != nil {
|
|
result.Error = fmt.Sprintf("执行失败: %v", execErr)
|
|
logger.Printf("[subsession] %s 执行失败: %v", p.Type(), execErr)
|
|
resultCh <- result
|
|
return
|
|
}
|
|
|
|
// 检查超时
|
|
select {
|
|
case <-subCtx.Done():
|
|
result.Error = "子会话超时"
|
|
logger.Printf("[subsession] %s 超时 (limit=%v)", p.Type(), p.Timeout())
|
|
default:
|
|
if subResult != nil {
|
|
result = *subResult
|
|
result.Type = p.Type()
|
|
logger.Printf("[subsession] %s 完成: 摘要=%s", p.Type(), truncate(result.Summary, 50))
|
|
}
|
|
}
|
|
|
|
m.getBus().Publish(bus.BusEvent{
|
|
Type: bus.EventSubSessionCompleted,
|
|
Payload: bus.SubSessionPayload{SubType: p.Type(), Status: resultSummaryStatus(result), Summary: result.Summary, Details: result.Details},
|
|
})
|
|
|
|
resultCh <- result
|
|
}(provider)
|
|
}
|
|
|
|
// 等待所有子会话完成,关闭通道
|
|
go func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
logger.Printf("[subsession] wait goroutine panic 恢复: %v", r)
|
|
}
|
|
}()
|
|
wg.Wait()
|
|
close(resultCh)
|
|
}()
|
|
|
|
return resultCh
|
|
}
|
|
|
|
// generateID 生成随机 ID
|
|
func generateID() string {
|
|
b := make([]byte, 12)
|
|
rand.Read(b)
|
|
return fmt.Sprintf("sub-%x", b)
|
|
}
|
|
|
|
// resultSummaryStatus returns "completed" or "failed" for bus events.
|
|
func resultSummaryStatus(r model.SubSessionResult) string {
|
|
if r.Error != "" {
|
|
return "failed"
|
|
}
|
|
return "completed"
|
|
}
|
|
|
|
// truncate 截断字符串
|
|
func truncate(s string, maxLen int) string {
|
|
runes := []rune(s)
|
|
if len(runes) <= maxLen {
|
|
return s
|
|
}
|
|
return string(runes[:maxLen]) + "..."
|
|
}
|
|
|
|
// Ensure llm is used
|
|
var _ = llm.NewAdapter
|