mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat(channel): structured tool-call IM display with edit-in-place
Introduce a new `show_tool_calls_in_im` bot setting plus a full overhaul of how tool calls are surfaced in IM channels: - Add per-bot setting + migration (0072) and expose through settings API / handlers / frontend SDK. - Introduce a `toolCallDroppingStream` wrapper that filters tool_call_* events when the setting is off, keeping the rest of the stream intact. - Add a shared `ToolCallPresentation` model (Header / Body blocks / Footer) with plain and Markdown renderers, and a per-tool formatter registry that produces rich output (e.g. `web_search` link lists, `list` directory previews, `exec` stdout/stderr tails) instead of raw JSON dumps. - High-capability adapters (Telegram, Feishu, Matrix, Slack, Discord) now flush pre-text and then send ONE tool-call message per call, editing it in-place from `running` to `completed` / `failed`; mapping from callID to platform message ID is tracked per stream, with a fallback to a new message if the edit fails. Low-capability adapters (WeCom, QQ, DingTalk) keep posting a single final message, but now benefit from the same rich per-tool formatting. - Suppress the early duplicate `EventToolCallStart` (from `sdk.ToolInputStartPart`) so that the SDK's final `StreamToolCallPart` remains the single source of truth for tool call start, preventing duplicated "running" bubbles in IM. - Stop auto-populating `InputSummary` / `ResultSummary` after a per-tool formatter runs, which previously leaked the raw JSON result as a fallback footer underneath the formatted body. Add regression tests for the formatters, the Markdown renderer, the edit-in-place flow on Telegram/Matrix, and the JSON-leak guard on `list`.
This commit is contained in:
@@ -101,6 +101,9 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest
|
||||
if req.PersistFullToolResults != nil {
|
||||
current.PersistFullToolResults = *req.PersistFullToolResults
|
||||
}
|
||||
if req.ShowToolCallsInIM != nil {
|
||||
current.ShowToolCallsInIM = *req.ShowToolCallsInIM
|
||||
}
|
||||
timezoneValue := pgtype.Text{}
|
||||
if req.Timezone != nil {
|
||||
normalized, err := normalizeOptionalTimezone(*req.Timezone)
|
||||
@@ -215,6 +218,7 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest
|
||||
TranscriptionModelID: transcriptionModelUUID,
|
||||
BrowserContextID: browserContextUUID,
|
||||
PersistFullToolResults: current.PersistFullToolResults,
|
||||
ShowToolCallsInIm: current.ShowToolCallsInIM,
|
||||
})
|
||||
if err != nil {
|
||||
return Settings{}, err
|
||||
@@ -310,6 +314,7 @@ func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings {
|
||||
row.TranscriptionModelID,
|
||||
row.BrowserContextID,
|
||||
row.PersistFullToolResults,
|
||||
row.ShowToolCallsInIm,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -335,6 +340,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings {
|
||||
row.TranscriptionModelID,
|
||||
row.BrowserContextID,
|
||||
row.PersistFullToolResults,
|
||||
row.ShowToolCallsInIm,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -359,6 +365,7 @@ func normalizeBotSettingsFields(
|
||||
transcriptionModelID pgtype.UUID,
|
||||
browserContextID pgtype.UUID,
|
||||
persistFullToolResults bool,
|
||||
showToolCallsInIM bool,
|
||||
) Settings {
|
||||
settings := normalizeBotSetting(language, "", reasoningEnabled, reasoningEffort, heartbeatEnabled, heartbeatInterval, compactionEnabled, compactionThreshold, compactionRatio)
|
||||
if timezone.Valid {
|
||||
@@ -395,6 +402,7 @@ func normalizeBotSettingsFields(
|
||||
settings.BrowserContextID = uuid.UUID(browserContextID.Bytes).String()
|
||||
}
|
||||
settings.PersistFullToolResults = persistFullToolResults
|
||||
settings.ShowToolCallsInIM = showToolCallsInIM
|
||||
return settings
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user