From c6f5c3fea04cb83342704a4cbd0c51be3142d0fb Mon Sep 17 00:00:00 2001 From: AskaEth Date: Mon, 5 Oct 2026 13:23:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(capability):=20=E8=BD=AC=E7=A0=81=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=E8=A3=85=E9=85=8D=E8=BF=9B=E7=BB=84=E5=90=88=E6=A0=B9?= =?UTF-8?q?=EF=BC=88=E5=B7=A5=E5=85=B7=E7=9C=9F=E8=BF=9B=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E8=A1=A8=E3=80=81=E6=97=A5=E5=BF=97=E5=B8=A6=20source=EF=BC=89?= =?UTF-8?q?+=20design/25=20=E9=9D=9Ebit-exact=20=E7=95=99=E7=97=95?= =?UTF-8?q?=E6=9B=B4=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/cyrene/wire.go | 11 +++++--- cmd/cyrene/wire_runtime.go | 1 + cmd/cyrene/wire_transcode.go | 44 +++++++++++++++++++++++++++++++ cmd/cyrene/wire_transcode_test.go | 34 ++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 cmd/cyrene/wire_transcode.go create mode 100644 cmd/cyrene/wire_transcode_test.go diff --git a/cmd/cyrene/wire.go b/cmd/cyrene/wire.go index 29d5a17..e357eae 100644 --- a/cmd/cyrene/wire.go +++ b/cmd/cyrene/wire.go @@ -11,6 +11,7 @@ import ( "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/mcp" pluginmgr "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/plugin" "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry" + "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/transcode" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/session" "git.yeij.top/AskaEth/Cyrene_v2/internal/config" "git.yeij.top/AskaEth/Cyrene_v2/internal/db" @@ -62,9 +63,13 @@ type app struct { llm *model.RuntimeLLM personas *personareg.Registry caps *registry.Registry - dispatcher *tool.Dispatcher - memStore *memory.Store - retriever *memory.Retriever + // transcoder 是本地转码引擎(design/25):进程内、同步、纯本地,供渠道与其它 + // 模块经依赖注入调用;同一个引擎也以 `transcode` 工具注册进 caps。装配只告警、 + // 绝不阻塞启动,见 wire_transcode.go。 + transcoder transcode.Transcoder + dispatcher *tool.Dispatcher + memStore *memory.Store + retriever *memory.Retriever // embedder 与 memoryTidy 属于记忆整理(design/23):整理只在 embedder // 上算召回相似度,绝不接触会话/历史读数。 embedder embedding.Embedder diff --git a/cmd/cyrene/wire_runtime.go b/cmd/cyrene/wire_runtime.go index f78d0f3..62e3899 100644 --- a/cmd/cyrene/wire_runtime.go +++ b/cmd/cyrene/wire_runtime.go @@ -165,6 +165,7 @@ func (a *app) wireRuntime() error { } } a.wireVision() + a.wireTranscode() // 上下文溢出压缩:预算按模型窗口推导(CONTEXT_MAX_TOKENS 未设置时; // 显式 >0 = 覆盖、0/负 = 关闭),推导与「窗口未声明」的 WARN 都在 diff --git a/cmd/cyrene/wire_transcode.go b/cmd/cyrene/wire_transcode.go new file mode 100644 index 0000000..b0e7e13 --- /dev/null +++ b/cmd/cyrene/wire_transcode.go @@ -0,0 +1,44 @@ +package main + +import ( + "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/transcode" + "git.yeij.top/AskaEth/Cyrene_v2/internal/logger" +) + +// transcodeLogSource 是转码能力的日志来源(design/25 §5.3)。引擎自身不带前缀, +// 前缀由组合根在这里用 logger.WithSource 接上 —— 漏接则日志无来源,撞 +// AGENTS §10 红线。 +const transcodeLogSource = "capability/transcode" + +// wireTranscode 构造本地转码引擎,并把它暴露成能力注册表里的 `transcode` 工具 +// (design/25 §1.3.2 第二层注册;与 wireVision 同族)。 +// +// 两道失败都**只告警、绝不阻塞启动**:编解码器注册不全(RegistrationError)时引擎 +// 仍可用已注册的路,工具注册失败(重名等)时能力面缺席、进程内 DI 路径照常。 +// 转码是可选能力,缺了它其余子系统不受影响,因此这一步没有错误返回值。 +func (a *app) wireTranscode() { + log := logger.WithSource(a.log, transcodeLogSource) + tr := transcode.NewEngine( + transcode.DefaultLimits(), + transcode.WithCodecs(transcode.DefaultCodecs()...), + transcode.WithLogger(log), + ) + if err := tr.RegistrationError(); err != nil { + log.Warn("transcode codec registration incomplete", "error", err) + } + a.transcoder = tr + if err := transcode.RegisterTool(a.caps, tr); err != nil { + log.Warn("transcode tool register skipped", "error", err) + return + } + log.Info("transcode tool registered", "formats", formatNames(tr.Formats())) +} + +// formatNames 把已注册的格式转成可读字符串,供启动日志列出这条路支持什么。 +func formatNames(formats []transcode.Format) []string { + names := make([]string, 0, len(formats)) + for _, f := range formats { + names = append(names, transcode.FormatName(f)) + } + return names +} diff --git a/cmd/cyrene/wire_transcode_test.go b/cmd/cyrene/wire_transcode_test.go new file mode 100644 index 0000000..2a03fed --- /dev/null +++ b/cmd/cyrene/wire_transcode_test.go @@ -0,0 +1,34 @@ +package main + +import ( + "testing" + + "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry" + "git.yeij.top/AskaEth/Cyrene_v2/internal/capability/transcode" +) + +// TestWireTranscodeRegistersTool 断言 wireTranscode 真的把转码装配起来了:引擎挂到 +// app 上、`transcode` 工具进了能力注册表、且默认的 amr→wav 路可用。它是「组合根漏接 +// 转码」这一整类回归的哨兵(design/25 §1.3.2)。 +func TestWireTranscodeRegistersTool(t *testing.T) { + a := &app{log: testLog(), caps: registry.New()} + + a.wireTranscode() + + if a.transcoder == nil { + t.Fatal("wireTranscode 之后 a.transcoder 仍为 nil(引擎没被装配)") + } + cap, err := a.caps.Get(transcode.ToolName) + if err != nil { + t.Fatalf("transcode 工具没进能力注册表: %v", err) + } + if cap.Kind != registry.KindTool { + t.Fatalf("能力类型 = %q, want %q", cap.Kind, registry.KindTool) + } + if _, ok := cap.Value.(*transcode.Tool); !ok { + t.Fatalf("能力值类型 = %T, want *transcode.Tool", cap.Value) + } + if !a.transcoder.Supports(transcode.FormatAMR, transcode.FormatWAV) { + t.Fatal("装配后 amr->wav 这条路不可用(默认编解码器没进引擎)") + } +}