test(agent): cover image fallback execution path for image candidates

This commit is contained in:
Alix-007 2026-03-25 15:21:52 +08:00
parent 89ff10ceb0
commit fd40b3df17

View file

@ -91,6 +91,33 @@ func (r *recordingProvider) GetDefaultModel() string {
return "mock-model"
}
type imageFallbackProbeProvider struct {
calls []string
}
func (p *imageFallbackProbeProvider) Chat(
ctx context.Context,
messages []providers.Message,
tools []providers.ToolDefinition,
model string,
opts map[string]any,
) (*providers.LLMResponse, error) {
p.calls = append(p.calls, model)
if model == "gemini-2.5-flash-lite" {
// This error is non-retriable in text fallback Execute(), but should still
// fail over in ExecuteImage() which only treats image size/dimension as terminal.
return nil, fmt.Errorf("string should match pattern")
}
return &providers.LLMResponse{
Content: "image fallback ok",
ToolCalls: []providers.ToolCall{},
}, nil
}
func (p *imageFallbackProbeProvider) GetDefaultModel() string {
return "image-fallback-probe"
}
func newTestAgentLoop(
t *testing.T,
) (al *AgentLoop, cfg *config.Config, msgBus *bus.MessageBus, provider *mockProvider, cleanup func()) {
@ -620,6 +647,54 @@ func TestRunAgentLoop_DoesNotUseImageModelWhenOnlyHistoryHasImages(t *testing.T)
}
}
func TestRunAgentLoop_UsesImageFallbackExecutionForImageCandidates(t *testing.T) {
tmpDir := t.TempDir()
cfg := &config.Config{
Agents: config.AgentsConfig{
Defaults: config.AgentDefaults{
Workspace: tmpDir,
ModelName: "text-main",
ImageModel: "vision-main",
ImageModelFallbacks: []string{"vision-backup"},
MaxTokens: 4096,
MaxToolIterations: 5,
},
},
ModelList: []*config.ModelConfig{
{ModelName: "text-main", Model: "openai/gpt-5.4"},
{ModelName: "vision-main", Model: "gemini/gemini-2.5-flash-lite"},
{ModelName: "vision-backup", Model: "anthropic/claude-3-7-sonnet"},
},
}
msgBus := bus.NewMessageBus()
provider := &imageFallbackProbeProvider{}
al := NewAgentLoop(cfg, msgBus, provider)
agent := al.registry.GetDefaultAgent()
resp, err := al.runAgentLoop(context.Background(), agent, processOptions{
SessionKey: "image-fallback-session",
Channel: "telegram",
ChatID: "chat-1",
UserMessage: "describe this image",
Media: []string{"data:image/png;base64,AAAA"},
DefaultResponse: "fallback",
SendResponse: false,
})
if err != nil {
t.Fatalf("runAgentLoop returned error: %v", err)
}
if resp != "image fallback ok" {
t.Fatalf("response = %q, want %q", resp, "image fallback ok")
}
if len(provider.calls) != 2 {
t.Fatalf("provider calls = %d, want 2", len(provider.calls))
}
if provider.calls[0] != "gemini-2.5-flash-lite" || provider.calls[1] != "claude-3-7-sonnet" {
t.Fatalf("provider calls = %v, want [gemini-2.5-flash-lite claude-3-7-sonnet]", provider.calls)
}
}
func TestNewAgentLoop_StateInitialized(t *testing.T) {
// Create temp workspace
tmpDir, err := os.MkdirTemp("", "agent-test-*")