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:
@@ -511,7 +511,7 @@ WITH updated AS (
|
||||
SET display_name = $1,
|
||||
updated_at = now()
|
||||
WHERE bots.id = $2
|
||||
RETURNING id, owner_user_id, display_name, avatar_url, timezone, is_active, status, language, reasoning_enabled, reasoning_effort, chat_model_id, search_provider_id, memory_provider_id, heartbeat_enabled, heartbeat_interval, heartbeat_prompt, heartbeat_model_id, compaction_enabled, compaction_threshold, compaction_ratio, compaction_model_id, title_model_id, image_model_id, discuss_probe_model_id, tts_model_id, transcription_model_id, browser_context_id, persist_full_tool_results, metadata, created_at, updated_at, acl_default_effect
|
||||
RETURNING id, owner_user_id, display_name, avatar_url, timezone, is_active, status, language, reasoning_enabled, reasoning_effort, chat_model_id, search_provider_id, memory_provider_id, heartbeat_enabled, heartbeat_interval, heartbeat_prompt, heartbeat_model_id, compaction_enabled, compaction_threshold, compaction_ratio, compaction_model_id, title_model_id, image_model_id, discuss_probe_model_id, tts_model_id, transcription_model_id, browser_context_id, persist_full_tool_results, show_tool_calls_in_im, metadata, created_at, updated_at, acl_default_effect
|
||||
)
|
||||
SELECT
|
||||
updated.id AS id,
|
||||
|
||||
@@ -37,6 +37,7 @@ type Bot struct {
|
||||
TranscriptionModelID pgtype.UUID `json:"transcription_model_id"`
|
||||
BrowserContextID pgtype.UUID `json:"browser_context_id"`
|
||||
PersistFullToolResults bool `json:"persist_full_tool_results"`
|
||||
ShowToolCallsInIm bool `json:"show_tool_calls_in_im"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
|
||||
@@ -33,6 +33,7 @@ SET language = 'auto',
|
||||
transcription_model_id = NULL,
|
||||
browser_context_id = NULL,
|
||||
persist_full_tool_results = false,
|
||||
show_tool_calls_in_im = false,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
`
|
||||
@@ -65,7 +66,8 @@ SELECT
|
||||
tts_models.id AS tts_model_id,
|
||||
transcription_models.id AS transcription_model_id,
|
||||
browser_contexts.id AS browser_context_id,
|
||||
bots.persist_full_tool_results
|
||||
bots.persist_full_tool_results,
|
||||
bots.show_tool_calls_in_im
|
||||
FROM bots
|
||||
LEFT JOIN models AS chat_models ON chat_models.id = bots.chat_model_id
|
||||
LEFT JOIN models AS heartbeat_models ON heartbeat_models.id = bots.heartbeat_model_id
|
||||
@@ -103,6 +105,7 @@ type GetSettingsByBotIDRow struct {
|
||||
TranscriptionModelID pgtype.UUID `json:"transcription_model_id"`
|
||||
BrowserContextID pgtype.UUID `json:"browser_context_id"`
|
||||
PersistFullToolResults bool `json:"persist_full_tool_results"`
|
||||
ShowToolCallsInIm bool `json:"show_tool_calls_in_im"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetSettingsByBotID(ctx context.Context, id pgtype.UUID) (GetSettingsByBotIDRow, error) {
|
||||
@@ -131,6 +134,7 @@ func (q *Queries) GetSettingsByBotID(ctx context.Context, id pgtype.UUID) (GetSe
|
||||
&i.TranscriptionModelID,
|
||||
&i.BrowserContextID,
|
||||
&i.PersistFullToolResults,
|
||||
&i.ShowToolCallsInIm,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -159,9 +163,10 @@ WITH updated AS (
|
||||
transcription_model_id = COALESCE($19::uuid, bots.transcription_model_id),
|
||||
browser_context_id = COALESCE($20::uuid, bots.browser_context_id),
|
||||
persist_full_tool_results = $21,
|
||||
show_tool_calls_in_im = $22,
|
||||
updated_at = now()
|
||||
WHERE bots.id = $22
|
||||
RETURNING bots.id, bots.language, bots.reasoning_enabled, bots.reasoning_effort, bots.heartbeat_enabled, bots.heartbeat_interval, bots.heartbeat_prompt, bots.compaction_enabled, bots.compaction_threshold, bots.compaction_ratio, bots.timezone, bots.chat_model_id, bots.heartbeat_model_id, bots.compaction_model_id, bots.title_model_id, bots.image_model_id, bots.search_provider_id, bots.memory_provider_id, bots.tts_model_id, bots.transcription_model_id, bots.browser_context_id, bots.persist_full_tool_results
|
||||
WHERE bots.id = $23
|
||||
RETURNING bots.id, bots.language, bots.reasoning_enabled, bots.reasoning_effort, bots.heartbeat_enabled, bots.heartbeat_interval, bots.heartbeat_prompt, bots.compaction_enabled, bots.compaction_threshold, bots.compaction_ratio, bots.timezone, bots.chat_model_id, bots.heartbeat_model_id, bots.compaction_model_id, bots.title_model_id, bots.image_model_id, bots.search_provider_id, bots.memory_provider_id, bots.tts_model_id, bots.transcription_model_id, bots.browser_context_id, bots.persist_full_tool_results, bots.show_tool_calls_in_im
|
||||
)
|
||||
SELECT
|
||||
updated.id AS bot_id,
|
||||
@@ -185,7 +190,8 @@ SELECT
|
||||
tts_models.id AS tts_model_id,
|
||||
transcription_models.id AS transcription_model_id,
|
||||
browser_contexts.id AS browser_context_id,
|
||||
updated.persist_full_tool_results
|
||||
updated.persist_full_tool_results,
|
||||
updated.show_tool_calls_in_im
|
||||
FROM updated
|
||||
LEFT JOIN models AS chat_models ON chat_models.id = updated.chat_model_id
|
||||
LEFT JOIN models AS heartbeat_models ON heartbeat_models.id = updated.heartbeat_model_id
|
||||
@@ -221,6 +227,7 @@ type UpsertBotSettingsParams struct {
|
||||
TranscriptionModelID pgtype.UUID `json:"transcription_model_id"`
|
||||
BrowserContextID pgtype.UUID `json:"browser_context_id"`
|
||||
PersistFullToolResults bool `json:"persist_full_tool_results"`
|
||||
ShowToolCallsInIm bool `json:"show_tool_calls_in_im"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
}
|
||||
|
||||
@@ -247,6 +254,7 @@ type UpsertBotSettingsRow struct {
|
||||
TranscriptionModelID pgtype.UUID `json:"transcription_model_id"`
|
||||
BrowserContextID pgtype.UUID `json:"browser_context_id"`
|
||||
PersistFullToolResults bool `json:"persist_full_tool_results"`
|
||||
ShowToolCallsInIm bool `json:"show_tool_calls_in_im"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsParams) (UpsertBotSettingsRow, error) {
|
||||
@@ -272,6 +280,7 @@ func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsPa
|
||||
arg.TranscriptionModelID,
|
||||
arg.BrowserContextID,
|
||||
arg.PersistFullToolResults,
|
||||
arg.ShowToolCallsInIm,
|
||||
arg.ID,
|
||||
)
|
||||
var i UpsertBotSettingsRow
|
||||
@@ -298,6 +307,7 @@ func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsPa
|
||||
&i.TranscriptionModelID,
|
||||
&i.BrowserContextID,
|
||||
&i.PersistFullToolResults,
|
||||
&i.ShowToolCallsInIm,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user