feat(ai-core): trace instrumentation, LLM thinker, decay scheduler
- orchestrator: Tracer interface with spans for intent analysis, persona, subsession dispatch, context build, LLM streaming and each tool call; traces finish when the stream completes — the admin Trace waterfall finally receives data - background: Thinker generates real LLM thoughts (post-chat + silence triggers, recent history as context, empty/overlong outputs discarded); thoughts broadcast to web clients as 'thinking' WS messages; pipeline postChatHook triggers post-chat thinking after each round - memory: ApplyDecayAll + ListUserIDs with a 6h decay/purge cron in main (was implemented but never scheduled)
This commit is contained in:
1 parent
8495a4b055
commit
8603839711
8 files changed
+338
-10
No files matched your search
@@ -32,6 +32,7 @@ import (
|
||||
webfetch "git.yeij.top/AskaEth/Cyrene_v2/plugins/web-fetch"
|
||||
websearch "git.yeij.top/AskaEth/Cyrene_v2/plugins/web-search"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/services/ai-core/background"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/services/ai-core/llm"
|
||||
llmlog "git.yeij.top/AskaEth/Cyrene_v2/services/ai-core/llm"
|
||||
tracepkg "git.yeij.top/AskaEth/Cyrene_v2/services/ai-core/trace"
|
||||
@@ -244,6 +245,37 @@ func main() {
|
||||
perfMon := perfpkg.NewMonitor(120, 10*time.Second)
|
||||
perfMon.Start()
|
||||
|
||||
// Wire tracing into the dialogue pipeline
|
||||
orch.SetTracer(pipelineTracer{traceLogger})
|
||||
|
||||
// === 12.5 Background Thinker ===
|
||||
thinker := background.New(true, 5*time.Minute, 30*time.Minute)
|
||||
thinker.SetLLM(&memory.LLMAdapterBridge{Adapter: llmSelector.DefaultAdapter()})
|
||||
thinker.SetHistoryProvider(func(sessionID string, limit int) []model.Message {
|
||||
msgs := ctxStore.Get(sessionID)
|
||||
if len(msgs) > limit {
|
||||
msgs = msgs[len(msgs)-limit:]
|
||||
}
|
||||
return msgs
|
||||
})
|
||||
thinker.Start(context.Background())
|
||||
orch.SetPostChatHook(thinker.TriggerPostChat)
|
||||
|
||||
// === 12.6 Memory decay scheduler ===
|
||||
if memSvc != nil {
|
||||
go func() {
|
||||
ticker := time.NewTicker(6 * time.Hour)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
if n, err := memSvc.ApplyDecayAll(context.Background()); err != nil {
|
||||
logger.Infof("[memory] decay run failed: %v", err)
|
||||
} else if n > 0 {
|
||||
logger.Infof("[memory] decay applied (%d memories updated)", n)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
gwDeps := api.Dependencies{
|
||||
AuthMgr: authMgr,
|
||||
Orchestrator: orch,
|
||||
@@ -267,6 +299,13 @@ func main() {
|
||||
wsHub := ws.NewHub(orch)
|
||||
gwDeps.WSHub = wsHub
|
||||
|
||||
// Push background thoughts to connected web clients
|
||||
go func() {
|
||||
for th := range thinker.ThoughtChan() {
|
||||
wsHub.Broadcast(map[string]any{"type": "thinking", "content": th.Content})
|
||||
}
|
||||
}()
|
||||
|
||||
apiRouter := api.New(gwDeps)
|
||||
|
||||
wsMux := http.NewServeMux()
|
||||
|
||||
@@ -40,6 +40,12 @@ func (a *traceAdapter) Recent(n int) []map[string]any {
|
||||
|
||||
func (a *traceAdapter) Clear() { a.Logger.Clear() }
|
||||
|
||||
// pipelineTracer adapts trace.Logger to orchestrator.Tracer
|
||||
// (StartTrace's return value is discarded at the call site).
|
||||
type pipelineTracer struct{ *tracepkg.Logger }
|
||||
|
||||
func (p pipelineTracer) StartTrace(id, userMsg string) { p.Logger.StartTrace(id, userMsg) }
|
||||
|
||||
func spanToMap(s tracepkg.Span) map[string]any {
|
||||
m := map[string]any{
|
||||
"name": s.Name, "label": s.Label,
|
||||
|
||||
Reference in new issue
Block a user