@botpress/adk-cli 1.19.0-beta.4 → 1.19.0-beta.5
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/{README-wjb7z7f0.md → README-824wa4jj.md} +1 -1
- package/dist/SKILL-75ym6tcj.md +257 -0
- package/dist/SKILL-chnwzmt1.md +548 -0
- package/dist/SKILL-d4bs53y3.md +244 -0
- package/dist/SKILL-h4b2f1n9.md +114 -0
- package/dist/SKILL-hbw5nt95.md +111 -0
- package/dist/actions-h7hxxvxr.md +823 -0
- package/dist/adk-build-emhwd0tg.md +39 -0
- package/dist/adk-debug-yrrdtpmb.md +23 -0
- package/dist/adk-dev-console-7z9rtjpy.md +17 -0
- package/dist/adk-document-ras6zzzh.md +68 -0
- package/dist/adk-eval-vx76dwq0.md +17 -0
- package/dist/adk-explain-fcsaw852.md +22 -0
- package/dist/adk-init-9kmxmbbe.md +30 -0
- package/dist/adk-ship-dybzn884.md +29 -0
- package/dist/adk-test-xq67savs.md +27 -0
- package/dist/adk-validate-1p4skv4m.md +31 -0
- package/dist/advanced-patterns-z86m9h1a.md +669 -0
- package/dist/agent-config-1aqvkrgf.md +450 -0
- package/dist/agent-steps-zj4c1n9t.md +115 -0
- package/dist/assets-q7jt8r2q.md +237 -0
- package/dist/autonomous-execution-fymc12e5.md +747 -0
- package/dist/cli-c31e06yr.md +1334 -0
- package/dist/cli.js +970 -566
- package/dist/commands.json +24 -3
- package/dist/common-failures-ht29w2db.md +231 -0
- package/dist/component-registry-bbpw0see.md +43 -0
- package/dist/context-api-760cvh0h.md +461 -0
- package/dist/conversation-analysis-8ka4nrm4.md +420 -0
- package/dist/conversation-lifecycle-4seqqjc1.md +311 -0
- package/dist/conversations-gwfvxaee.md +1111 -0
- package/dist/custom-components-y5wdgpey.md +167 -0
- package/dist/debug-workflow-vz84q80v.md +239 -0
- package/dist/desk-k54ktvsd.md +852 -0
- package/dist/doc-standards-ck6ax2jd.md +124 -0
- package/dist/eval-format-7n9brja4.md +354 -0
- package/dist/evals-y1zcr1mc.md +11 -0
- package/dist/explain-config-1yaq3zzd.md +424 -0
- package/dist/files-dzt0wxn3.md +918 -0
- package/dist/integration-actions-xang7tma.md +659 -0
- package/dist/integrations-k5ss07f4.md +266 -0
- package/dist/interfaces-6t0bpxs4.md +97 -0
- package/dist/knowledge-bases-20rg8p5c.md +456 -0
- package/dist/llm-debugging-76ng8rzm.md +209 -0
- package/dist/mcp-server-n1asyzhv.md +112 -0
- package/dist/messages-gkkg0b54.md +769 -0
- package/dist/model-configuration-f273kgd0.md +201 -0
- package/dist/multi-agent-dashboard-1mdfjdts.md +70 -0
- package/dist/pages-xn18xn73.md +273 -0
- package/dist/patterns-mistakes-5vb99dhf.md +469 -0
- package/dist/plugins-ttnx8f49.md +280 -0
- package/dist/tables-x45t36dd.md +1136 -0
- package/dist/tags-46td3qrg.md +516 -0
- package/dist/test-patterns-hj47w4nc.md +355 -0
- package/dist/testing-workflow-2vkp932f.md +225 -0
- package/dist/tools-wk7bq24z.md +581 -0
- package/dist/trace-summarization-hdyx1c2e.md +313 -0
- package/dist/traces-and-logs-f2kjfh19.md +203 -0
- package/dist/triggers-4j2cc86x.md +719 -0
- package/dist/ui-server-entry.js +952 -137
- package/dist/workflow-steps-mrdrf1c5.md +851 -0
- package/dist/workflows-gadx3s69.md +1408 -0
- package/dist/zai-agent-reference-e6644aad.md +454 -0
- package/dist/zai-complete-guide-mnna5d6f.md +1408 -0
- package/package.json +4 -4
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
# Files API
|
|
2
|
+
|
|
3
|
+
The Files API provides structured file storage with automatic indexing, semantic search capabilities, and CDN-backed content delivery.
|
|
4
|
+
|
|
5
|
+
## Accessing the Files API
|
|
6
|
+
|
|
7
|
+
All file operations use the Botpress client. Get the client using the Context API:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { context } from "@botpress/runtime";
|
|
11
|
+
|
|
12
|
+
const client = context.get("client");
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This pattern is used in all examples below. See **[Context API](./context-api.md)** for more details.
|
|
16
|
+
|
|
17
|
+
## Basic Concepts
|
|
18
|
+
|
|
19
|
+
### What is the Files API?
|
|
20
|
+
- **Persistent storage**: Upload and manage files for your bot
|
|
21
|
+
- **Semantic search**: Optional vector indexing for searchable content
|
|
22
|
+
- **CDN delivery**: Fast file access through CloudFront
|
|
23
|
+
- **Access control**: Fine-grained permissions and policies
|
|
24
|
+
- **Metadata & tags**: Organize files with custom attributes
|
|
25
|
+
|
|
26
|
+
### File Properties
|
|
27
|
+
|
|
28
|
+
Files have the following core properties:
|
|
29
|
+
- **id**: Unique file identifier
|
|
30
|
+
- **key**: User-defined unique key per bot
|
|
31
|
+
- **url**: CDN URL for file content access
|
|
32
|
+
- **size**: File size in bytes
|
|
33
|
+
- **contentType**: MIME type (e.g., "application/pdf")
|
|
34
|
+
- **tags**: Key-value pairs for organization
|
|
35
|
+
- **metadata**: Custom JSON metadata
|
|
36
|
+
- **accessPolicies**: Array of access control policies
|
|
37
|
+
- **status**: Upload/indexing status
|
|
38
|
+
- **createdAt/updatedAt**: Timestamps
|
|
39
|
+
|
|
40
|
+
## Creating and Uploading Files
|
|
41
|
+
|
|
42
|
+
### Basic File Upload
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const { file } = await client.uploadFile({
|
|
46
|
+
key: "documents/report-2024.pdf", // Unique key
|
|
47
|
+
content: fileBuffer, // File content (Buffer/ArrayBuffer/Blob/string)
|
|
48
|
+
contentType: "application/pdf", // MIME type
|
|
49
|
+
tags: {
|
|
50
|
+
category: "reports",
|
|
51
|
+
year: "2024"
|
|
52
|
+
},
|
|
53
|
+
index: true, // Enable semantic search
|
|
54
|
+
accessPolicies: ["public_content"] // Make publicly accessible
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
console.log("File ID:", file.id);
|
|
58
|
+
console.log("File URL:", file.url);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Upload Options
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
await client.uploadFile({
|
|
65
|
+
key: "unique-file-key", // Required: Unique identifier
|
|
66
|
+
content: fileBuffer, // Required: File content
|
|
67
|
+
contentType: "text/plain", // Optional: MIME type
|
|
68
|
+
|
|
69
|
+
// Indexing options
|
|
70
|
+
index: true, // Enable semantic search
|
|
71
|
+
indexing: {
|
|
72
|
+
configuration: {
|
|
73
|
+
vision: {
|
|
74
|
+
transcribePages: true, // OCR for images/PDFs
|
|
75
|
+
indexPages: "all" // Index all pages
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Access control
|
|
81
|
+
accessPolicies: [
|
|
82
|
+
"public_content", // Public URL access
|
|
83
|
+
"integrations" // Integration access
|
|
84
|
+
],
|
|
85
|
+
|
|
86
|
+
// Organization
|
|
87
|
+
tags: {
|
|
88
|
+
category: "documents",
|
|
89
|
+
department: "sales"
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// Custom metadata
|
|
93
|
+
metadata: {
|
|
94
|
+
author: "John Doe",
|
|
95
|
+
version: "1.0"
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
// Expiry (max 90 days)
|
|
99
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
100
|
+
|
|
101
|
+
// Immediate public access (requires public_content policy)
|
|
102
|
+
publicContentImmediatelyAccessible: true
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Uploading Text Content
|
|
107
|
+
|
|
108
|
+
For simple text/JSON content:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const { file } = await client.uploadFile({
|
|
112
|
+
key: "config/settings.json",
|
|
113
|
+
content: jsonContent, // String content
|
|
114
|
+
contentType: "application/json",
|
|
115
|
+
index: false
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Uploading from URL
|
|
120
|
+
|
|
121
|
+
To upload from an external URL:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const { file } = await client.uploadFile({
|
|
125
|
+
key: "imported/image.jpg",
|
|
126
|
+
url: "https://example.com/image.jpg", // Fetch from URL
|
|
127
|
+
contentType: "image/jpeg",
|
|
128
|
+
index: false
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Retrieving Files
|
|
133
|
+
|
|
134
|
+
### Get File Metadata
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Get by ID
|
|
138
|
+
const { file } = await client.getFile({
|
|
139
|
+
id: "file_abc123"
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Get by key
|
|
143
|
+
const { file } = await client.getFile({
|
|
144
|
+
id: "documents/report-2024.pdf" // Can use key as ID
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
console.log("File URL:", file.url);
|
|
148
|
+
console.log("File size:", file.size);
|
|
149
|
+
console.log("Status:", file.status);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Access File Content
|
|
153
|
+
|
|
154
|
+
Files are accessed via URL. The URL type depends on access policies:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const { file } = await client.getFile({ id: fileId });
|
|
158
|
+
|
|
159
|
+
// For public files: permanent CDN URL
|
|
160
|
+
// For private files: temporary presigned S3 URL
|
|
161
|
+
|
|
162
|
+
const response = await fetch(file.url);
|
|
163
|
+
const content = await response.text();
|
|
164
|
+
|
|
165
|
+
// For JSON files
|
|
166
|
+
const data = await response.json();
|
|
167
|
+
|
|
168
|
+
// For binary files
|
|
169
|
+
const buffer = await response.arrayBuffer();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### File Status States
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
type FileStatus =
|
|
176
|
+
| "upload_pending" // Awaiting content upload
|
|
177
|
+
| "upload_completed" // Upload successful
|
|
178
|
+
| "upload_failed" // Upload failed
|
|
179
|
+
| "indexing_pending" // Queued for indexing
|
|
180
|
+
| "indexing_completed" // Indexed and searchable
|
|
181
|
+
| "indexing_failed" // Indexing failed
|
|
182
|
+
|
|
183
|
+
// Check file status
|
|
184
|
+
const { file } = await client.getFile({ id: fileId });
|
|
185
|
+
if (file.status === "indexing_completed") {
|
|
186
|
+
console.log("File is indexed and searchable");
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Listing Files
|
|
191
|
+
|
|
192
|
+
### Basic Listing
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const { files, meta } = await client.listFiles({
|
|
196
|
+
limit: 20,
|
|
197
|
+
offset: 0
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
files.forEach(file => {
|
|
201
|
+
console.log(file.key, file.size, file.status);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Pagination
|
|
205
|
+
if (meta.nextToken) {
|
|
206
|
+
const nextPage = await client.listFiles({
|
|
207
|
+
nextToken: meta.nextToken
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Filtering by Tags
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Single tag filter
|
|
216
|
+
const { files } = await client.listFiles({
|
|
217
|
+
tags: { category: "reports" }
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Multiple values (OR condition)
|
|
221
|
+
const { files } = await client.listFiles({
|
|
222
|
+
tags: {
|
|
223
|
+
category: ["reports", "documents"]
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Exclude values
|
|
228
|
+
const { files } = await client.listFiles({
|
|
229
|
+
tags: {
|
|
230
|
+
category: {
|
|
231
|
+
not: "archived"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Filtering by IDs
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const { files } = await client.listFiles({
|
|
241
|
+
ids: ["file_123", "file_456", "file_789"]
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Sorting
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
const { files } = await client.listFiles({
|
|
249
|
+
sortBy: "createdAt",
|
|
250
|
+
sortOrder: "desc",
|
|
251
|
+
limit: 10
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Available sort fields:
|
|
255
|
+
// - key
|
|
256
|
+
// - size
|
|
257
|
+
// - createdAt
|
|
258
|
+
// - updatedAt
|
|
259
|
+
// - status
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Semantic Search
|
|
263
|
+
|
|
264
|
+
### Searching File Content
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const { passages } = await client.searchFiles({
|
|
268
|
+
query: "machine learning algorithms",
|
|
269
|
+
limit: 5
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
passages.forEach(passage => {
|
|
273
|
+
console.log("File:", passage.file.key);
|
|
274
|
+
console.log("Content:", passage.content);
|
|
275
|
+
console.log("Similarity:", passage.score);
|
|
276
|
+
console.log("Page:", passage.meta.pageNumber);
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Search Options
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const { passages } = await client.searchFiles({
|
|
284
|
+
query: "customer feedback",
|
|
285
|
+
|
|
286
|
+
// Filtering
|
|
287
|
+
tags: { category: "support" },
|
|
288
|
+
|
|
289
|
+
// Context
|
|
290
|
+
contextDepth: 2, // Include 2 surrounding passages
|
|
291
|
+
includeBreadcrumb: true, // Include document structure
|
|
292
|
+
withContext: true, // Return detailed context
|
|
293
|
+
|
|
294
|
+
// Consolidation
|
|
295
|
+
consolidate: true, // Group by file, sort by position
|
|
296
|
+
|
|
297
|
+
// Pagination
|
|
298
|
+
limit: 10
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Search Result Structure
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
{
|
|
306
|
+
passages: [
|
|
307
|
+
{
|
|
308
|
+
content: "The matched text passage...",
|
|
309
|
+
score: 0.89, // Similarity score (0-1)
|
|
310
|
+
meta: {
|
|
311
|
+
type: "paragraph", // passage | title | subtitle | etc.
|
|
312
|
+
subtype: "text",
|
|
313
|
+
pageNumber: 5,
|
|
314
|
+
position: 12,
|
|
315
|
+
sourceUrl: "https://..."
|
|
316
|
+
},
|
|
317
|
+
file: {
|
|
318
|
+
id: "file_abc123",
|
|
319
|
+
key: "documents/report.pdf",
|
|
320
|
+
contentType: "application/pdf",
|
|
321
|
+
tags: { category: "reports" },
|
|
322
|
+
createdAt: "2024-01-15T10:30:00Z",
|
|
323
|
+
updatedAt: "2024-01-15T10:30:00Z"
|
|
324
|
+
},
|
|
325
|
+
context: [ // Only if withContext: true
|
|
326
|
+
{
|
|
327
|
+
type: "preceding",
|
|
328
|
+
content: "Previous passage..."
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
type: "current",
|
|
332
|
+
content: "The matched text passage..."
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
type: "subsequent",
|
|
336
|
+
content: "Next passage..."
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Updating Files
|
|
345
|
+
|
|
346
|
+
### Update File Metadata
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// Update without changing content
|
|
350
|
+
const { file } = await client.updateFileMetadata({
|
|
351
|
+
id: fileId,
|
|
352
|
+
|
|
353
|
+
metadata: {
|
|
354
|
+
version: "2.0",
|
|
355
|
+
updatedBy: "Jane Doe"
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
tags: {
|
|
359
|
+
status: "reviewed",
|
|
360
|
+
newTag: "value"
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
accessPolicies: ["public_content"],
|
|
364
|
+
|
|
365
|
+
expiresAt: new Date("2024-12-31").toISOString()
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Metadata Update Behavior
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
// Partial updates - existing keys preserved
|
|
373
|
+
await client.updateFileMetadata({
|
|
374
|
+
id: fileId,
|
|
375
|
+
metadata: {
|
|
376
|
+
newKey: "newValue"
|
|
377
|
+
// Other metadata keys remain unchanged
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Delete metadata key
|
|
382
|
+
await client.updateFileMetadata({
|
|
383
|
+
id: fileId,
|
|
384
|
+
metadata: {
|
|
385
|
+
oldKey: null // Removes this key
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Delete tag
|
|
390
|
+
await client.updateFileMetadata({
|
|
391
|
+
id: fileId,
|
|
392
|
+
tags: {
|
|
393
|
+
oldTag: null // Removes this tag
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Replace File Content
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
// Upload new content with same key (replaces old content)
|
|
402
|
+
const { file } = await client.uploadFile({
|
|
403
|
+
key: existingFileKey,
|
|
404
|
+
content: newContent,
|
|
405
|
+
contentType: "text/plain"
|
|
406
|
+
});
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Managing File Passages
|
|
410
|
+
|
|
411
|
+
### List File Passages
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
const { passages, meta } = await client.listFilePassages({
|
|
415
|
+
id: fileId,
|
|
416
|
+
limit: 20
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
passages.forEach(passage => {
|
|
420
|
+
console.log("Passage ID:", passage.id);
|
|
421
|
+
console.log("Content:", passage.content);
|
|
422
|
+
console.log("Type:", passage.meta.type);
|
|
423
|
+
console.log("Page:", passage.meta.pageNumber);
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Set Custom Passages
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
// Replace all passages with custom content
|
|
431
|
+
await client.setFilePassages({
|
|
432
|
+
id: fileId,
|
|
433
|
+
passages: [
|
|
434
|
+
{
|
|
435
|
+
content: "# Introduction\n\nThis is the first section...",
|
|
436
|
+
type: "title"
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
content: "Main content paragraph here.",
|
|
440
|
+
type: "paragraph",
|
|
441
|
+
pageNumber: 1
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
content: "## Subsection\n\nMore details...",
|
|
445
|
+
type: "subtitle"
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// File status becomes "indexing_pending"
|
|
451
|
+
// Check status later to confirm completion
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Deleting Files
|
|
455
|
+
|
|
456
|
+
### Delete Single File
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// Delete by ID
|
|
460
|
+
await client.deleteFile({ id: "file_abc123" });
|
|
461
|
+
|
|
462
|
+
// Delete by key
|
|
463
|
+
await client.deleteFile({ id: "documents/report.pdf" });
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Cleanup
|
|
467
|
+
|
|
468
|
+
When a file is deleted:
|
|
469
|
+
- File metadata removed from database
|
|
470
|
+
- File content removed from S3
|
|
471
|
+
- Vector embeddings removed (if indexed)
|
|
472
|
+
- All passages deleted
|
|
473
|
+
|
|
474
|
+
## Knowledge Bases
|
|
475
|
+
|
|
476
|
+
For high-level RAG implementation with automatic indexing and search integration, see **[Knowledge Bases](./knowledge-bases.md)**.
|
|
477
|
+
|
|
478
|
+
The Files API provides low-level file management. Knowledge Bases use the Files API internally but provide a simpler interface for document indexing and semantic search in AI responses.
|
|
479
|
+
|
|
480
|
+
## Copying Files
|
|
481
|
+
|
|
482
|
+
### Copy Within Bot
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
const { file } = await client.copyFile({
|
|
486
|
+
idOrKey: sourceFileId,
|
|
487
|
+
destinationKey: "backups/report-copy.pdf",
|
|
488
|
+
overwrite: false // Fail if destination exists
|
|
489
|
+
});
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Copy Across Bots
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
const { file } = await client.copyFile({
|
|
496
|
+
idOrKey: sourceFileId,
|
|
497
|
+
destinationKey: "imported/report.pdf",
|
|
498
|
+
destinationBotId: targetBotId,
|
|
499
|
+
overwrite: true
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Supported File Types
|
|
504
|
+
|
|
505
|
+
### Indexable Formats
|
|
506
|
+
|
|
507
|
+
These file types support semantic search indexing:
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
const INDEXABLE_TYPES = {
|
|
511
|
+
// Documents
|
|
512
|
+
pdf: "application/pdf",
|
|
513
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
514
|
+
|
|
515
|
+
// Text
|
|
516
|
+
txt: "text/plain",
|
|
517
|
+
md: "text/markdown",
|
|
518
|
+
html: "text/html",
|
|
519
|
+
|
|
520
|
+
// Images (with vision processing)
|
|
521
|
+
jpg: "image/jpeg",
|
|
522
|
+
jpeg: "image/jpeg",
|
|
523
|
+
png: "image/png"
|
|
524
|
+
};
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Vision Processing
|
|
528
|
+
|
|
529
|
+
For images and PDFs with vision enabled:
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
await client.uploadFile({
|
|
533
|
+
key: "documents/invoice.pdf",
|
|
534
|
+
content: pdfBuffer,
|
|
535
|
+
contentType: "application/pdf",
|
|
536
|
+
index: true,
|
|
537
|
+
indexing: {
|
|
538
|
+
configuration: {
|
|
539
|
+
vision: {
|
|
540
|
+
transcribePages: true, // OCR text extraction
|
|
541
|
+
indexPages: "all" // or [1, 2, 3] for specific pages
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Usage in Actions/Workflows
|
|
549
|
+
|
|
550
|
+
### In Actions
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
import { Action, z, context } from "@botpress/runtime";
|
|
554
|
+
|
|
555
|
+
export const uploadDocument = new Action({
|
|
556
|
+
name: "uploadDocument",
|
|
557
|
+
input: z.object({
|
|
558
|
+
content: z.string(),
|
|
559
|
+
filename: z.string()
|
|
560
|
+
}),
|
|
561
|
+
output: z.object({
|
|
562
|
+
fileId: z.string(),
|
|
563
|
+
fileUrl: z.string()
|
|
564
|
+
}),
|
|
565
|
+
|
|
566
|
+
async handler({ input }) {
|
|
567
|
+
const client = context.get("client");
|
|
568
|
+
|
|
569
|
+
// Upload file
|
|
570
|
+
const { file } = await client.uploadFile({
|
|
571
|
+
key: `uploads/${input.filename}`,
|
|
572
|
+
content: input.content,
|
|
573
|
+
contentType: "text/plain",
|
|
574
|
+
index: true,
|
|
575
|
+
tags: {
|
|
576
|
+
source: "action",
|
|
577
|
+
type: "user-upload"
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
return {
|
|
582
|
+
fileId: file.id,
|
|
583
|
+
fileUrl: file.url
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### In Workflows
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
import { Workflow, z, context } from "@botpress/runtime";
|
|
593
|
+
|
|
594
|
+
export const ProcessDocumentWorkflow = new Workflow({
|
|
595
|
+
name: "processDocument",
|
|
596
|
+
input: z.object({ documentKey: z.string() }),
|
|
597
|
+
|
|
598
|
+
async handler({ input, step }) {
|
|
599
|
+
const client = context.get("client");
|
|
600
|
+
|
|
601
|
+
// Get file
|
|
602
|
+
const file = await step("get-file", async () => {
|
|
603
|
+
const { file } = await client.getFile({
|
|
604
|
+
id: input.documentKey
|
|
605
|
+
});
|
|
606
|
+
return file;
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Wait for indexing
|
|
610
|
+
await step("wait-indexing", async () => {
|
|
611
|
+
let status = file.status;
|
|
612
|
+
while (status === "INDEXING_PENDING") {
|
|
613
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
614
|
+
const { file: updated } = await client.getFile({
|
|
615
|
+
id: file.id
|
|
616
|
+
});
|
|
617
|
+
status = updated.status;
|
|
618
|
+
}
|
|
619
|
+
return status;
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// Search content
|
|
623
|
+
const results = await step("search", async () => {
|
|
624
|
+
const { passages } = await client.searchFiles({
|
|
625
|
+
query: "important findings",
|
|
626
|
+
tags: { key: input.documentKey }
|
|
627
|
+
});
|
|
628
|
+
return passages;
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
return {
|
|
632
|
+
processed: true,
|
|
633
|
+
findings: results.length
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
## Best Practices
|
|
640
|
+
|
|
641
|
+
### 1. Use Meaningful Keys
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
// ✅ Good - Clear, hierarchical structure
|
|
645
|
+
await client.uploadFile({
|
|
646
|
+
key: "documents/2024/reports/q1-sales.pdf"
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// ❌ Bad - Unclear, no structure
|
|
650
|
+
await client.uploadFile({
|
|
651
|
+
key: "file123.pdf"
|
|
652
|
+
});
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### 2. Tag Appropriately
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
// ✅ Good - Useful, queryable tags
|
|
659
|
+
tags: {
|
|
660
|
+
category: "reports",
|
|
661
|
+
year: "2024",
|
|
662
|
+
quarter: "Q1",
|
|
663
|
+
department: "sales"
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// ❌ Bad - Too specific or redundant
|
|
667
|
+
tags: {
|
|
668
|
+
filename: "q1-sales.pdf", // Already in key
|
|
669
|
+
timestamp: "1234567890" // Use createdAt instead
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### 3. Handle File Status
|
|
674
|
+
|
|
675
|
+
```typescript
|
|
676
|
+
// ✅ Good - Check and handle status
|
|
677
|
+
const { file } = await client.getFile({ id: fileId });
|
|
678
|
+
|
|
679
|
+
switch (file.status) {
|
|
680
|
+
case "UPLOAD_PENDING":
|
|
681
|
+
console.log("Waiting for upload...");
|
|
682
|
+
break;
|
|
683
|
+
case "INDEXING_PENDING":
|
|
684
|
+
console.log("Indexing in progress...");
|
|
685
|
+
break;
|
|
686
|
+
case "INDEXING_COMPLETED":
|
|
687
|
+
// File is searchable
|
|
688
|
+
break;
|
|
689
|
+
case "INDEXING_FAILED":
|
|
690
|
+
console.error("Indexing failed:", file.failedStatusReason);
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### 4. Set Appropriate Expiry
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
// ✅ Good - Reasonable expiry for temporary files
|
|
699
|
+
await client.uploadFile({
|
|
700
|
+
key: "temp/cache-data.json",
|
|
701
|
+
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString() // 1 day
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
// For permanent files, omit expiresAt
|
|
705
|
+
await client.uploadFile({
|
|
706
|
+
key: "documents/permanent-record.pdf"
|
|
707
|
+
// No expiresAt = never expires
|
|
708
|
+
});
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### 5. Optimize Indexing
|
|
712
|
+
|
|
713
|
+
```typescript
|
|
714
|
+
// ✅ Good - Index only searchable content
|
|
715
|
+
await client.uploadFile({
|
|
716
|
+
key: "documents/report.pdf",
|
|
717
|
+
content: pdfBuffer,
|
|
718
|
+
index: true // Searchable document
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
await client.uploadFile({
|
|
722
|
+
key: "images/logo.png",
|
|
723
|
+
content: imageBuffer,
|
|
724
|
+
index: false // Not searchable, save quota
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// ✅ Good - Use vision only when needed
|
|
728
|
+
await client.uploadFile({
|
|
729
|
+
key: "documents/scanned-invoice.pdf",
|
|
730
|
+
content: pdfBuffer,
|
|
731
|
+
index: true,
|
|
732
|
+
indexing: {
|
|
733
|
+
configuration: {
|
|
734
|
+
vision: {
|
|
735
|
+
transcribePages: true // OCR needed
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
## Common Patterns
|
|
743
|
+
|
|
744
|
+
### File Upload with Validation
|
|
745
|
+
|
|
746
|
+
```typescript
|
|
747
|
+
export const uploadWithValidation = async (
|
|
748
|
+
fileContent: Buffer,
|
|
749
|
+
filename: string
|
|
750
|
+
) => {
|
|
751
|
+
const client = context.get("client");
|
|
752
|
+
|
|
753
|
+
// Validate file size (max 100MB)
|
|
754
|
+
const MAX_SIZE = 100 * 1024 * 1024;
|
|
755
|
+
if (fileContent.length > MAX_SIZE) {
|
|
756
|
+
throw new Error("File too large");
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Validate file type
|
|
760
|
+
const contentType = getContentType(filename);
|
|
761
|
+
if (!ALLOWED_TYPES.includes(contentType)) {
|
|
762
|
+
throw new Error("File type not allowed");
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Upload
|
|
766
|
+
const { file } = await client.uploadFile({
|
|
767
|
+
key: `uploads/${Date.now()}-${filename}`,
|
|
768
|
+
content: fileContent,
|
|
769
|
+
contentType,
|
|
770
|
+
index: true,
|
|
771
|
+
tags: {
|
|
772
|
+
originalName: filename,
|
|
773
|
+
uploadedAt: new Date().toISOString()
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
return file;
|
|
778
|
+
};
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Search with Context
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
export const searchWithContext = async (
|
|
785
|
+
query: string,
|
|
786
|
+
category?: string
|
|
787
|
+
) => {
|
|
788
|
+
const client = context.get("client");
|
|
789
|
+
|
|
790
|
+
const { passages } = await client.searchFiles({
|
|
791
|
+
query,
|
|
792
|
+
tags: category ? { category } : undefined,
|
|
793
|
+
contextDepth: 2,
|
|
794
|
+
includeBreadcrumb: true,
|
|
795
|
+
limit: 5
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
return passages.map(p => ({
|
|
799
|
+
content: p.content,
|
|
800
|
+
source: p.file.key,
|
|
801
|
+
relevance: p.score,
|
|
802
|
+
location: `Page ${p.meta.pageNumber || "N/A"}`
|
|
803
|
+
}));
|
|
804
|
+
};
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
### Bulk File Operations
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
export const bulkUpload = async (files: FileData[]) => {
|
|
811
|
+
const client = context.get("client");
|
|
812
|
+
|
|
813
|
+
const results = await Promise.allSettled(
|
|
814
|
+
files.map(async (fileData) => {
|
|
815
|
+
const { file } = await client.uploadFile({
|
|
816
|
+
key: fileData.key,
|
|
817
|
+
content: fileData.content,
|
|
818
|
+
contentType: fileData.contentType,
|
|
819
|
+
index: true
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
return file;
|
|
823
|
+
})
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
const successful = results
|
|
827
|
+
.filter(r => r.status === "fulfilled")
|
|
828
|
+
.map(r => r.value);
|
|
829
|
+
|
|
830
|
+
const failed = results
|
|
831
|
+
.filter(r => r.status === "rejected")
|
|
832
|
+
.map(r => r.reason);
|
|
833
|
+
|
|
834
|
+
return { successful, failed };
|
|
835
|
+
};
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
## Troubleshooting
|
|
839
|
+
|
|
840
|
+
### Common Issues
|
|
841
|
+
|
|
842
|
+
1. **Upload fails with 403 error**
|
|
843
|
+
- Ensure you have proper access policies set
|
|
844
|
+
- Check that the bot token has file upload permissions
|
|
845
|
+
- Verify the file size is within limits
|
|
846
|
+
|
|
847
|
+
2. **File not searchable**
|
|
848
|
+
- Check `file.status` is `INDEXING_COMPLETED`
|
|
849
|
+
- Verify `index: true` was set during upload
|
|
850
|
+
- Ensure file type is in supported indexable formats
|
|
851
|
+
- Check billing plan supports indexing features
|
|
852
|
+
|
|
853
|
+
3. **Search returns no results**
|
|
854
|
+
- Verify files are indexed (`status: "INDEXING_COMPLETED"`)
|
|
855
|
+
- Check tag filters aren't too restrictive
|
|
856
|
+
- Try broader search queries
|
|
857
|
+
- Ensure files have searchable content
|
|
858
|
+
|
|
859
|
+
4. **File access denied**
|
|
860
|
+
- Verify access policies are correctly set
|
|
861
|
+
- Check workspace member permissions
|
|
862
|
+
- For integration files, ensure proper namespace
|
|
863
|
+
|
|
864
|
+
5. **Quota exceeded**
|
|
865
|
+
- Check storage usage in workspace settings
|
|
866
|
+
- Delete unused files or increase quota
|
|
867
|
+
- Consider file expiry policies for temporary files
|
|
868
|
+
|
|
869
|
+
## Limits and Quotas
|
|
870
|
+
|
|
871
|
+
- **Maximum file size**: 100 MB
|
|
872
|
+
- **Maximum expiry**: 90 days from upload
|
|
873
|
+
- **Maximum tags per file**: 50
|
|
874
|
+
- **Maximum indexable file size**: varies by file type
|
|
875
|
+
- **Search result limit**: Configurable, recommended max 100
|
|
876
|
+
|
|
877
|
+
## Migration Notes
|
|
878
|
+
|
|
879
|
+
### Current API (ADK 1.13+)
|
|
880
|
+
|
|
881
|
+
The current Files API uses direct content upload:
|
|
882
|
+
|
|
883
|
+
```typescript
|
|
884
|
+
// ✅ CURRENT - Direct content parameter
|
|
885
|
+
await client.uploadFile({
|
|
886
|
+
key: "unique-key",
|
|
887
|
+
content: fileBuffer, // Direct upload
|
|
888
|
+
contentType: "application/pdf",
|
|
889
|
+
tags: { category: "docs" }
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// Alternative: Upload from URL
|
|
893
|
+
await client.uploadFile({
|
|
894
|
+
key: "unique-key",
|
|
895
|
+
url: "https://example.com/file.pdf", // Fetch from URL
|
|
896
|
+
tags: { category: "docs" }
|
|
897
|
+
});
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
### Retrieving File Content
|
|
901
|
+
|
|
902
|
+
Files are accessed via their URL:
|
|
903
|
+
|
|
904
|
+
```typescript
|
|
905
|
+
const { file } = await client.getFile({ id: fileId });
|
|
906
|
+
|
|
907
|
+
// Fetch file content
|
|
908
|
+
const response = await fetch(file.url);
|
|
909
|
+
const content = await response.arrayBuffer();
|
|
910
|
+
|
|
911
|
+
// For text/JSON
|
|
912
|
+
const text = await response.text();
|
|
913
|
+
const json = await response.json();
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
**URL Types:**
|
|
917
|
+
- **Public files** (`public_content` policy): Permanent CDN URL
|
|
918
|
+
- **Private files**: Temporary presigned URL (use shortly after retrieval)
|