feat(runtime+admin): A2 LLM异步提取接线 + A4 Tracing接线 + A6通知后端聚合
A2 异步记忆提取:
- loop/memory.go: asyncExtractor接口(ExtractAsync)+extractMemoryAsync(60s超时,WithOutCancel,dispatch即返回,后台落库,失败仅Warn)+persistMemories共用
- run.go 三个回合落定点改异步提取; 删除run.go逐步提取(design/03'每轮后', 避免多轮工具N次LLM调用/重复写)
- cmd/cyrene/extractor.go buildMemoryExtractor: llmGroupUsable为真→LLMExtractor, 否则回落ManualExtractor(日志说明); keylessEndpoint放行loopback/私网
A4 可观测性 Tracing:
- cmd/cyrene/observability.go: newAppTraceWriter+traceFanout(多sink)+gatewaySpanSink(走gw.ObserveSpan,喂环形缓冲+LLM/subagent指标)
- wire.go 注入; 未改trace/gateway数据结构
A6 通知后端:
- internal/services/gateway/notifications.go(311行): GET /admin/notifications?type=&before=&limit= (adminAuth)
- 聚合已有源(obs.audit SQLite + s.backthink), createdAt倒序, id=source:id; 类型派生(channel/system/audit/backthink); 无新建表/无新Deps/wire零改动
- 诚实: backthink仅shouldPush计, 未注入源为空, 报错500, 未读游标后置
- 契约与前端Y一致: {id,type,title,body,source,createdAt}, before含游标(<=), type未知400
- Docs/api-client v0.33
全仓 build/vet/test绿(56包)+win/darwin/linux-arm64交叉编译绿; 新增测试(memory_async/extractor/observability/notifications)
This commit is contained in:
1 parent
34c8cc4dca
commit
153a9d6211
12 files changed
+1427
-11
No files matched your search
+14
-3
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -117,6 +116,11 @@ type app struct {
|
||||
// satisfied by *gateway.Server (BroadcastMood); held as an interface so the
|
||||
// composition root is unit-testable with a fake sink.
|
||||
moodSink moodEmitter
|
||||
// traceWriter fans finished trace spans out to the JSONL stderr recorder and,
|
||||
// once the gateway exists, to the gateway's observability buffer behind
|
||||
// GET /api/v1/admin/observability/trace (design/19 §2). It is held so
|
||||
// wireFrontend can register the gateway sink on the already-running tracer.
|
||||
traceWriter *traceFanout
|
||||
}
|
||||
|
||||
// capacityEmitter is the gateway-facing sink for capacity_awaken broadcasts.
|
||||
@@ -360,7 +364,10 @@ func (a *app) wireRuntime() error {
|
||||
personaID := a.defaultPersonaID()
|
||||
store := a.memStore
|
||||
|
||||
tracer := trace.New(trace.NewJSONRecorder(os.Stderr))
|
||||
// Spans fan out to the JSONL stderr recorder and — once wireFrontend has built
|
||||
// the gateway — to its observability buffer (cmd/cyrene/observability.go).
|
||||
a.traceWriter = newAppTraceWriter()
|
||||
tracer := trace.New(a.traceWriter)
|
||||
// Realtime flow visualization (design/19 §1): the publisher accumulates the
|
||||
// loop's trace spans into a per-session flow graph and publishes
|
||||
// ws.graph_update on each span transition. It is gated to the owner's web
|
||||
@@ -413,7 +420,7 @@ func (a *app) wireRuntime() error {
|
||||
OnTurnEnd: a.onTurnEnd,
|
||||
Memory: store,
|
||||
Retriever: a.retriever,
|
||||
Extractor: memory.NewManualExtractor(personaID, "owner"),
|
||||
Extractor: a.buildMemoryExtractor(personaID),
|
||||
Checkpoint: checkpoint,
|
||||
Tracer: tracer,
|
||||
Flow: flowPub,
|
||||
@@ -738,6 +745,10 @@ func (a *app) wireFrontend() error {
|
||||
}
|
||||
gw.SetVoice(a.voice)
|
||||
a.gw = gw // assign BEFORE wiring hooks below, which capture a.gw / gw (nil-receiver panic otherwise)
|
||||
// Observability trace sink (design/19 §2): now that the gateway exists, its
|
||||
// ring buffer must be fed by the loop's spans, or the admin trace/metrics
|
||||
// endpoints stay empty.
|
||||
wireGatewayTraceSink(a, gw)
|
||||
if a.channelBridge != nil {
|
||||
// Route the channel bridge's owner-private persisted messages into the same
|
||||
// `message_persisted` broadcast the orchestrator uses, so the Web client mirrors
|
||||
|
||||
Reference in new issue
Block a user