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.
- package/package.json +30 -0
- package/pnpm-workspace.yaml +3 -0
- package/src/agent-flow/adapter/backend.ts +99 -0
- package/src/agent-flow/components/ChatIframePanel.vue +773 -0
- package/src/agent-flow/components/DatasetItemCard.vue +313 -0
- package/src/agent-flow/components/MemoryWindow.vue +104 -0
- package/src/agent-flow/components/PromptEditor.vue +804 -0
- package/src/agent-flow/components/PromptEditorTagPanel.vue +460 -0
- package/src/agent-flow/components/SvgIcon.vue +32 -0
- package/src/agent-flow/components/ToolItemCard.vue +169 -0
- package/src/agent-flow/components/VariableCard.vue +161 -0
- package/src/agent-flow/components/VariableSelector.vue +162 -0
- package/src/agent-flow/components/chat-iframe-types.ts +35 -0
- package/src/agent-flow/components/stubs/DatasetChooser.vue +70 -0
- package/src/agent-flow/components/stubs/Embedding.vue +22 -0
- package/src/agent-flow/components/stubs/ModelSelect.vue +24 -0
- package/src/agent-flow/components/stubs/RestfulSelectCard.vue +24 -0
- package/src/agent-flow/components/stubs/ToolChooser.vue +96 -0
- package/src/agent-flow/components/stubs/TriggersItemCard.vue +126 -0
- package/src/agent-flow/components/stubs/VariableModal.vue +115 -0
- package/src/agent-flow/index.ts +73 -0
- package/src/agent-flow/stores/workflow.ts +72 -0
- package/src/agent-flow/utils/langUtils.ts +173 -0
- package/src/agent-flow/workflow/Ability.vue +65 -0
- package/src/agent-flow/workflow/AuthorizationItem.vue +101 -0
- package/src/agent-flow/workflow/ConversationVar.vue +42 -0
- package/src/agent-flow/workflow/CustomEdge.vue +94 -0
- package/src/agent-flow/workflow/EnvironmentVar.vue +40 -0
- package/src/agent-flow/workflow/FlowDesigner.vue +2802 -0
- package/src/agent-flow/workflow/Icon.vue +179 -0
- package/src/agent-flow/workflow/NodeConfig.vue +509 -0
- package/src/agent-flow/workflow/NodeConfigCard.vue +460 -0
- package/src/agent-flow/workflow/NodeHandle.vue +147 -0
- package/src/agent-flow/workflow/NodeHeaderToolbar.vue +77 -0
- package/src/agent-flow/workflow/NodeHoverToolbar.vue +176 -0
- package/src/agent-flow/workflow/OutputItemCard.vue +668 -0
- package/src/agent-flow/workflow/ParamTableItem.vue +200 -0
- package/src/agent-flow/workflow/StructTable.vue +202 -0
- package/src/agent-flow/workflow/VarInsertField.vue +517 -0
- package/src/agent-flow/workflow/VarSelectField.vue +223 -0
- package/src/agent-flow/workflow/VariableSelectorItem.vue +48 -0
- package/src/agent-flow/workflow/WfField.vue +206 -0
- package/src/agent-flow/workflow/_config-base.css +470 -0
- package/src/agent-flow/workflow/composables/useNode.js +55 -0
- package/src/agent-flow/workflow/index.vue +53 -0
- package/src/agent-flow/workflow/nodeCard/AgentNodeCard.vue +180 -0
- package/src/agent-flow/workflow/nodeCard/AnswerNodeCard.vue +65 -0
- package/src/agent-flow/workflow/nodeCard/ClassifierCard.vue +152 -0
- package/src/agent-flow/workflow/nodeCard/CodeNodeCard.vue +139 -0
- package/src/agent-flow/workflow/nodeCard/ConditionNodeCard.vue +323 -0
- package/src/agent-flow/workflow/nodeCard/DocumentExtractorCard.vue +83 -0
- package/src/agent-flow/workflow/nodeCard/EndNodeCard.vue +123 -0
- package/src/agent-flow/workflow/nodeCard/HttpNodeCard.vue +451 -0
- package/src/agent-flow/workflow/nodeCard/HumanInputCard.vue +152 -0
- package/src/agent-flow/workflow/nodeCard/IterationCard.vue +66 -0
- package/src/agent-flow/workflow/nodeCard/KnowledgeRetrievalCard.vue +48 -0
- package/src/agent-flow/workflow/nodeCard/LLMNodeCard.vue +206 -0
- package/src/agent-flow/workflow/nodeCard/ListOperatorCard.vue +188 -0
- package/src/agent-flow/workflow/nodeCard/ParameterExtractorCard.vue +352 -0
- package/src/agent-flow/workflow/nodeCard/ServiceApiNodeCard.vue +188 -0
- package/src/agent-flow/workflow/nodeCard/StartNodeCard.vue +216 -0
- package/src/agent-flow/workflow/nodeCard/VariableAssignerCard.vue +125 -0
- package/src/agent-flow/workflow/nodeCard/VariableNodeCard.vue +121 -0
- package/src/agent-flow/workflow/nodes/AgentNode.vue +122 -0
- package/src/agent-flow/workflow/nodes/AnswerNode.vue +99 -0
- package/src/agent-flow/workflow/nodes/ClassifierNode.vue +199 -0
- package/src/agent-flow/workflow/nodes/CodeNode.vue +152 -0
- package/src/agent-flow/workflow/nodes/ConditionNode.vue +232 -0
- package/src/agent-flow/workflow/nodes/DocumentExtractorNode.vue +96 -0
- package/src/agent-flow/workflow/nodes/EndNode.vue +119 -0
- package/src/agent-flow/workflow/nodes/FileUploadNode.vue +97 -0
- package/src/agent-flow/workflow/nodes/HttpNode.vue +204 -0
- package/src/agent-flow/workflow/nodes/HumanInputNode.vue +110 -0
- package/src/agent-flow/workflow/nodes/IterationNode.vue +98 -0
- package/src/agent-flow/workflow/nodes/KnowledgeRetrievalNode.vue +253 -0
- package/src/agent-flow/workflow/nodes/LLMNode.vue +134 -0
- package/src/agent-flow/workflow/nodes/ListOperatorNode.vue +111 -0
- package/src/agent-flow/workflow/nodes/LoopNode.vue +94 -0
- package/src/agent-flow/workflow/nodes/ParameterExtractorNode.vue +147 -0
- package/src/agent-flow/workflow/nodes/ServiceApiNode.vue +161 -0
- package/src/agent-flow/workflow/nodes/StartNode.vue +199 -0
- package/src/agent-flow/workflow/nodes/TemplateNode.vue +103 -0
- package/src/agent-flow/workflow/nodes/UserInputNode.vue +92 -0
- package/src/agent-flow/workflow/nodes/VariableAssignerNode.vue +145 -0
- package/src/agent-flow/workflow/nodes/VariableNode.vue +130 -0
- package/src/agent-flow/workflow/nodes/_node-base.css +338 -0
- package/src/agent-flow/workflow/utils/node_card_form.ts +335 -0
- package/src/agent-flow/workflow/utils/node_config.ts +63 -0
- package/src/agent-flow/workflow/utils/workflow_utils.ts +151 -0
- package/src/agent-studio/adapters/springAgentStart.ts +390 -0
- package/src/agent-studio/api/index.ts +1 -0
- package/src/agent-studio/api/types.ts +173 -0
- package/src/agent-studio/components/AgentApiDocs.vue +500 -0
- package/src/agent-studio/components/AgentAppsPage.vue +1601 -0
- package/src/agent-studio/components/AgentCardGrid.vue +259 -0
- package/src/agent-studio/components/AgentChatPage.vue +553 -0
- package/src/agent-studio/components/AgentDebugPanel.vue +350 -0
- package/src/agent-studio/components/AgentLogsPanel.vue +391 -0
- package/src/agent-studio/components/AgentMonitorPanel.vue +204 -0
- package/src/agent-studio/components/AgentOrchestrate.vue +505 -0
- package/src/agent-studio/components/AgentPromptEditor.vue +237 -0
- package/src/agent-studio/components/AgentStudioShell.vue +227 -0
- package/src/agent-studio/components/AgentToolsPanel.vue +394 -0
- package/src/agent-studio/components/AgentVariablesPanel.vue +205 -0
- package/src/agent-studio/components/ApiKeyManager.vue +251 -0
- package/src/agent-studio/components/AppDesignDrawer.vue +3391 -0
- package/src/agent-studio/components/CreateAppModal.vue +737 -0
- package/src/agent-studio/components/SparkChart.vue +200 -0
- package/src/agent-studio/components/VariableEditorModal.vue +499 -0
- package/src/agent-studio/composables/useAgentStudio.ts +347 -0
- package/src/agent-studio/index.ts +60 -0
- package/src/agent-studio/panels/DrawerFlowDesigner.vue +203 -0
- package/src/agent-studio/panels/LogAnnotationPanel.vue +490 -0
- package/src/agent-studio/panels/MonitorPanel.vue +535 -0
- package/src/agent-studio/types.ts +242 -0
- package/src/index.ts +155 -0
- package/src/knowledge-hub/adapters/springAgentStart.ts +472 -0
- package/src/knowledge-hub/components/AddDocumentsWizard.vue +1452 -0
- package/src/knowledge-hub/components/CreateDatasetWizard.vue +1723 -0
- package/src/knowledge-hub/components/DatasetCardGrid.vue +467 -0
- package/src/knowledge-hub/components/DatasetDetailDrawer.vue +888 -0
- package/src/knowledge-hub/components/DatasetPicker.vue +65 -0
- package/src/knowledge-hub/components/DatasetPickerModal.vue +526 -0
- package/src/knowledge-hub/components/DatasetSettingsPanel.vue +908 -0
- package/src/knowledge-hub/components/DatasetSidebar.vue +781 -0
- package/src/knowledge-hub/components/DocumentChunksView.vue +802 -0
- package/src/knowledge-hub/components/DocumentTable.vue +552 -0
- package/src/knowledge-hub/components/HitTestingPanel.vue +74 -0
- package/src/knowledge-hub/components/KnowledgeApp.vue +66 -0
- package/src/knowledge-hub/components/KnowledgeHubApp.vue +504 -0
- package/src/knowledge-hub/components/RecallTestingPanelV2.vue +515 -0
- package/src/knowledge-hub/components/RetrievalConfigPopover.vue +270 -0
- package/src/knowledge-hub/components/RetrievalMethodPicker.vue +547 -0
- package/src/knowledge-hub/components/RetrievedList.vue +24 -0
- package/src/knowledge-hub/components/internal/KhDialog.vue +195 -0
- package/src/knowledge-hub/composables/useKnowledge.ts +89 -0
- package/src/knowledge-hub/i18n/index.ts +2 -0
- package/src/knowledge-hub/i18n/messages.ts +422 -0
- package/src/knowledge-hub/i18n/useKhI18n.ts +107 -0
- package/src/knowledge-hub/index.ts +46 -0
- package/src/knowledge-hub/styles/index.css +4 -0
- package/src/knowledge-hub/styles/tokens.css +138 -0
- package/src/knowledge-hub/types/api.ts +213 -0
- package/src/knowledge-hub/types/dataset.ts +88 -0
- package/src/knowledge-hub/types/document.ts +45 -0
- package/src/knowledge-hub/types/index.ts +9 -0
- package/src/knowledge-hub/types/retrieval.ts +46 -0
- package/src/provider-hub/components/CredentialForm.vue +103 -0
- package/src/provider-hub/components/DefaultModelsPanel.vue +270 -0
- package/src/provider-hub/components/GroupedModelSelect.vue +445 -0
- package/src/provider-hub/components/ModelCardGrid.vue +332 -0
- package/src/provider-hub/components/ModelParameterDrawer.vue +739 -0
- package/src/provider-hub/components/ModelPickerPopover.vue +1282 -0
- package/src/provider-hub/components/ProviderApp.vue +159 -0
- package/src/provider-hub/components/ProviderCredentialModal.vue +321 -0
- package/src/provider-hub/components/ProviderGallery.vue +131 -0
- package/src/provider-hub/components/ProviderHubShell.vue +1992 -0
- package/src/provider-hub/components/ProviderIcon.vue +130 -0
- package/src/provider-hub/composables/useProviderHub.ts +358 -0
- package/src/provider-hub/index.ts +43 -0
- package/src/provider-hub/svg/MiniMax.svg +1 -0
- package/src/provider-hub/svg/Volcengine.svg +1 -0
- package/src/provider-hub/svg/Zhipu.svg +1 -0
- package/src/provider-hub/svg/deepseek.svg +1 -0
- package/src/provider-hub/svg/moonshot.svg +1 -0
- package/src/provider-hub/svg/openai.svg +1 -0
- package/src/provider-hub/svg/qwen.svg +1 -0
- package/src/provider-hub/svg/siliconflow.svg +1 -0
- package/src/provider-hub/types.ts +256 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* KnowledgeHubApp — the drop-in top-level component for the knowledge-base
|
|
4
|
+
* feature. Consumers only need to implement the `KnowledgeHubApi` interface
|
|
5
|
+
* (a bag of async REST calls) and drop this component in — the whole Dify-
|
|
6
|
+
* parity flow is contained inside:
|
|
7
|
+
*
|
|
8
|
+
* ┌────────────────────────────────────────────────────────┐
|
|
9
|
+
* │ <KnowledgeHubApp :api="apiImpl" /> │
|
|
10
|
+
* └────────────────────────────────────────────────────────┘
|
|
11
|
+
*
|
|
12
|
+
* renders the card grid.
|
|
13
|
+
* +新建 click → left-side wizard drawer (self-contained)
|
|
14
|
+
* dataset card click → right-side detail drawer with 4 tabs +
|
|
15
|
+
* inline chunks browser (no route change)
|
|
16
|
+
*
|
|
17
|
+
* The parent app doesn't need routes, watchers or dialog plumbing for the
|
|
18
|
+
* knowledge feature. Emits `changed` when list-affecting mutations happen so
|
|
19
|
+
* consumers can refresh their own counters if needed.
|
|
20
|
+
*
|
|
21
|
+
* Expose `openCreate()` / `openDetail(id)` via defineExpose so the host can
|
|
22
|
+
* also trigger the drawers programmatically (per the demo request).
|
|
23
|
+
*/
|
|
24
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
25
|
+
|
|
26
|
+
import { type KhLocale, provideKhI18n, useKhI18n } from '../i18n';
|
|
27
|
+
import type {
|
|
28
|
+
DatasetCardItem,
|
|
29
|
+
DatasetDetailHub,
|
|
30
|
+
DatasetTab,
|
|
31
|
+
EmbeddingModelOption,
|
|
32
|
+
KnowledgeHubApi,
|
|
33
|
+
ProcessRule,
|
|
34
|
+
RetrievalConfig,
|
|
35
|
+
} from '../types';
|
|
36
|
+
import CreateDatasetWizard from './CreateDatasetWizard.vue';
|
|
37
|
+
import DatasetCardGrid from './DatasetCardGrid.vue';
|
|
38
|
+
import DatasetDetailDrawer from './DatasetDetailDrawer.vue';
|
|
39
|
+
import KhDialog from './internal/KhDialog.vue';
|
|
40
|
+
|
|
41
|
+
interface Props {
|
|
42
|
+
api: KnowledgeHubApi;
|
|
43
|
+
/**
|
|
44
|
+
* Optional locale — either a language code ('zh-CN' | 'en-US') or a partial
|
|
45
|
+
* message catalog for host-owned overrides. Default: zh-CN.
|
|
46
|
+
*/
|
|
47
|
+
locale?: KhLocale;
|
|
48
|
+
/** Optional page title override — falls back to the locale catalog. */
|
|
49
|
+
title?: string;
|
|
50
|
+
/** Optional description override — falls back to the locale catalog. */
|
|
51
|
+
description?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const props = defineProps<Props>();
|
|
55
|
+
provideKhI18n(() => props.locale);
|
|
56
|
+
const { t } = useKhI18n();
|
|
57
|
+
|
|
58
|
+
const displayTitle = computed(() => props.title ?? t('app.title'));
|
|
59
|
+
const displayDescription = computed(
|
|
60
|
+
() => props.description ?? t('app.description'),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const emit = defineEmits<{
|
|
64
|
+
(e: 'changed'): void;
|
|
65
|
+
}>();
|
|
66
|
+
|
|
67
|
+
// -------- state
|
|
68
|
+
const datasets = ref<DatasetCardItem[]>([]);
|
|
69
|
+
const loading = ref(false);
|
|
70
|
+
const keyword = ref('');
|
|
71
|
+
|
|
72
|
+
const wizardOpen = ref(false);
|
|
73
|
+
const detailOpen = ref(false);
|
|
74
|
+
const detailId = ref<null | string>(null);
|
|
75
|
+
const detailInitialTab = ref<DatasetTab | undefined>(undefined);
|
|
76
|
+
|
|
77
|
+
const embeddingModels = ref<EmbeddingModelOption[]>([]);
|
|
78
|
+
const rerankModels = ref<Array<{ id: string; label: string }>>([]);
|
|
79
|
+
|
|
80
|
+
// -------- helpers
|
|
81
|
+
function notifySuccess(msg: string) {
|
|
82
|
+
props.api.onSuccess?.(msg);
|
|
83
|
+
}
|
|
84
|
+
function notifyError(msg: string) {
|
|
85
|
+
props.api.onError?.(msg);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// -------- load
|
|
89
|
+
async function refresh() {
|
|
90
|
+
loading.value = true;
|
|
91
|
+
try {
|
|
92
|
+
datasets.value = await props.api.listDatasets();
|
|
93
|
+
} catch (e: any) {
|
|
94
|
+
notifyError(`${t('common.loadFailed')}: ${e?.message ?? e}`);
|
|
95
|
+
datasets.value = [];
|
|
96
|
+
} finally {
|
|
97
|
+
loading.value = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function loadEmbeddingModels() {
|
|
102
|
+
try {
|
|
103
|
+
embeddingModels.value = await props.api.listEmbeddingModels();
|
|
104
|
+
} catch {
|
|
105
|
+
embeddingModels.value = [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function loadRerankModels() {
|
|
110
|
+
if (!props.api.listRerankModels) {
|
|
111
|
+
rerankModels.value = [];
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
rerankModels.value = await props.api.listRerankModels();
|
|
116
|
+
} catch {
|
|
117
|
+
rerankModels.value = [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
onMounted(async () => {
|
|
122
|
+
await Promise.all([refresh(), loadEmbeddingModels(), loadRerankModels()]);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// -------- create
|
|
126
|
+
function openCreate() {
|
|
127
|
+
// Re-load embedding + rerank models each time so newly-registered ones show up.
|
|
128
|
+
loadEmbeddingModels();
|
|
129
|
+
loadRerankModels();
|
|
130
|
+
wizardOpen.value = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function onWizardCreate(payload: {
|
|
134
|
+
description: string;
|
|
135
|
+
embeddingModelId: string;
|
|
136
|
+
files: File[];
|
|
137
|
+
indexingTechnique: 'ECONOMY' | 'HIGH_QUALITY';
|
|
138
|
+
name: string;
|
|
139
|
+
processRule: ProcessRule;
|
|
140
|
+
retrievalConfig: RetrievalConfig;
|
|
141
|
+
}) {
|
|
142
|
+
try {
|
|
143
|
+
const created = await props.api.createDataset({
|
|
144
|
+
name: payload.name,
|
|
145
|
+
description: payload.description,
|
|
146
|
+
embeddingModelId: payload.embeddingModelId || undefined,
|
|
147
|
+
indexingTechnique: payload.indexingTechnique,
|
|
148
|
+
processRule: payload.processRule,
|
|
149
|
+
retrievalConfig: payload.retrievalConfig,
|
|
150
|
+
});
|
|
151
|
+
// Serial uploads keep progress easy to reason about.
|
|
152
|
+
for (const file of payload.files) {
|
|
153
|
+
await props.api.uploadDocument(created.id, file);
|
|
154
|
+
}
|
|
155
|
+
notifySuccess(t('common.createdSuccess'));
|
|
156
|
+
wizardOpen.value = false;
|
|
157
|
+
await refresh();
|
|
158
|
+
emit('changed');
|
|
159
|
+
// Auto-open the detail drawer on the freshly-created dataset — matches Dify.
|
|
160
|
+
openDetail(created.id);
|
|
161
|
+
} catch (e: any) {
|
|
162
|
+
notifyError(`${t('common.createFailed')}: ${e?.message ?? e}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// -------- create empty (KhDialog prompt, replaces window.prompt)
|
|
167
|
+
const emptyPromptOpen = ref(false);
|
|
168
|
+
function onWizardSkipEmpty() {
|
|
169
|
+
// Ask the user via our own dialog — no native browser prompts.
|
|
170
|
+
wizardOpen.value = false;
|
|
171
|
+
emptyPromptOpen.value = true;
|
|
172
|
+
}
|
|
173
|
+
async function onEmptyPromptConfirm(name: string | undefined) {
|
|
174
|
+
const trimmed = (name ?? '').trim();
|
|
175
|
+
if (!trimmed) return;
|
|
176
|
+
try {
|
|
177
|
+
const created = await props.api.createDataset({
|
|
178
|
+
name: trimmed,
|
|
179
|
+
indexingTechnique: 'ECONOMY',
|
|
180
|
+
});
|
|
181
|
+
notifySuccess(t('common.createdSuccess'));
|
|
182
|
+
await refresh();
|
|
183
|
+
emit('changed');
|
|
184
|
+
openDetail(created.id);
|
|
185
|
+
} catch (e: any) {
|
|
186
|
+
notifyError(`${t('common.createFailed')}: ${e?.message ?? e}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// -------- open detail
|
|
191
|
+
function openDetail(id: string, tab?: DatasetTab) {
|
|
192
|
+
detailId.value = id;
|
|
193
|
+
detailInitialTab.value = tab;
|
|
194
|
+
detailOpen.value = true;
|
|
195
|
+
}
|
|
196
|
+
function openSettings(d: DatasetCardItem) {
|
|
197
|
+
openDetail(d.id, 'settings');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// -------- delete from card (KhDialog confirm, replaces window.confirm)
|
|
201
|
+
const deleteConfirmOpen = ref(false);
|
|
202
|
+
const deleteTarget = ref<DatasetCardItem | null>(null);
|
|
203
|
+
|
|
204
|
+
function onDeleteCard(d: DatasetCardItem) {
|
|
205
|
+
deleteTarget.value = d;
|
|
206
|
+
deleteConfirmOpen.value = true;
|
|
207
|
+
}
|
|
208
|
+
async function onDeleteConfirm() {
|
|
209
|
+
const d = deleteTarget.value;
|
|
210
|
+
if (!d) return;
|
|
211
|
+
try {
|
|
212
|
+
await props.api.deleteDataset(d.id);
|
|
213
|
+
notifySuccess(t('common.deleteSuccess'));
|
|
214
|
+
await refresh();
|
|
215
|
+
emit('changed');
|
|
216
|
+
} catch (e: any) {
|
|
217
|
+
notifyError(`${e?.message ?? e}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// -------- detail drawer hub adapter (satisfies DatasetDetailHub interface)
|
|
222
|
+
const detailHub = computed<DatasetDetailHub>(() => ({
|
|
223
|
+
loadDataset: (id) => props.api.getDataset(id),
|
|
224
|
+
updateDataset: (id, patch) => props.api.updateDataset(id, patch),
|
|
225
|
+
deleteDataset: async (id) => {
|
|
226
|
+
await props.api.deleteDataset(id);
|
|
227
|
+
await refresh();
|
|
228
|
+
emit('changed');
|
|
229
|
+
},
|
|
230
|
+
listDocuments: (id) => props.api.listDocuments(id),
|
|
231
|
+
uploadDocuments: async (id, files) => {
|
|
232
|
+
for (const f of files) await props.api.uploadDocument(id, f);
|
|
233
|
+
},
|
|
234
|
+
deleteDocument: (id, docId) => props.api.deleteDocument(id, docId),
|
|
235
|
+
setDocumentEnabled: props.api.setDocumentEnabled,
|
|
236
|
+
listSegments: (id, docId, page, pageSize) =>
|
|
237
|
+
props.api.listSegments(id, docId, page, pageSize),
|
|
238
|
+
loadDocumentMetadata: (id, docId) => props.api.loadDocumentMetadata(id, docId),
|
|
239
|
+
updateSegment: (id, docId, segId, content) =>
|
|
240
|
+
props.api.updateSegment(id, docId, segId, content),
|
|
241
|
+
deleteSegment: (id, docId, segId) => props.api.deleteSegment(id, docId, segId),
|
|
242
|
+
setSegmentEnabled: (id, docId, segId, enabled) =>
|
|
243
|
+
props.api.setSegmentEnabled(id, docId, segId, enabled),
|
|
244
|
+
appendSegment: props.api.appendSegment,
|
|
245
|
+
retrieve: (id, req) => props.api.retrieve(id, req),
|
|
246
|
+
listRecallHistory: (id, limit) => props.api.listRecallHistory(id, limit),
|
|
247
|
+
listEmbeddingModels: () => props.api.listEmbeddingModels(),
|
|
248
|
+
previewChunks: props.api.previewChunks,
|
|
249
|
+
onCopyApi: props.api.onCopyApi,
|
|
250
|
+
}));
|
|
251
|
+
|
|
252
|
+
function onDetailChanged() {
|
|
253
|
+
refresh();
|
|
254
|
+
emit('changed');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
watch(detailOpen, (v) => {
|
|
258
|
+
if (!v) {
|
|
259
|
+
// Refresh on close in case anything mutated inside the drawer.
|
|
260
|
+
refresh();
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// -------- public API for host programmatic control
|
|
265
|
+
defineExpose({
|
|
266
|
+
openCreate,
|
|
267
|
+
openDetail,
|
|
268
|
+
refresh,
|
|
269
|
+
});
|
|
270
|
+
</script>
|
|
271
|
+
|
|
272
|
+
<template>
|
|
273
|
+
<div class="kh-app">
|
|
274
|
+
<!--
|
|
275
|
+
header — replace the whole thing with #header, or extend with #header-actions
|
|
276
|
+
to keep the title/search but add extra buttons on the right.
|
|
277
|
+
-->
|
|
278
|
+
<slot
|
|
279
|
+
name="header"
|
|
280
|
+
:title="displayTitle"
|
|
281
|
+
:description="displayDescription"
|
|
282
|
+
:search-value="keyword"
|
|
283
|
+
:on-search="(v: string) => (keyword = v)"
|
|
284
|
+
:open-create="openCreate"
|
|
285
|
+
>
|
|
286
|
+
<div class="kh-app-head">
|
|
287
|
+
<div>
|
|
288
|
+
<div class="kh-app-title">{{ displayTitle }}</div>
|
|
289
|
+
<div class="kh-app-desc">{{ displayDescription }}</div>
|
|
290
|
+
</div>
|
|
291
|
+
<div class="kh-app-toolbar">
|
|
292
|
+
<input
|
|
293
|
+
v-model="keyword"
|
|
294
|
+
class="kh-app-search"
|
|
295
|
+
:placeholder="t('app.searchPlaceholder')"
|
|
296
|
+
/>
|
|
297
|
+
<slot name="header-actions" :open-create="openCreate" />
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
</slot>
|
|
301
|
+
|
|
302
|
+
<!--
|
|
303
|
+
First-time-use nudge: no embedding model registered yet. Only shows once
|
|
304
|
+
the initial fetch has resolved with an empty list — avoids a flash while
|
|
305
|
+
loading. Consumers wire {@code api.onGoToEmbeddingSetup} to their model
|
|
306
|
+
route so the "去配置模型" button jumps there in-app. When the callback
|
|
307
|
+
is omitted the card still renders (useful hint) but without the button.
|
|
308
|
+
-->
|
|
309
|
+
<div
|
|
310
|
+
v-if="!loading && embeddingModels.length === 0"
|
|
311
|
+
class="kh-app-nudge"
|
|
312
|
+
>
|
|
313
|
+
<div class="kh-app-nudge-icon">🎯</div>
|
|
314
|
+
<div class="kh-app-nudge-body">
|
|
315
|
+
<div class="kh-app-nudge-title">{{ t('app.nudgeEmbeddingTitle') }}</div>
|
|
316
|
+
<div class="kh-app-nudge-desc">{{ t('app.nudgeEmbeddingDesc') }}</div>
|
|
317
|
+
<button
|
|
318
|
+
v-if="api.onGoToEmbeddingSetup"
|
|
319
|
+
class="kh-app-nudge-btn"
|
|
320
|
+
type="button"
|
|
321
|
+
@click="api.onGoToEmbeddingSetup?.()"
|
|
322
|
+
>
|
|
323
|
+
{{ t('app.nudgeEmbeddingCta') }}
|
|
324
|
+
</button>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<!-- card grid — replace with #cards to render your own layout, or use #empty
|
|
329
|
+
to override just the empty state. -->
|
|
330
|
+
<div class="kh-app-body">
|
|
331
|
+
<div v-if="loading" class="kh-app-loading">
|
|
332
|
+
<slot name="loading">{{ t('app.loading') }}</slot>
|
|
333
|
+
</div>
|
|
334
|
+
<template v-else-if="datasets.length === 0">
|
|
335
|
+
<slot name="empty" :open-create="openCreate">
|
|
336
|
+
<DatasetCardGrid
|
|
337
|
+
:datasets="datasets"
|
|
338
|
+
:keyword="keyword"
|
|
339
|
+
@create="openCreate"
|
|
340
|
+
@open="(d: DatasetCardItem) => openDetail(d.id)"
|
|
341
|
+
@settings="openSettings"
|
|
342
|
+
@delete="onDeleteCard"
|
|
343
|
+
/>
|
|
344
|
+
</slot>
|
|
345
|
+
</template>
|
|
346
|
+
<slot
|
|
347
|
+
v-else
|
|
348
|
+
name="cards"
|
|
349
|
+
:datasets="datasets"
|
|
350
|
+
:keyword="keyword"
|
|
351
|
+
:open-create="openCreate"
|
|
352
|
+
:open-detail="openDetail"
|
|
353
|
+
:open-settings="openSettings"
|
|
354
|
+
:on-delete="onDeleteCard"
|
|
355
|
+
>
|
|
356
|
+
<DatasetCardGrid
|
|
357
|
+
:datasets="datasets"
|
|
358
|
+
:keyword="keyword"
|
|
359
|
+
@create="openCreate"
|
|
360
|
+
@open="(d: DatasetCardItem) => openDetail(d.id)"
|
|
361
|
+
@settings="openSettings"
|
|
362
|
+
@delete="onDeleteCard"
|
|
363
|
+
/>
|
|
364
|
+
</slot>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<!-- create wizard (left-side drawer). previewChunks runs through the api
|
|
368
|
+
adapter so the step-2 preview matches real ingestion output. -->
|
|
369
|
+
<CreateDatasetWizard
|
|
370
|
+
:open="wizardOpen"
|
|
371
|
+
:embedding-models="embeddingModels"
|
|
372
|
+
:rerank-models="rerankModels"
|
|
373
|
+
:preview-chunks="api.previewChunks"
|
|
374
|
+
@update:open="wizardOpen = $event"
|
|
375
|
+
@create="onWizardCreate"
|
|
376
|
+
@skip-to-empty="onWizardSkipEmpty"
|
|
377
|
+
/>
|
|
378
|
+
|
|
379
|
+
<!-- detail drawer (right-side) -->
|
|
380
|
+
<DatasetDetailDrawer
|
|
381
|
+
:open="detailOpen"
|
|
382
|
+
:dataset-id="detailId"
|
|
383
|
+
:initial-tab="detailInitialTab"
|
|
384
|
+
:hub="detailHub"
|
|
385
|
+
@update:open="detailOpen = $event"
|
|
386
|
+
@refresh-list="onDetailChanged"
|
|
387
|
+
/>
|
|
388
|
+
|
|
389
|
+
<!-- inline prompt for "create empty knowledge base" — replaces window.prompt -->
|
|
390
|
+
<KhDialog
|
|
391
|
+
:open="emptyPromptOpen"
|
|
392
|
+
:title="t('wizard.createEmptyPromptTitle')"
|
|
393
|
+
:prompt-default="t('wizard.createEmptyPromptDefault')"
|
|
394
|
+
@update:open="emptyPromptOpen = $event"
|
|
395
|
+
@confirm="onEmptyPromptConfirm"
|
|
396
|
+
/>
|
|
397
|
+
|
|
398
|
+
<!-- inline confirm for dataset delete — replaces window.confirm -->
|
|
399
|
+
<KhDialog
|
|
400
|
+
:open="deleteConfirmOpen"
|
|
401
|
+
:title="t('card.delete')"
|
|
402
|
+
:content="deleteTarget
|
|
403
|
+
? t('card.confirmDelete', { name: deleteTarget.name })
|
|
404
|
+
: ''"
|
|
405
|
+
:ok-text="t('common.delete')"
|
|
406
|
+
danger
|
|
407
|
+
@update:open="deleteConfirmOpen = $event"
|
|
408
|
+
@confirm="onDeleteConfirm"
|
|
409
|
+
/>
|
|
410
|
+
</div>
|
|
411
|
+
</template>
|
|
412
|
+
|
|
413
|
+
<style scoped>
|
|
414
|
+
.kh-app {
|
|
415
|
+
display: flex;
|
|
416
|
+
flex-direction: column;
|
|
417
|
+
gap: var(--kh-space-4);
|
|
418
|
+
}
|
|
419
|
+
.kh-app-head {
|
|
420
|
+
display: flex;
|
|
421
|
+
justify-content: space-between;
|
|
422
|
+
align-items: flex-end;
|
|
423
|
+
gap: var(--kh-space-3);
|
|
424
|
+
}
|
|
425
|
+
.kh-app-title {
|
|
426
|
+
font-size: 20px;
|
|
427
|
+
font-weight: 600;
|
|
428
|
+
color: var(--kh-color-text-primary);
|
|
429
|
+
}
|
|
430
|
+
.kh-app-desc {
|
|
431
|
+
margin-top: 2px;
|
|
432
|
+
font-size: var(--kh-fs-md);
|
|
433
|
+
color: var(--kh-color-text-muted);
|
|
434
|
+
}
|
|
435
|
+
/* First-time-use nudge — "先注册 Embedding 模型" empty-state card. */
|
|
436
|
+
.kh-app-nudge {
|
|
437
|
+
display: flex;
|
|
438
|
+
align-items: flex-start;
|
|
439
|
+
gap: var(--kh-space-3);
|
|
440
|
+
padding: var(--kh-space-3) var(--kh-space-4);
|
|
441
|
+
background: #fff;
|
|
442
|
+
border: 1px solid var(--kh-color-border);
|
|
443
|
+
border-radius: 8px;
|
|
444
|
+
}
|
|
445
|
+
.kh-app-nudge-icon {
|
|
446
|
+
font-size: 22px;
|
|
447
|
+
line-height: 1;
|
|
448
|
+
}
|
|
449
|
+
.kh-app-nudge-body {
|
|
450
|
+
flex: 1;
|
|
451
|
+
}
|
|
452
|
+
.kh-app-nudge-title {
|
|
453
|
+
font-size: var(--kh-fs-md);
|
|
454
|
+
font-weight: 500;
|
|
455
|
+
color: var(--kh-color-text-primary);
|
|
456
|
+
}
|
|
457
|
+
.kh-app-nudge-desc {
|
|
458
|
+
margin-top: 4px;
|
|
459
|
+
font-size: var(--kh-fs-sm, 13px);
|
|
460
|
+
color: var(--kh-color-text-muted);
|
|
461
|
+
line-height: 1.55;
|
|
462
|
+
}
|
|
463
|
+
.kh-app-nudge-btn {
|
|
464
|
+
margin-top: 8px;
|
|
465
|
+
padding: 4px 12px;
|
|
466
|
+
background: #4f46e5;
|
|
467
|
+
color: #fff;
|
|
468
|
+
border: none;
|
|
469
|
+
border-radius: 6px;
|
|
470
|
+
font-size: 13px;
|
|
471
|
+
cursor: pointer;
|
|
472
|
+
transition: background 0.15s ease;
|
|
473
|
+
}
|
|
474
|
+
.kh-app-nudge-btn:hover {
|
|
475
|
+
background: #4338ca;
|
|
476
|
+
}
|
|
477
|
+
.kh-app-toolbar {
|
|
478
|
+
display: flex;
|
|
479
|
+
gap: var(--kh-space-2);
|
|
480
|
+
}
|
|
481
|
+
.kh-app-search {
|
|
482
|
+
padding: 6px var(--kh-space-3);
|
|
483
|
+
border: 1px solid var(--kh-color-border);
|
|
484
|
+
border-radius: var(--kh-input-radius);
|
|
485
|
+
outline: none;
|
|
486
|
+
font-size: var(--kh-fs-lg);
|
|
487
|
+
min-width: 240px;
|
|
488
|
+
background: var(--kh-input-bg);
|
|
489
|
+
color: var(--kh-color-text-primary);
|
|
490
|
+
}
|
|
491
|
+
.kh-app-search:focus {
|
|
492
|
+
border-color: var(--kh-color-primary);
|
|
493
|
+
box-shadow: var(--kh-focus-ring);
|
|
494
|
+
}
|
|
495
|
+
.kh-app-body {
|
|
496
|
+
min-height: 200px;
|
|
497
|
+
}
|
|
498
|
+
.kh-app-loading {
|
|
499
|
+
padding: 60px 20px;
|
|
500
|
+
text-align: center;
|
|
501
|
+
color: var(--kh-color-text-muted);
|
|
502
|
+
font-size: var(--kh-fs-md);
|
|
503
|
+
}
|
|
504
|
+
</style>
|