feat: Implement vision capabilities and image generation tools, adding media handling, dedicated configurations, and trace optimization for image data.

This commit is contained in:
viettranx committed 2026-02-26 22:28:27 +07:00
1 parent 112fddb401
commit d5cc5a745d
19 files changed
+865 -46

No files matched your search

+4
View File
@@ -182,6 +182,10 @@ func runGateway() {
toolsReg.Register(webFetchTool)
slog.Info("web_fetch tool enabled")
// Vision fallback tool (for non-vision providers like MiniMax)
toolsReg.Register(tools.NewReadImageTool(providerRegistry))
toolsReg.Register(tools.NewCreateImageTool(providerRegistry))
// TTS (text-to-speech) system
ttsMgr := setupTTS(cfg)
if ttsMgr != nil {
+19 -2
View File
@@ -150,6 +150,7 @@ func consumeInboundMessages(ctx context.Context, msgBus *bus.MessageBus, agents
outCh := sched.ScheduleWithOpts(ctx, "main", agent.RunRequest{
SessionKey: sessionKey,
Message: msg.Content,
Media: msg.Media,
Channel: msg.Channel,
ChatID: msg.ChatID,
PeerKind: peerKind,
@@ -225,12 +226,28 @@ func consumeInboundMessages(ctx context.Context, msgBus *bus.MessageBus, agents
}
// Publish response back to the channel
msgBus.PublishOutbound(bus.OutboundMessage{
outMsg := bus.OutboundMessage{
Channel: channel,
ChatID: chatID,
Content: outcome.Result.Content,
Metadata: meta,
})
}
// Convert media results from agent run to outbound media attachments
for _, mr := range outcome.Result.Media {
outMsg.Media = append(outMsg.Media, bus.MediaAttachment{
URL: mr.Path,
ContentType: mr.ContentType,
})
if mr.AsVoice {
if outMsg.Metadata == nil {
outMsg.Metadata = make(map[string]string)
}
outMsg.Metadata["audio_as_voice"] = "true"
}
}
msgBus.PublishOutbound(outMsg)
}(msg.Channel, msg.ChatID, sessionKey, runID, outMeta)
}
+9 -2
View File
@@ -3,6 +3,7 @@ package cmd
import (
"context"
"log/slog"
"strings"
"github.com/nextlevelbuilder/goclaw/internal/config"
"github.com/nextlevelbuilder/goclaw/internal/providers"
@@ -51,7 +52,8 @@ func registerProviders(registry *providers.Registry, cfg *config.Config) {
}
if cfg.Providers.MiniMax.APIKey != "" {
registry.Register(providers.NewOpenAIProvider("minimax", cfg.Providers.MiniMax.APIKey, "https://api.minimax.io/v1", "MiniMax-M2.5"))
registry.Register(providers.NewOpenAIProvider("minimax", cfg.Providers.MiniMax.APIKey, "https://api.minimax.io/v1", "MiniMax-M2.5").
WithChatPath("/text/chatcompletion_v2"))
slog.Info("registered provider", "name", "minimax")
}
@@ -82,7 +84,12 @@ func registerProvidersFromDB(registry *providers.Registry, provStore store.Provi
if p.ProviderType == "anthropic_native" {
registry.Register(providers.NewAnthropicProvider(p.APIKey))
} else {
registry.Register(providers.NewOpenAIProvider(p.Name, p.APIKey, p.APIBase, ""))
prov := providers.NewOpenAIProvider(p.Name, p.APIKey, p.APIBase, "")
// MiniMax native API uses a different chat path for vision support.
if p.Name == "minimax" && strings.Contains(p.APIBase, "minimax.io") {
prov.WithChatPath("/text/chatcompletion_v2")
}
registry.Register(prov)
}
slog.Info("registered provider from DB", "name", p.Name)
}