@swiss-ai-hub/web 0.297.9 → 0.298.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.
@@ -1,17 +1,36 @@
1
1
  <template>
2
+ <div
3
+ v-if="checkedDocuments.length > 0"
4
+ class="mb-2 flex items-center justify-between"
5
+ >
6
+ <p class="text-sm">
7
+ {{ t('document.list.selected_count', { count: checkedDocuments.length }) }}
8
+ </p>
9
+ <Button
10
+ size="small"
11
+ severity="danger"
12
+ icon="pi pi-trash"
13
+ :label="t('document.delete.button_selected')"
14
+ :loading="isBatchDeleting"
15
+ @click="confirmBatchDelete"
16
+ />
17
+ </div>
2
18
  <DataTable
19
+ v-model:selection="checkedDocuments"
3
20
  :value="documents"
4
21
  table-style="min-width: 50rem"
5
- selection-mode="single"
6
- :selection="selectedDocument"
7
22
  :row-class="getRowClass"
8
23
  :sort-field="sortField ?? undefined"
9
24
  :sort-order="sortOrder"
10
25
  removable-sort
11
26
  size="small"
12
- @update:selection="handleSelection"
27
+ @row-click="handleRowClick"
13
28
  @sort="handleSort"
14
29
  >
30
+ <Column
31
+ selection-mode="multiple"
32
+ header-style="width: 3rem"
33
+ />
15
34
  <Column
16
35
  field="document_title"
17
36
  :header="t('document.list.title')"
@@ -25,7 +44,18 @@
25
44
  {{ data.document_title }}
26
45
  </p>
27
46
  <div
28
- v-if="!data.is_ingested"
47
+ v-if="isDocumentDeleting(data)"
48
+ class="flex items-center gap-2"
49
+ >
50
+ <Tag
51
+ :value="t('document.delete.deleting')"
52
+ size="small"
53
+ icon="pi pi-trash"
54
+ severity="danger"
55
+ />
56
+ </div>
57
+ <div
58
+ v-else-if="!data.is_ingested"
29
59
  class="flex items-center gap-2"
30
60
  >
31
61
  <Tag
@@ -68,34 +98,55 @@
68
98
  </Column>
69
99
  <Column
70
100
  field="source"
71
- :header="t('document.list.download')"
101
+ :header="t('document.list.actions')"
72
102
  >
73
103
  <template #body="{ data }">
74
- <Button
75
- v-if="data.source"
76
- rounded
77
- size="small"
78
- variant="outlined"
79
- icon="pi pi-download"
80
- @click="() => downloadFile(data.id)"
81
- />
82
- <span
83
- v-else
84
- class="text-sm text-surface-400"
85
- >-</span>
104
+ <div class="flex items-center gap-2">
105
+ <Button
106
+ v-if="data.source"
107
+ v-tooltip.top="t('document.list.download')"
108
+ rounded
109
+ size="small"
110
+ variant="outlined"
111
+ icon="pi pi-download"
112
+ @click.stop="() => downloadFile(data.id)"
113
+ />
114
+ <Button
115
+ v-if="!isDocumentDeleting(data)"
116
+ v-tooltip.top="t('document.delete.button')"
117
+ rounded
118
+ size="small"
119
+ variant="outlined"
120
+ severity="danger"
121
+ icon="pi pi-trash"
122
+ :loading="isDeleting"
123
+ @click.stop="() => confirmDelete(data)"
124
+ />
125
+ </div>
86
126
  </template>
87
127
  </Column>
88
128
  </DataTable>
89
129
  </template>
90
130
 
91
131
  <script setup lang="ts">
132
+ import { useConfirm } from 'primevue/useconfirm'
133
+ import { useToast } from 'primevue/usetoast'
134
+
92
135
  import type { DocumentDto } from '@core/sdk/client'
93
- import type { DataTableSortEvent } from 'primevue/datatable'
136
+ import type { DataTableRowClickEvent, DataTableSortEvent } from 'primevue/datatable'
94
137
 
95
138
  const route = useRoute()
96
139
  const { t } = useI18n()
97
140
  const { tenantId } = useTenant()
98
141
  const { getDocumentSourceUrl } = useDocumentUrl()
142
+ const { deleteDocument, isDeleting } = useDeleteDocument()
143
+ const { deleteDocuments, isDeleting: isBatchDeleting } = useDeleteDocuments()
144
+ const { isScheduled, schedule, unschedule } = useScheduledDeletions(
145
+ () => route.params.db as string,
146
+ () => route.params.namespace as string,
147
+ )
148
+ const confirm = useConfirm()
149
+ const toast = useToast()
99
150
 
100
151
  const props = defineProps<{
101
152
  documents: DocumentDto[]
@@ -106,23 +157,56 @@ const props = defineProps<{
106
157
  const emit = defineEmits<{
107
158
  selected: [document: DocumentDto]
108
159
  sort: [field: string | null, order: 1 | -1]
160
+ deleted: [documentIds: string[]]
109
161
  }>()
110
162
 
163
+ const checkedDocuments = ref<DocumentDto[]>([])
164
+
111
165
  const formatted = (datestr: string) => useDateFormat(new Date(datestr), 'DD.MM.YYYY')
112
- const selectedDocument = computed(() => {
113
- return props.documents.filter((document: DocumentDto) => {
114
- return document.id === route.params.document_id
115
- })
116
- })
117
166
 
118
- const handleSelection = (document: DocumentDto) => {
119
- if (document.is_ingested) {
167
+ const isDocumentDeleting = (document: DocumentDto) => isScheduled(document.id) && document.is_ingested
168
+
169
+ // Processing documents are mid-ingestion; removing their source now would race the pipeline.
170
+ const isDocumentDeletable = (document: DocumentDto) => document.is_ingested && !isDocumentDeleting(document)
171
+
172
+ // Ids derive from the source URI, so re-uploading a just-deleted file reuses its id. When that id
173
+ // reappears as a placeholder, the scheduled entry is stale and must be cleared.
174
+ watch(
175
+ () => props.documents,
176
+ (documents) => {
177
+ const reUploadedIds = (documents ?? [])
178
+ .filter(document => !document.is_ingested && isScheduled(document.id))
179
+ .map(document => document.id)
180
+ if (reUploadedIds.length > 0) {
181
+ unschedule(reUploadedIds)
182
+ }
183
+ },
184
+ { immediate: true },
185
+ )
186
+
187
+ // Drop any selected document that is no longer deletable — either picked by "select all" while
188
+ // processing, or flipped to "deleting" by a single-row delete after it was already selected.
189
+ watch(
190
+ () => checkedDocuments.value.filter(document => !isDocumentDeletable(document)).length,
191
+ (nonDeletableCount) => {
192
+ if (nonDeletableCount > 0) {
193
+ checkedDocuments.value = checkedDocuments.value.filter(isDocumentDeletable)
194
+ }
195
+ },
196
+ )
197
+
198
+ const handleRowClick = (event: DataTableRowClickEvent) => {
199
+ const document = event.data as DocumentDto
200
+ if (document.is_ingested && !isDocumentDeleting(document)) {
120
201
  emit('selected', document)
121
202
  }
122
203
  }
123
204
 
124
205
  const getRowClass = (data: DocumentDto) => {
125
- return data.is_ingested ? '' : 'opacity-50 cursor-not-allowed pointer-events-none'
206
+ if (!data.is_ingested || isDocumentDeleting(data)) {
207
+ return 'opacity-50 cursor-not-allowed pointer-events-none'
208
+ }
209
+ return data.id === route.params.document_id ? 'bg-surface-100 dark:bg-surface-800' : ''
126
210
  }
127
211
 
128
212
  const handleSort = (event: DataTableSortEvent) => {
@@ -137,4 +221,93 @@ const downloadFile = async (documentId: string) => {
137
221
  const url = await getDocumentSourceUrl(tenantId.value!, database, namespace, documentId)
138
222
  window.open(url, '_blank')
139
223
  }
224
+
225
+ const confirmDelete = (document: DocumentDto) => {
226
+ confirm.require({
227
+ message: t('document.delete.confirmMessage', { title: document.document_title }),
228
+ header: t('document.delete.title'),
229
+ icon: 'pi pi-exclamation-triangle',
230
+ rejectLabel: t('common.actions.cancel'),
231
+ acceptLabel: t('document.delete.button'),
232
+ acceptClass: 'p-button-danger',
233
+ accept: () => handleDelete(document),
234
+ })
235
+ }
236
+
237
+ const handleDelete = async (document: DocumentDto) => {
238
+ try {
239
+ await deleteDocument({
240
+ tenantId: tenantId.value!,
241
+ database: route.params.db as string,
242
+ namespace: route.params.namespace as string,
243
+ documentId: document.id,
244
+ })
245
+ schedule([document.id])
246
+ toast.add({
247
+ severity: 'success',
248
+ summary: t('document.delete.success'),
249
+ life: 3000,
250
+ })
251
+ emit('deleted', [document.id])
252
+ }
253
+ catch (error) {
254
+ toast.add({
255
+ severity: 'error',
256
+ summary: t('document.delete.error'),
257
+ detail: error instanceof Error ? error.message : String(error),
258
+ life: 5000,
259
+ })
260
+ }
261
+ }
262
+
263
+ const confirmBatchDelete = () => {
264
+ confirm.require({
265
+ message: t('document.delete.confirmMessageBatch', { count: checkedDocuments.value.length }),
266
+ header: t('document.delete.title'),
267
+ icon: 'pi pi-exclamation-triangle',
268
+ rejectLabel: t('common.actions.cancel'),
269
+ acceptLabel: t('document.delete.button'),
270
+ acceptClass: 'p-button-danger',
271
+ accept: handleBatchDelete,
272
+ })
273
+ }
274
+
275
+ const handleBatchDelete = async () => {
276
+ const documentIds = checkedDocuments.value.map(document => document.id)
277
+ try {
278
+ const response = await deleteDocuments({
279
+ tenantId: tenantId.value!,
280
+ database: route.params.db as string,
281
+ namespace: route.params.namespace as string,
282
+ documentIds,
283
+ })
284
+ const deletedIds = response.results.filter(result => result.status === 'scheduled').map(result => result.document_id)
285
+ schedule(deletedIds)
286
+ const failedCount = response.results.length - deletedIds.length
287
+ if (failedCount > 0) {
288
+ toast.add({
289
+ severity: 'warn',
290
+ summary: t('document.delete.partial', { deleted: deletedIds.length, failed: failedCount }),
291
+ life: 5000,
292
+ })
293
+ }
294
+ else {
295
+ toast.add({
296
+ severity: 'success',
297
+ summary: t('document.delete.success'),
298
+ life: 3000,
299
+ })
300
+ }
301
+ checkedDocuments.value = []
302
+ emit('deleted', deletedIds)
303
+ }
304
+ catch (error) {
305
+ toast.add({
306
+ severity: 'error',
307
+ summary: t('document.delete.error'),
308
+ detail: error instanceof Error ? error.message : String(error),
309
+ life: 5000,
310
+ })
311
+ }
312
+ }
140
313
  </script>
@@ -0,0 +1,32 @@
1
+ import { deleteDocument } from '@core/sdk/client'
2
+
3
+ export const useDeleteDocument = defineMutation(() => {
4
+ const queryCache = useQueryCache()
5
+
6
+ const {
7
+ mutateAsync: deleteDocumentMutation,
8
+ isPending: isDeleting,
9
+ error: deleteError,
10
+ } = useMutation({
11
+ mutation: async ({ tenantId, database, namespace, documentId }: { tenantId: string, database: string, namespace: string, documentId: string }) => {
12
+ await deleteDocument({
13
+ composable: '$fetch',
14
+ path: {
15
+ tenant_id: tenantId,
16
+ database,
17
+ namespace,
18
+ document_id: documentId,
19
+ },
20
+ })
21
+
22
+ queryCache.invalidateQueries({ key: ['tenant', tenantId, 'knowledge', 'databases', database, 'namespaces', namespace, 'documents'] })
23
+ queryCache.invalidateQueries({ key: ['tenant', tenantId, 'knowledge', 'databases'] })
24
+ },
25
+ })
26
+
27
+ return {
28
+ deleteDocument: deleteDocumentMutation,
29
+ isDeleting,
30
+ deleteError,
31
+ }
32
+ })
@@ -0,0 +1,38 @@
1
+ import { batchDeleteDocuments } from '@core/sdk/client'
2
+
3
+ import type { BatchDeleteDocumentsResponse } from '@core/sdk/client'
4
+
5
+ export const useDeleteDocuments = defineMutation(() => {
6
+ const queryCache = useQueryCache()
7
+
8
+ const {
9
+ mutateAsync: deleteDocumentsMutation,
10
+ isPending: isDeleting,
11
+ error: deleteError,
12
+ } = useMutation({
13
+ mutation: async ({ tenantId, database, namespace, documentIds }: { tenantId: string, database: string, namespace: string, documentIds: string[] }): Promise<BatchDeleteDocumentsResponse> => {
14
+ const response = await batchDeleteDocuments({
15
+ composable: '$fetch',
16
+ path: {
17
+ tenant_id: tenantId,
18
+ database,
19
+ namespace,
20
+ },
21
+ body: {
22
+ document_ids: documentIds,
23
+ },
24
+ })
25
+
26
+ queryCache.invalidateQueries({ key: ['tenant', tenantId, 'knowledge', 'databases', database, 'namespaces', namespace, 'documents'] })
27
+ queryCache.invalidateQueries({ key: ['tenant', tenantId, 'knowledge', 'databases'] })
28
+
29
+ return response
30
+ },
31
+ })
32
+
33
+ return {
34
+ deleteDocuments: deleteDocumentsMutation,
35
+ isDeleting,
36
+ deleteError,
37
+ }
38
+ })
@@ -0,0 +1,48 @@
1
+ import { useStorage } from '@vueuse/core'
2
+
3
+ import type { MaybeRefOrGetter } from 'vue'
4
+
5
+ // Deletion is eventual (the pipeline cleans the stores minutes after the API returns 202), so the
6
+ // list keeps returning a deleted document for a while. We persist scheduled ids in localStorage —
7
+ // surviving page refreshes — to drive a "Deleting" badge, and expire them after a TTL so a failed
8
+ // cleanup eventually re-surfaces the row instead of hiding it forever.
9
+ const TTL_MS = 30 * 60 * 1000
10
+
11
+ const store = useStorage<Record<string, number>>('aihub:scheduled-deletions', {})
12
+
13
+ function entryKey(database: string, namespace: string, documentId: string): string {
14
+ return `${database}/${namespace}/${documentId}`
15
+ }
16
+
17
+ export function useScheduledDeletions(database: MaybeRefOrGetter<string>, namespace: MaybeRefOrGetter<string>) {
18
+ function isScheduled(documentId: string): boolean {
19
+ const scheduledAt = store.value[entryKey(toValue(database), toValue(namespace), documentId)]
20
+ return scheduledAt != null && Date.now() - scheduledAt < TTL_MS
21
+ }
22
+
23
+ function schedule(documentIds: string[]): void {
24
+ const now = Date.now()
25
+ const next: Record<string, number> = {}
26
+ for (const [key, scheduledAt] of Object.entries(store.value)) {
27
+ if (now - scheduledAt < TTL_MS) {
28
+ next[key] = scheduledAt
29
+ }
30
+ }
31
+ for (const documentId of documentIds) {
32
+ next[entryKey(toValue(database), toValue(namespace), documentId)] = now
33
+ }
34
+ store.value = next
35
+ }
36
+
37
+ function unschedule(documentIds: string[]): void {
38
+ const keysToRemove = new Set(
39
+ documentIds.map(documentId => entryKey(toValue(database), toValue(namespace), documentId)),
40
+ )
41
+ const remaining = Object.entries(store.value).filter(([key]) => !keysToRemove.has(key))
42
+ if (remaining.length !== Object.keys(store.value).length) {
43
+ store.value = Object.fromEntries(remaining)
44
+ }
45
+ }
46
+
47
+ return { isScheduled, schedule, unschedule }
48
+ }
@@ -432,6 +432,22 @@ document:
432
432
  number_of_pages: Anzahl der Seiten
433
433
  download: Download
434
434
  is_processing: Verarbeitung läuft
435
+ actions: Aktionen
436
+ selected_count: '{count} Dokument(e) ausgewählt'
437
+ delete:
438
+ title: Dokument löschen
439
+ deleting: Wird gelöscht
440
+ button: Löschen
441
+ button_selected: Ausgewählte löschen
442
+ confirmMessage: Möchten Sie dieses Dokument wirklich löschen? Es wird dauerhaft
443
+ aus der Wissensdatenbank entfernt. Diese Aktion kann nicht rückgängig gemacht
444
+ werden.
445
+ confirmMessageBatch: Möchten Sie {count} Dokument(e) wirklich löschen? Sie werden
446
+ dauerhaft aus der Wissensdatenbank entfernt. Diese Aktion kann nicht rückgängig
447
+ gemacht werden.
448
+ success: Löschung des Dokuments geplant
449
+ partial: Löschung von {deleted} Dokument(en) geplant, {failed} fehlgeschlagen
450
+ error: Dokument konnte nicht gelöscht werden
435
451
  knowledge:
436
452
  title: Wissensdatenbanken
437
453
  created_at: 'Erstellt:'
@@ -426,6 +426,20 @@ document:
426
426
  number_of_pages: Number of Pages
427
427
  download: Download
428
428
  is_processing: Processing
429
+ actions: Actions
430
+ selected_count: '{count} document(s) selected'
431
+ delete:
432
+ title: Delete Document
433
+ deleting: Deleting
434
+ button: Delete
435
+ button_selected: Delete selected
436
+ confirmMessage: Are you sure you want to delete this document? It will be permanently
437
+ removed from the knowledge base. This action cannot be undone.
438
+ confirmMessageBatch: Are you sure you want to delete {count} document(s)? They
439
+ will be permanently removed from the knowledge base. This action cannot be undone.
440
+ success: Document scheduled for deletion
441
+ partial: '{deleted} document(s) scheduled for deletion, {failed} failed'
442
+ error: Failed to delete document
429
443
  knowledge:
430
444
  title: Knowledge Databases
431
445
  created_at: 'Created:'
@@ -429,6 +429,21 @@ document:
429
429
  number_of_pages: Nombre de pages
430
430
  download: Télécharger
431
431
  is_processing: Traitement en cours
432
+ actions: Actions
433
+ selected_count: '{count} document(s) sélectionné(s)'
434
+ delete:
435
+ title: Supprimer le document
436
+ deleting: Suppression en cours
437
+ button: Supprimer
438
+ button_selected: Supprimer la sélection
439
+ confirmMessage: Voulez-vous vraiment supprimer ce document ? Il sera définitivement
440
+ retiré de la base de connaissances. Cette action est irréversible.
441
+ confirmMessageBatch: Voulez-vous vraiment supprimer {count} document(s) ? Ils
442
+ seront définitivement retirés de la base de connaissances. Cette action est
443
+ irréversible.
444
+ success: Suppression du document planifiée
445
+ partial: Suppression de {deleted} document(s) planifiée, {failed} en échec
446
+ error: Échec de la suppression du document
432
447
  knowledge:
433
448
  title: Bases de connaissances
434
449
  created_at: 'Créé le:'
@@ -428,6 +428,20 @@ document:
428
428
  number_of_pages: Numero di pagine
429
429
  download: Scarica
430
430
  is_processing: Elaborazione in corso
431
+ actions: Azioni
432
+ selected_count: '{count} documento/i selezionato/i'
433
+ delete:
434
+ title: Elimina documento
435
+ deleting: Eliminazione in corso
436
+ button: Elimina
437
+ button_selected: Elimina selezionati
438
+ confirmMessage: Vuoi davvero eliminare questo documento? Verrà rimosso definitivamente
439
+ dalla base di conoscenza. Questa azione non può essere annullata.
440
+ confirmMessageBatch: Vuoi davvero eliminare {count} documento/i? Verranno rimossi
441
+ definitivamente dalla base di conoscenza. Questa azione non può essere annullata.
442
+ success: Eliminazione del documento pianificata
443
+ partial: Eliminazione di {deleted} documento/i pianificata, {failed} non riuscito/i
444
+ error: Eliminazione del documento non riuscita
431
445
  knowledge:
432
446
  title: Basi di conoscenza
433
447
  created_at: 'Creato:'
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "bbv Software Services AG (https://www.bbv.ch)",
5
5
  "type": "module",
6
- "version": "0.297.9",
6
+ "version": "0.298.0",
7
7
  "description": "Swiss AI Hub - Admin & Management UI (Nuxt 3 layer)",
8
8
  "main": "./nuxt.config.ts",
9
9
  "repository": {
@@ -1,25 +1,48 @@
1
1
  <template>
2
2
  <div class="flex flex-col gap-2">
3
- <SelectButton
4
- v-if="navItems"
5
- :model-value="activeNavItem"
6
- :options="navItems"
7
- data-key="key"
8
- option-label="name"
9
- size="small"
10
- @update:model-value="toNavItem"
11
- />
3
+ <div class="flex items-center justify-between gap-2">
4
+ <SelectButton
5
+ v-if="navItems"
6
+ :model-value="activeNavItem"
7
+ :options="navItems"
8
+ data-key="key"
9
+ option-label="name"
10
+ size="small"
11
+ @update:model-value="toNavItem"
12
+ />
13
+ <Button
14
+ v-tooltip.top="t('document.delete.button')"
15
+ size="small"
16
+ severity="danger"
17
+ variant="outlined"
18
+ icon="pi pi-trash"
19
+ :label="t('document.delete.button')"
20
+ :loading="isDeleting"
21
+ @click="confirmDelete"
22
+ />
23
+ </div>
12
24
  <NuxtPage />
13
25
  </div>
14
26
  </template>
15
27
 
16
28
  <script setup lang="ts">
29
+ import { useConfirm } from 'primevue/useconfirm'
30
+ import { useToast } from 'primevue/usetoast'
31
+
17
32
  import type { NavItem } from '@core/types/NavItem'
18
33
 
19
34
  const router = useRouter()
20
35
  const route = useRoute()
21
36
  const tenantPath = useTenantPath()
22
37
  const { t } = useI18n()
38
+ const { tenantId } = useTenant()
39
+ const { deleteDocument, isDeleting } = useDeleteDocument()
40
+ const { schedule } = useScheduledDeletions(
41
+ () => route.params.db as string,
42
+ () => route.params.namespace as string,
43
+ )
44
+ const confirm = useConfirm()
45
+ const toast = useToast()
23
46
 
24
47
  const subPath = (path: string) => {
25
48
  return `/service/knowledge/${route.params.db}/${route.params.namespace}/${route.params.document_id}/${path}`
@@ -45,4 +68,42 @@ const toNavItem = (navItem: NavItem) => {
45
68
  const activeNavItem = computed<NavItem | undefined>(() => {
46
69
  return navItems.value?.find(navItem => navItem.isActive())
47
70
  })
71
+
72
+ const confirmDelete = () => {
73
+ confirm.require({
74
+ message: t('document.delete.confirmMessage'),
75
+ header: t('document.delete.title'),
76
+ icon: 'pi pi-exclamation-triangle',
77
+ rejectLabel: t('common.actions.cancel'),
78
+ acceptLabel: t('document.delete.button'),
79
+ acceptClass: 'p-button-danger',
80
+ accept: handleDelete,
81
+ })
82
+ }
83
+
84
+ const handleDelete = async () => {
85
+ try {
86
+ await deleteDocument({
87
+ tenantId: tenantId.value!,
88
+ database: route.params.db as string,
89
+ namespace: route.params.namespace as string,
90
+ documentId: route.params.document_id as string,
91
+ })
92
+ schedule([route.params.document_id as string])
93
+ toast.add({
94
+ severity: 'success',
95
+ summary: t('document.delete.success'),
96
+ life: 3000,
97
+ })
98
+ router.push(tenantPath(`/service/knowledge/${route.params.db}/${route.params.namespace}`))
99
+ }
100
+ catch (error) {
101
+ toast.add({
102
+ severity: 'error',
103
+ summary: t('document.delete.error'),
104
+ detail: error instanceof Error ? error.message : String(error),
105
+ life: 5000,
106
+ })
107
+ }
108
+ }
48
109
  </script>
@@ -32,6 +32,7 @@
32
32
  :sort-order="sortState.order"
33
33
  @selected="toDocument"
34
34
  @sort="handleSort"
35
+ @deleted="handleDeleted"
35
36
  />
36
37
 
37
38
  <div class="mt-4">
@@ -141,4 +142,11 @@ const openUploadModal = () => {
141
142
  const handleUpload = () => {
142
143
  refetch()
143
144
  }
145
+
146
+ const handleDeleted = (documentIds: string[]) => {
147
+ refetch()
148
+ if (documentIds.includes(route.params.document_id as string)) {
149
+ router.push(tenantPath(`/service/knowledge/${route.params.db}/${route.params.namespace}`))
150
+ }
151
+ }
144
152
  </script>
@@ -4,6 +4,7 @@ export {
4
4
  addAgentToThread,
5
5
  addUserToThread,
6
6
  assignRole,
7
+ batchDeleteDocuments,
7
8
  chatCompletionWithAssistants,
8
9
  createAgentInstance,
9
10
  createDataset,
@@ -17,6 +18,7 @@ export {
17
18
  deleteAgentInstance,
18
19
  deleteAllOrganizationMemories,
19
20
  deleteAllUserMemories,
21
+ deleteDocument,
20
22
  deleteOrganizationMemory,
21
23
  deleteProcessInstance,
22
24
  deleteRole,
@@ -176,6 +178,13 @@ export {
176
178
  type BaseRetrieveMemoryEventWritable,
177
179
  type BaseStoreMemoryEvent,
178
180
  type BaseStoreMemoryEventWritable,
181
+ type BatchDeleteDocumentsData,
182
+ type BatchDeleteDocumentsError,
183
+ type BatchDeleteDocumentsErrors,
184
+ type BatchDeleteDocumentsRequest,
185
+ type BatchDeleteDocumentsResponse,
186
+ type BatchDeleteDocumentsResponse2,
187
+ type BatchDeleteDocumentsResponses,
179
188
  type BodyCreateTranscriptionTenantIdOpenaiAudioTranscriptionsPost,
180
189
  type BucketMetadataFilters,
181
190
  type BucketNamespacePair,
@@ -317,6 +326,10 @@ export {
317
326
  type DeleteAllUserMemoriesData,
318
327
  type DeleteAllUserMemoriesResponse,
319
328
  type DeleteAllUserMemoriesResponses,
329
+ type DeleteDocumentData,
330
+ type DeleteDocumentError,
331
+ type DeleteDocumentErrors,
332
+ type DeleteDocumentResponses,
320
333
  type DeleteMemoryResponse,
321
334
  type DeleteOrganizationMemoryData,
322
335
  type DeleteOrganizationMemoryError,
@@ -344,6 +357,7 @@ export {
344
357
  type DisplayStatistics,
345
358
  type DisplayStatisticsWritable,
346
359
  type DocumentBlock,
360
+ type DocumentDeletionResult,
347
361
  type DocumentDto,
348
362
  type DocumentParsingMetadata,
349
363
  type DocumentParsingResponse,
@@ -879,6 +893,7 @@ export {
879
893
  type SignedUrlDto,
880
894
  type Slider,
881
895
  type SliderWritable,
896
+ SortOrder,
882
897
  type StandaloneQuestionCondenserEvent,
883
898
  type StandaloneQuestionCondenserEventWritable,
884
899
  type StartEvent,
@@ -2199,6 +2199,43 @@ export const BaseStoreMemoryEventSchema = {
2199
2199
  "Abstract base class for memory storage events.\n\n### Why BaseStoreMemoryEvent?\nThis event serves dual purposes in the Swiss AI Agent Protocol:\n- As a control event, it notifies downstream systems that memory state has changed\n- As a display event, it provides transparency to users about what was learned or stored\n\nAgents emit this event after persisting insights to long-term memory storage. The event captures\nboth the semantic changes (added/updated/deleted memories) and the knowledge graph updates\n(new/removed relations between entities). This transparency is crucial for user trust - they can\nsee what the agent learned and verify accuracy.\n\nThe event structure follows mem0's MemoryAdded response format, enabling real-time UI updates,\naudit trails, and triggering downstream workflows that depend on memory state.\n\nConcrete subclasses differentiate between user-scoped and organization-scoped memory storage.",
2200
2200
  } as const;
2201
2201
 
2202
+ export const BatchDeleteDocumentsRequestSchema = {
2203
+ properties: {
2204
+ document_ids: {
2205
+ items: {
2206
+ type: "string",
2207
+ },
2208
+ type: "array",
2209
+ maxItems: 100,
2210
+ minItems: 1,
2211
+ title: "Document Ids",
2212
+ description: "IDs of the documents to delete",
2213
+ },
2214
+ },
2215
+ type: "object",
2216
+ required: ["document_ids"],
2217
+ title: "BatchDeleteDocumentsRequest",
2218
+ description:
2219
+ "Request payload for deleting multiple documents from a knowledge namespace.",
2220
+ } as const;
2221
+
2222
+ export const BatchDeleteDocumentsResponseSchema = {
2223
+ properties: {
2224
+ results: {
2225
+ items: {
2226
+ $ref: "#/components/schemas/DocumentDeletionResult",
2227
+ },
2228
+ type: "array",
2229
+ title: "Results",
2230
+ description: "Deletion outcome per requested document",
2231
+ },
2232
+ },
2233
+ type: "object",
2234
+ required: ["results"],
2235
+ title: "BatchDeleteDocumentsResponse",
2236
+ description: "Per-document results of a best-effort batch deletion.",
2237
+ } as const;
2238
+
2202
2239
  export const Body_create_transcription__tenant_id__openai_audio_transcriptions_postSchema =
2203
2240
  {
2204
2241
  properties: {
@@ -6666,6 +6703,26 @@ export const DocumentDTOSchema = {
6666
6703
  title: "DocumentDTO",
6667
6704
  } as const;
6668
6705
 
6706
+ export const DocumentDeletionResultSchema = {
6707
+ properties: {
6708
+ document_id: {
6709
+ type: "string",
6710
+ title: "Document Id",
6711
+ description: "ID of the document",
6712
+ },
6713
+ status: {
6714
+ type: "string",
6715
+ enum: ["scheduled", "not_found", "failed"],
6716
+ title: "Status",
6717
+ description: "Deletion outcome for this document",
6718
+ },
6719
+ },
6720
+ type: "object",
6721
+ required: ["document_id", "status"],
6722
+ title: "DocumentDeletionResult",
6723
+ description: "Outcome of a single document deletion within a batch request.",
6724
+ } as const;
6725
+
6669
6726
  export const DocumentParsingMetadataSchema = {
6670
6727
  properties: {
6671
6728
  filename: {
@@ -19546,6 +19603,12 @@ export const SliderSchema = {
19546
19603
  description: "https://formkit-primevue.netlify.app/inputs/Slider",
19547
19604
  } as const;
19548
19605
 
19606
+ export const SortOrderSchema = {
19607
+ type: "integer",
19608
+ enum: [1, -1],
19609
+ title: "SortOrder",
19610
+ } as const;
19611
+
19549
19612
  export const StandaloneQuestionCondenserEventSchema = {
19550
19613
  properties: {
19551
19614
  event_id: {
@@ -30,6 +30,9 @@ import type {
30
30
  AssignRoleData,
31
31
  AssignRoleError,
32
32
  AssignRoleResponse,
33
+ BatchDeleteDocumentsData,
34
+ BatchDeleteDocumentsError,
35
+ BatchDeleteDocumentsResponse2,
33
36
  ChatCompletionWithAssistantsData,
34
37
  ChatCompletionWithAssistantsError,
35
38
  ChatCompletionWithAssistantsResponse,
@@ -66,6 +69,8 @@ import type {
66
69
  DeleteAllOrganizationMemoriesResponse,
67
70
  DeleteAllUserMemoriesData,
68
71
  DeleteAllUserMemoriesResponse,
72
+ DeleteDocumentData,
73
+ DeleteDocumentError,
69
74
  DeleteOrganizationMemoryData,
70
75
  DeleteOrganizationMemoryError,
71
76
  DeleteOrganizationMemoryResponse,
@@ -2611,6 +2616,41 @@ export const getDatabases = <
2611
2616
  ...options,
2612
2617
  });
2613
2618
 
2619
+ /**
2620
+ * Delete multiple documents
2621
+ *
2622
+ * Best-effort scheduling of multiple document deletions with a per-document result.
2623
+ */
2624
+ export const batchDeleteDocuments = <
2625
+ TComposable extends Composable = "$fetch",
2626
+ DefaultT extends BatchDeleteDocumentsResponse2 =
2627
+ BatchDeleteDocumentsResponse2,
2628
+ >(
2629
+ options: Options<
2630
+ TComposable,
2631
+ BatchDeleteDocumentsData,
2632
+ BatchDeleteDocumentsResponse2,
2633
+ DefaultT
2634
+ >,
2635
+ ) =>
2636
+ (options.client ?? client).delete<
2637
+ TComposable,
2638
+ BatchDeleteDocumentsResponse2 | DefaultT,
2639
+ BatchDeleteDocumentsError,
2640
+ DefaultT
2641
+ >({
2642
+ security: [
2643
+ { scheme: "bearer", type: "http" },
2644
+ { scheme: "bearer", type: "http" },
2645
+ ],
2646
+ url: "/{tenant_id}/knowledge/databases/{database}/namespaces/{namespace}/documents",
2647
+ ...options,
2648
+ headers: {
2649
+ "Content-Type": "application/json",
2650
+ ...options.headers,
2651
+ },
2652
+ });
2653
+
2614
2654
  /**
2615
2655
  * Get Documents For Namespace
2616
2656
  *
@@ -2644,6 +2684,32 @@ export const getDocumentsForNamespace = <
2644
2684
  ...options,
2645
2685
  });
2646
2686
 
2687
+ /**
2688
+ * Delete document
2689
+ *
2690
+ * Deletes the document's source file from the data lake and schedules cleanup of the
2691
+ * doc store and vector store via the pipeline's reconciliation.
2692
+ */
2693
+ export const deleteDocument = <
2694
+ TComposable extends Composable = "$fetch",
2695
+ DefaultT = undefined,
2696
+ >(
2697
+ options: Options<TComposable, DeleteDocumentData, unknown, DefaultT>,
2698
+ ) =>
2699
+ (options.client ?? client).delete<
2700
+ TComposable,
2701
+ unknown | DefaultT,
2702
+ DeleteDocumentError,
2703
+ DefaultT
2704
+ >({
2705
+ security: [
2706
+ { scheme: "bearer", type: "http" },
2707
+ { scheme: "bearer", type: "http" },
2708
+ ],
2709
+ url: "/{tenant_id}/knowledge/databases/{database}/namespaces/{namespace}/documents/{document_id}",
2710
+ ...options,
2711
+ });
2712
+
2647
2713
  /**
2648
2714
  * Get Document By Id
2649
2715
  *
@@ -1688,6 +1688,34 @@ export type BaseStoreMemoryEvent = {
1688
1688
  [key: string]: unknown;
1689
1689
  };
1690
1690
 
1691
+ /**
1692
+ * BatchDeleteDocumentsRequest
1693
+ *
1694
+ * Request payload for deleting multiple documents from a knowledge namespace.
1695
+ */
1696
+ export type BatchDeleteDocumentsRequest = {
1697
+ /**
1698
+ * Document Ids
1699
+ *
1700
+ * IDs of the documents to delete
1701
+ */
1702
+ document_ids: Array<string>;
1703
+ };
1704
+
1705
+ /**
1706
+ * BatchDeleteDocumentsResponse
1707
+ *
1708
+ * Per-document results of a best-effort batch deletion.
1709
+ */
1710
+ export type BatchDeleteDocumentsResponse = {
1711
+ /**
1712
+ * Results
1713
+ *
1714
+ * Deletion outcome per requested document
1715
+ */
1716
+ results: Array<DocumentDeletionResult>;
1717
+ };
1718
+
1691
1719
  /**
1692
1720
  * Body_create_transcription__tenant_id__openai_audio_transcriptions_post
1693
1721
  */
@@ -4565,6 +4593,26 @@ export type DocumentDto = {
4565
4593
  document_title?: string | null;
4566
4594
  };
4567
4595
 
4596
+ /**
4597
+ * DocumentDeletionResult
4598
+ *
4599
+ * Outcome of a single document deletion within a batch request.
4600
+ */
4601
+ export type DocumentDeletionResult = {
4602
+ /**
4603
+ * Document Id
4604
+ *
4605
+ * ID of the document
4606
+ */
4607
+ document_id: string;
4608
+ /**
4609
+ * Status
4610
+ *
4611
+ * Deletion outcome for this document
4612
+ */
4613
+ status: "scheduled" | "not_found" | "failed";
4614
+ };
4615
+
4568
4616
  /**
4569
4617
  * DocumentParsingMetadata
4570
4618
  *
@@ -13170,6 +13218,16 @@ export type Slider = {
13170
13218
  [key: string]: unknown;
13171
13219
  };
13172
13220
 
13221
+ /**
13222
+ * SortOrder
13223
+ */
13224
+ export const SortOrder = { 1: 1, "-1": -1 } as const;
13225
+
13226
+ /**
13227
+ * SortOrder
13228
+ */
13229
+ export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder];
13230
+
13173
13231
  /**
13174
13232
  * StandaloneQuestionCondenserEvent
13175
13233
  *
@@ -24456,13 +24514,13 @@ export type GetUserThreadsData = {
24456
24514
  *
24457
24515
  * Filter threads created from this date
24458
24516
  */
24459
- from?: string | null;
24517
+ from?: Date | null;
24460
24518
  /**
24461
24519
  * To
24462
24520
  *
24463
24521
  * Filter threads created up to this date
24464
24522
  */
24465
- to?: string | null;
24523
+ to?: Date | null;
24466
24524
  /**
24467
24525
  * Page Number
24468
24526
  *
@@ -24482,11 +24540,9 @@ export type GetUserThreadsData = {
24482
24540
  */
24483
24541
  sort_field?: string;
24484
24542
  /**
24485
- * Sort Order
24486
- *
24487
24543
  * Sort order: 1 for ascending, -1 for descending
24488
24544
  */
24489
- sort_order?: -1 | 1;
24545
+ sort_order?: SortOrder;
24490
24546
  };
24491
24547
  url: "/{tenant_id}/threads/";
24492
24548
  };
@@ -26588,6 +26644,48 @@ export type GetDatabasesResponses = {
26588
26644
  export type GetDatabasesResponse =
26589
26645
  GetDatabasesResponses[keyof GetDatabasesResponses];
26590
26646
 
26647
+ export type BatchDeleteDocumentsData = {
26648
+ body: BatchDeleteDocumentsRequest;
26649
+ path: {
26650
+ /**
26651
+ * Tenant Id
26652
+ *
26653
+ * Tenant identifier: a name, ObjectId, or 'active'
26654
+ */
26655
+ tenant_id: string;
26656
+ /**
26657
+ * Database name
26658
+ */
26659
+ database: string;
26660
+ /**
26661
+ * Namespace
26662
+ */
26663
+ namespace: string;
26664
+ };
26665
+ query?: never;
26666
+ url: "/{tenant_id}/knowledge/databases/{database}/namespaces/{namespace}/documents";
26667
+ };
26668
+
26669
+ export type BatchDeleteDocumentsErrors = {
26670
+ /**
26671
+ * Validation Error
26672
+ */
26673
+ 422: HttpValidationError;
26674
+ };
26675
+
26676
+ export type BatchDeleteDocumentsError =
26677
+ BatchDeleteDocumentsErrors[keyof BatchDeleteDocumentsErrors];
26678
+
26679
+ export type BatchDeleteDocumentsResponses = {
26680
+ /**
26681
+ * Successful Response
26682
+ */
26683
+ 202: BatchDeleteDocumentsResponse;
26684
+ };
26685
+
26686
+ export type BatchDeleteDocumentsResponse2 =
26687
+ BatchDeleteDocumentsResponses[keyof BatchDeleteDocumentsResponses];
26688
+
26591
26689
  export type GetDocumentsForNamespaceData = {
26592
26690
  body?: never;
26593
26691
  path: {
@@ -26661,6 +26759,49 @@ export type GetDocumentsForNamespaceResponses = {
26661
26759
  export type GetDocumentsForNamespaceResponse =
26662
26760
  GetDocumentsForNamespaceResponses[keyof GetDocumentsForNamespaceResponses];
26663
26761
 
26762
+ export type DeleteDocumentData = {
26763
+ body?: never;
26764
+ path: {
26765
+ /**
26766
+ * Tenant Id
26767
+ *
26768
+ * Tenant identifier: a name, ObjectId, or 'active'
26769
+ */
26770
+ tenant_id: string;
26771
+ /**
26772
+ * Database name
26773
+ */
26774
+ database: string;
26775
+ /**
26776
+ * Namespace
26777
+ */
26778
+ namespace: string;
26779
+ /**
26780
+ * Document ID
26781
+ */
26782
+ document_id: string;
26783
+ };
26784
+ query?: never;
26785
+ url: "/{tenant_id}/knowledge/databases/{database}/namespaces/{namespace}/documents/{document_id}";
26786
+ };
26787
+
26788
+ export type DeleteDocumentErrors = {
26789
+ /**
26790
+ * Validation Error
26791
+ */
26792
+ 422: HttpValidationError;
26793
+ };
26794
+
26795
+ export type DeleteDocumentError =
26796
+ DeleteDocumentErrors[keyof DeleteDocumentErrors];
26797
+
26798
+ export type DeleteDocumentResponses = {
26799
+ /**
26800
+ * Successful Response
26801
+ */
26802
+ 202: unknown;
26803
+ };
26804
+
26664
26805
  export type GetDocumentByIdData = {
26665
26806
  body?: never;
26666
26807
  path: {