feat: add slash command support (e.g., /show model, /help)
This commit is contained in:
parent
6ce7659090
commit
580e9c28cf
2 changed files with 97 additions and 0 deletions
|
|
@ -592,6 +592,9 @@ func gatewayCmd() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject channel manager into agent loop for command handling
|
||||||
|
agentLoop.SetChannelManager(channelManager)
|
||||||
|
|
||||||
var transcriber *voice.GroqTranscriber
|
var transcriber *voice.GroqTranscriber
|
||||||
if cfg.Providers.Groq.APIKey != "" {
|
if cfg.Providers.Groq.APIKey != "" {
|
||||||
transcriber = voice.NewGroqTranscriber(cfg.Providers.Groq.APIKey)
|
transcriber = voice.NewGroqTranscriber(cfg.Providers.Groq.APIKey)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ 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/constants"
|
"github.com/sipeed/picoclaw/pkg/constants"
|
||||||
"github.com/sipeed/picoclaw/pkg/logger"
|
"github.com/sipeed/picoclaw/pkg/logger"
|
||||||
|
|
@ -41,6 +42,7 @@ type AgentLoop struct {
|
||||||
tools *tools.ToolRegistry
|
tools *tools.ToolRegistry
|
||||||
running atomic.Bool
|
running atomic.Bool
|
||||||
summarizing sync.Map // Tracks which sessions are currently being summarized
|
summarizing sync.Map // Tracks which sessions are currently being summarized
|
||||||
|
channelManager *channels.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// processOptions configures how a message is processed
|
// processOptions configures how a message is processed
|
||||||
|
|
@ -198,6 +200,10 @@ func (al *AgentLoop) RegisterTool(tool tools.Tool) {
|
||||||
al.tools.Register(tool)
|
al.tools.Register(tool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) SetChannelManager(cm *channels.Manager) {
|
||||||
|
al.channelManager = cm
|
||||||
|
}
|
||||||
|
|
||||||
// RecordLastChannel records the last active channel for this workspace.
|
// RecordLastChannel records the last active channel for this workspace.
|
||||||
// This uses the atomic state save mechanism to prevent data loss on crash.
|
// This uses the atomic state save mechanism to prevent data loss on crash.
|
||||||
func (al *AgentLoop) RecordLastChannel(channel string) error {
|
func (al *AgentLoop) RecordLastChannel(channel string) error {
|
||||||
|
|
@ -262,6 +268,11 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
|
||||||
return al.processSystemMessage(ctx, msg)
|
return al.processSystemMessage(ctx, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for commands
|
||||||
|
if response, handled := al.handleCommand(ctx, msg); handled {
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Process as user message
|
// Process as user message
|
||||||
return al.runAgentLoop(ctx, processOptions{
|
return al.runAgentLoop(ctx, processOptions{
|
||||||
SessionKey: msg.SessionKey,
|
SessionKey: msg.SessionKey,
|
||||||
|
|
@ -775,3 +786,86 @@ func (al *AgentLoop) estimateTokens(messages []providers.Message) int {
|
||||||
}
|
}
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (al *AgentLoop) handleCommand(ctx context.Context, msg bus.InboundMessage) (string, bool) {
|
||||||
|
content := strings.TrimSpace(msg.Content)
|
||||||
|
if !strings.HasPrefix(content, "/") {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Fields(content)
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := parts[0]
|
||||||
|
args := parts[1:]
|
||||||
|
|
||||||
|
switch cmd {
|
||||||
|
case "/show":
|
||||||
|
if len(args) < 1 {
|
||||||
|
return "Usage: /show [model|channel]", true
|
||||||
|
}
|
||||||
|
switch args[0] {
|
||||||
|
case "model":
|
||||||
|
return fmt.Sprintf("Current model: %s", al.model), true
|
||||||
|
case "channel":
|
||||||
|
return fmt.Sprintf("Current channel: %s", msg.Channel), true
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Unknown show target: %s", args[0]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
case "/list":
|
||||||
|
if len(args) < 1 {
|
||||||
|
return "Usage: /list [models|channels]", true
|
||||||
|
}
|
||||||
|
switch args[0] {
|
||||||
|
case "models":
|
||||||
|
// TODO: Fetch available models dynamically if possible
|
||||||
|
return "Available models: glm-4.7, claude-3-5-sonnet, gpt-4o (configured in config.json/env)", true
|
||||||
|
case "channels":
|
||||||
|
if al.channelManager == nil {
|
||||||
|
return "Channel manager not initialized", true
|
||||||
|
}
|
||||||
|
channels := al.channelManager.GetEnabledChannels()
|
||||||
|
if len(channels) == 0 {
|
||||||
|
return "No channels enabled", true
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Enabled channels: %s", strings.Join(channels, ", ")), true
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Unknown list target: %s", args[0]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
case "/switch":
|
||||||
|
if len(args) < 3 || args[1] != "to" {
|
||||||
|
return "Usage: /switch [model|channel] to <name>", true
|
||||||
|
}
|
||||||
|
target := args[0]
|
||||||
|
value := args[2]
|
||||||
|
|
||||||
|
switch target {
|
||||||
|
case "model":
|
||||||
|
oldModel := al.model
|
||||||
|
al.model = value
|
||||||
|
return fmt.Sprintf("Switched model from %s to %s", oldModel, value), true
|
||||||
|
case "channel":
|
||||||
|
// This changes the 'default' channel for some operations, or effectively redirects output?
|
||||||
|
// For now, let's just validate if the channel exists
|
||||||
|
if al.channelManager == nil {
|
||||||
|
return "Channel manager not initialized", true
|
||||||
|
}
|
||||||
|
if _, exists := al.channelManager.GetChannel(value); !exists && value != "cli" {
|
||||||
|
return fmt.Sprintf("Channel '%s' not found or not enabled", value), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If message came from CLI, maybe we want to redirect CLI output to this channel?
|
||||||
|
// That would require state persistence about "redirected channel"
|
||||||
|
// For now, just acknowledged.
|
||||||
|
return fmt.Sprintf("Switched target channel to %s (Note: this currently only validates existence)", value), true
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Unknown switch target: %s", target), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue