vue-agent-start 0.1.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.
Files changed (169) hide show
  1. package/package.json +30 -0
  2. package/pnpm-workspace.yaml +3 -0
  3. package/src/agent-flow/adapter/backend.ts +99 -0
  4. package/src/agent-flow/components/ChatIframePanel.vue +773 -0
  5. package/src/agent-flow/components/DatasetItemCard.vue +313 -0
  6. package/src/agent-flow/components/MemoryWindow.vue +104 -0
  7. package/src/agent-flow/components/PromptEditor.vue +804 -0
  8. package/src/agent-flow/components/PromptEditorTagPanel.vue +460 -0
  9. package/src/agent-flow/components/SvgIcon.vue +32 -0
  10. package/src/agent-flow/components/ToolItemCard.vue +169 -0
  11. package/src/agent-flow/components/VariableCard.vue +161 -0
  12. package/src/agent-flow/components/VariableSelector.vue +162 -0
  13. package/src/agent-flow/components/chat-iframe-types.ts +35 -0
  14. package/src/agent-flow/components/stubs/DatasetChooser.vue +70 -0
  15. package/src/agent-flow/components/stubs/Embedding.vue +22 -0
  16. package/src/agent-flow/components/stubs/ModelSelect.vue +24 -0
  17. package/src/agent-flow/components/stubs/RestfulSelectCard.vue +24 -0
  18. package/src/agent-flow/components/stubs/ToolChooser.vue +96 -0
  19. package/src/agent-flow/components/stubs/TriggersItemCard.vue +126 -0
  20. package/src/agent-flow/components/stubs/VariableModal.vue +115 -0
  21. package/src/agent-flow/index.ts +73 -0
  22. package/src/agent-flow/stores/workflow.ts +72 -0
  23. package/src/agent-flow/utils/langUtils.ts +173 -0
  24. package/src/agent-flow/workflow/Ability.vue +65 -0
  25. package/src/agent-flow/workflow/AuthorizationItem.vue +101 -0
  26. package/src/agent-flow/workflow/ConversationVar.vue +42 -0
  27. package/src/agent-flow/workflow/CustomEdge.vue +94 -0
  28. package/src/agent-flow/workflow/EnvironmentVar.vue +40 -0
  29. package/src/agent-flow/workflow/FlowDesigner.vue +2802 -0
  30. package/src/agent-flow/workflow/Icon.vue +179 -0
  31. package/src/agent-flow/workflow/NodeConfig.vue +509 -0
  32. package/src/agent-flow/workflow/NodeConfigCard.vue +460 -0
  33. package/src/agent-flow/workflow/NodeHandle.vue +147 -0
  34. package/src/agent-flow/workflow/NodeHeaderToolbar.vue +77 -0
  35. package/src/agent-flow/workflow/NodeHoverToolbar.vue +176 -0
  36. package/src/agent-flow/workflow/OutputItemCard.vue +668 -0
  37. package/src/agent-flow/workflow/ParamTableItem.vue +200 -0
  38. package/src/agent-flow/workflow/StructTable.vue +202 -0
  39. package/src/agent-flow/workflow/VarInsertField.vue +517 -0
  40. package/src/agent-flow/workflow/VarSelectField.vue +223 -0
  41. package/src/agent-flow/workflow/VariableSelectorItem.vue +48 -0
  42. package/src/agent-flow/workflow/WfField.vue +206 -0
  43. package/src/agent-flow/workflow/_config-base.css +470 -0
  44. package/src/agent-flow/workflow/composables/useNode.js +55 -0
  45. package/src/agent-flow/workflow/index.vue +53 -0
  46. package/src/agent-flow/workflow/nodeCard/AgentNodeCard.vue +180 -0
  47. package/src/agent-flow/workflow/nodeCard/AnswerNodeCard.vue +65 -0
  48. package/src/agent-flow/workflow/nodeCard/ClassifierCard.vue +152 -0
  49. package/src/agent-flow/workflow/nodeCard/CodeNodeCard.vue +139 -0
  50. package/src/agent-flow/workflow/nodeCard/ConditionNodeCard.vue +323 -0
  51. package/src/agent-flow/workflow/nodeCard/DocumentExtractorCard.vue +83 -0
  52. package/src/agent-flow/workflow/nodeCard/EndNodeCard.vue +123 -0
  53. package/src/agent-flow/workflow/nodeCard/HttpNodeCard.vue +451 -0
  54. package/src/agent-flow/workflow/nodeCard/HumanInputCard.vue +152 -0
  55. package/src/agent-flow/workflow/nodeCard/IterationCard.vue +66 -0
  56. package/src/agent-flow/workflow/nodeCard/KnowledgeRetrievalCard.vue +48 -0
  57. package/src/agent-flow/workflow/nodeCard/LLMNodeCard.vue +206 -0
  58. package/src/agent-flow/workflow/nodeCard/ListOperatorCard.vue +188 -0
  59. package/src/agent-flow/workflow/nodeCard/ParameterExtractorCard.vue +352 -0
  60. package/src/agent-flow/workflow/nodeCard/ServiceApiNodeCard.vue +188 -0
  61. package/src/agent-flow/workflow/nodeCard/StartNodeCard.vue +216 -0
  62. package/src/agent-flow/workflow/nodeCard/VariableAssignerCard.vue +125 -0
  63. package/src/agent-flow/workflow/nodeCard/VariableNodeCard.vue +121 -0
  64. package/src/agent-flow/workflow/nodes/AgentNode.vue +122 -0
  65. package/src/agent-flow/workflow/nodes/AnswerNode.vue +99 -0
  66. package/src/agent-flow/workflow/nodes/ClassifierNode.vue +199 -0
  67. package/src/agent-flow/workflow/nodes/CodeNode.vue +152 -0
  68. package/src/agent-flow/workflow/nodes/ConditionNode.vue +232 -0
  69. package/src/agent-flow/workflow/nodes/DocumentExtractorNode.vue +96 -0
  70. package/src/agent-flow/workflow/nodes/EndNode.vue +119 -0
  71. package/src/agent-flow/workflow/nodes/FileUploadNode.vue +97 -0
  72. package/src/agent-flow/workflow/nodes/HttpNode.vue +204 -0
  73. package/src/agent-flow/workflow/nodes/HumanInputNode.vue +110 -0
  74. package/src/agent-flow/workflow/nodes/IterationNode.vue +98 -0
  75. package/src/agent-flow/workflow/nodes/KnowledgeRetrievalNode.vue +253 -0
  76. package/src/agent-flow/workflow/nodes/LLMNode.vue +134 -0
  77. package/src/agent-flow/workflow/nodes/ListOperatorNode.vue +111 -0
  78. package/src/agent-flow/workflow/nodes/LoopNode.vue +94 -0
  79. package/src/agent-flow/workflow/nodes/ParameterExtractorNode.vue +147 -0
  80. package/src/agent-flow/workflow/nodes/ServiceApiNode.vue +161 -0
  81. package/src/agent-flow/workflow/nodes/StartNode.vue +199 -0
  82. package/src/agent-flow/workflow/nodes/TemplateNode.vue +103 -0
  83. package/src/agent-flow/workflow/nodes/UserInputNode.vue +92 -0
  84. package/src/agent-flow/workflow/nodes/VariableAssignerNode.vue +145 -0
  85. package/src/agent-flow/workflow/nodes/VariableNode.vue +130 -0
  86. package/src/agent-flow/workflow/nodes/_node-base.css +338 -0
  87. package/src/agent-flow/workflow/utils/node_card_form.ts +335 -0
  88. package/src/agent-flow/workflow/utils/node_config.ts +63 -0
  89. package/src/agent-flow/workflow/utils/workflow_utils.ts +151 -0
  90. package/src/agent-studio/adapters/springAgentStart.ts +390 -0
  91. package/src/agent-studio/api/index.ts +1 -0
  92. package/src/agent-studio/api/types.ts +173 -0
  93. package/src/agent-studio/components/AgentApiDocs.vue +500 -0
  94. package/src/agent-studio/components/AgentAppsPage.vue +1601 -0
  95. package/src/agent-studio/components/AgentCardGrid.vue +259 -0
  96. package/src/agent-studio/components/AgentChatPage.vue +553 -0
  97. package/src/agent-studio/components/AgentDebugPanel.vue +350 -0
  98. package/src/agent-studio/components/AgentLogsPanel.vue +391 -0
  99. package/src/agent-studio/components/AgentMonitorPanel.vue +204 -0
  100. package/src/agent-studio/components/AgentOrchestrate.vue +505 -0
  101. package/src/agent-studio/components/AgentPromptEditor.vue +237 -0
  102. package/src/agent-studio/components/AgentStudioShell.vue +227 -0
  103. package/src/agent-studio/components/AgentToolsPanel.vue +394 -0
  104. package/src/agent-studio/components/AgentVariablesPanel.vue +205 -0
  105. package/src/agent-studio/components/ApiKeyManager.vue +251 -0
  106. package/src/agent-studio/components/AppDesignDrawer.vue +3391 -0
  107. package/src/agent-studio/components/CreateAppModal.vue +737 -0
  108. package/src/agent-studio/components/SparkChart.vue +200 -0
  109. package/src/agent-studio/components/VariableEditorModal.vue +499 -0
  110. package/src/agent-studio/composables/useAgentStudio.ts +347 -0
  111. package/src/agent-studio/index.ts +60 -0
  112. package/src/agent-studio/panels/DrawerFlowDesigner.vue +203 -0
  113. package/src/agent-studio/panels/LogAnnotationPanel.vue +490 -0
  114. package/src/agent-studio/panels/MonitorPanel.vue +535 -0
  115. package/src/agent-studio/types.ts +242 -0
  116. package/src/index.ts +155 -0
  117. package/src/knowledge-hub/adapters/springAgentStart.ts +472 -0
  118. package/src/knowledge-hub/components/AddDocumentsWizard.vue +1452 -0
  119. package/src/knowledge-hub/components/CreateDatasetWizard.vue +1723 -0
  120. package/src/knowledge-hub/components/DatasetCardGrid.vue +467 -0
  121. package/src/knowledge-hub/components/DatasetDetailDrawer.vue +888 -0
  122. package/src/knowledge-hub/components/DatasetPicker.vue +65 -0
  123. package/src/knowledge-hub/components/DatasetPickerModal.vue +526 -0
  124. package/src/knowledge-hub/components/DatasetSettingsPanel.vue +908 -0
  125. package/src/knowledge-hub/components/DatasetSidebar.vue +781 -0
  126. package/src/knowledge-hub/components/DocumentChunksView.vue +802 -0
  127. package/src/knowledge-hub/components/DocumentTable.vue +552 -0
  128. package/src/knowledge-hub/components/HitTestingPanel.vue +74 -0
  129. package/src/knowledge-hub/components/KnowledgeApp.vue +66 -0
  130. package/src/knowledge-hub/components/KnowledgeHubApp.vue +504 -0
  131. package/src/knowledge-hub/components/RecallTestingPanelV2.vue +515 -0
  132. package/src/knowledge-hub/components/RetrievalConfigPopover.vue +270 -0
  133. package/src/knowledge-hub/components/RetrievalMethodPicker.vue +547 -0
  134. package/src/knowledge-hub/components/RetrievedList.vue +24 -0
  135. package/src/knowledge-hub/components/internal/KhDialog.vue +195 -0
  136. package/src/knowledge-hub/composables/useKnowledge.ts +89 -0
  137. package/src/knowledge-hub/i18n/index.ts +2 -0
  138. package/src/knowledge-hub/i18n/messages.ts +422 -0
  139. package/src/knowledge-hub/i18n/useKhI18n.ts +107 -0
  140. package/src/knowledge-hub/index.ts +46 -0
  141. package/src/knowledge-hub/styles/index.css +4 -0
  142. package/src/knowledge-hub/styles/tokens.css +138 -0
  143. package/src/knowledge-hub/types/api.ts +213 -0
  144. package/src/knowledge-hub/types/dataset.ts +88 -0
  145. package/src/knowledge-hub/types/document.ts +45 -0
  146. package/src/knowledge-hub/types/index.ts +9 -0
  147. package/src/knowledge-hub/types/retrieval.ts +46 -0
  148. package/src/provider-hub/components/CredentialForm.vue +103 -0
  149. package/src/provider-hub/components/DefaultModelsPanel.vue +270 -0
  150. package/src/provider-hub/components/GroupedModelSelect.vue +445 -0
  151. package/src/provider-hub/components/ModelCardGrid.vue +332 -0
  152. package/src/provider-hub/components/ModelParameterDrawer.vue +739 -0
  153. package/src/provider-hub/components/ModelPickerPopover.vue +1282 -0
  154. package/src/provider-hub/components/ProviderApp.vue +159 -0
  155. package/src/provider-hub/components/ProviderCredentialModal.vue +321 -0
  156. package/src/provider-hub/components/ProviderGallery.vue +131 -0
  157. package/src/provider-hub/components/ProviderHubShell.vue +1992 -0
  158. package/src/provider-hub/components/ProviderIcon.vue +130 -0
  159. package/src/provider-hub/composables/useProviderHub.ts +358 -0
  160. package/src/provider-hub/index.ts +43 -0
  161. package/src/provider-hub/svg/MiniMax.svg +1 -0
  162. package/src/provider-hub/svg/Volcengine.svg +1 -0
  163. package/src/provider-hub/svg/Zhipu.svg +1 -0
  164. package/src/provider-hub/svg/deepseek.svg +1 -0
  165. package/src/provider-hub/svg/moonshot.svg +1 -0
  166. package/src/provider-hub/svg/openai.svg +1 -0
  167. package/src/provider-hub/svg/qwen.svg +1 -0
  168. package/src/provider-hub/svg/siliconflow.svg +1 -0
  169. package/src/provider-hub/types.ts +256 -0
@@ -0,0 +1,460 @@
1
+ <script setup lang="ts">
2
+ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
3
+
4
+ import { DownOutlined, RightOutlined } from '@ant-design/icons-vue';
5
+
6
+ import { useWorkflowStore } from '@/stores/workflow';
7
+ import workflow_utils from '@/workflow/utils/workflow_utils';
8
+
9
+ /**
10
+ * 快速插入变量面板
11
+ *
12
+ * 作用:在 PromptEditor 里输入 `/` 或点击 "+" 按钮触发,弹出上游节点的输出变量
13
+ * 列表;选中一项后 emit('select', [nodeId, ...path], tagMeta, node),宿主
14
+ * 组件负责实际把变量插入到文本里。
15
+ *
16
+ * 定位策略:给定 targetElement 时,紧贴其下方;空间不足则翻到上方。
17
+ * viewport 边界会自动收敛。
18
+ */
19
+
20
+ const props = defineProps({
21
+ show: { type: Boolean, default: false },
22
+ nodeId: { type: String, default: '' },
23
+ /** 传入 targetElement 后自动跟随其位置 */
24
+ targetElement: { type: Object as any, default: () => null },
25
+ /** slash 模式下父组件维护高亮索引;插入 tag 也会用到 */
26
+ isSlashMode: { type: Boolean, default: false },
27
+ selectedIndex: { type: Number, default: 0 },
28
+ /** slash 后已经输入的过滤字符 */
29
+ search: { type: String, default: '' },
30
+ /** 传入固定位置时优先使用 targetElement,两个都没给才用 position */
31
+ position: { type: Object, default: () => ({ top: '0px', left: '0px' }) },
32
+ });
33
+
34
+ const emit = defineEmits(['close', 'select']);
35
+
36
+ const panelRef = ref<HTMLElement | null>(null);
37
+ const panelPos = ref({ top: '0px', left: '0px' });
38
+ const nodeList = ref<any[]>([]);
39
+ const expandedKeys = ref<Set<string>>(new Set());
40
+ const workflowStore = useWorkflowStore();
41
+
42
+ /** ---------- 定位 ---------- */
43
+ function updatePosition() {
44
+ if (!props.targetElement) {
45
+ panelPos.value = {
46
+ top: props.position?.top ?? '0px',
47
+ left: props.position?.left ?? '0px',
48
+ };
49
+ return;
50
+ }
51
+ const rect = props.targetElement.getBoundingClientRect();
52
+ const panelH = panelRef.value?.offsetHeight || 320;
53
+ const panelW = panelRef.value?.offsetWidth || 300;
54
+ const vh = window.innerHeight;
55
+ const vw = window.innerWidth;
56
+
57
+ let top = rect.bottom + 6;
58
+ let left = rect.left;
59
+ if (top + panelH > vh) top = Math.max(10, rect.top - panelH - 6);
60
+ if (left + panelW > vw) left = Math.max(10, vw - panelW - 10);
61
+
62
+ panelPos.value = { top: `${top}px`, left: `${left}px` };
63
+ }
64
+
65
+ /** ---------- 上游节点列表 ---------- */
66
+ function refreshNodeList() {
67
+ if (!props.nodeId) {
68
+ nodeList.value = [];
69
+ return;
70
+ }
71
+ nodeList.value = workflowStore.getParentNodeList(props.nodeId) || [];
72
+ }
73
+
74
+ /** ---------- 搜索/展开 ---------- */
75
+ function getNodeOutputList(node: any) {
76
+ return workflow_utils.getOutputList(node.data) ?? [];
77
+ }
78
+
79
+ function hasChildren(tag: any) {
80
+ return tag?.children && tag.children.length > 0;
81
+ }
82
+
83
+ function toggleExpand(key: string) {
84
+ const next = new Set(expandedKeys.value);
85
+ if (next.has(key)) next.delete(key);
86
+ else next.add(key);
87
+ expandedKeys.value = next;
88
+ }
89
+
90
+ /** 对单个节点的输出根据 search 过滤 */
91
+ function filterOutputs(list: any[]): any[] {
92
+ const q = props.search?.trim().toLowerCase();
93
+ if (!q) return list;
94
+ const match = (t: any) =>
95
+ (t.label && t.label.toLowerCase().includes(q)) ||
96
+ (t.description && t.description.toLowerCase().includes(q)) ||
97
+ (t.name && t.name.toLowerCase().includes(q));
98
+
99
+ return list
100
+ .map((t: any) => {
101
+ if (match(t)) return t;
102
+ if (hasChildren(t)) {
103
+ const kids = filterOutputs(t.children);
104
+ if (kids.length > 0) return { ...t, children: kids };
105
+ }
106
+ return null;
107
+ })
108
+ .filter(Boolean);
109
+ }
110
+
111
+ /** slash 模式下 flatten 用于键盘导航时命中 selectedIndex 位置 */
112
+ const flatFiltered = computed(() => {
113
+ const arr: any[] = [];
114
+ for (const node of nodeList.value) {
115
+ const outputs = filterOutputs(getNodeOutputList(node));
116
+ for (const t of outputs) {
117
+ arr.push({ ...t, __nodeId: node.id });
118
+ if (hasChildren(t) && expandedKeys.value.has(t.name)) {
119
+ for (const c of t.children) {
120
+ arr.push({ ...c, __nodeId: node.id, __parentName: t.name });
121
+ }
122
+ }
123
+ }
124
+ }
125
+ return arr;
126
+ });
127
+
128
+ /** ---------- 事件 ---------- */
129
+ function selectTag(variableSelector: string[], tag: any, node: any) {
130
+ emit('select', variableSelector, tag, node);
131
+ }
132
+
133
+ function close() {
134
+ emit('close');
135
+ }
136
+
137
+ function onDocMouseDown(e: MouseEvent) {
138
+ if (!panelRef.value) return;
139
+ if (!panelRef.value.contains(e.target as Node)) close();
140
+ }
141
+
142
+ /** ESC 快捷键关闭面板;capture 阶段拦截,避免宿主编辑器把事件吃掉。 */
143
+ function onDocKeyDown(e: KeyboardEvent) {
144
+ if (!props.show) return;
145
+ if (e.key === 'Escape') {
146
+ e.preventDefault();
147
+ e.stopPropagation();
148
+ close();
149
+ }
150
+ }
151
+
152
+ /** ---------- 生命周期 ---------- */
153
+ onMounted(() => {
154
+ document.addEventListener('mousedown', onDocMouseDown);
155
+ document.addEventListener('keydown', onDocKeyDown, true);
156
+ window.addEventListener('scroll', updatePosition, true);
157
+ window.addEventListener('resize', updatePosition);
158
+ refreshNodeList();
159
+ });
160
+
161
+ onUnmounted(() => {
162
+ document.removeEventListener('mousedown', onDocMouseDown);
163
+ document.removeEventListener('keydown', onDocKeyDown, true);
164
+ window.removeEventListener('scroll', updatePosition, true);
165
+ window.removeEventListener('resize', updatePosition);
166
+ });
167
+
168
+ watch(
169
+ () => [props.show, props.targetElement],
170
+ () => {
171
+ if (props.show) {
172
+ refreshNodeList();
173
+ nextTick(updatePosition);
174
+ }
175
+ },
176
+ { immediate: true },
177
+ );
178
+
179
+ watch(() => props.nodeId, refreshNodeList);
180
+ watch(() => props.position, updatePosition);
181
+
182
+ /** 判断当前 selectedIndex 对应的项是否命中,给项加 active */
183
+ function isActive(nodeId: string, parentName: string | null, name: string) {
184
+ if (!props.isSlashMode) return false;
185
+ const item = flatFiltered.value[props.selectedIndex];
186
+ if (!item) return false;
187
+ return (
188
+ item.__nodeId === nodeId &&
189
+ (item.__parentName ?? null) === parentName &&
190
+ item.name === name
191
+ );
192
+ }
193
+ </script>
194
+
195
+ <template>
196
+ <div
197
+ v-if="show"
198
+ ref="panelRef"
199
+ class="wf-tag-panel"
200
+ :style="panelPos"
201
+ @mousedown.stop
202
+ >
203
+ <div class="wf-tag-panel-header">
204
+ <div class="wf-tag-panel-title">快速插入变量</div>
205
+ <button class="wf-tag-panel-close" @click="close">
206
+ <svg viewBox="0 0 24 24" width="14" height="14">
207
+ <path
208
+ fill="currentColor"
209
+ d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
210
+ />
211
+ </svg>
212
+ </button>
213
+ </div>
214
+
215
+ <div class="wf-tag-panel-body">
216
+ <div v-if="nodeList.length === 0" class="wf-tag-panel-empty">
217
+ 当前节点没有上游变量。请先连接一个上游节点。
218
+ </div>
219
+
220
+ <div v-for="node in nodeList" :key="node.id" class="wf-tag-group">
221
+ <div class="wf-tag-group-title">{{ node.data?.label || node.id }}</div>
222
+ <div class="wf-tag-list">
223
+ <template
224
+ v-for="item in filterOutputs(getNodeOutputList(node))"
225
+ :key="item.name"
226
+ >
227
+ <div
228
+ class="wf-tag-item"
229
+ :class="{ 'is-active': isActive(node.id, null, item.name) }"
230
+ @mousedown.prevent
231
+ @click.prevent="selectTag([node.id, item.name], item, node)"
232
+ >
233
+ <span
234
+ v-if="hasChildren(item)"
235
+ class="wf-tag-expand"
236
+ @click.stop="toggleExpand(item.name)"
237
+ >
238
+ <RightOutlined v-if="!expandedKeys.has(item.name)" />
239
+ <DownOutlined v-else />
240
+ </span>
241
+ <span v-else class="wf-tag-expand-placeholder"></span>
242
+
243
+ <span class="wf-tag-icon" :data-type="item.type">
244
+ {{ item.type?.[0]?.toUpperCase() || '·' }}
245
+ </span>
246
+ <span class="wf-tag-label">
247
+ {{ item.label || item.name }}
248
+ </span>
249
+ <span v-if="item.type" class="wf-tag-type">{{ item.type }}</span>
250
+ </div>
251
+
252
+ <template v-if="hasChildren(item) && expandedKeys.has(item.name)">
253
+ <div
254
+ v-for="child in item.children"
255
+ :key="`${item.name}-${child.name}`"
256
+ class="wf-tag-item wf-tag-item-child"
257
+ :class="{ 'is-active': isActive(node.id, item.name, child.name) }"
258
+ @mousedown.prevent
259
+ @click.prevent="
260
+ selectTag([node.id, item.name, child.name], child, node)
261
+ "
262
+ >
263
+ <span class="wf-tag-expand-placeholder"></span>
264
+ <span class="wf-tag-icon" :data-type="child.type">
265
+ {{ child.type?.[0]?.toUpperCase() || '·' }}
266
+ </span>
267
+ <span class="wf-tag-label">
268
+ {{ child.label || child.name }}
269
+ </span>
270
+ <span v-if="child.type" class="wf-tag-type">{{ child.type }}</span>
271
+ </div>
272
+ </template>
273
+ </template>
274
+ </div>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </template>
279
+
280
+ <style scoped>
281
+ .wf-tag-panel {
282
+ position: fixed;
283
+ z-index: 2000;
284
+ display: flex;
285
+ flex-direction: column;
286
+ width: 300px;
287
+ max-height: 360px;
288
+ background: #ffffff;
289
+ border: 1px solid #e5e7eb;
290
+ border-radius: 10px;
291
+ box-shadow: 0 12px 32px rgba(15, 23, 42, 0.16);
292
+ overflow: hidden;
293
+ font-size: 13px;
294
+ color: #1f2937;
295
+ }
296
+
297
+ .wf-tag-panel-header {
298
+ display: flex;
299
+ align-items: center;
300
+ justify-content: space-between;
301
+ padding: 8px 12px;
302
+ background: #f9fafb;
303
+ border-bottom: 1px solid #f0f0f0;
304
+ }
305
+
306
+ .wf-tag-panel-title {
307
+ font-size: 12px;
308
+ font-weight: 600;
309
+ color: #4b5563;
310
+ letter-spacing: 0.3px;
311
+ text-transform: uppercase;
312
+ }
313
+
314
+ .wf-tag-panel-close {
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: center;
318
+ width: 22px;
319
+ height: 22px;
320
+ color: #6b7280;
321
+ background: transparent;
322
+ border: none;
323
+ border-radius: 4px;
324
+ cursor: pointer;
325
+ transition: background 0.15s, color 0.15s;
326
+ }
327
+
328
+ .wf-tag-panel-close:hover {
329
+ background: #e5e7eb;
330
+ color: #1f2937;
331
+ }
332
+
333
+ .wf-tag-panel-body {
334
+ flex: 1;
335
+ min-height: 0;
336
+ padding: 4px 0;
337
+ overflow-y: auto;
338
+ background: #ffffff;
339
+ }
340
+
341
+ .wf-tag-panel-body::-webkit-scrollbar {
342
+ width: 6px;
343
+ }
344
+ .wf-tag-panel-body::-webkit-scrollbar-thumb {
345
+ background: #d1d5db;
346
+ border-radius: 3px;
347
+ }
348
+
349
+ .wf-tag-panel-empty {
350
+ padding: 20px 16px;
351
+ text-align: center;
352
+ font-size: 12px;
353
+ color: #9ca3af;
354
+ }
355
+
356
+ .wf-tag-group {
357
+ padding: 4px 0;
358
+ }
359
+
360
+ .wf-tag-group + .wf-tag-group {
361
+ border-top: 1px solid #f3f4f6;
362
+ }
363
+
364
+ .wf-tag-group-title {
365
+ padding: 6px 14px 4px;
366
+ font-size: 11px;
367
+ font-weight: 600;
368
+ color: #6b7280;
369
+ letter-spacing: 0.4px;
370
+ text-transform: uppercase;
371
+ }
372
+
373
+ .wf-tag-item {
374
+ display: flex;
375
+ align-items: center;
376
+ gap: 8px;
377
+ padding: 6px 14px;
378
+ cursor: pointer;
379
+ transition: background 0.12s;
380
+ }
381
+
382
+ .wf-tag-item:hover,
383
+ .wf-tag-item.is-active {
384
+ background: #eef2ff;
385
+ }
386
+
387
+ .wf-tag-item.is-active {
388
+ color: #4338ca;
389
+ }
390
+
391
+ .wf-tag-item-child {
392
+ padding-left: 34px;
393
+ }
394
+
395
+ .wf-tag-expand {
396
+ display: flex;
397
+ align-items: center;
398
+ justify-content: center;
399
+ width: 14px;
400
+ height: 14px;
401
+ color: #9ca3af;
402
+ }
403
+
404
+ .wf-tag-expand:hover {
405
+ color: #4338ca;
406
+ }
407
+
408
+ .wf-tag-expand-placeholder {
409
+ width: 14px;
410
+ height: 14px;
411
+ }
412
+
413
+ .wf-tag-icon {
414
+ display: inline-flex;
415
+ align-items: center;
416
+ justify-content: center;
417
+ width: 18px;
418
+ height: 18px;
419
+ font-size: 10px;
420
+ font-weight: 700;
421
+ color: #ffffff;
422
+ background: #6366f1;
423
+ border-radius: 4px;
424
+ text-transform: uppercase;
425
+ }
426
+
427
+ .wf-tag-icon[data-type='string'] {
428
+ background: #3b82f6;
429
+ }
430
+ .wf-tag-icon[data-type='number'] {
431
+ background: #f59e0b;
432
+ }
433
+ .wf-tag-icon[data-type='boolean'] {
434
+ background: #10b981;
435
+ }
436
+ .wf-tag-icon[data-type='array'] {
437
+ background: #a855f7;
438
+ }
439
+ .wf-tag-icon[data-type='object'] {
440
+ background: #6b7280;
441
+ }
442
+ .wf-tag-icon[data-type='file'] {
443
+ background: #ef4444;
444
+ }
445
+
446
+ .wf-tag-label {
447
+ flex: 1;
448
+ min-width: 0;
449
+ overflow: hidden;
450
+ text-overflow: ellipsis;
451
+ white-space: nowrap;
452
+ }
453
+
454
+ .wf-tag-type {
455
+ flex: none;
456
+ font-size: 11px;
457
+ color: #9ca3af;
458
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
459
+ }
460
+ </style>
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * 极简的 SvgIcon 兜底实现。
4
+ * 宿主应用通常有自己的 icon 方案(iconify / svg-sprite),
5
+ * 建议通过 provide 覆盖 <SvgIcon /> 或在使用侧用 slot 替换。
6
+ */
7
+ defineProps<{ name?: string; size?: number | string; color?: string }>();
8
+ </script>
9
+
10
+ <template>
11
+ <span
12
+ class="agent-flow-svg-icon"
13
+ :style="{
14
+ display: 'inline-flex',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
17
+ width: (typeof size === 'number' ? `${size}px` : size) || '1em',
18
+ height: (typeof size === 'number' ? `${size}px` : size) || '1em',
19
+ color: color,
20
+ }"
21
+ >
22
+ <svg
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ viewBox="0 0 24 24"
25
+ width="100%"
26
+ height="100%"
27
+ fill="currentColor"
28
+ >
29
+ <circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="1.5" />
30
+ </svg>
31
+ </span>
32
+ </template>
@@ -0,0 +1,169 @@
1
+ <script setup lang="ts">
2
+ import { h, ref } from 'vue';
3
+
4
+ import {
5
+ DeleteOutlined,
6
+ PlusOutlined,
7
+ ToolOutlined,
8
+ } from '@ant-design/icons-vue';
9
+
10
+ import ToolChooser from '@/components/stubs/ToolChooser.vue';
11
+
12
+ const formState: any = defineModel();
13
+ const toolChooserRef = ref();
14
+ const toolItems: any = ref([]);
15
+
16
+ const toolChooserSubmit = (data: any) => {
17
+ if (!Array.isArray(formState.value.tools)) formState.value.tools = [];
18
+ if (!data.id) {
19
+ data.id = Date.now();
20
+ formState.value.tools.push(data);
21
+ }
22
+ };
23
+
24
+ function openChooser() {
25
+ toolChooserRef.value?.showModal?.();
26
+ }
27
+
28
+ function removeTool(index: number) {
29
+ formState.value.tools.splice(index, 1);
30
+ }
31
+ </script>
32
+
33
+ <template>
34
+ <div class="wf-tools">
35
+ <div class="wf-tools-header">
36
+ <span class="wf-tools-hint">添加工具增强 Agent 能力</span>
37
+ <a-button
38
+ :icon="h(PlusOutlined)"
39
+ size="small"
40
+ type="primary"
41
+ @click="openChooser"
42
+ />
43
+ </div>
44
+
45
+ <div v-if="!formState.tools || formState.tools.length === 0" class="wf-tools-empty">
46
+ 尚未添加工具
47
+ </div>
48
+
49
+ <div v-else class="wf-tools-list">
50
+ <div
51
+ v-for="(tool, index) in formState.tools"
52
+ :key="tool.id ?? index"
53
+ class="wf-tools-item"
54
+ >
55
+ <div class="wf-tools-item-left">
56
+ <div class="wf-tools-item-icon">
57
+ <ToolOutlined />
58
+ </div>
59
+ <div class="wf-tools-item-name">
60
+ {{ tool.name || tool.label || `工具 ${index + 1}` }}
61
+ </div>
62
+ </div>
63
+ <div class="wf-tools-item-right">
64
+ <a-switch
65
+ v-model:checked="tool.enabled"
66
+ size="small"
67
+ :default-checked="true"
68
+ />
69
+ <a-button
70
+ :icon="h(DeleteOutlined)"
71
+ type="text"
72
+ size="small"
73
+ danger
74
+ @click="removeTool(index)"
75
+ />
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ <ToolChooser
81
+ ref="toolChooserRef"
82
+ :main-data="toolItems"
83
+ @form-submit="toolChooserSubmit"
84
+ />
85
+ </div>
86
+ </template>
87
+
88
+ <style scoped>
89
+ .wf-tools {
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 8px;
93
+ }
94
+
95
+ .wf-tools-header {
96
+ display: flex;
97
+ align-items: center;
98
+ justify-content: space-between;
99
+ }
100
+
101
+ .wf-tools-hint {
102
+ font-size: 11px;
103
+ color: #9ca3af;
104
+ }
105
+
106
+ .wf-tools-empty {
107
+ padding: 16px;
108
+ text-align: center;
109
+ font-size: 12px;
110
+ color: #9ca3af;
111
+ background: #f9fafb;
112
+ border: 1px dashed #e5e7eb;
113
+ border-radius: 6px;
114
+ }
115
+
116
+ .wf-tools-list {
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: 6px;
120
+ }
121
+
122
+ .wf-tools-item {
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: space-between;
126
+ padding: 6px 10px;
127
+ background: #ffffff;
128
+ border: 1px solid #e5e7eb;
129
+ border-radius: 6px;
130
+ transition: border-color 0.15s;
131
+ }
132
+
133
+ .wf-tools-item:hover {
134
+ border-color: #6366f1;
135
+ }
136
+
137
+ .wf-tools-item-left {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 8px;
141
+ min-width: 0;
142
+ }
143
+
144
+ .wf-tools-item-icon {
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ width: 28px;
149
+ height: 28px;
150
+ color: #7c3aed;
151
+ background: #f3e8ff;
152
+ border-radius: 6px;
153
+ }
154
+
155
+ .wf-tools-item-name {
156
+ font-size: 12px;
157
+ font-weight: 500;
158
+ color: #1f2937;
159
+ overflow: hidden;
160
+ text-overflow: ellipsis;
161
+ white-space: nowrap;
162
+ }
163
+
164
+ .wf-tools-item-right {
165
+ display: flex;
166
+ align-items: center;
167
+ gap: 4px;
168
+ }
169
+ </style>