mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat: add per-message model and reasoning effort override
Allow users to select a different model and reasoning effort level directly from the chat input toolbar, overriding the bot defaults on a per-message basis. The backend accepts optional model_id and reasoning_effort parameters via both WebSocket and HTTP APIs, with request-level values taking priority over bot/session settings. - Backend: extend wsClientMessage and LocalChannelMessageRequest with model_id/reasoning_effort fields; add ReasoningEffort to ChatRequest; update resolver to prioritize request-level reasoning effort - Frontend: add ModelOptions and ReasoningEffortSelect shared components; refactor model-select to reuse ModelOptions; add model/reasoning selectors to chat input toolbar; initialize from bot settings - Regenerate swagger spec and TypeScript SDK
This commit is contained in:
@@ -516,7 +516,7 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel
|
||||
return result
|
||||
}
|
||||
|
||||
chunkCh, streamErrCh := p.runner.StreamChat(ctx, conversation.ChatRequest{
|
||||
chatReq := conversation.ChatRequest{
|
||||
BotID: identity.BotID,
|
||||
ChatID: activeChatID,
|
||||
SessionID: sessionID,
|
||||
@@ -536,7 +536,14 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel
|
||||
UserMessagePersisted: false,
|
||||
Attachments: attachments,
|
||||
OutboundAssetCollector: assetCollector,
|
||||
})
|
||||
}
|
||||
if mid, _ := msg.Metadata["model_id"].(string); strings.TrimSpace(mid) != "" {
|
||||
chatReq.Model = strings.TrimSpace(mid)
|
||||
}
|
||||
if re, _ := msg.Metadata["reasoning_effort"].(string); strings.TrimSpace(re) != "" {
|
||||
chatReq.ReasoningEffort = strings.TrimSpace(re)
|
||||
}
|
||||
chunkCh, streamErrCh := p.runner.StreamChat(ctx, chatReq)
|
||||
|
||||
var (
|
||||
finalMessages []conversation.ModelMessage
|
||||
|
||||
@@ -234,8 +234,12 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r
|
||||
inlineImages := extractNativeImageParts(mergedAttachments)
|
||||
|
||||
reasoningEffort := ""
|
||||
if chatModel.HasCompatibility(models.CompatReasoning) && botSettings.ReasoningEnabled {
|
||||
reasoningEffort = botSettings.ReasoningEffort
|
||||
if chatModel.HasCompatibility(models.CompatReasoning) {
|
||||
if re := strings.TrimSpace(req.ReasoningEffort); re != "" {
|
||||
reasoningEffort = re
|
||||
} else if botSettings.ReasoningEnabled {
|
||||
reasoningEffort = botSettings.ReasoningEffort
|
||||
}
|
||||
}
|
||||
|
||||
var reasoningConfig *models.ReasoningConfig
|
||||
|
||||
@@ -241,13 +241,14 @@ type ChatRequest struct {
|
||||
// Set by the inbound channel processor; called by the resolver at persist time.
|
||||
OutboundAssetCollector func() []OutboundAssetRef `json:"-"`
|
||||
|
||||
Query string `json:"query"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
Channels []string `json:"channels,omitempty"`
|
||||
CurrentChannel string `json:"current_channel,omitempty"`
|
||||
Messages []ModelMessage `json:"messages,omitempty"`
|
||||
Attachments []ChatAttachment `json:"attachments,omitempty"`
|
||||
Query string `json:"query"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||
Channels []string `json:"channels,omitempty"`
|
||||
CurrentChannel string `json:"current_channel,omitempty"`
|
||||
Messages []ModelMessage `json:"messages,omitempty"`
|
||||
Attachments []ChatAttachment `json:"attachments,omitempty"`
|
||||
}
|
||||
|
||||
// ChatResponse is the output of a non-streaming chat call.
|
||||
|
||||
@@ -168,7 +168,9 @@ func formatLocalStreamEvent(event channel.StreamEvent) ([]byte, error) {
|
||||
|
||||
// LocalChannelMessageRequest is the request body for posting a local channel message.
|
||||
type LocalChannelMessageRequest struct {
|
||||
Message channel.Message `json:"message"`
|
||||
Message channel.Message `json:"message"`
|
||||
ModelID string `json:"model_id,omitempty"`
|
||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||
}
|
||||
|
||||
// PostMessage godoc
|
||||
@@ -234,6 +236,18 @@ func (h *LocalChannelHandler) PostMessage(c echo.Context) error {
|
||||
ReceivedAt: time.Now().UTC(),
|
||||
Source: "local",
|
||||
}
|
||||
if mid := strings.TrimSpace(req.ModelID); mid != "" {
|
||||
if msg.Metadata == nil {
|
||||
msg.Metadata = make(map[string]any)
|
||||
}
|
||||
msg.Metadata["model_id"] = mid
|
||||
}
|
||||
if re := strings.TrimSpace(req.ReasoningEffort); re != "" {
|
||||
if msg.Metadata == nil {
|
||||
msg.Metadata = make(map[string]any)
|
||||
}
|
||||
msg.Metadata["reasoning_effort"] = re
|
||||
}
|
||||
if err := h.channelManager.HandleInbound(c.Request().Context(), cfg, msg); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@@ -245,10 +259,12 @@ var wsUpgrader = websocket.Upgrader{
|
||||
}
|
||||
|
||||
type wsClientMessage struct {
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
SessionID string `json:"session_id,omitempty"`
|
||||
Attachments []json.RawMessage `json:"attachments,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Text string `json:"text,omitempty"`
|
||||
SessionID string `json:"session_id,omitempty"`
|
||||
Attachments []json.RawMessage `json:"attachments,omitempty"`
|
||||
ModelID string `json:"model_id,omitempty"`
|
||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||
}
|
||||
|
||||
// wsWriter serialises all WebSocket writes through a single goroutine to
|
||||
@@ -419,6 +435,8 @@ func (h *LocalChannelHandler) HandleWebSocket(c echo.Context) error {
|
||||
CurrentChannel: h.channelType.String(),
|
||||
Channels: []string{h.channelType.String()},
|
||||
Attachments: chatAttachments,
|
||||
Model: strings.TrimSpace(msg.ModelID),
|
||||
ReasoningEffort: strings.TrimSpace(msg.ReasoningEffort),
|
||||
}
|
||||
if streamErr := h.resolver.StreamChatWS(streamCtx, req, eventCh, abortCh); streamErr != nil {
|
||||
if ctx.Err() == nil {
|
||||
|
||||
Reference in New Issue
Block a user