8bcb90cdb2
管理端新增「运维 › 服务状态」:总览横幅 + 摘要数字 + 请求链路拓扑 (客户端→网关→NATS→调度→MCP,按健康三态着色)+ 应用服务卡(含探针 延迟)+ 基建磁贴 + MCP 工具按能力域分组(中文名/作用)。 探活机制(全走 NATS,无 HTTP): - mcp-go/mcp-py 新增 list_tools 自省工具,能应答=在线 + 上报工具清单 - dispatcher 无端点 → 新增 NATS 心跳主题 sundynix.health.dispatcher (ServeHealth 应答 model/ready/uptime),网关用 bus.Ping 探 - 网关 GET /api/v1/admin/status 并发聚合四探针 + 各项延迟 mcp-go 重构:switch → map 注册表(buildRegistry),dispatch 与 list_tools 共用单一事实源,杜绝漂移;每个工具带中文名 + 作用描述。mcp-py 同样补元信息。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
87 lines
3.0 KiB
Go
87 lines
3.0 KiB
Go
// Package nats 是调度器对共享 bus 的薄封装(消费任务 / 回写 Token)。
|
||
package nats
|
||
|
||
import (
|
||
"context"
|
||
"log"
|
||
|
||
sharedbus "github.com/sundynix/sundynix-shared/bus"
|
||
"github.com/sundynix/sundynix-shared/contract"
|
||
)
|
||
|
||
// TaskHandler 处理单个任务。
|
||
type TaskHandler func(ctx context.Context, t *contract.Task) error
|
||
|
||
// Subscriber 包装共享 bus,向调度器暴露消费能力。
|
||
type Subscriber struct {
|
||
inner *sharedbus.Bus
|
||
}
|
||
|
||
// MustConnect 接入 NATS 并确保任务流存在(消费者声明在 Consume 时完成)。
|
||
func MustConnect(url string) *Subscriber {
|
||
inner, err := sharedbus.Connect(url)
|
||
if err != nil {
|
||
log.Fatalf("[dispatcher/nats] connect: %v", err)
|
||
}
|
||
if err := inner.EnsureTaskStream(context.Background()); err != nil {
|
||
log.Fatalf("[dispatcher/nats] ensure stream: %v", err)
|
||
}
|
||
log.Printf("[dispatcher/nats] connected %s", url)
|
||
return &Subscriber{inner: inner}
|
||
}
|
||
|
||
// ConsumeTasks 从 sundynix.tasks.* 持续消费任务(队列组负载均衡),阻塞至 ctx 取消。
|
||
func (s *Subscriber) ConsumeTasks(ctx context.Context, h TaskHandler) error {
|
||
stop, err := s.inner.ConsumeTasks(ctx, func(c context.Context, t *contract.Task) error {
|
||
return h(c, t)
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer stop()
|
||
<-ctx.Done()
|
||
return ctx.Err()
|
||
}
|
||
|
||
// PublishToken / CompleteStream 让 Subscriber 满足 eino.TokenSink,
|
||
// 把推理 Token 回流到 sundynix.streams.<taskID>。
|
||
func (s *Subscriber) PublishToken(taskID string, token []byte) error {
|
||
return s.inner.PublishToken(taskID, token)
|
||
}
|
||
|
||
func (s *Subscriber) CompleteStream(taskID string) error {
|
||
return s.inner.CompleteStream(taskID)
|
||
}
|
||
|
||
// PublishExec / CompleteExec 让 Subscriber 满足 eino.ExecSink,
|
||
// 把执行轨迹事件回流到 sundynix.exec.<taskID>(与 Token 流分开)。
|
||
func (s *Subscriber) PublishExec(taskID string, data []byte) error {
|
||
return s.inner.PublishExec(taskID, data)
|
||
}
|
||
|
||
func (s *Subscriber) CompleteExec(taskID string) error {
|
||
return s.inner.CompleteExec(taskID)
|
||
}
|
||
|
||
// CallTool 让 Subscriber 满足 eino.ToolCaller,经 NATS request-reply 调起第 5 层 MCP 工具。
|
||
func (s *Subscriber) CallTool(ctx context.Context, subject string, call *contract.ToolCall) (*contract.ToolResult, error) {
|
||
return s.inner.CallTool(ctx, subject, call)
|
||
}
|
||
|
||
// ServeHealth 在 dispatcher 心跳主题上应答探活,让管理端「服务状态」判定其在线。
|
||
func (s *Subscriber) ServeHealth(provide func() []byte) (func() error, error) {
|
||
return s.inner.ServeHealth(contract.SubjectHealthDispatcher, provide)
|
||
}
|
||
|
||
// RequestModelConfig 向控制面(Gateway)取当前激活的对话模型配置。
|
||
func (s *Subscriber) RequestModelConfig(ctx context.Context) (*contract.ModelConfig, error) {
|
||
return s.inner.RequestConfig(ctx, contract.ConfigKindChat)
|
||
}
|
||
|
||
// SubscribeModelConfigUpdated 订阅对话模型配置热更新。
|
||
func (s *Subscriber) SubscribeModelConfigUpdated(onUpdate func(*contract.ModelConfig)) (func() error, error) {
|
||
return s.inner.SubscribeConfigUpdated(contract.ConfigKindChat, onUpdate)
|
||
}
|
||
|
||
func (s *Subscriber) Close() { s.inner.Close() }
|