@agenticmail/enterprise 0.5.23 → 0.5.25
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/dist/chunk-ATHZCB2M.js +12666 -0
- package/dist/chunk-JIZXHTCS.js +1977 -0
- package/dist/chunk-ZI6WADMQ.js +898 -0
- package/dist/cli.js +1 -1
- package/dist/dashboard/pages/agents.js +14 -4
- package/dist/dashboard/pages/knowledge.js +197 -12
- package/dist/index.js +3 -3
- package/dist/routes-X6QJLELJ.js +5740 -0
- package/dist/runtime-7UL67FL5.js +47 -0
- package/dist/server-SQABBBNX.js +11 -0
- package/dist/setup-QZYRQ4CL.js +20 -0
- package/package.json +1 -1
- package/src/dashboard/pages/agents.js +14 -4
- package/src/dashboard/pages/knowledge.js +197 -12
- package/src/engine/knowledge-routes.ts +43 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-ATHZCB2M.js";
|
|
18
|
+
import "./chunk-TYW5XTOW.js";
|
|
19
|
+
import "./chunk-JLSQOQ5L.js";
|
|
20
|
+
import {
|
|
21
|
+
PROVIDER_REGISTRY,
|
|
22
|
+
listAllProviders,
|
|
23
|
+
resolveApiKeyForProvider,
|
|
24
|
+
resolveProvider
|
|
25
|
+
} from "./chunk-67KZYSLU.js";
|
|
26
|
+
import "./chunk-KFQGP6VL.js";
|
|
27
|
+
export {
|
|
28
|
+
AgentRuntime,
|
|
29
|
+
EmailChannel,
|
|
30
|
+
FollowUpScheduler,
|
|
31
|
+
PROVIDER_REGISTRY,
|
|
32
|
+
SessionManager,
|
|
33
|
+
SubAgentManager,
|
|
34
|
+
ToolRegistry,
|
|
35
|
+
callLLM,
|
|
36
|
+
createAgentRuntime,
|
|
37
|
+
createNoopHooks,
|
|
38
|
+
createRuntimeHooks,
|
|
39
|
+
estimateMessageTokens,
|
|
40
|
+
estimateTokens,
|
|
41
|
+
executeTool,
|
|
42
|
+
listAllProviders,
|
|
43
|
+
resolveApiKeyForProvider,
|
|
44
|
+
resolveProvider,
|
|
45
|
+
runAgentLoop,
|
|
46
|
+
toolsToDefinitions
|
|
47
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-ZI6WADMQ.js";
|
|
10
|
+
import "./chunk-HEK7L3DT.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -233,7 +233,7 @@ export function DeploymentProgress({ agentId, onComplete }) {
|
|
|
233
233
|
export function CreateAgentWizard({ onClose, onCreated, toast }) {
|
|
234
234
|
const [step, setStep] = useState(0);
|
|
235
235
|
const steps = ['Role', 'Basics', 'Persona', 'Skills', 'Permissions', 'Deployment', 'Review'];
|
|
236
|
-
const [form, setForm] = useState({ name: '', email: '', role: 'assistant', description: '', personality: '', skills: [], preset: null, customTools: { allowed: [], blocked: [] }, deployTarget: 'docker', knowledgeBases: [], provider: '
|
|
236
|
+
const [form, setForm] = useState({ name: '', email: '', role: 'assistant', description: '', personality: '', skills: [], preset: null, customTools: { allowed: [], blocked: [] }, deployTarget: 'docker', knowledgeBases: [], provider: '', model: '', approvalRequired: true, soulId: null, avatar: null, gender: '', dateOfBirth: '', maritalStatus: '', culturalBackground: '', language: 'en-us', autoOnboard: true, maxRiskLevel: 'medium', blockedSideEffects: ['runs-code', 'deletes-data', 'financial', 'controls-device'], approvalForRiskLevels: ['high', 'critical'], approvalForSideEffects: ['sends-email', 'sends-message'], rateLimits: { toolCallsPerMinute: 30, toolCallsPerHour: 500, toolCallsPerDay: 5000, externalActionsPerHour: 50 }, constraints: { maxConcurrentTasks: 5, maxSessionDurationMinutes: 480, sandboxMode: false }, traits: { communication: 'direct', detail: 'detail-oriented', energy: 'calm', humor: 'warm', formality: 'adaptive', empathy: 'moderate', patience: 'patient', creativity: 'creative' } });
|
|
237
237
|
const [allSkills, setAllSkills] = useState({});
|
|
238
238
|
const [providers, setProviders] = useState([]);
|
|
239
239
|
const [providerModels, setProviderModels] = useState([]);
|
|
@@ -254,15 +254,25 @@ export function CreateAgentWizard({ onClose, onCreated, toast }) {
|
|
|
254
254
|
apiCall('/providers').then(function(d) {
|
|
255
255
|
var provList = d.providers || [];
|
|
256
256
|
setProviders(provList);
|
|
257
|
-
var
|
|
258
|
-
if (
|
|
257
|
+
var configuredProviders = provList.filter(function(p) { return p.configured; });
|
|
258
|
+
if (configuredProviders.length === 0) { setShowSetupGuide(true); }
|
|
259
|
+
else {
|
|
260
|
+
// Auto-select first configured provider if none set
|
|
261
|
+
setForm(function(f) {
|
|
262
|
+
if (!f.provider) {
|
|
263
|
+
return Object.assign({}, f, { provider: configuredProviders[0].id });
|
|
264
|
+
}
|
|
265
|
+
return f;
|
|
266
|
+
});
|
|
267
|
+
}
|
|
259
268
|
setSetupChecked(true);
|
|
260
269
|
}).catch(function() { setShowSetupGuide(true); setSetupChecked(true); });
|
|
261
270
|
}, []);
|
|
262
271
|
|
|
263
272
|
// Fetch models when provider changes
|
|
264
273
|
useEffect(function() {
|
|
265
|
-
var p = form.provider
|
|
274
|
+
var p = form.provider;
|
|
275
|
+
if (!p) return;
|
|
266
276
|
apiCall('/providers/' + p + '/models').then(function(d) {
|
|
267
277
|
var models = d.models || [];
|
|
268
278
|
setProviderModels(models);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h, useState, useEffect, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
|
|
1
|
+
import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
|
|
2
2
|
import { I } from '../components/icons.js';
|
|
3
3
|
import { Modal } from '../components/modal.js';
|
|
4
4
|
|
|
@@ -7,9 +7,18 @@ export function KnowledgeBasePage() {
|
|
|
7
7
|
const [kbs, setKbs] = useState([]);
|
|
8
8
|
const [creating, setCreating] = useState(false);
|
|
9
9
|
const [form, setForm] = useState({ name: '', description: '' });
|
|
10
|
+
const [selected, setSelected] = useState(null); // full KB detail
|
|
11
|
+
const [docs, setDocs] = useState([]);
|
|
12
|
+
const [chunks, setChunks] = useState([]);
|
|
13
|
+
const [selectedDoc, setSelectedDoc] = useState(null);
|
|
14
|
+
const [editing, setEditing] = useState(false);
|
|
15
|
+
const [editForm, setEditForm] = useState({ name: '', description: '' });
|
|
16
|
+
const [loading, setLoading] = useState(false);
|
|
10
17
|
|
|
11
|
-
const load = (
|
|
12
|
-
|
|
18
|
+
const load = useCallback(() => {
|
|
19
|
+
engineCall('/knowledge-bases').then(d => setKbs(d.knowledgeBases || [])).catch(() => {});
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => { load(); }, [load]);
|
|
13
22
|
|
|
14
23
|
const create = async () => {
|
|
15
24
|
try {
|
|
@@ -19,26 +28,202 @@ export function KnowledgeBasePage() {
|
|
|
19
28
|
} catch (e) { toast(e.message, 'error'); }
|
|
20
29
|
};
|
|
21
30
|
|
|
31
|
+
const selectKb = async (kb) => {
|
|
32
|
+
setLoading(true);
|
|
33
|
+
try {
|
|
34
|
+
const detail = await engineCall('/knowledge-bases/' + kb.id);
|
|
35
|
+
const kbData = detail.knowledgeBase || detail;
|
|
36
|
+
setSelected(kbData);
|
|
37
|
+
setDocs(kbData.documents || []);
|
|
38
|
+
setChunks([]);
|
|
39
|
+
setSelectedDoc(null);
|
|
40
|
+
setEditForm({ name: kbData.name || '', description: kbData.description || '' });
|
|
41
|
+
} catch (e) {
|
|
42
|
+
toast('Failed to load knowledge base: ' + e.message, 'error');
|
|
43
|
+
}
|
|
44
|
+
setLoading(false);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const loadDocChunks = async (doc) => {
|
|
48
|
+
setSelectedDoc(doc);
|
|
49
|
+
try {
|
|
50
|
+
const res = await engineCall('/knowledge-bases/' + selected.id + '/documents/' + doc.id + '/chunks');
|
|
51
|
+
setChunks(res.chunks || []);
|
|
52
|
+
} catch {
|
|
53
|
+
// chunks endpoint might not exist, try search with empty query
|
|
54
|
+
setChunks([]);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const deleteKb = async (id) => {
|
|
59
|
+
try {
|
|
60
|
+
await engineCall('/knowledge-bases/' + id, { method: 'DELETE' });
|
|
61
|
+
toast('Knowledge base deleted', 'success');
|
|
62
|
+
setSelected(null); load();
|
|
63
|
+
} catch (e) { toast(e.message, 'error'); }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const deleteDoc = async (docId) => {
|
|
67
|
+
if (!selected) return;
|
|
68
|
+
try {
|
|
69
|
+
await engineCall('/knowledge-bases/' + selected.id + '/documents/' + docId, { method: 'DELETE' });
|
|
70
|
+
toast('Document deleted', 'success');
|
|
71
|
+
setDocs(d => d.filter(x => x.id !== docId));
|
|
72
|
+
if (selectedDoc && selectedDoc.id === docId) { setSelectedDoc(null); setChunks([]); }
|
|
73
|
+
} catch (e) { toast(e.message, 'error'); }
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const saveEdit = async () => {
|
|
77
|
+
if (!selected) return;
|
|
78
|
+
try {
|
|
79
|
+
await engineCall('/knowledge-bases/' + selected.id, { method: 'PUT', body: JSON.stringify({ name: editForm.name, description: editForm.description }) });
|
|
80
|
+
toast('Knowledge base updated', 'success');
|
|
81
|
+
setSelected(s => ({ ...s, name: editForm.name, description: editForm.description }));
|
|
82
|
+
setEditing(false);
|
|
83
|
+
load();
|
|
84
|
+
} catch (e) { toast(e.message, 'error'); }
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ── Detail View ──
|
|
88
|
+
if (selected) {
|
|
89
|
+
return h(Fragment, null,
|
|
90
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
|
|
91
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 12 } },
|
|
92
|
+
h('button', { className: 'btn btn-secondary btn-sm', onClick: () => setSelected(null) }, '\u2190 Back'),
|
|
93
|
+
editing
|
|
94
|
+
? h('input', { className: 'input', value: editForm.name, onChange: e => setEditForm(f => ({ ...f, name: e.target.value })), style: { fontSize: 18, fontWeight: 700, padding: '4px 8px' } })
|
|
95
|
+
: h('h1', { style: { fontSize: 20, fontWeight: 700, margin: 0 } }, selected.name)
|
|
96
|
+
),
|
|
97
|
+
h('div', { style: { display: 'flex', gap: 8 } },
|
|
98
|
+
editing
|
|
99
|
+
? h(Fragment, null,
|
|
100
|
+
h('button', { className: 'btn btn-secondary btn-sm', onClick: () => setEditing(false) }, 'Cancel'),
|
|
101
|
+
h('button', { className: 'btn btn-primary btn-sm', onClick: saveEdit }, 'Save')
|
|
102
|
+
)
|
|
103
|
+
: h(Fragment, null,
|
|
104
|
+
h('button', { className: 'btn btn-secondary btn-sm', onClick: () => setEditing(true) }, I.journal(), ' Edit'),
|
|
105
|
+
h('button', { className: 'btn btn-danger btn-sm', onClick: () => deleteKb(selected.id) }, I.trash(), ' Delete')
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
),
|
|
109
|
+
|
|
110
|
+
// Description
|
|
111
|
+
h('div', { className: 'card', style: { marginBottom: 16 } },
|
|
112
|
+
h('div', { className: 'card-body' },
|
|
113
|
+
editing
|
|
114
|
+
? h('textarea', { className: 'input', rows: 3, value: editForm.description, onChange: e => setEditForm(f => ({ ...f, description: e.target.value })), placeholder: 'Knowledge base description...' })
|
|
115
|
+
: h('p', { style: { color: 'var(--text-secondary)', fontSize: 13, margin: 0 } }, selected.description || 'No description'),
|
|
116
|
+
h('div', { style: { display: 'flex', gap: 12, marginTop: 12, fontSize: 12, color: 'var(--text-muted)' } },
|
|
117
|
+
h('span', null, 'ID: ', h('code', null, selected.id)),
|
|
118
|
+
selected.createdAt && h('span', null, 'Created: ', new Date(selected.createdAt).toLocaleDateString()),
|
|
119
|
+
h('span', null, docs.length + ' document(s)'),
|
|
120
|
+
selected.agentIds && h('span', null, (selected.agentIds.length || 0) + ' agent(s)')
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
),
|
|
124
|
+
|
|
125
|
+
// Stats
|
|
126
|
+
selected.stats && h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 16 } },
|
|
127
|
+
[
|
|
128
|
+
{ label: 'Documents', value: selected.stats.documentCount || docs.length },
|
|
129
|
+
{ label: 'Chunks', value: selected.stats.chunkCount || 0 },
|
|
130
|
+
{ label: 'Total Tokens', value: selected.stats.totalTokens || 0 },
|
|
131
|
+
{ label: 'Queries', value: selected.stats.queryCount || 0 },
|
|
132
|
+
].map(s => h('div', { key: s.label, className: 'card', style: { textAlign: 'center', padding: 12 } },
|
|
133
|
+
h('div', { style: { fontSize: 22, fontWeight: 700, color: 'var(--brand-color, #6366f1)' } }, typeof s.value === 'number' && s.value > 1000 ? (s.value / 1000).toFixed(1) + 'K' : s.value),
|
|
134
|
+
h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 2 } }, s.label)
|
|
135
|
+
))
|
|
136
|
+
),
|
|
137
|
+
|
|
138
|
+
// Two-panel layout: docs list + chunk preview
|
|
139
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
|
|
140
|
+
|
|
141
|
+
// Documents list
|
|
142
|
+
h('div', { className: 'card' },
|
|
143
|
+
h('div', { className: 'card-header' },
|
|
144
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
|
|
145
|
+
h('h3', { style: { margin: 0 } }, 'Documents'),
|
|
146
|
+
h('span', { className: 'badge badge-neutral' }, docs.length)
|
|
147
|
+
)
|
|
148
|
+
),
|
|
149
|
+
h('div', { className: 'card-body-flush' },
|
|
150
|
+
docs.length === 0
|
|
151
|
+
? h('div', { style: { padding: 24, textAlign: 'center', color: 'var(--text-muted)' } }, 'No documents in this knowledge base')
|
|
152
|
+
: docs.map(doc =>
|
|
153
|
+
h('div', { key: doc.id, style: {
|
|
154
|
+
padding: '10px 16px', borderBottom: '1px solid var(--border)', cursor: 'pointer',
|
|
155
|
+
background: selectedDoc && selectedDoc.id === doc.id ? 'var(--bg-secondary)' : 'transparent',
|
|
156
|
+
transition: 'background 0.15s'
|
|
157
|
+
}, onClick: () => loadDocChunks(doc) },
|
|
158
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
|
|
159
|
+
h('div', null,
|
|
160
|
+
h('div', { style: { fontWeight: 600, fontSize: 13 } }, doc.name || doc.id),
|
|
161
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } },
|
|
162
|
+
[doc.sourceType, doc.mimeType, doc.size ? (doc.size > 1024 ? (doc.size / 1024).toFixed(1) + ' KB' : doc.size + ' B') : null, doc.status].filter(Boolean).join(' \u2022 ')
|
|
163
|
+
)
|
|
164
|
+
),
|
|
165
|
+
h('button', { className: 'btn btn-sm', style: { padding: '2px 6px', fontSize: 11, color: 'var(--danger)' }, onClick: (e) => { e.stopPropagation(); deleteDoc(doc.id); } }, I.trash())
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
),
|
|
171
|
+
|
|
172
|
+
// Chunk preview
|
|
173
|
+
h('div', { className: 'card' },
|
|
174
|
+
h('div', { className: 'card-header' },
|
|
175
|
+
h('h3', { style: { margin: 0 } }, selectedDoc ? 'Chunks: ' + (selectedDoc.name || selectedDoc.id) : 'Select a document')
|
|
176
|
+
),
|
|
177
|
+
h('div', { className: 'card-body', style: { maxHeight: 500, overflow: 'auto' } },
|
|
178
|
+
!selectedDoc
|
|
179
|
+
? h('div', { style: { textAlign: 'center', color: 'var(--text-muted)', padding: 24 } }, 'Click a document to preview its chunks')
|
|
180
|
+
: chunks.length === 0
|
|
181
|
+
? h('div', { style: { textAlign: 'center', color: 'var(--text-muted)', padding: 24 } }, 'No chunks found. The document may not have been processed yet.')
|
|
182
|
+
: chunks.map((chunk, i) =>
|
|
183
|
+
h('div', { key: chunk.id || i, style: { padding: '10px 0', borderBottom: '1px solid var(--border)' } },
|
|
184
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 4 } },
|
|
185
|
+
h('span', { style: { fontSize: 11, fontWeight: 600, color: 'var(--text-muted)' } }, 'Chunk #' + (chunk.position ?? i + 1)),
|
|
186
|
+
chunk.tokenCount && h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, chunk.tokenCount + ' tokens')
|
|
187
|
+
),
|
|
188
|
+
h('div', { style: { fontSize: 13, lineHeight: 1.5, whiteSpace: 'pre-wrap', color: 'var(--text-primary)' } }, chunk.content || '(empty)')
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── List View ──
|
|
22
198
|
return h(Fragment, null,
|
|
23
199
|
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
|
|
24
|
-
h('div', null,
|
|
200
|
+
h('div', null,
|
|
201
|
+
h('h1', { style: { fontSize: 20, fontWeight: 700 } }, 'Knowledge Bases'),
|
|
202
|
+
h('p', { style: { color: 'var(--text-muted)', fontSize: 13 } }, 'Document ingestion and RAG retrieval for agents')
|
|
203
|
+
),
|
|
25
204
|
h('button', { className: 'btn btn-primary', onClick: () => setCreating(true) }, I.plus(), ' New Knowledge Base')
|
|
26
205
|
),
|
|
206
|
+
|
|
27
207
|
creating && h(Modal, { title: 'Create Knowledge Base', onClose: () => setCreating(false), footer: h(Fragment, null, h('button', { className: 'btn btn-secondary', onClick: () => setCreating(false) }, 'Cancel'), h('button', { className: 'btn btn-primary', onClick: create, disabled: !form.name }, 'Create')) },
|
|
28
208
|
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Name'), h('input', { className: 'input', value: form.name, onChange: e => setForm(f => ({ ...f, name: e.target.value })) })),
|
|
29
209
|
h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Description'), h('textarea', { className: 'input', value: form.description, onChange: e => setForm(f => ({ ...f, description: e.target.value })) }))
|
|
30
210
|
),
|
|
31
|
-
|
|
211
|
+
|
|
212
|
+
loading && h('div', { style: { textAlign: 'center', padding: 40 } }, 'Loading...'),
|
|
213
|
+
|
|
214
|
+
!loading && kbs.length === 0
|
|
32
215
|
? h('div', { className: 'card' }, h('div', { className: 'card-body' }, h('div', { className: 'empty-state' }, I.knowledge(), h('h3', null, 'No knowledge bases'), h('p', null, 'Create a knowledge base to give agents access to your documents, policies, and data.'))))
|
|
33
|
-
: h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 16 } }, kbs.map(kb =>
|
|
34
|
-
h('div', { key: kb.id, className: 'card' },
|
|
216
|
+
: !loading && h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 16 } }, kbs.map(kb =>
|
|
217
|
+
h('div', { key: kb.id, className: 'card', style: { cursor: 'pointer', transition: 'border-color 0.15s' }, onClick: () => selectKb(kb) },
|
|
35
218
|
h('div', { className: 'card-body' },
|
|
36
219
|
h('h3', { style: { fontSize: 15, fontWeight: 600, marginBottom: 4 } }, kb.name),
|
|
37
|
-
h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginBottom: 12 } }, kb.description || 'No description'),
|
|
38
|
-
h('div', { style: { display: 'flex', gap: 8 } },
|
|
39
|
-
h('span', { className: 'badge badge-info' }, (kb.documents?.length || 0) + '
|
|
40
|
-
h('span', { className: 'badge badge-neutral' }, (kb.
|
|
41
|
-
|
|
220
|
+
h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginBottom: 12, minHeight: 32 } }, kb.description || 'No description'),
|
|
221
|
+
h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
|
|
222
|
+
h('span', { className: 'badge badge-info' }, (kb.stats?.documentCount || kb.documents?.length || 0) + ' docs'),
|
|
223
|
+
h('span', { className: 'badge badge-neutral' }, (kb.stats?.chunkCount || 0) + ' chunks'),
|
|
224
|
+
kb.agentIds && kb.agentIds.length > 0 && h('span', { className: 'badge badge-success' }, kb.agentIds.length + ' agent(s)')
|
|
225
|
+
),
|
|
226
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 8 } }, 'Click to view details \u2192')
|
|
42
227
|
)
|
|
43
228
|
)
|
|
44
229
|
))
|
|
@@ -59,6 +59,49 @@ export function createKnowledgeRoutes(knowledgeBase: KnowledgeBaseEngine) {
|
|
|
59
59
|
return c.json({ context });
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
// Update knowledge base name/description
|
|
63
|
+
router.put('/knowledge-bases/:id', async (c) => {
|
|
64
|
+
const id = c.req.param('id');
|
|
65
|
+
const kb = knowledgeBase.getKnowledgeBase(id);
|
|
66
|
+
if (!kb) return c.json({ error: 'Knowledge base not found' }, 404);
|
|
67
|
+
const body = await c.req.json();
|
|
68
|
+
if (body.name !== undefined) (kb as any).name = body.name;
|
|
69
|
+
if (body.description !== undefined) (kb as any).description = body.description;
|
|
70
|
+
(kb as any).updatedAt = new Date().toISOString();
|
|
71
|
+
// Persist to DB if engine supports it
|
|
72
|
+
if ((knowledgeBase as any).db) {
|
|
73
|
+
try {
|
|
74
|
+
await (knowledgeBase as any).db.execute(
|
|
75
|
+
'UPDATE knowledge_bases SET name = $1, description = $2, updated_at = $3 WHERE id = $4',
|
|
76
|
+
[kb.name, kb.description, (kb as any).updatedAt, id]
|
|
77
|
+
);
|
|
78
|
+
} catch { /* in-memory only fallback */ }
|
|
79
|
+
}
|
|
80
|
+
return c.json({ knowledgeBase: kb });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Get chunks for a specific document
|
|
84
|
+
router.get('/knowledge-bases/:kbId/documents/:docId/chunks', (c) => {
|
|
85
|
+
const kbId = c.req.param('kbId');
|
|
86
|
+
const docId = c.req.param('docId');
|
|
87
|
+
const kb = knowledgeBase.getKnowledgeBase(kbId);
|
|
88
|
+
if (!kb) return c.json({ error: 'Knowledge base not found' }, 404);
|
|
89
|
+
// Get chunks from the internal chunks map
|
|
90
|
+
const allChunks = (knowledgeBase as any).chunks || new Map();
|
|
91
|
+
const docChunks: any[] = [];
|
|
92
|
+
if (allChunks instanceof Map) {
|
|
93
|
+
allChunks.forEach((chunk: any) => {
|
|
94
|
+
if (chunk.documentId === docId) docChunks.push(chunk);
|
|
95
|
+
});
|
|
96
|
+
} else if (typeof allChunks === 'object') {
|
|
97
|
+
for (const [, chunk] of Object.entries(allChunks)) {
|
|
98
|
+
if ((chunk as any).documentId === docId) docChunks.push(chunk);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
docChunks.sort((a: any, b: any) => (a.position || 0) - (b.position || 0));
|
|
102
|
+
return c.json({ chunks: docChunks, total: docChunks.length });
|
|
103
|
+
});
|
|
104
|
+
|
|
62
105
|
router.delete('/knowledge-bases/:id', (c) => {
|
|
63
106
|
const ok = knowledgeBase.deleteKnowledgeBase(c.req.param('id'));
|
|
64
107
|
return ok ? c.json({ success: true }) : c.json({ error: 'Not found' }, 404);
|