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,547 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * RetrievalMethodPicker — Dify-parity 检索方法 selector, extracted so the same
4
+ * UI serves the create wizard (step 2), the settings panel, and the recall
5
+ * testing popover without three near-identical copies of the same 300 lines.
6
+ *
7
+ * v-models a {@link RetrievalConfig}; consumers only care about that shape.
8
+ *
9
+ * Layout inside is:
10
+ * ┌─ 向量检索 (radio card, VECTOR-specific: Rerank + TopK + Score) ─┐
11
+ * ├─ 全文检索 (radio card, TopK + Score) ─────────────────────────┤
12
+ * └─ 混合检索 (radio card, [权重设置|Rerank] tabs + TopK + Score) ─┘
13
+ *
14
+ * When {@code indexingTechnique === 'ECONOMY'} the vector/hybrid cards are
15
+ * disabled (Dify parity — economy indexing only supports keyword search).
16
+ */
17
+ import { computed, ref, watch } from 'vue';
18
+
19
+ import type {
20
+ IndexingTechnique,
21
+ RetrievalConfig,
22
+ } from '../types/dataset';
23
+ import type { RetrievalMethod } from '../types/retrieval';
24
+
25
+ interface Props {
26
+ modelValue: RetrievalConfig;
27
+ rerankModels?: Array<{ id: string; label: string }>;
28
+ /** Restricts VECTOR / HYBRID selection when set to 'ECONOMY'. */
29
+ indexingTechnique?: IndexingTechnique;
30
+ /**
31
+ * 'inline' (default) is the roomy wizard/settings layout;
32
+ * 'compact' tightens paddings for popover use.
33
+ */
34
+ variant?: 'compact' | 'inline';
35
+ }
36
+ const props = withDefaults(defineProps<Props>(), {
37
+ rerankModels: () => [],
38
+ variant: 'inline',
39
+ });
40
+ const emit = defineEmits<{
41
+ (e: 'update:modelValue', v: RetrievalConfig): void;
42
+ }>();
43
+
44
+ // Local ref shadow so v-model round-trips cleanly through partial edits.
45
+ const method = ref<RetrievalMethod>(props.modelValue.method ?? 'VECTOR');
46
+ const topK = ref<number>(props.modelValue.topK ?? 3);
47
+ const scoreThreshold = ref<number>(props.modelValue.scoreThreshold ?? 0.5);
48
+ const scoreThresholdEnabled = ref<boolean>(
49
+ props.modelValue.scoreThreshold !== undefined,
50
+ );
51
+ const vectorWeight = ref<number>(props.modelValue.vectorWeight ?? 0.7);
52
+ const rerankEnabled = ref<boolean>(!!props.modelValue.rerankEnabled);
53
+ const rerankModelId = ref<string>(props.modelValue.rerankModelId ?? '');
54
+ // The hybrid card has two sub-tabs — the user picks either weight-based
55
+ // blending or a rerank model to combine vector+keyword results.
56
+ const hybridSubMode = ref<'rerank' | 'weight'>(
57
+ props.modelValue.rerankEnabled ? 'rerank' : 'weight',
58
+ );
59
+
60
+ // Push local edits back up as one atomic config. Watchers are cheap; the
61
+ // consumer sees a single object mutation per user tap.
62
+ function emitConfig() {
63
+ emit('update:modelValue', {
64
+ method: method.value,
65
+ topK: topK.value,
66
+ scoreThreshold: scoreThresholdEnabled.value ? scoreThreshold.value : undefined,
67
+ vectorWeight: method.value === 'HYBRID' ? vectorWeight.value : undefined,
68
+ rerankEnabled: rerankEnabled.value,
69
+ rerankModelId: rerankEnabled.value ? rerankModelId.value : undefined,
70
+ // rerankerName mirrors dify's flag: 'model' when a real reranker is used,
71
+ // 'weighted' for pure weight blending in hybrid mode.
72
+ rerankerName:
73
+ method.value === 'HYBRID'
74
+ ? hybridSubMode.value === 'rerank' && rerankEnabled.value
75
+ ? 'model'
76
+ : 'weighted'
77
+ : rerankEnabled.value
78
+ ? 'model'
79
+ : undefined,
80
+ });
81
+ }
82
+
83
+ watch(
84
+ [
85
+ method,
86
+ topK,
87
+ scoreThreshold,
88
+ scoreThresholdEnabled,
89
+ vectorWeight,
90
+ rerankEnabled,
91
+ rerankModelId,
92
+ hybridSubMode,
93
+ ],
94
+ emitConfig,
95
+ );
96
+
97
+ // Re-hydrate locals when parent overwrites modelValue (e.g. dataset row load).
98
+ watch(
99
+ () => props.modelValue,
100
+ (v) => {
101
+ if (!v) return;
102
+ method.value = v.method ?? 'VECTOR';
103
+ topK.value = v.topK ?? 3;
104
+ scoreThreshold.value = v.scoreThreshold ?? 0.5;
105
+ scoreThresholdEnabled.value = v.scoreThreshold !== undefined;
106
+ vectorWeight.value = v.vectorWeight ?? 0.7;
107
+ rerankEnabled.value = !!v.rerankEnabled;
108
+ rerankModelId.value = v.rerankModelId ?? '';
109
+ hybridSubMode.value = v.rerankEnabled ? 'rerank' : 'weight';
110
+ },
111
+ { deep: true },
112
+ );
113
+
114
+ const disableVectorish = computed(
115
+ () => props.indexingTechnique === 'ECONOMY',
116
+ );
117
+
118
+ function pickMethod(m: RetrievalMethod) {
119
+ if (disableVectorish.value && (m === 'VECTOR' || m === 'HYBRID')) return;
120
+ method.value = m;
121
+ }
122
+ </script>
123
+
124
+ <template>
125
+ <div class="rmp" :class="[`rmp-${variant}`]">
126
+ <!-- 向量检索 -->
127
+ <div
128
+ class="rmp-card"
129
+ :class="{
130
+ 'rmp-card-active': method === 'VECTOR',
131
+ 'rmp-card-disabled': disableVectorish,
132
+ }"
133
+ @click="pickMethod('VECTOR')"
134
+ >
135
+ <div class="rmp-head">
136
+ <span
137
+ class="rmp-radio"
138
+ :class="{ 'rmp-radio-on': method === 'VECTOR' }"
139
+ />
140
+ <span class="rmp-title">◈ 向量检索</span>
141
+ </div>
142
+ <div class="rmp-desc">
143
+ 通过生成查询嵌入,查询与其向量表示最相似的文本分段。
144
+ </div>
145
+ <div
146
+ v-if="method === 'VECTOR' && !disableVectorish"
147
+ class="rmp-body"
148
+ @click.stop
149
+ >
150
+ <label class="rmp-check">
151
+ <input type="checkbox" v-model="rerankEnabled" />
152
+ <span>Rerank 模型</span>
153
+ <span class="rmp-hint-inline">对检索结果按语义相关度重排</span>
154
+ </label>
155
+ <select
156
+ v-if="rerankEnabled"
157
+ v-model="rerankModelId"
158
+ class="rmp-select"
159
+ >
160
+ <option value="">选择一个 Rerank 模型</option>
161
+ <option v-for="m in rerankModels" :key="m.id" :value="m.id">
162
+ {{ m.label }}
163
+ </option>
164
+ </select>
165
+ <div class="rmp-slider-row">
166
+ <div class="rmp-slider-label">
167
+ <span>Top K</span>
168
+ <span class="rmp-slider-val">{{ topK }}</span>
169
+ </div>
170
+ <input
171
+ v-model.number="topK"
172
+ type="range"
173
+ min="1"
174
+ max="10"
175
+ step="1"
176
+ class="rmp-range"
177
+ />
178
+ </div>
179
+ <div class="rmp-slider-row">
180
+ <div class="rmp-slider-label">
181
+ <label class="rmp-check rmp-check-inline">
182
+ <input type="checkbox" v-model="scoreThresholdEnabled" />
183
+ <span>Score 阈值</span>
184
+ </label>
185
+ <span class="rmp-slider-val">
186
+ {{ scoreThresholdEnabled ? scoreThreshold.toFixed(2) : '—' }}
187
+ </span>
188
+ </div>
189
+ <input
190
+ v-model.number="scoreThreshold"
191
+ type="range"
192
+ min="0"
193
+ max="1"
194
+ step="0.01"
195
+ class="rmp-range"
196
+ :disabled="!scoreThresholdEnabled"
197
+ />
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ <!-- 全文检索 -->
203
+ <div
204
+ class="rmp-card"
205
+ :class="{ 'rmp-card-active': method === 'FULL_TEXT' }"
206
+ @click="pickMethod('FULL_TEXT')"
207
+ >
208
+ <div class="rmp-head">
209
+ <span
210
+ class="rmp-radio"
211
+ :class="{ 'rmp-radio-on': method === 'FULL_TEXT' }"
212
+ />
213
+ <span class="rmp-title">≡ 全文检索</span>
214
+ </div>
215
+ <div class="rmp-desc">
216
+ 索引文档中的所有词汇,从而允许用户查询任意词汇。
217
+ </div>
218
+ <div v-if="method === 'FULL_TEXT'" class="rmp-body" @click.stop>
219
+ <div class="rmp-slider-row">
220
+ <div class="rmp-slider-label">
221
+ <span>Top K</span>
222
+ <span class="rmp-slider-val">{{ topK }}</span>
223
+ </div>
224
+ <input
225
+ v-model.number="topK"
226
+ type="range"
227
+ min="1"
228
+ max="10"
229
+ step="1"
230
+ class="rmp-range"
231
+ />
232
+ </div>
233
+ <div class="rmp-slider-row">
234
+ <div class="rmp-slider-label">
235
+ <label class="rmp-check rmp-check-inline">
236
+ <input type="checkbox" v-model="scoreThresholdEnabled" />
237
+ <span>Score 阈值</span>
238
+ </label>
239
+ <span class="rmp-slider-val">
240
+ {{ scoreThresholdEnabled ? scoreThreshold.toFixed(2) : '—' }}
241
+ </span>
242
+ </div>
243
+ <input
244
+ v-model.number="scoreThreshold"
245
+ type="range"
246
+ min="0"
247
+ max="1"
248
+ step="0.01"
249
+ class="rmp-range"
250
+ :disabled="!scoreThresholdEnabled"
251
+ />
252
+ </div>
253
+ </div>
254
+ </div>
255
+
256
+ <!-- 混合检索 -->
257
+ <div
258
+ class="rmp-card"
259
+ :class="{
260
+ 'rmp-card-active': method === 'HYBRID',
261
+ 'rmp-card-disabled': disableVectorish,
262
+ }"
263
+ @click="pickMethod('HYBRID')"
264
+ >
265
+ <div class="rmp-head">
266
+ <span
267
+ class="rmp-radio"
268
+ :class="{ 'rmp-radio-on': method === 'HYBRID' }"
269
+ />
270
+ <span class="rmp-title">⚡ 混合检索</span>
271
+ <span class="rmp-badge">推荐</span>
272
+ </div>
273
+ <div class="rmp-desc">
274
+ 同时执行全文与向量检索并重排;可选权重或 Rerank 模型融合两路结果。
275
+ </div>
276
+ <div
277
+ v-if="method === 'HYBRID' && !disableVectorish"
278
+ class="rmp-body"
279
+ @click.stop
280
+ >
281
+ <div class="rmp-tabs">
282
+ <button
283
+ type="button"
284
+ class="rmp-tab"
285
+ :class="{ 'rmp-tab-active': hybridSubMode === 'weight' }"
286
+ @click="hybridSubMode = 'weight'"
287
+ >
288
+ 权重设置
289
+ </button>
290
+ <button
291
+ type="button"
292
+ class="rmp-tab"
293
+ :class="{ 'rmp-tab-active': hybridSubMode === 'rerank' }"
294
+ @click="hybridSubMode = 'rerank'"
295
+ >
296
+ Rerank 模型
297
+ </button>
298
+ </div>
299
+ <div v-if="hybridSubMode === 'weight'" class="rmp-slider-row">
300
+ <div class="rmp-slider-label">
301
+ <span>
302
+ 语义 {{ vectorWeight.toFixed(1) }} · 关键词
303
+ {{ (1 - vectorWeight).toFixed(1) }}
304
+ </span>
305
+ </div>
306
+ <input
307
+ v-model.number="vectorWeight"
308
+ type="range"
309
+ min="0"
310
+ max="1"
311
+ step="0.1"
312
+ class="rmp-range"
313
+ />
314
+ </div>
315
+ <div v-else>
316
+ <label class="rmp-check">
317
+ <input type="checkbox" v-model="rerankEnabled" />
318
+ <span>启用 Rerank 模型</span>
319
+ </label>
320
+ <select
321
+ v-if="rerankEnabled"
322
+ v-model="rerankModelId"
323
+ class="rmp-select"
324
+ >
325
+ <option value="">选择一个 Rerank 模型</option>
326
+ <option v-for="m in rerankModels" :key="m.id" :value="m.id">
327
+ {{ m.label }}
328
+ </option>
329
+ </select>
330
+ </div>
331
+ <div class="rmp-slider-row">
332
+ <div class="rmp-slider-label">
333
+ <span>Top K</span>
334
+ <span class="rmp-slider-val">{{ topK }}</span>
335
+ </div>
336
+ <input
337
+ v-model.number="topK"
338
+ type="range"
339
+ min="1"
340
+ max="10"
341
+ step="1"
342
+ class="rmp-range"
343
+ />
344
+ </div>
345
+ <div class="rmp-slider-row">
346
+ <div class="rmp-slider-label">
347
+ <label class="rmp-check rmp-check-inline">
348
+ <input type="checkbox" v-model="scoreThresholdEnabled" />
349
+ <span>Score 阈值</span>
350
+ </label>
351
+ <span class="rmp-slider-val">
352
+ {{ scoreThresholdEnabled ? scoreThreshold.toFixed(2) : '—' }}
353
+ </span>
354
+ </div>
355
+ <input
356
+ v-model.number="scoreThreshold"
357
+ type="range"
358
+ min="0"
359
+ max="1"
360
+ step="0.01"
361
+ class="rmp-range"
362
+ :disabled="!scoreThresholdEnabled"
363
+ />
364
+ </div>
365
+ </div>
366
+ </div>
367
+
368
+ <p v-if="disableVectorish" class="rmp-econ-note">
369
+ 当前索引方式为「经济」,仅支持全文检索。
370
+ </p>
371
+ </div>
372
+ </template>
373
+
374
+ <style scoped>
375
+ .rmp {
376
+ display: flex;
377
+ flex-direction: column;
378
+ gap: 10px;
379
+ }
380
+ .rmp-compact {
381
+ gap: 6px;
382
+ }
383
+
384
+ .rmp-card {
385
+ padding: 12px 14px;
386
+ background: #fff;
387
+ border: 1px solid #e2e8f0;
388
+ border-radius: 10px;
389
+ cursor: pointer;
390
+ transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
391
+ }
392
+ .rmp-card:hover {
393
+ border-color: #cbd5e1;
394
+ }
395
+ .rmp-card-active {
396
+ border-color: transparent;
397
+ outline: 1.5px solid #6366f1;
398
+ box-shadow: 0 3px 10px rgba(99, 102, 241, 0.14);
399
+ background: #fbfbff;
400
+ }
401
+ .rmp-card-disabled {
402
+ cursor: not-allowed;
403
+ opacity: 0.55;
404
+ }
405
+
406
+ .rmp-compact .rmp-card {
407
+ padding: 10px 12px;
408
+ }
409
+
410
+ .rmp-head {
411
+ display: flex;
412
+ align-items: center;
413
+ gap: 8px;
414
+ }
415
+ .rmp-radio {
416
+ width: 14px;
417
+ height: 14px;
418
+ border-radius: 50%;
419
+ border: 1.5px solid #cbd5e1;
420
+ background: #fff;
421
+ transition: all 0.15s;
422
+ }
423
+ .rmp-radio-on {
424
+ border-color: #6366f1;
425
+ box-shadow: inset 0 0 0 3.5px #6366f1;
426
+ }
427
+ .rmp-title {
428
+ font-size: 13px;
429
+ font-weight: 600;
430
+ color: #0f172a;
431
+ }
432
+ .rmp-badge {
433
+ padding: 1px 6px;
434
+ font-size: 10px;
435
+ font-weight: 600;
436
+ color: #fff;
437
+ background: #4f46e5;
438
+ border-radius: 4px;
439
+ }
440
+ .rmp-desc {
441
+ margin-top: 4px;
442
+ padding-left: 22px;
443
+ font-size: 12px;
444
+ color: #64748b;
445
+ line-height: 1.5;
446
+ }
447
+ .rmp-body {
448
+ margin-top: 10px;
449
+ padding-left: 22px;
450
+ display: flex;
451
+ flex-direction: column;
452
+ gap: 10px;
453
+ }
454
+ .rmp-check {
455
+ display: flex;
456
+ align-items: center;
457
+ gap: 6px;
458
+ font-size: 12px;
459
+ color: #334155;
460
+ cursor: pointer;
461
+ }
462
+ .rmp-check input[type='checkbox'] {
463
+ width: 14px;
464
+ height: 14px;
465
+ accent-color: #6366f1;
466
+ }
467
+ .rmp-check-inline {
468
+ display: inline-flex;
469
+ }
470
+ .rmp-hint-inline {
471
+ margin-left: 6px;
472
+ font-size: 11px;
473
+ color: #94a3b8;
474
+ }
475
+
476
+ .rmp-select {
477
+ width: 100%;
478
+ padding: 6px 10px;
479
+ font-size: 12px;
480
+ color: #0f172a;
481
+ background: #fff;
482
+ border: 1px solid #d1d5db;
483
+ border-radius: 6px;
484
+ outline: none;
485
+ }
486
+ .rmp-select:focus {
487
+ border-color: #6366f1;
488
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
489
+ }
490
+
491
+ .rmp-slider-row {
492
+ display: flex;
493
+ flex-direction: column;
494
+ gap: 4px;
495
+ }
496
+ .rmp-slider-label {
497
+ display: flex;
498
+ justify-content: space-between;
499
+ align-items: center;
500
+ font-size: 12px;
501
+ color: #64748b;
502
+ }
503
+ .rmp-slider-val {
504
+ font-size: 12px;
505
+ color: #4338ca;
506
+ font-weight: 500;
507
+ }
508
+ .rmp-range {
509
+ width: 100%;
510
+ accent-color: #6366f1;
511
+ }
512
+ .rmp-range:disabled {
513
+ opacity: 0.5;
514
+ }
515
+
516
+ .rmp-tabs {
517
+ display: inline-flex;
518
+ padding: 2px;
519
+ gap: 2px;
520
+ background: #eef2ff;
521
+ border-radius: 6px;
522
+ }
523
+ .rmp-tab {
524
+ padding: 3px 10px;
525
+ background: transparent;
526
+ border: none;
527
+ border-radius: 4px;
528
+ font-size: 11px;
529
+ color: #64748b;
530
+ cursor: pointer;
531
+ }
532
+ .rmp-tab-active {
533
+ background: #fff;
534
+ color: #4338ca;
535
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
536
+ }
537
+
538
+ .rmp-econ-note {
539
+ margin: 0;
540
+ padding: 6px 10px;
541
+ font-size: 11px;
542
+ color: #92400e;
543
+ background: #fef3c7;
544
+ border: 1px solid #fde68a;
545
+ border-radius: 6px;
546
+ }
547
+ </style>
@@ -0,0 +1,24 @@
1
+ <script setup lang="ts">
2
+ import type { RetrievedSegment } from '../types';
3
+
4
+ defineProps<{
5
+ hits: RetrievedSegment[];
6
+ }>();
7
+ </script>
8
+
9
+ <template>
10
+ <div v-if="hits.length === 0" class="py-4 text-center text-sm text-gray-400">
11
+ 暂无结果
12
+ </div>
13
+ <div
14
+ v-for="h in hits"
15
+ :key="h.segmentId"
16
+ class="mb-2 rounded border border-gray-100 p-2"
17
+ >
18
+ <div class="mb-1 flex items-center justify-between text-xs text-gray-500">
19
+ <span>#{{ h.position }} · doc {{ h.documentId?.slice(0, 8) }}</span>
20
+ <span>综合 {{ h.score.toFixed(3) }}</span>
21
+ </div>
22
+ <div class="whitespace-pre-wrap text-sm">{{ h.content }}</div>
23
+ </div>
24
+ </template>