Files
HomeAgent/internal/agent/api/provider_caps_test.go
JianFeeeee 051247e10f fix(memory): 时间线按声明渲染工具回执 + 能力判定改为配置层
时间线此前只写「→ 调用工具: a, b」,工具取回的正文下一轮彻底消失:
模型记得自己查过,却不记得查到了什么(刚说的话转头就忘)。
ToolDef 上的 Cleaner / NoMemory 声明一直存在,缺的是消费端。

- formatMergedTimeline 渲染 e.ToolResults:Cleaner 优先,NoMemory 工具跳过,
  单条 160 字、每事件最多 3 条(+N more),预算估算同步计入
- 模态能力改由配置层声明(ProviderManager.SetCapabilities),
  不再靠 provider.(ModalProvider) 断言穿过 adapter 与安全装饰器
- a2a 通道 RecallPolicy none→auto(NoMemory 仍 true:可读不写)
2026-10-27 12:16:06 +08:00

63 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
// 模态能力必须由**配置层**声明,而不是靠 adapter 的类型断言去猜。
//
// 现场(反复出现的"配了视觉却说没有视觉"):内核用
// `provider.(ModalProvider)` 断言判定能力,而该断言要穿过 adapter 与
// 安全装饰器两层 —— 任一层漏实现方法,能力就静默变成 false。
//
// 用户指正(2026-10-27):是否支持视觉是**配置事实**
//(core.llm.sources.<name>.vision=true),adapter 只负责格式转换。
import (
"context"
"testing"
)
// plainProvider 故意**不实现** ModalProvider —— 模拟"装饰器漏方法"的情形。
type plainProvider struct{ name string }
func (p *plainProvider) Name() string { return p.name }
func (p *plainProvider) MaxContextTokens() int { return 8192 }
func (p *plainProvider) Chat(ctx context.Context, req *CompletionRequest) (*CompletionResponse, error) {
return nil, nil
}
func (p *plainProvider) ChatStream(ctx context.Context, req *CompletionRequest) (<-chan StreamChunk, error) {
ch := make(chan StreamChunk)
close(ch)
return ch, nil
}
func TestProviderManagerCapsDeclaredByConfig(t *testing.T) {
m := NewProviderManager()
p := &plainProvider{name: "proxy"}
m.Register("proxy", p)
// 断言兜底:plainProvider 不是 ModalProvider ⇒ false
if ProviderSupportsVision(p) {
t.Fatal("前提不成立:plainProvider 不该被断言成支持视觉")
}
// 配置声明后 ⇒ true(这就是"配置层说了算")
m.SetCapabilities("proxy", ProviderCaps{Vision: true})
if !m.SupportsVision("proxy") {
t.Fatal("配置声明 vision=true 后应判定为支持视觉,不能因为 provider 没实现 ModalProvider 就丢掉能力")
}
if m.SupportsAudio("proxy") {
t.Fatal("未声明 audio 时不应判定支持音频")
}
}
func TestProviderManagerCapsFallBackToAssertion(t *testing.T) {
m := NewProviderManager()
p := &plainProvider{name: "legacy"}
m.Register("legacy", p)
// 没声明(ok=false)⇒ 退回旧路径的断言结果
if _, ok := m.Capabilities("legacy"); ok {
t.Fatal("未声明时 Capabilities 应返回 ok=false")
}
if m.SupportsVision("legacy") {
t.Fatal("未声明且非 ModalProvider 时应为 false")
}
}