@tbox.cn/app-template-agent 0.7.5 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +5 -0
- package/.tbox/app.json +9 -1
- package/AGENTS.md +12 -4
- package/ENV.md +105 -21
- package/README.md +40 -38
- package/apps/client/VISUAL_RESTORE.md +11 -11
- package/apps/client/index.html +3 -1
- package/apps/client/package.json +9 -8
- package/apps/client/src/business/App.tsx +4 -4
- package/apps/client/src/business/README.md +5 -11
- package/apps/client/src/cards/registry.ts +23 -8
- package/apps/client/src/main.tsx +36 -2
- package/apps/client/src/modules.ts +7 -4
- package/apps/client/src/pages/index/index.tsx +18 -162
- package/apps/client/src/platform/AgentChatApp.tsx +7 -6
- package/apps/client/src/platform/agent-runtime.ts +5 -0
- package/apps/client/src/store/chat.ts +47 -185
- package/apps/client/src/test/setup.ts +7 -0
- package/apps/client/vite.config.ts +16 -1
- package/apps/server/package.json +0 -3
- package/apps/server/src/app-tools.ts +2 -1
- package/apps/server/src/app.ts +248 -119
- package/apps/server/src/config/integration.ts +2 -6
- package/apps/server/src/dev.ts +0 -1
- package/apps/server/src/intent-routes.ts +3 -3
- package/apps/server/src/modules.ts +4 -4
- package/apps/server/src/pre-tools.ts +4 -3
- package/apps/server/tests/agent-prompt.test.ts +81 -0
- package/apps/server/tests/cards-registry.test.ts +1 -3
- package/apps/server/tests/deployment-loader.test.ts +35 -56
- package/apps/server/tests/tool-runtime.test.ts +25 -61
- package/package.json +5 -4
- package/packages/agent-ui-kit/LICENSE +21 -0
- package/packages/agent-ui-kit/README.md +17 -0
- package/packages/agent-ui-kit/package.json +58 -0
- package/packages/agent-ui-kit/src/ChatPage.less +62 -0
- package/packages/agent-ui-kit/src/ChatPage.tsx +305 -0
- package/packages/agent-ui-kit/src/chat-config.test.ts +82 -0
- package/packages/agent-ui-kit/src/chat-config.ts +62 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/DebugButton.tsx +2 -2
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.less +19 -16
- package/{apps/client → packages/agent-ui-kit}/src/components/ActionBar/index.tsx +10 -15
- package/packages/agent-ui-kit/src/components/CardRenderer.tsx +36 -0
- package/packages/agent-ui-kit/src/components/ChatHeader/index.less +148 -0
- package/packages/agent-ui-kit/src/components/ChatHeader/index.test.tsx +74 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatHeader/index.tsx +27 -15
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.less +47 -39
- package/packages/agent-ui-kit/src/components/ChatInput/index.test.tsx +156 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/index.tsx +13 -4
- package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/useVoicePress.ts +2 -2
- package/packages/agent-ui-kit/src/components/HistoryEntry/index.less +49 -0
- package/packages/agent-ui-kit/src/components/HistoryEntry/index.tsx +42 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.less +3 -3
- package/{apps/client → packages/agent-ui-kit}/src/components/MediaList/index.tsx +6 -6
- package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.less +25 -20
- package/packages/agent-ui-kit/src/components/MessageBubble/index.test.tsx +112 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/MessageBubble/index.tsx +22 -17
- package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.less +2 -2
- package/{apps/client → packages/agent-ui-kit}/src/components/PullDownIndicator/index.tsx +2 -2
- package/packages/agent-ui-kit/src/components/QuickCommands/index.less +63 -0
- package/packages/agent-ui-kit/src/components/QuickCommands/index.test.tsx +99 -0
- package/packages/agent-ui-kit/src/components/QuickCommands/index.tsx +52 -0
- package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.less +1 -1
- package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.test.ts +346 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/detail.ts +204 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.less +312 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.test.tsx +261 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/index.tsx +212 -0
- package/packages/agent-ui-kit/src/components/ToolCallBlock/titles.ts +102 -0
- package/packages/agent-ui-kit/src/hooks/use-drag-gesture.test.ts +66 -0
- package/packages/agent-ui-kit/src/hooks/use-drag-gesture.ts +129 -0
- package/packages/agent-ui-kit/src/hooks/use-pull-down-history.ts +241 -0
- package/packages/agent-ui-kit/src/hooks/use-pull-down-wheel-native.test.ts +64 -0
- package/{apps/client → packages/agent-ui-kit}/src/hooks/useMediaUpload.ts +3 -3
- package/{apps/client → packages/agent-ui-kit}/src/hooks/useTTSAutoPlay.ts +7 -5
- package/packages/agent-ui-kit/src/index.ts +69 -0
- package/packages/agent-ui-kit/src/store-context.tsx +92 -0
- package/packages/agent-ui-kit/src/test/setup.ts +7 -0
- package/packages/agent-ui-kit/src/theme/contract.json +72 -0
- package/packages/agent-ui-kit/src/theme/global.less +99 -0
- package/packages/agent-ui-kit/src/theme/motion.less +19 -0
- package/packages/agent-ui-kit/src/theme/theme-contract.test.ts +69 -0
- package/packages/agent-ui-kit/src/theme/theme.ts +17 -0
- package/packages/agent-ui-kit/src/theme/tokens.less +79 -0
- package/packages/agent-ui-kit/src/utils/haptics.test.ts +51 -0
- package/packages/agent-ui-kit/src/utils/haptics.ts +44 -0
- package/packages/agent-ui-kit/src/utils/pull-down-machine.test.ts +188 -0
- package/packages/agent-ui-kit/src/utils/pull-down-machine.ts +157 -0
- package/packages/agent-ui-kit/src/utils/tool-call-display.test.ts +240 -0
- package/packages/agent-ui-kit/src/utils/tool-call-display.ts +146 -0
- package/packages/agent-ui-kit/src/vite-env.d.ts +1 -0
- package/packages/agent-ui-kit/tsconfig.json +8 -0
- package/packages/agent-ui-kit/vitest.config.ts +7 -0
- package/pnpm-lock.template.yaml +177 -111
- package/pnpm-workspace.template.yaml +12 -4
- package/profiles/mall/config/agent.jsonc +33 -0
- package/profiles/mall/config/chat.json +21 -0
- package/profiles/mall/config/knowledge.json +5 -0
- package/profiles/mall/config/llm_provider.json +5 -0
- package/profiles/mall/config/plugins.json +3 -0
- package/profiles/mall.json +16 -0
- package/scripts/check-workspace.mjs +3 -2
- package/apps/client/assets/copy@2x.png +0 -0
- package/apps/client/assets/debug.png +0 -0
- package/apps/client/assets/dislike@2x.png +0 -0
- package/apps/client/assets/like@2x.png +0 -0
- package/apps/client/assets/speaker@2x.png +0 -0
- package/apps/client/src/cards/CardRenderer.tsx +0 -20
- package/apps/client/src/components/ChatHeader/index.less +0 -104
- package/apps/client/src/components/ErrorBoundary.tsx +0 -2
- package/apps/client/src/components/QuickCommands/index.less +0 -48
- package/apps/client/src/components/QuickCommands/index.tsx +0 -29
- package/apps/client/src/components/ToolCallBlock/index.less +0 -131
- package/apps/client/src/components/ToolCallBlock/index.tsx +0 -125
- package/apps/client/src/examples/tourism/TourismLanding.tsx +0 -96
- package/apps/client/src/examples/tourism/index.less +0 -192
- package/apps/client/src/pages/index/index.less +0 -43
- package/apps/client/src/store/chat-config.ts +0 -40
- package/apps/client/src/styles/global.less +0 -60
- package/apps/client/src/styles/tokens.less +0 -60
- package/apps/server/src/config/deployment.ts +0 -47
- package/packages/module-weather/package.json +0 -36
- package/packages/module-weather/skills/weather-guide/SKILL.md +0 -22
- package/packages/module-weather/skills/weather-guide/references/dressing-tips.md +0 -5
- package/packages/module-weather/src/client/cards/weather/index.less +0 -22
- package/packages/module-weather/src/client/cards/weather/index.tsx +0 -14
- package/packages/module-weather/src/client/index.ts +0 -19
- package/packages/module-weather/src/index.ts +0 -2
- package/packages/module-weather/src/server/cards/weather/meta.ts +0 -27
- package/packages/module-weather/src/server/index.ts +0 -45
- package/packages/module-weather/src/server/pre-tools.ts +0 -20
- package/packages/module-weather/src/server/tool.ts +0 -29
- package/packages/module-weather/tbox.module.json +0 -27
- package/packages/module-weather/tsconfig.json +0 -14
- /package/{apps/client → packages/agent-ui-kit}/assets/keyboard-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/more-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/new-chat.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/quick-command-star.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/send-gradient.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/stop@2x.png +0 -0
- /package/{apps/client → packages/agent-ui-kit}/assets/voice-circle.svg +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoicePressField.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/VoiceRecordingOverlay.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ChatInput/voice/machine.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.less +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ReasoningBlock/index.tsx +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/components/ScrollToBottomButton/index.tsx +0 -0
- /package/{apps/client/src/types/chat.ts → packages/agent-ui-kit/src/types.ts} +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/copyText.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/imagePreview.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/session-error.ts +0 -0
- /package/{apps/client → packages/agent-ui-kit}/src/utils/toast.ts +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// agent 提示词配置:instructions = 业务段(置前于 SDK 平台规则段);首行身份句,数组 = 提示词行(\n 拼接)。
|
|
2
|
+
// 分层契约:平台规则段管卡片机制协议;本文件管跨域基线(数据新鲜度/注入块定性/操作入口/沟通风格/兜底承接);
|
|
3
|
+
// 域内细则归各域 SKILL.md(经领域指引块注入,域内细则优先于本段基线)。
|
|
4
|
+
// memoryInstructions:装配长期记忆时由 SDK 追加(未装配不拼接)。
|
|
5
|
+
// 注意:不写与平台卡片协议冲突的指令——出不出卡由模型经 <agent-ui-cards> 标签决策,平台协议优先。
|
|
6
|
+
{
|
|
7
|
+
"instructions": [
|
|
8
|
+
"你是商圈智能助理——用户逛商场时的商圈搭子,服务到店顾客,帮助其高效完成会员、停车、优惠与导购相关事务。",
|
|
9
|
+
"# 服务范围",
|
|
10
|
+
"会员(积分/会员卡/入会)、停车(费用/缴费/寻车)、优惠营销(活动/领券/我的券)、导购(商户/门店/商品)、客服(服务政策/退换货/发票/投诉/营业时间等基础信息)。",
|
|
11
|
+
"# 业务规则",
|
|
12
|
+
"业务数据(价格、库存、活动、积分、费用、状态)随时可能变化,以本轮工具结果为准:凡涉及业务数据的问题(查询或操作),必须先调用工具取得本轮结果再作答,禁止凭历史工具结果或常识直接回答。",
|
|
13
|
+
"用户一次提问涉及多个业务域时(如「推荐鞋店 + 积分怎么算」),对每个域分别调用对应工具——即使本轮只注入了其中一个域的操作指引,其余域也必须调工具拿本轮结果,不凭历史轮结论作答。",
|
|
14
|
+
"操作类(缴费、领券、绑定车牌、入会等)都由对应卡片上的按钮完成——需要用户动手时,先拿到本轮结果再引导操作。",
|
|
15
|
+
"智能体自身没有导航能力:回复正文不提及、不承诺导航(「带您过去」「发您路线」等),也不反问「要不要导航过去」——导航入口只存在于卡片上的导航按钮,由用户自行点击;正文最多客观告知门店位置(楼层/铺位)。",
|
|
16
|
+
"user 消息前部可能收到系统注入的 <system-reminder> 数据块(非用户输入;类型与语义以平台协议为准):内部注入,不向用户复述其来源与分类。",
|
|
17
|
+
"pre-tool 块是系统预执行的域数据(商场楼层/类目、客服 FAQ 条目、碰一碰活动详情等):其中 FAQ 条目属政策知识;实时列表/数字类问题(可领券、积分余额、停车费)仍须调用对应域实时工具。",
|
|
18
|
+
"领域指引块是当前业务域的操作规程:命中当前请求所属领域时遵循其场景细则与红线,域内细则与本段不一致时以指引块为准。",
|
|
19
|
+
"支付与授权类操作严格走系统引导流程,不承诺未经系统确认的结果。",
|
|
20
|
+
"# 沟通风格",
|
|
21
|
+
"语言跟随用户:用户用中文提问就用中文回答,用英文提问就用英文回答;禁止在中文对话中夹杂英文句子(专有名词除外)。",
|
|
22
|
+
"语气亲切、简洁,先给结果再给动作;回复正文按轻量 markdown 输出(渲染支持换行、加粗、列表)。",
|
|
23
|
+
"正文保持分段可扫读:多项并列各占一行、组间空一行;长回复按「结论 / 明细 / 收尾」分 2-3 段,不整段堆叠。",
|
|
24
|
+
"重点才加粗(关键数字、门槛、时效,一条回复 3 处以内);不使用标题、表格、代码块、颜色样式——气泡窄屏显示不开。",
|
|
25
|
+
"即将调用工具时,先用一句贴合本轮诉求的话承接再发起(多诉求时一句覆盖全部),让用户立即看到回应;承接句换着说法勿模板化,只说要做什么、不预告结果。",
|
|
26
|
+
"任何回复正文不出现工具名、接口名、字段名(如 listVehicles、customer-service-faq 这类内部词汇),也不出现\"知识库\"\"FAQ\"\"检索\"这类机制词——用户只看得到白话与结果,需要提及操作时用业务语言(如「帮您查停车费」)。",
|
|
27
|
+
"# 兜底承接",
|
|
28
|
+
"超出服务范围或查不到时如实说明,并按最近承接路径引导:服务政策与商场基础信息(营业时间等)转客服 FAQ,FAQ 无结果时如实说明暂无收录并引导联系人工客服;近似导购的诉求转找店逛店;仍解决不了转人工客服。"
|
|
29
|
+
],
|
|
30
|
+
"memoryInstructions": [
|
|
31
|
+
"长期记忆:用户透露的稳定偏好(口味/忌口)、常逛商场与常用服务、车牌等事实值得记住——跨会话服务更贴心;闲聊与一次性信息不记,记忆与当下需求冲突时以当下为准。"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"enableImageUpload": false,
|
|
3
|
+
"enableVideoUpload": false,
|
|
4
|
+
"enableTTS": false,
|
|
5
|
+
"enableVoiceInput": true,
|
|
6
|
+
"defaultInputMode": "TEXT",
|
|
7
|
+
"quickCommands": [
|
|
8
|
+
{
|
|
9
|
+
"label": "今日优惠",
|
|
10
|
+
"query": "现在有什么优惠?"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"label": "品牌导览",
|
|
14
|
+
"query": "品牌导览"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"label": "活动中心",
|
|
18
|
+
"query": "活动中心"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "mall 画像(单一模板统一化 W4):create --profile mall 执行器(W5 接线)按 modules 序列加装 + configOverlay 叠加(--config-dir 通道)。provider 轴独立(缺省 mock-only 演示态;vendor 档由部署者后续 add)。",
|
|
3
|
+
"name": "mall",
|
|
4
|
+
"description": "商圈 AI Agent:家页形态(home 槽)+ 域装配 + 六业务域(导购/会员/停车/营销/活动/客服)",
|
|
5
|
+
"modules": [
|
|
6
|
+
"@tbox.cn/app-module-mall-core",
|
|
7
|
+
"@tbox.cn/app-module-mall-home",
|
|
8
|
+
"@tbox.cn/app-module-shopping-guide",
|
|
9
|
+
"@tbox.cn/app-module-member",
|
|
10
|
+
"@tbox.cn/app-module-parking",
|
|
11
|
+
"@tbox.cn/app-module-promotion",
|
|
12
|
+
"@tbox.cn/app-module-alipay-activity",
|
|
13
|
+
"@tbox.cn/app-module-customer-service"
|
|
14
|
+
],
|
|
15
|
+
"configOverlay": "profiles/mall/config"
|
|
16
|
+
}
|
|
@@ -100,9 +100,10 @@ function printFailure(findings) {
|
|
|
100
100
|
process.exitCode = 1;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/** 豁免:SDK
|
|
103
|
+
/** 豁免:SDK/契约/UI-Kit 包无 tbox.module.json(002 §2.3 / 003 §4.1;kit 为壳 UI 源码包,
|
|
104
|
+
* 单一模板统一化 W2-5——无模块贡献面,非业务模块) */
|
|
104
105
|
function isExemptPackage(dirName) {
|
|
105
|
-
return dirName === 'app-sdk' || dirName === 'shared' || dirName.startsWith('contracts');
|
|
106
|
+
return dirName === 'app-sdk' || dirName === 'shared' || dirName.startsWith('contracts') || dirName === 'agent-ui-kit';
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
/** 校验 packages/* 每个业务包必须有 tbox.module.json(002 §2.3 / 003 §4.1) */
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 卡片渲染入口 — 注入本地 registry 后委托 SDK 的 CardRenderer。
|
|
3
|
-
* 011 B3:动作按钮 → sendUiAction(token 来自 payload.actionTokens)。
|
|
4
|
-
* P1:onAction 升级为完整 UiAction(CardRenderer 按 kind 分发后回传,桥一行透传)。
|
|
5
|
-
*/
|
|
6
|
-
import { CardRenderer as SdkCardRenderer } from '@tbox.cn/app-sdk/client';
|
|
7
|
-
import type { TboxCardPayload, UiAction } from '@tbox.cn/app-contracts';
|
|
8
|
-
import { CARD_REGISTRY } from './registry';
|
|
9
|
-
import { useChatStore } from '@/store/chat';
|
|
10
|
-
|
|
11
|
-
export function CardRenderer({ card }: { card: TboxCardPayload }) {
|
|
12
|
-
const sendUiAction = useChatStore((s) => s.sendUiAction);
|
|
13
|
-
return (
|
|
14
|
-
<SdkCardRenderer
|
|
15
|
-
card={card}
|
|
16
|
-
registry={CARD_REGISTRY}
|
|
17
|
-
onAction={(action: UiAction) => sendUiAction(card.id, action)}
|
|
18
|
-
/>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/* 顶部居中浮动控件:毛玻璃按钮组 + 下方小手柄;折叠时只剩手柄。对齐原模板。 */
|
|
2
|
-
.chat-header {
|
|
3
|
-
position: absolute;
|
|
4
|
-
top: 4px;
|
|
5
|
-
left: 0;
|
|
6
|
-
right: 0;
|
|
7
|
-
z-index: 100;
|
|
8
|
-
display: flex;
|
|
9
|
-
flex-direction: column;
|
|
10
|
-
align-items: center;
|
|
11
|
-
padding: 4px 16px 0;
|
|
12
|
-
pointer-events: none;
|
|
13
|
-
|
|
14
|
-
&__group {
|
|
15
|
-
pointer-events: auto;
|
|
16
|
-
display: flex;
|
|
17
|
-
align-items: center;
|
|
18
|
-
justify-content: center;
|
|
19
|
-
gap: 8px;
|
|
20
|
-
margin-bottom: 8px;
|
|
21
|
-
padding: 12px 16px;
|
|
22
|
-
background: rgba(255, 255, 255, 0.4);
|
|
23
|
-
backdrop-filter: blur(10px);
|
|
24
|
-
border-radius: 31px;
|
|
25
|
-
max-height: 100px;
|
|
26
|
-
overflow: hidden;
|
|
27
|
-
opacity: 1;
|
|
28
|
-
transform: translateY(0) scale(1);
|
|
29
|
-
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
30
|
-
|
|
31
|
-
&--collapsed {
|
|
32
|
-
opacity: 0;
|
|
33
|
-
transform: translateY(-20px) scale(0.95);
|
|
34
|
-
max-height: 0;
|
|
35
|
-
margin-bottom: 0;
|
|
36
|
-
padding-top: 0;
|
|
37
|
-
padding-bottom: 0;
|
|
38
|
-
pointer-events: none;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
&__btn {
|
|
43
|
-
display: flex;
|
|
44
|
-
align-items: center;
|
|
45
|
-
gap: 8px;
|
|
46
|
-
appearance: none;
|
|
47
|
-
border: none;
|
|
48
|
-
padding: 12px 24px;
|
|
49
|
-
background: #fff;
|
|
50
|
-
border-radius: 92px;
|
|
51
|
-
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
|
52
|
-
transition: all 0.2s ease;
|
|
53
|
-
|
|
54
|
-
&:active {
|
|
55
|
-
transform: scale(0.98);
|
|
56
|
-
background: #f5f5f5;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
&__btn-icon {
|
|
61
|
-
width: 32px;
|
|
62
|
-
height: 32px;
|
|
63
|
-
flex-shrink: 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
&__btn-text {
|
|
67
|
-
font-size: 24px;
|
|
68
|
-
color: #4b6282;
|
|
69
|
-
font-weight: 500;
|
|
70
|
-
white-space: nowrap;
|
|
71
|
-
line-height: 1;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
&__toggle {
|
|
75
|
-
pointer-events: auto;
|
|
76
|
-
appearance: none;
|
|
77
|
-
border: none;
|
|
78
|
-
display: flex;
|
|
79
|
-
align-items: center;
|
|
80
|
-
justify-content: center;
|
|
81
|
-
width: 110px;
|
|
82
|
-
height: 30px;
|
|
83
|
-
background: rgba(255, 255, 255, 0.8);
|
|
84
|
-
border-radius: 14px;
|
|
85
|
-
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
|
|
86
|
-
transition: all 0.2s ease;
|
|
87
|
-
|
|
88
|
-
&:active {
|
|
89
|
-
transform: scale(0.98);
|
|
90
|
-
background: #f5f5f5;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
&__arrow {
|
|
95
|
-
display: flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
justify-content: center;
|
|
98
|
-
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
99
|
-
|
|
100
|
-
&--up {
|
|
101
|
-
transform: rotate(180deg);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
.quick-commands {
|
|
2
|
-
position: relative;
|
|
3
|
-
z-index: 1;
|
|
4
|
-
width: 100%;
|
|
5
|
-
max-width: var(--tbox-content-max-width);
|
|
6
|
-
margin: 0 auto;
|
|
7
|
-
box-sizing: border-box;
|
|
8
|
-
display: flex;
|
|
9
|
-
gap: 16px;
|
|
10
|
-
overflow-x: auto;
|
|
11
|
-
padding: 0 24px 12px;
|
|
12
|
-
-ms-overflow-style: none;
|
|
13
|
-
scrollbar-width: none;
|
|
14
|
-
|
|
15
|
-
&::-webkit-scrollbar {
|
|
16
|
-
display: none;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
&__item {
|
|
20
|
-
flex-shrink: 0;
|
|
21
|
-
display: inline-flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
gap: 8px;
|
|
24
|
-
height: 61px;
|
|
25
|
-
padding: 0 24px 0 16px;
|
|
26
|
-
appearance: none;
|
|
27
|
-
border: none;
|
|
28
|
-
background: #fff;
|
|
29
|
-
border-radius: 31px;
|
|
30
|
-
box-shadow: 0 2px 12px rgba(110, 139, 180, 0.12);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
&__icon {
|
|
34
|
-
width: 32px;
|
|
35
|
-
height: 32px;
|
|
36
|
-
object-fit: contain;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
&__icon-svg {
|
|
40
|
-
color: var(--tbox-primary);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
&__label {
|
|
44
|
-
font-size: 26px;
|
|
45
|
-
color: rgba(31, 59, 99, 0.85);
|
|
46
|
-
white-space: nowrap;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// ⚠️ 平台标准组件:输入框上方的「快捷指令」。视觉还原时不改本组件、不另画。
|
|
2
|
-
// 改内容只改 client/config/chat.json 的 quickCommands 数组({ label, query, icon? })。
|
|
3
|
-
// 011 I2:服务端 HelloAck.chatConfig.quickCommands 优先(单一真源),本地 chat.json 为 fallback。
|
|
4
|
-
// label 只写纯文字;前面的星标由本组件渲染(icon 字段或默认 ✦),不要把 ✦/emoji 写进 label。
|
|
5
|
-
import { useChatConfigStore } from '@/store/chat-config';
|
|
6
|
-
import { useChatStore } from '@/store/chat';
|
|
7
|
-
import starIcon from '../../../assets/quick-command-star.svg';
|
|
8
|
-
import './index.less';
|
|
9
|
-
|
|
10
|
-
export function QuickCommands({ onCommand }: { onCommand: (query: string) => void }) {
|
|
11
|
-
const staticCommands = useChatConfigStore((s) => s.quickCommands);
|
|
12
|
-
const serverCommands = useChatStore((s) => s.helloAck?.chatConfig.quickCommands);
|
|
13
|
-
// 服务端下发优先;未收到 HELLO_ACK(非 mall 部署)回落本地静态
|
|
14
|
-
const commands = (serverCommands && serverCommands.length > 0
|
|
15
|
-
? serverCommands.map((c) => ({ label: c.label, query: c.prompt }))
|
|
16
|
-
: staticCommands) as { label: string; query: string }[];
|
|
17
|
-
if (!commands.length) return null;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="quick-commands">
|
|
21
|
-
{commands.map((cmd) => (
|
|
22
|
-
<button key={cmd.label} className="quick-commands__item" onClick={() => onCommand(cmd.query)}>
|
|
23
|
-
<img className="quick-commands__icon" src={starIcon} alt="" />
|
|
24
|
-
<span className="quick-commands__label">{cmd.label}</span>
|
|
25
|
-
</button>
|
|
26
|
-
))}
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
.tool-calls {
|
|
2
|
-
display: flex;
|
|
3
|
-
flex-direction: column;
|
|
4
|
-
gap: 8px;
|
|
5
|
-
margin: 0 0 12px;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.tool-call {
|
|
9
|
-
border-radius: 12px;
|
|
10
|
-
overflow: hidden;
|
|
11
|
-
|
|
12
|
-
&--calling {
|
|
13
|
-
background: rgba(16, 163, 127, 0.08);
|
|
14
|
-
}
|
|
15
|
-
&--done {
|
|
16
|
-
background: rgba(0, 0, 0, 0.03);
|
|
17
|
-
}
|
|
18
|
-
&--error {
|
|
19
|
-
background: rgba(239, 68, 68, 0.08);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
&__header {
|
|
23
|
-
display: flex;
|
|
24
|
-
align-items: center;
|
|
25
|
-
justify-content: space-between;
|
|
26
|
-
padding: 12px 16px;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
&__left {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
gap: 8px;
|
|
33
|
-
min-width: 0;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
&__spinner {
|
|
37
|
-
width: 14px;
|
|
38
|
-
height: 14px;
|
|
39
|
-
border: 2px solid rgba(16, 163, 127, 0.3);
|
|
40
|
-
border-top-color: #10a37f;
|
|
41
|
-
border-radius: 50%;
|
|
42
|
-
flex-shrink: 0;
|
|
43
|
-
animation: tool-spin 0.8s linear infinite;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// 010:前置工具「自动」徽标(与模型主动调用区分)
|
|
47
|
-
&__badge {
|
|
48
|
-
font-size: 18px;
|
|
49
|
-
line-height: 1;
|
|
50
|
-
padding: 3px 8px;
|
|
51
|
-
border-radius: 8px;
|
|
52
|
-
background: rgba(93, 126, 198, 0.12);
|
|
53
|
-
color: #5f7ec6;
|
|
54
|
-
flex-shrink: 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
&__name {
|
|
58
|
-
font-size: 22px;
|
|
59
|
-
line-height: 1.4;
|
|
60
|
-
color: #4a5568;
|
|
61
|
-
font-weight: 500;
|
|
62
|
-
overflow: hidden;
|
|
63
|
-
text-overflow: ellipsis;
|
|
64
|
-
white-space: nowrap;
|
|
65
|
-
}
|
|
66
|
-
&--calling &__name {
|
|
67
|
-
color: #10a37f;
|
|
68
|
-
}
|
|
69
|
-
&--error &__name {
|
|
70
|
-
color: #ef4444;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
&__toggle {
|
|
74
|
-
font-size: 20px;
|
|
75
|
-
color: #8b95b0;
|
|
76
|
-
flex-shrink: 0;
|
|
77
|
-
margin-left: 8px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
&__result {
|
|
81
|
-
padding: 0 16px 12px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
&__result-text {
|
|
85
|
-
font-size: 20px;
|
|
86
|
-
color: #6b7a99;
|
|
87
|
-
line-height: 1.6;
|
|
88
|
-
font-family: Menlo, Monaco, Consolas, monospace;
|
|
89
|
-
white-space: pre-wrap;
|
|
90
|
-
word-break: break-all;
|
|
91
|
-
|
|
92
|
-
&--error {
|
|
93
|
-
color: #dc2626;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 018 dev 视图输入/输出双栏(紧凑单行 + 头尾保留)
|
|
98
|
-
&__io {
|
|
99
|
-
display: flex;
|
|
100
|
-
flex-direction: column;
|
|
101
|
-
gap: 4px;
|
|
102
|
-
|
|
103
|
-
& + & {
|
|
104
|
-
margin-top: 12px;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
&__io-label {
|
|
109
|
-
font-size: 18px;
|
|
110
|
-
color: #8a97ad;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
&__io-text {
|
|
114
|
-
font-size: 20px;
|
|
115
|
-
color: #6b7a99;
|
|
116
|
-
line-height: 1.6;
|
|
117
|
-
font-family: Menlo, Monaco, Consolas, monospace;
|
|
118
|
-
white-space: pre-wrap;
|
|
119
|
-
word-break: break-all;
|
|
120
|
-
|
|
121
|
-
&--error {
|
|
122
|
-
color: #dc2626;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
@keyframes tool-spin {
|
|
128
|
-
to {
|
|
129
|
-
transform: rotate(360deg);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { Check, X } from '@phosphor-icons/react';
|
|
3
|
-
import { parseMemoryToolCallArgs, parsePreToolToolCallArgs } from '@tbox.cn/app-contracts';
|
|
4
|
-
import type { ToolCallInfo } from '@/types/chat';
|
|
5
|
-
import { describeToolCall, formatCompactToolIo } from '@tbox.cn/app-sdk/client';
|
|
6
|
-
import './index.less';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 工具调用状态卡片(调用中 spinner / 完成 / 失败)。
|
|
10
|
-
* 010:展示语义判定下沉 SDK describeToolCall(skill/preTool/normal/memory 四分支),
|
|
11
|
-
* 本组件退化为纯渲染(徽标/颜色/展开交互)。
|
|
12
|
-
* 018 tool-debug:debug 门控在渲染层——prod 恒无出入参展开入口(I4);
|
|
13
|
-
* dev 详情走 formatCompactToolIo(紧凑单行 + 头尾保留),输入输出双栏。
|
|
14
|
-
*/
|
|
15
|
-
export default function ToolCallBlock({
|
|
16
|
-
toolCalls,
|
|
17
|
-
debug = false,
|
|
18
|
-
}: {
|
|
19
|
-
toolCalls: ToolCallInfo[];
|
|
20
|
-
/** 018 tool-debug:dev 视图(出入参展开 + 紧凑格式);prod 缺省 false */
|
|
21
|
-
debug?: boolean;
|
|
22
|
-
}) {
|
|
23
|
-
const [expandedId, setExpandedId] = useState<string | null>(null);
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className="tool-calls">
|
|
27
|
-
{toolCalls.map((tc) => {
|
|
28
|
-
const display = describeToolCall(tc);
|
|
29
|
-
const isCalling = tc.status === 'calling';
|
|
30
|
-
const isError =
|
|
31
|
-
tc.status === 'error' ||
|
|
32
|
-
(display.kind === 'preTool' && display.status === 'error') ||
|
|
33
|
-
(display.kind === 'memory' && display.status === 'degraded');
|
|
34
|
-
const isDone = !isCalling && !isError;
|
|
35
|
-
const isExpanded = expandedId === tc.id;
|
|
36
|
-
// 018 I4:prod 恒无展开入口;dev 按 display.hasDetail
|
|
37
|
-
const hasDetail = debug && display.hasDetail;
|
|
38
|
-
const stateClass = isError ? 'tool-call--error' : isCalling ? 'tool-call--calling' : 'tool-call--done';
|
|
39
|
-
|
|
40
|
-
const displayName = (() => {
|
|
41
|
-
if (display.kind === 'skill') {
|
|
42
|
-
return isCalling ? `加载技能 ${display.label}` : `已加载技能 ${display.label}`;
|
|
43
|
-
}
|
|
44
|
-
if (display.kind === 'preTool') {
|
|
45
|
-
if (display.status === 'calling') return `自动查询 ${display.label}`;
|
|
46
|
-
if (display.status === 'error') return `自动查询失败 ${display.label}`;
|
|
47
|
-
return `已自动获取 ${display.label}`;
|
|
48
|
-
}
|
|
49
|
-
if (display.kind === 'memory') {
|
|
50
|
-
return display.label;
|
|
51
|
-
}
|
|
52
|
-
return isCalling
|
|
53
|
-
? `调用 ${display.label}`
|
|
54
|
-
: isDone
|
|
55
|
-
? `${display.label} 已完成`
|
|
56
|
-
: `${display.label} 调用失败`;
|
|
57
|
-
})();
|
|
58
|
-
|
|
59
|
-
// 详情文本(018:dev 态才计算——prod 无展开无渲染需求)
|
|
60
|
-
const detailText = (() => {
|
|
61
|
-
if (!hasDetail) return '';
|
|
62
|
-
if (display.kind === 'preTool') {
|
|
63
|
-
return (display.status === 'error' ? display.errorText : display.resultText) ?? '';
|
|
64
|
-
}
|
|
65
|
-
if (display.kind === 'memory') {
|
|
66
|
-
return display.detailText ?? '';
|
|
67
|
-
}
|
|
68
|
-
return isError ? (tc.error ?? '') : (tc.result ?? '');
|
|
69
|
-
})();
|
|
70
|
-
// dev 输入侧:normal 用 tc.args;preTool 解包 toolArgs(对齐 mall 视图模型);memory 取 query
|
|
71
|
-
const inputText = (() => {
|
|
72
|
-
if (!hasDetail) return '';
|
|
73
|
-
if (display.kind === 'normal') return tc.args ?? '';
|
|
74
|
-
if (display.kind === 'preTool') {
|
|
75
|
-
return JSON.stringify(parsePreToolToolCallArgs(tc.args)?.toolArgs ?? {});
|
|
76
|
-
}
|
|
77
|
-
if (display.kind === 'memory') {
|
|
78
|
-
return parseMemoryToolCallArgs(tc.args)?.query ?? '';
|
|
79
|
-
}
|
|
80
|
-
return '';
|
|
81
|
-
})();
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
<div key={tc.id} className={`tool-call ${stateClass}`}>
|
|
85
|
-
<div
|
|
86
|
-
className="tool-call__header"
|
|
87
|
-
onClick={() => hasDetail && setExpandedId(isExpanded ? null : tc.id)}
|
|
88
|
-
>
|
|
89
|
-
<div className="tool-call__left">
|
|
90
|
-
{isCalling && <div className="tool-call__spinner" />}
|
|
91
|
-
{isDone && <Check size={16} color="#10a37f" strokeWidth={2.5} />}
|
|
92
|
-
{isError && <X size={16} color="#ef4444" strokeWidth={2.5} />}
|
|
93
|
-
<span className="tool-call__name">{displayName}</span>
|
|
94
|
-
{display.kind === 'preTool' && (
|
|
95
|
-
<span className="tool-call__badge">自动</span>
|
|
96
|
-
)}
|
|
97
|
-
</div>
|
|
98
|
-
{hasDetail && (
|
|
99
|
-
<span className={`tool-call__toggle${isExpanded ? ' tool-call__toggle--up' : ''}`}>
|
|
100
|
-
{isExpanded ? '收起' : '详情'}
|
|
101
|
-
</span>
|
|
102
|
-
)}
|
|
103
|
-
</div>
|
|
104
|
-
{isExpanded && hasDetail && (
|
|
105
|
-
<div className="tool-call__result">
|
|
106
|
-
{inputText && (
|
|
107
|
-
<div className="tool-call__io">
|
|
108
|
-
<span className="tool-call__io-label">输入</span>
|
|
109
|
-
<span className="tool-call__io-text">{formatCompactToolIo(inputText)}</span>
|
|
110
|
-
</div>
|
|
111
|
-
)}
|
|
112
|
-
<div className="tool-call__io">
|
|
113
|
-
<span className="tool-call__io-label">输出</span>
|
|
114
|
-
<span className={`tool-call__io-text${isError ? ' tool-call__io-text--error' : ''}`}>
|
|
115
|
-
{formatCompactToolIo(detailText) || '(无)'}
|
|
116
|
-
</span>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
)}
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
})}
|
|
123
|
-
</div>
|
|
124
|
-
);
|
|
125
|
-
}
|