mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
✨ feat(follow-up): extend follow-up chip suggestions to general chat (#15101)
* ✨ feat(follow-up): add foundation types for chat follow-up chips - FollowUpExtractInput.threadId for portal thread isolation - UserSystemAgentConfig.followUpAction (global enable + model) - LobeAgentChatConfig.enableFollowUpChips (per-agent opt-in) - ConversationHooks.onAssistantTurnSettled first-class member - Remove dead onGenerationStart/Complete/Cancelled hooks - DEFAULT_SYSTEM_AGENT_CONFIG.followUpAction off by default - DEFAULT_AGENT_CHAT_CONFIG.enableFollowUpChips false default * ♻️ refactor(follow-up): key follow-up store by conversation for concurrency - Convert useFollowUpActionStore from single-slot to slots map - conversationKey = messageMapKey(agentId, topicId, threadId?) for parity with chat store - contextSelectors.conversationKey exposes the key from ConversationProvider - FollowUpChips and ChatItem consume conversationKey - Onboarding hook adopts the new keyed API - Pass threadId through to extract (server filter lands in T3) * 🐛 fix(follow-up): address T2 code review feedback - Restore design-intent comments for 20s timeout and race guard - Remove dead pendingMessageId field from FollowUpActionSlot - Remove unused slotFor selector - Trim chipsFor / FollowUpActionSlot JSDoc to design intent only - Gate useOnboardingFollowUp against missing onboardingAgentId - removeSlot uses destructure; slotStatus uses ?? for falsy safety * ✨ feat(follow-up): filter extract by threadId for portal thread isolation - FollowUpActionService.extract honours optional threadId - threadId provided → eq(messages.threadId, threadId) - threadId absent → isNull(messages.threadId) so main topic never surfaces thread replies - Tests cover both branches * ✨ feat(conversation): emit onAssistantTurnSettled hook from provider - AssistantTurnSettledWatcher fires hooks.onAssistantTurnSettled(messageId, { reason }) once per turn - Reason derived from the most recent terminal Operation for the message id - Reason mapping: cancelled → stopped, type=regenerate → regenerated, type=continue → continued, else → completed - Settlement gated on idle + no pending tool intervention (mirrors Onboarding's logic) - Tests cover all four reason branches + intervention gating + no double-fire + fallback log - Onboarding bespoke prop untouched (migrates in T6) * 🐛 fix(conversation): scope settlement reason to turn-level operations - TURN_LEVEL_TYPES filter excludes child sub-ops (callLLM, executeToolCall, etc.) before sorting by endTime - Prevents successful regenerate/continue being misreported as 'completed' when a child finishes after the parent - Tests cover parent/child ordering for all reason branches * ✨ feat(follow-up): add useChatFollowUp hook and wire chat mount sites - New mergeConversationHooks composes multiple hooks with boolean short-circuit - useChatFollowUp computes effective enable (global × per-agent × valid model) - Registers onBeforeSendMessage/Continue/Regenerate to clear slot and onAssistantTurnSettled to extract - Mount sites: agent route ConversationArea, FloatingChatPanel, Portal Thread Chat (last in chain per §4.6) - Skips on reason='stopped'; skips when effective is false - Group chat intentionally not mounted * ♻️ refactor(onboarding): migrate settlement to ConversationHooks first-class - Drop bespoke onAssistantTurnSettled prop and duplicate useEffect from AgentOnboardingConversation - useOnboardingFollowUp returns ConversationHooks { onBeforeSendMessage, onAssistantTurnSettled } - Split settlement work: context-sync + builtin refresh runs first, chip extract runs after - Phase snapshot captured at memoize time preserves original prevPhase semantics - Settlement detection now lives solely in AssistantTurnSettledWatcher * ✨ feat(settings): add Follow-up suggestions controls (global + per-agent) - Global System Agent page: new Follow-up Suggestions panel (model picker + enable toggle) - Per-agent chat controls: enableFollowUpChips toggle with hint when global not configured - i18n keys: setting.systemAgent.followUpAction.*, setting.settingChat.enableFollowUpChips.* - Hint surfaces when user toggles per-agent ON but global is disabled/unmodeled * 🔧 chore(follow-up): T8 — scoped lint cleanup and comment discipline pass * 🐛 fix(follow-up): align conversationKey selector with callsite + wrap single hook - contextSelectors.conversationKey forwards full context (scope/isNew/groupId/subAgentId) so portal-thread NEW state matches callsite-computed keys - ConversationArea wraps chat-follow-up via mergeConversationHooks for spec §4.6 ordering robustness - Both per final-review Important concerns * ✅ test(settings): update follow-up defaults snapshots * ✨ feat(follow-up): surface model in service-model page + default to mini - Add followUpAction to /service-model OPTIONAL_FEATURE_ITEMS so model/provider and enable Switch render alongside inputCompletion and promptRewrite - Seed DEFAULT_FOLLOW_UP_ACTION_SYSTEM_AGENT_ITEM with DEFAULT_MINI model/provider so out-of-box config has a valid model; users only need to flip enabled - Sync settings selector snapshot
This commit is contained in:
@@ -25,7 +25,7 @@ The two reference tools to read end-to-end:
|
||||
1. **先保证折叠态可读。** 每个 API 都必须有 Inspector;用户不展开也应该能看懂 “正在做什么 / 对什么做 / 当前结果是什么”。Inspector 不应该只展示函数名和原始参数。
|
||||
2. **Inspector 是一句话,不是详情页。** 优先表达动作、关键对象、数量、状态,例如 “分析图片 3 张”“搜索 12 个结果”“读取 config.json”。长文本、列表和结构化结果放到 Render 或 Portal。
|
||||
3. **Inspector 要覆盖执行生命周期。** `args` 还在 streaming、工具执行中、执行完成、执行失败时都应该有稳定展示;必要时同时读取 `args`、`partialArgs` 和 `pluginState`,避免出现空白、跳变或只显示半截参数。
|
||||
4. **文案要随状态切换时态。** 同一个动作在 loading 与 completed 两个阶段必须用不同的措辞:执行中用现在进行时(“正在创建任务 / Creating task / 正在搜索”),执行完成后切到完成态(“已创建任务 / Task created / 已找到 N 条”)。Inspector chip 会一直留在聊天记录里——如果一直挂着 “正在 xxx”,几小时后回看历史时会读起来像还在跑。约定的 i18n 形式是 `<api>.loading` / `<api>.completed` 一对键(见 `lobe-agent.apiName.callSubAgent.{loading,completed}` 与 `lobe-claude-code.task.{create,list,update,get}.{loading,completed}`),渲染时按 `isArgumentsStreaming || isLoading` 决定取哪一个。只读 / 查询类(“查看任务”这种本来就是名词性的)可以共用一个键。
|
||||
4. **文案要随状态切换时态。** 同一个动作在 loading 与 completed 两个阶段必须用不同的措辞:执行中用现在进行时(“正在创建任务 / Creating task / 正在搜索”),执行完成后切到完成态(“已创建任务 / Task created / 已找到 N 条”)。Inspector chip 会一直留在聊天记录里 —— 如果一直挂着 “正在 xxx”,几小时后回看历史时会读起来像还在跑。约定的 i18n 形式是 `<api>.loading` / `<api>.completed` 一对键(见 `lobe-agent.apiName.callSubAgent.{loading,completed}` 与 `lobe-claude-code.task.{create,list,update,get}.{loading,completed}`),渲染时按 `isArgumentsStreaming || isLoading` 决定取哪一个。只读 / 查询类(“查看任务” 这种本来就是名词性的)可以共用一个键。
|
||||
5. **只有结构化结果才需要 Render。** 如果工具结果只是自然语言总结,通常不需要 Render;如果结果包含列表、媒体、文件、表格、代码、diff、地图、时间线、权限请求等结构,就应该提供 Render。
|
||||
6. **Render 要帮助用户检查结果,而不是复述参数。** Render 的主体应该围绕工具产物组织:可预览、可比较、可筛选、可定位。参数只作为上下文辅助出现,不要把 Render 做成一块更大的 args dump。
|
||||
7. **参数和结果要一起参与渲染。** 好的 Tool UI 通常同时用 `args` 解释意图,用 `pluginState` 展示真实执行结果;但 `pluginState` 只放结果域数据,不要反向塞入可以从 `args` 推导出的内容。
|
||||
|
||||
Reference in New Issue
Block a user