feat: Centralize reasoning channel ID management, make agent reasoning context-aware, formalize LLM response types, and enable Docker debug mode.
This commit is contained in:
parent
08c6b84f9f
commit
0e67b3492f
18 changed files with 129 additions and 97 deletions
|
|
@ -467,47 +467,32 @@ func (al *AgentLoop) runAgentLoop(ctx context.Context, agent *AgentInstance, opt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) targetReasoningChannelID(channelName string) (chatID string) {
|
func (al *AgentLoop) targetReasoningChannelID(channelName string) (chatID string) {
|
||||||
channels := al.cfg.Channels
|
if al.channelManager == nil {
|
||||||
|
return ""
|
||||||
switch channelName {
|
|
||||||
case "telegram":
|
|
||||||
return channels.Telegram.ReasoningChannelID
|
|
||||||
case "whatsapp":
|
|
||||||
return channels.WhatsApp.ReasoningChannelID
|
|
||||||
case "feishu":
|
|
||||||
return channels.Feishu.ReasoningChannelID
|
|
||||||
case "discord":
|
|
||||||
return channels.Discord.ReasoningChannelID
|
|
||||||
case "maixcam":
|
|
||||||
return channels.MaixCam.ReasoningChannelID
|
|
||||||
case "qq":
|
|
||||||
return channels.QQ.ReasoningChannelID
|
|
||||||
case "dingtalk":
|
|
||||||
return channels.DingTalk.ReasoningChannelID
|
|
||||||
case "slack":
|
|
||||||
return channels.Slack.ReasoningChannelID
|
|
||||||
case "line":
|
|
||||||
return channels.LINE.ReasoningChannelID
|
|
||||||
case "onebot":
|
|
||||||
return channels.OneBot.ReasoningChannelID
|
|
||||||
case "wecom":
|
|
||||||
return channels.WeCom.ReasoningChannelID
|
|
||||||
case "wecom_app":
|
|
||||||
return channels.WeComApp.ReasoningChannelID
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
if ch, ok := al.channelManager.GetChannel(channelName); ok {
|
||||||
|
return ch.ReasoningChannelID()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) handleReasoning(reasoningContent, channelName, channelID string) {
|
func (al *AgentLoop) handleReasoning(ctx context.Context, reasoningContent, channelName, channelID string) {
|
||||||
if reasoningContent == "" || channelName == "" || channelID == "" {
|
if reasoningContent == "" || channelName == "" || channelID == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
al.bus.PublishOutbound(bus.OutboundMessage{
|
select {
|
||||||
Channel: channelName,
|
case <-ctx.Done():
|
||||||
ChatID: channelID,
|
return
|
||||||
Content: reasoningContent,
|
default:
|
||||||
})
|
al.bus.PublishOutbound(bus.OutboundMessage{
|
||||||
|
Channel: channelName,
|
||||||
|
ChatID: channelID,
|
||||||
|
Content: reasoningContent,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// runLLMIteration executes the LLM call loop with tool handling.
|
// runLLMIteration executes the LLM call loop with tool handling.
|
||||||
|
|
@ -634,7 +619,7 @@ func (al *AgentLoop) runLLMIteration(
|
||||||
return "", iteration, fmt.Errorf("LLM call failed after retries: %w", err)
|
return "", iteration, fmt.Errorf("LLM call failed after retries: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go al.handleReasoning(response.Reasoning, opts.Channel, al.targetReasoningChannelID(opts.Channel))
|
go al.handleReasoning(ctx, response.Reasoning, opts.Channel, al.targetReasoningChannelID(opts.Channel))
|
||||||
// Log LLM response details
|
// Log LLM response details
|
||||||
logger.InfoCF("agent", "LLM response",
|
logger.InfoCF("agent", "LLM response",
|
||||||
map[string]any{
|
map[string]any{
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,22 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/bus"
|
"github.com/sipeed/picoclaw/pkg/bus"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/channels"
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
"github.com/sipeed/picoclaw/pkg/providers"
|
"github.com/sipeed/picoclaw/pkg/providers"
|
||||||
"github.com/sipeed/picoclaw/pkg/tools"
|
"github.com/sipeed/picoclaw/pkg/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type fakeChannel struct{ id string }
|
||||||
|
|
||||||
|
func (f *fakeChannel) Name() string { return "fake" }
|
||||||
|
func (f *fakeChannel) Start(ctx context.Context) error { return nil }
|
||||||
|
func (f *fakeChannel) Stop(ctx context.Context) error { return nil }
|
||||||
|
func (f *fakeChannel) Send(ctx context.Context, msg bus.OutboundMessage) error { return nil }
|
||||||
|
func (f *fakeChannel) IsRunning() bool { return true }
|
||||||
|
func (f *fakeChannel) IsAllowed(string) bool { return true }
|
||||||
|
func (f *fakeChannel) ReasoningChannelID() string { return f.id }
|
||||||
|
|
||||||
func TestRecordLastChannel(t *testing.T) {
|
func TestRecordLastChannel(t *testing.T) {
|
||||||
// Create temp workspace
|
// Create temp workspace
|
||||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||||
|
|
@ -648,23 +659,30 @@ func TestTargetReasoningChannelID_AllChannels(t *testing.T) {
|
||||||
MaxToolIterations: 10,
|
MaxToolIterations: 10,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Channels: config.ChannelsConfig{
|
|
||||||
WhatsApp: config.WhatsAppConfig{ReasoningChannelID: "rid-whatsapp"},
|
|
||||||
Telegram: config.TelegramConfig{ReasoningChannelID: "rid-telegram"},
|
|
||||||
Feishu: config.FeishuConfig{ReasoningChannelID: "rid-feishu"},
|
|
||||||
Discord: config.DiscordConfig{ReasoningChannelID: "rid-discord"},
|
|
||||||
MaixCam: config.MaixCamConfig{ReasoningChannelID: "rid-maixcam"},
|
|
||||||
QQ: config.QQConfig{ReasoningChannelID: "rid-qq"},
|
|
||||||
DingTalk: config.DingTalkConfig{ReasoningChannelID: "rid-dingtalk"},
|
|
||||||
Slack: config.SlackConfig{ReasoningChannelID: "rid-slack"},
|
|
||||||
LINE: config.LINEConfig{ReasoningChannelID: "rid-line"},
|
|
||||||
OneBot: config.OneBotConfig{ReasoningChannelID: "rid-onebot"},
|
|
||||||
WeCom: config.WeComConfig{ReasoningChannelID: "rid-wecom"},
|
|
||||||
WeComApp: config.WeComAppConfig{ReasoningChannelID: "rid-wecom-app"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
al := NewAgentLoop(cfg, bus.NewMessageBus(), &mockProvider{})
|
al := NewAgentLoop(cfg, bus.NewMessageBus(), &mockProvider{})
|
||||||
|
chManager, err := channels.NewManager(&config.Config{}, bus.NewMessageBus())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create channel manager: %v", err)
|
||||||
|
}
|
||||||
|
for name, id := range map[string]string{
|
||||||
|
"whatsapp": "rid-whatsapp",
|
||||||
|
"telegram": "rid-telegram",
|
||||||
|
"feishu": "rid-feishu",
|
||||||
|
"discord": "rid-discord",
|
||||||
|
"maixcam": "rid-maixcam",
|
||||||
|
"qq": "rid-qq",
|
||||||
|
"dingtalk": "rid-dingtalk",
|
||||||
|
"slack": "rid-slack",
|
||||||
|
"line": "rid-line",
|
||||||
|
"onebot": "rid-onebot",
|
||||||
|
"wecom": "rid-wecom",
|
||||||
|
"wecom_app": "rid-wecom-app",
|
||||||
|
} {
|
||||||
|
chManager.RegisterChannel(name, &fakeChannel{id: id})
|
||||||
|
}
|
||||||
|
al.SetChannelManager(chManager)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
channel string
|
channel string
|
||||||
wantID string
|
wantID string
|
||||||
|
|
@ -718,7 +736,7 @@ func TestHandleReasoning(t *testing.T) {
|
||||||
|
|
||||||
t.Run("skips when any required field is empty", func(t *testing.T) {
|
t.Run("skips when any required field is empty", func(t *testing.T) {
|
||||||
al, msgBus := newLoop(t)
|
al, msgBus := newLoop(t)
|
||||||
al.handleReasoning("reasoning", "telegram", "")
|
al.handleReasoning(context.Background(), "reasoning", "telegram", "")
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
@ -729,7 +747,7 @@ func TestHandleReasoning(t *testing.T) {
|
||||||
|
|
||||||
t.Run("publishes one message for non telegram", func(t *testing.T) {
|
t.Run("publishes one message for non telegram", func(t *testing.T) {
|
||||||
al, msgBus := newLoop(t)
|
al, msgBus := newLoop(t)
|
||||||
al.handleReasoning("hello reasoning", "slack", "channel-1")
|
al.handleReasoning(context.Background(), "hello reasoning", "slack", "channel-1")
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
@ -745,7 +763,7 @@ func TestHandleReasoning(t *testing.T) {
|
||||||
t.Run("publishes one message for telegram", func(t *testing.T) {
|
t.Run("publishes one message for telegram", func(t *testing.T) {
|
||||||
al, msgBus := newLoop(t)
|
al, msgBus := newLoop(t)
|
||||||
reasoning := "hello telegram reasoning"
|
reasoning := "hello telegram reasoning"
|
||||||
al.handleReasoning(reasoning, "telegram", "tg-chat")
|
al.handleReasoning(context.Background(), reasoning, "telegram", "tg-chat")
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
@ -764,4 +782,18 @@ func TestHandleReasoning(t *testing.T) {
|
||||||
t.Fatalf("content mismatch: got %q want %q", msg.Content, reasoning)
|
t.Fatalf("content mismatch: got %q want %q", msg.Content, reasoning)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
t.Run("expired ctx", func(t *testing.T) {
|
||||||
|
al, msgBus := newLoop(t)
|
||||||
|
reasoning := "hello telegram reasoning"
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
al.handleReasoning(ctx, reasoning, "telegram", "tg-chat")
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), 200*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
msg, ok := msgBus.SubscribeOutbound(ctx)
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("expected no outbound message, got %+v", msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,23 +14,32 @@ type Channel interface {
|
||||||
Send(ctx context.Context, msg bus.OutboundMessage) error
|
Send(ctx context.Context, msg bus.OutboundMessage) error
|
||||||
IsRunning() bool
|
IsRunning() bool
|
||||||
IsAllowed(senderID string) bool
|
IsAllowed(senderID string) bool
|
||||||
|
ReasoningChannelID() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseChannel struct {
|
type BaseChannel struct {
|
||||||
config any
|
config any
|
||||||
bus *bus.MessageBus
|
bus *bus.MessageBus
|
||||||
running bool
|
running bool
|
||||||
name string
|
name string
|
||||||
allowList []string
|
allowList []string
|
||||||
|
reasoningChannelID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBaseChannel(name string, config any, bus *bus.MessageBus, allowList []string) *BaseChannel {
|
func NewBaseChannel(
|
||||||
|
name string,
|
||||||
|
config any,
|
||||||
|
bus *bus.MessageBus,
|
||||||
|
allowList []string,
|
||||||
|
reasoningChannelID string,
|
||||||
|
) *BaseChannel {
|
||||||
return &BaseChannel{
|
return &BaseChannel{
|
||||||
config: config,
|
config: config,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
name: name,
|
name: name,
|
||||||
allowList: allowList,
|
allowList: allowList,
|
||||||
running: false,
|
reasoningChannelID: reasoningChannelID,
|
||||||
|
running: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,6 +47,10 @@ func (c *BaseChannel) Name() string {
|
||||||
return c.name
|
return c.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BaseChannel) ReasoningChannelID() string {
|
||||||
|
return c.reasoningChannelID
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BaseChannel) IsRunning() bool {
|
func (c *BaseChannel) IsRunning() bool {
|
||||||
return c.running
|
return c.running
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +94,11 @@ func (c *BaseChannel) IsAllowed(senderID string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []string, metadata map[string]string) {
|
func (c *BaseChannel) HandleMessage(
|
||||||
|
senderID, chatID, content string,
|
||||||
|
media []string,
|
||||||
|
metadata map[string]string,
|
||||||
|
) {
|
||||||
if !c.IsAllowed(senderID) {
|
if !c.IsAllowed(senderID) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func TestBaseChannelIsAllowed(t *testing.T) {
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
ch := NewBaseChannel("test", nil, nil, tt.allowList)
|
ch := NewBaseChannel("test", nil, nil, tt.allowList, "")
|
||||||
if got := ch.IsAllowed(tt.senderID); got != tt.want {
|
if got := ch.IsAllowed(tt.senderID); got != tt.want {
|
||||||
t.Fatalf("IsAllowed(%q) = %v, want %v", tt.senderID, got, tt.want)
|
t.Fatalf("IsAllowed(%q) = %v, want %v", tt.senderID, got, tt.want)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ func NewDingTalkChannel(cfg config.DingTalkConfig, messageBus *bus.MessageBus) (
|
||||||
return nil, fmt.Errorf("dingtalk client_id and client_secret are required")
|
return nil, fmt.Errorf("dingtalk client_id and client_secret are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
base := NewBaseChannel("dingtalk", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("dingtalk", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &DingTalkChannel{
|
return &DingTalkChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ func NewDiscordChannel(cfg config.DiscordConfig, bus *bus.MessageBus) (*DiscordC
|
||||||
return nil, fmt.Errorf("failed to create discord session: %w", err)
|
return nil, fmt.Errorf("failed to create discord session: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
base := NewBaseChannel("discord", cfg, bus, cfg.AllowFrom)
|
base := NewBaseChannel("discord", cfg, bus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &DiscordChannel{
|
return &DiscordChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ type FeishuChannel struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFeishuChannel(cfg config.FeishuConfig, bus *bus.MessageBus) (*FeishuChannel, error) {
|
func NewFeishuChannel(cfg config.FeishuConfig, bus *bus.MessageBus) (*FeishuChannel, error) {
|
||||||
base := NewBaseChannel("feishu", cfg, bus, cfg.AllowFrom)
|
base := NewBaseChannel("feishu", cfg, bus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &FeishuChannel{
|
return &FeishuChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func NewLINEChannel(cfg config.LINEConfig, messageBus *bus.MessageBus) (*LINECha
|
||||||
return nil, fmt.Errorf("line channel_secret and channel_access_token are required")
|
return nil, fmt.Errorf("line channel_secret and channel_access_token are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
base := NewBaseChannel("line", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("line", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &LINEChannel{
|
return &LINEChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ type MaixCamMessage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMaixCamChannel(cfg config.MaixCamConfig, bus *bus.MessageBus) (*MaixCamChannel, error) {
|
func NewMaixCamChannel(cfg config.MaixCamConfig, bus *bus.MessageBus) (*MaixCamChannel, error) {
|
||||||
base := NewBaseChannel("maixcam", cfg, bus, cfg.AllowFrom)
|
base := NewBaseChannel("maixcam", cfg, bus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &MaixCamChannel{
|
return &MaixCamChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ type oneBotMessageSegment struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOneBotChannel(cfg config.OneBotConfig, messageBus *bus.MessageBus) (*OneBotChannel, error) {
|
func NewOneBotChannel(cfg config.OneBotConfig, messageBus *bus.MessageBus) (*OneBotChannel, error) {
|
||||||
base := NewBaseChannel("onebot", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("onebot", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
const dedupSize = 1024
|
const dedupSize = 1024
|
||||||
return &OneBotChannel{
|
return &OneBotChannel{
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ type QQChannel struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQQChannel(cfg config.QQConfig, messageBus *bus.MessageBus) (*QQChannel, error) {
|
func NewQQChannel(cfg config.QQConfig, messageBus *bus.MessageBus) (*QQChannel, error) {
|
||||||
base := NewBaseChannel("qq", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("qq", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &QQChannel{
|
return &QQChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ func NewSlackChannel(cfg config.SlackConfig, messageBus *bus.MessageBus) (*Slack
|
||||||
|
|
||||||
socketClient := socketmode.New(api)
|
socketClient := socketmode.New(api)
|
||||||
|
|
||||||
base := NewBaseChannel("slack", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("slack", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &SlackChannel{
|
return &SlackChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann
|
||||||
return nil, fmt.Errorf("failed to create telegram bot: %w", err)
|
return nil, fmt.Errorf("failed to create telegram bot: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
base := NewBaseChannel("telegram", telegramCfg, bus, telegramCfg.AllowFrom)
|
base := NewBaseChannel("telegram", telegramCfg, bus, telegramCfg.AllowFrom, telegramCfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &TelegramChannel{
|
return &TelegramChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ func NewWeComBotChannel(cfg config.WeComConfig, messageBus *bus.MessageBus) (*We
|
||||||
return nil, fmt.Errorf("wecom token and webhook_url are required")
|
return nil, fmt.Errorf("wecom token and webhook_url are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
base := NewBaseChannel("wecom", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("wecom", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &WeComBotChannel{
|
return &WeComBotChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ func NewWeComAppChannel(cfg config.WeComAppConfig, messageBus *bus.MessageBus) (
|
||||||
return nil, fmt.Errorf("wecom_app corp_id, corp_secret and agent_id are required")
|
return nil, fmt.Errorf("wecom_app corp_id, corp_secret and agent_id are required")
|
||||||
}
|
}
|
||||||
|
|
||||||
base := NewBaseChannel("wecom_app", cfg, messageBus, cfg.AllowFrom)
|
base := NewBaseChannel("wecom_app", cfg, messageBus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &WeComAppChannel{
|
return &WeComAppChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type WhatsAppChannel struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWhatsAppChannel(cfg config.WhatsAppConfig, bus *bus.MessageBus) (*WhatsAppChannel, error) {
|
func NewWhatsAppChannel(cfg config.WhatsAppConfig, bus *bus.MessageBus) (*WhatsAppChannel, error) {
|
||||||
base := NewBaseChannel("whatsapp", cfg, bus, cfg.AllowFrom)
|
base := NewBaseChannel("whatsapp", cfg, bus, cfg.AllowFrom, cfg.ReasoningChannelID)
|
||||||
|
|
||||||
return &WhatsAppChannel{
|
return &WhatsAppChannel{
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ type (
|
||||||
ToolFunctionDefinition = protocoltypes.ToolFunctionDefinition
|
ToolFunctionDefinition = protocoltypes.ToolFunctionDefinition
|
||||||
ExtraContent = protocoltypes.ExtraContent
|
ExtraContent = protocoltypes.ExtraContent
|
||||||
GoogleExtra = protocoltypes.GoogleExtra
|
GoogleExtra = protocoltypes.GoogleExtra
|
||||||
|
ReasoningDetail = protocoltypes.ReasoningDetail
|
||||||
)
|
)
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
|
|
@ -148,15 +149,10 @@ func parseResponse(body []byte) (*LLMResponse, error) {
|
||||||
var apiResponse struct {
|
var apiResponse struct {
|
||||||
Choices []struct {
|
Choices []struct {
|
||||||
Message struct {
|
Message struct {
|
||||||
Reasoning string `json:"reasoning"`
|
Reasoning string `json:"reasoning"`
|
||||||
ReasoningDetails []struct {
|
ReasoningDetails []ReasoningDetail `json:"reasoning_details"`
|
||||||
Format string `json:"format"`
|
Content string `json:"content"`
|
||||||
Index int `json:"index"`
|
ToolCalls []struct {
|
||||||
Type string `json:"type"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
} `json:"reasoning_details"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
ToolCalls []struct {
|
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Function *struct {
|
Function *struct {
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,19 @@ type FunctionCall struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type LLMResponse struct {
|
type LLMResponse struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
||||||
FinishReason string `json:"finish_reason"`
|
FinishReason string `json:"finish_reason"`
|
||||||
Usage *UsageInfo `json:"usage,omitempty"`
|
Usage *UsageInfo `json:"usage,omitempty"`
|
||||||
Reasoning string `json:"reasoning"`
|
Reasoning string `json:"reasoning"`
|
||||||
ReasoningDetails []struct {
|
ReasoningDetails []ReasoningDetail `json:"reasoning_details"`
|
||||||
Format string `json:"format"`
|
}
|
||||||
Index int `json:"index"`
|
|
||||||
Type string `json:"type"`
|
type ReasoningDetail struct {
|
||||||
Text string `json:"text"`
|
Format string `json:"format"`
|
||||||
} `json:"reasoning_details"`
|
Index int `json:"index"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
type UsageInfo struct {
|
type UsageInfo struct {
|
||||||
PromptTokens int `json:"prompt_tokens"`
|
PromptTokens int `json:"prompt_tokens"`
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue