[Feature] 提供面向用户层单元测试的可脚本化 MockChatModel
- Dominant language
- Go
- Stars
- 13k
- Forks
- 1.1k
- Avg merge
- 4h 6m
- Merged PRs (30d)
- 41
Description
### Is your feature request related to a problem? Please describe.
eino 在 `internal/mock/components/model/ChatModel_mock.go` 有一份 mockgen 生成的 stub,但它在 `internal/` 下,只供框架自身单测使用。
当用户为自己的 Agent / Graph 写单元测试时,没有一个**官方、可脚本化、断言友好**的 ChatModel 假体可用,只能:
- 自己引 `gomock` 写 `EXPECT()` setup
- 或手写 struct 实现 `model.BaseChatModel` 接口
两种方式都有样板代码负担,且每个项目各写一套、不可移植。这直接影响「易维护」——测试难写,回归就难保障。
### Describe the solution you'd like
新增包 `components/model/modeltest`,提供链式、可断言的假体:
```go
m := modeltest.New().
OnTurn(0).Reply("hello").
OnTurn(1).ReplyToolCall("get_weather", `{"city":"sh"}`).
OnTurn(2).Reply("18C").
Build()
agent, _ := react.NewAgent(ctx, &react.AgentConfig{Model: m})
out, _ := agent.Generate(ctx, ...)
// 断言能力
require.Equal(t, "what's the weather", m.Calls()[0].LastUserMessage())
require.Equal(t, "get_weather", m.Calls()[1].LastToolMessageName())
```
要求:
1. 同时实现 `BaseChatModel` 与 `ToolCallingChatModel`,可作为 react / ChatModelAgent 的 drop-in
2. 支持 `Generate` 与 `Stream`(脚本化流式分片)
3. 支持 `OnPrompt(matcher).Reply(...)` 基于内容匹配的脚本
4. 内置调用历史 `Calls()`,记录每次入参 messages / tools / options 便于断言
5. 与现有 mockgen 生成的内部 mock 并存,用途不同(一个面向用户、一个面向框架内部)
不引入新外部依赖,不影响现有 ChatModel 实现。
### Describe alternatives you've considered
- **直接用 mockgen 的 `ChatModel_mock.go`**:在 `internal/` 下,对用户不可见;即便导出,`gomock` 的 `EXPECT()` 写多轮对话脚本也很啰嗦。
- **用户自写 struct 实现接口**:每个项目重复,且没有统一的调用历史 / 断言能力。
- **录制-回放真实模型响应**:可作为后续增强(fixture from trace),但首版先做手写脚本,依赖更少。
### Additional context
- 竞品参考:Pydantic AI 的 `TestModel` / `FunctionModel`、Vercel AI SDK 5 的 `MockLanguageModelV2`、AutoGen 的 `mock_response`、LangChain 的 `FakeChatModel`。
- 包名 `components/model/modeltest` 与 `schema/modeltest` 二选一可讨论。
愿意提交首版 PR。
Contributor guide
Research direction
Start by reading internal/mock/components/model/ChatModel_mock.go and the BaseChatModel and ToolCallingChatModel interfaces. Define the public components/model/modeltest package around the requested scripted turns, prompt matching, streaming, and call history. Done means it supports Generate and Stream as a drop-in model, exposes the stated assertions, adds coverage, introduces no external dependency, and preserves the existing internal mock.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- ai, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100