mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
fix(command): add missing command handler wiring and lint fixes
Wire SetCommandHandler into ChannelInboundProcessor so slash commands are intercepted before reaching the LLM. Also apply lint fixes across command package (strconv.Itoa, comment formatting, unused code removal) and remove obsolete tool-call-browser.vue component.
This commit is contained in:
+1
-1
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/memohai/memoh/internal/bind"
|
||||
"github.com/memohai/memoh/internal/boot"
|
||||
"github.com/memohai/memoh/internal/bots"
|
||||
"github.com/memohai/memoh/internal/browsercontexts"
|
||||
agentruntime "github.com/memohai/memoh/internal/bun/runtime"
|
||||
"github.com/memohai/memoh/internal/channel"
|
||||
"github.com/memohai/memoh/internal/channel/adapters/discord"
|
||||
@@ -48,7 +49,6 @@ import (
|
||||
emailmailgun "github.com/memohai/memoh/internal/email/adapters/mailgun"
|
||||
"github.com/memohai/memoh/internal/handlers"
|
||||
"github.com/memohai/memoh/internal/healthcheck"
|
||||
"github.com/memohai/memoh/internal/browsercontexts"
|
||||
channelchecker "github.com/memohai/memoh/internal/healthcheck/checkers/channel"
|
||||
mcpchecker "github.com/memohai/memoh/internal/healthcheck/checkers/mcp"
|
||||
"github.com/memohai/memoh/internal/heartbeat"
|
||||
|
||||
@@ -57,7 +57,7 @@ func (a *FeishuAdapter) enrichQuotedMessage(ctx context.Context, cfg channel.Cha
|
||||
a.logger.Debug("feishu quoted message fetch empty",
|
||||
slog.String("parent_id", parentID),
|
||||
slog.Int("code", code),
|
||||
slog.String("msg", respMsg),
|
||||
slog.String("response_msg", respMsg),
|
||||
)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/memohai/memoh/internal/auth"
|
||||
"github.com/memohai/memoh/internal/channel"
|
||||
"github.com/memohai/memoh/internal/channel/route"
|
||||
"github.com/memohai/memoh/internal/command"
|
||||
"github.com/memohai/memoh/internal/conversation"
|
||||
"github.com/memohai/memoh/internal/conversation/flow"
|
||||
"github.com/memohai/memoh/internal/inbox"
|
||||
@@ -55,18 +56,19 @@ type mediaIngestor interface {
|
||||
|
||||
// ChannelInboundProcessor routes channel inbound messages to the chat gateway.
|
||||
type ChannelInboundProcessor struct {
|
||||
runner flow.Runner
|
||||
routeResolver RouteResolver
|
||||
message messagepkg.Writer
|
||||
mediaService mediaIngestor
|
||||
reactor channelReactor
|
||||
inboxService *inbox.Service
|
||||
registry *channel.Registry
|
||||
logger *slog.Logger
|
||||
jwtSecret string
|
||||
tokenTTL time.Duration
|
||||
identity *IdentityResolver
|
||||
observer channel.StreamObserver
|
||||
runner flow.Runner
|
||||
routeResolver RouteResolver
|
||||
message messagepkg.Writer
|
||||
mediaService mediaIngestor
|
||||
reactor channelReactor
|
||||
inboxService *inbox.Service
|
||||
commandHandler *command.Handler
|
||||
registry *channel.Registry
|
||||
logger *slog.Logger
|
||||
jwtSecret string
|
||||
tokenTTL time.Duration
|
||||
identity *IdentityResolver
|
||||
observer channel.StreamObserver
|
||||
}
|
||||
|
||||
// NewChannelInboundProcessor creates a processor with channel identity-based resolution.
|
||||
@@ -146,6 +148,15 @@ func (p *ChannelInboundProcessor) SetInboxService(service *inbox.Service) {
|
||||
p.inboxService = service
|
||||
}
|
||||
|
||||
// SetCommandHandler configures the slash command handler for intercepting
|
||||
// /command messages before they reach the LLM.
|
||||
func (p *ChannelInboundProcessor) SetCommandHandler(handler *command.Handler) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
p.commandHandler = handler
|
||||
}
|
||||
|
||||
// HandleInbound processes an inbound channel message through identity resolution and chat gateway.
|
||||
func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel.ChannelConfig, msg channel.InboundMessage, sender channel.StreamReplySender) error {
|
||||
if p.runner == nil {
|
||||
@@ -195,6 +206,19 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel
|
||||
}
|
||||
|
||||
identity := state.Identity
|
||||
|
||||
// Intercept slash commands before they reach the LLM.
|
||||
if p.commandHandler != nil && p.commandHandler.IsCommand(text) {
|
||||
reply, err := p.commandHandler.Execute(ctx, strings.TrimSpace(identity.BotID), strings.TrimSpace(identity.ChannelIdentityID), text)
|
||||
if err != nil {
|
||||
reply = "Error: " + err.Error()
|
||||
}
|
||||
return sender.Send(ctx, channel.OutboundMessage{
|
||||
Target: strings.TrimSpace(msg.ReplyTarget),
|
||||
Message: channel.Message{Text: reply},
|
||||
})
|
||||
}
|
||||
|
||||
resolvedAttachments := p.ingestInboundAttachments(ctx, cfg, msg, strings.TrimSpace(identity.BotID), msg.Message.Attachments)
|
||||
attachments := mapChannelToChatAttachments(resolvedAttachments)
|
||||
text = buildInboundQuery(msg.Message, attachments)
|
||||
@@ -837,7 +861,6 @@ func buildChannelMessage(output conversation.AssistantOutput, capabilities chann
|
||||
return msg
|
||||
}
|
||||
|
||||
|
||||
func contentPartHasValue(part conversation.ContentPart) bool {
|
||||
if strings.TrimSpace(part.Text) != "" {
|
||||
return true
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -91,5 +90,5 @@ func buildPermString(read, write, del bool) string {
|
||||
if len(parts) == 0 {
|
||||
return "none"
|
||||
}
|
||||
return fmt.Sprintf("%s", strings.Join(parts, ", "))
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ import (
|
||||
//
|
||||
// Example output:
|
||||
//
|
||||
// - mybot
|
||||
// Description: A helpful assistant
|
||||
// ID: abc123
|
||||
// - mybot
|
||||
// Description: A helpful assistant
|
||||
// ID: abc123
|
||||
//
|
||||
// - another
|
||||
// Description: Something else
|
||||
// ID: def456
|
||||
// - another
|
||||
// Description: Something else
|
||||
// ID: def456
|
||||
func formatItems(items [][]kv) string {
|
||||
if len(items) == 0 {
|
||||
return ""
|
||||
@@ -42,8 +42,8 @@ func formatItems(items [][]kv) string {
|
||||
//
|
||||
// Example output:
|
||||
//
|
||||
// - ID: abc123
|
||||
// - Name: mybot
|
||||
// - ID: abc123
|
||||
// - Name: mybot
|
||||
func formatKV(pairs []kv) string {
|
||||
var b strings.Builder
|
||||
for _, p := range pairs {
|
||||
|
||||
+10
-10
@@ -49,17 +49,17 @@ type Handler struct {
|
||||
mcpConnService *mcp.ConnectionService
|
||||
inboxService *inbox.Service
|
||||
|
||||
modelsService *models.Service
|
||||
providersService *providers.Service
|
||||
memProvService *memprovider.Service
|
||||
searchProvService *searchproviders.Service
|
||||
browserCtxService *browsercontexts.Service
|
||||
emailService *emailpkg.Service
|
||||
modelsService *models.Service
|
||||
providersService *providers.Service
|
||||
memProvService *memprovider.Service
|
||||
searchProvService *searchproviders.Service
|
||||
browserCtxService *browsercontexts.Service
|
||||
emailService *emailpkg.Service
|
||||
emailOutboxService *emailpkg.OutboxService
|
||||
heartbeatService *heartbeat.Service
|
||||
queries *dbsqlc.Queries
|
||||
skillLoader SkillLoader
|
||||
containerFS ContainerFS
|
||||
heartbeatService *heartbeat.Service
|
||||
queries *dbsqlc.Queries
|
||||
skillLoader SkillLoader
|
||||
containerFS ContainerFS
|
||||
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
@@ -29,10 +29,6 @@ type fakeSubagentService struct {
|
||||
items []subagent.Subagent
|
||||
}
|
||||
|
||||
func (f *fakeSubagentService) list() []subagent.Subagent {
|
||||
return f.items
|
||||
}
|
||||
|
||||
type fakeScheduleService struct {
|
||||
items []schedule.Schedule
|
||||
}
|
||||
@@ -325,7 +321,7 @@ func TestNewCommands_NilServices(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// suppress unused warnings
|
||||
// suppress unused warnings.
|
||||
var (
|
||||
_ = fakeSubagentService{items: []subagent.Subagent{{ID: "1", Name: "test", CreatedAt: time.Now(), UpdatedAt: time.Now()}}}
|
||||
_ = fakeScheduleService{items: []schedule.Schedule{{ID: "1", Name: "test"}}}
|
||||
|
||||
@@ -13,7 +13,7 @@ type ParsedCommand struct {
|
||||
}
|
||||
|
||||
// Parse parses a raw command string into its components.
|
||||
// Expected format: /resource [action] [args...]
|
||||
// Expected format: /resource [action] [args...].
|
||||
func Parse(text string) (ParsedCommand, error) {
|
||||
text = strings.TrimSpace(text)
|
||||
if !strings.HasPrefix(text, "/") {
|
||||
|
||||
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/memohai/memoh/internal/schedule"
|
||||
@@ -45,7 +46,7 @@ func (h *Handler) buildScheduleGroup() *CommandGroup {
|
||||
}
|
||||
maxCalls := "unlimited"
|
||||
if item.MaxCalls != nil {
|
||||
maxCalls = fmt.Sprintf("%d", *item.MaxCalls)
|
||||
maxCalls = strconv.Itoa(*item.MaxCalls)
|
||||
}
|
||||
return formatKV([]kv{
|
||||
{"Name", item.Name},
|
||||
@@ -54,7 +55,7 @@ func (h *Handler) buildScheduleGroup() *CommandGroup {
|
||||
{"Command", item.Command},
|
||||
{"Enabled", boolStr(item.Enabled)},
|
||||
{"Max Calls", maxCalls},
|
||||
{"Current Calls", fmt.Sprintf("%d", item.CurrentCalls)},
|
||||
{"Current Calls", strconv.Itoa(item.CurrentCalls)},
|
||||
{"Created", item.CreatedAt.Format("2006-01-02 15:04:05")},
|
||||
{"Updated", item.UpdatedAt.Format("2006-01-02 15:04:05")},
|
||||
}), nil
|
||||
|
||||
@@ -23,8 +23,8 @@ func (h *Handler) buildSettingsGroup() *CommandGroup {
|
||||
{"Language", s.Language},
|
||||
{"Allow Guest", boolStr(s.AllowGuest)},
|
||||
{"Max Context Load Time", fmt.Sprintf("%d min", s.MaxContextLoadTime)},
|
||||
{"Max Context Tokens", fmt.Sprintf("%d", s.MaxContextTokens)},
|
||||
{"Max Inbox Items", fmt.Sprintf("%d", s.MaxInboxItems)},
|
||||
{"Max Context Tokens", strconv.Itoa(s.MaxContextTokens)},
|
||||
{"Max Inbox Items", strconv.Itoa(s.MaxInboxItems)},
|
||||
{"Reasoning Enabled", boolStr(s.ReasoningEnabled)},
|
||||
{"Reasoning Effort", s.ReasoningEffort},
|
||||
{"Heartbeat Enabled", boolStr(s.HeartbeatEnabled)},
|
||||
@@ -126,13 +126,6 @@ func settingsUpdateUsage() string {
|
||||
"- --heartbeat_model_id <id>"
|
||||
}
|
||||
|
||||
func valueOrNone(s string) string {
|
||||
if s == "" {
|
||||
return "(none)"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// resolveModelName resolves a model UUID to "model_name (provider_name)".
|
||||
func (h *Handler) resolveModelName(cc CommandContext, modelID string) string {
|
||||
if modelID == "" {
|
||||
|
||||
Reference in New Issue
Block a user