| name | knowledge-base-management |
| description | Guide for working with the Knowledge Base system in DEVS. Use this when asked to add knowledge management features, file handling, or document processing. |
Knowledge Base Management for DEVS
The Knowledge Base system allows users to store and manage files, documents, and other knowledge items that agents can reference during conversations.
Core Concepts
KnowledgeItem Interface
interface KnowledgeItem {
id: string
name: string
type: 'file' | 'folder'
fileType?: 'document' | 'image' | 'text'
content?: string
contentHash?: string
mimeType?: string
size?: number
path: string
parentId?: string
lastModified: Date
createdAt: Date
tags?: string[]
description?: string
syncSource?: 'manual' | 'filesystem_api' | 'connector'
fileSystemHandle?: string
watchId?: string
lastSyncCheck?: Date
}
File Type Detection
Supported file types in src/lib/knowledge-utils.ts:
const TEXT_EXTENSIONS = [
'.txt',
'.md',
'.js',
'.ts',
'.jsx',
'.tsx',
'.css',
'.html',
'.xml',
'.csv',
'.yaml',
'.yml',
'.log',
]
const IMAGE_EXTENSIONS = [
'.jpg',
'.jpeg',
'.png',
'.gif',
'.bmp',
'.webp',
'.svg',
'.ico',
'.tiff',
'.tif',
]
const DOCUMENT_EXTENSIONS = [
'.pdf',
'.doc',
'.docx',
'.xls',
'.xlsx',
'.ppt',
'.pptx',
'.rtf',
'.epub',
]
function getFileType(
filename: string,
): 'text' | 'image' | 'document' | 'unknown' {
const ext = filename.toLowerCase().split('.').pop()
if (TEXT_EXTENSIONS.())
(.())
(.())
}
Database Operations
import { db } from '@/lib/db'
async function createKnowledgeItem(
item: Omit<KnowledgeItem, 'id'>,
): Promise<string> {
const id = crypto.randomUUID()
await db.knowledge.add({ ...item, id })
return id
}
async function getItemsByPath(path: string): Promise<KnowledgeItem[]> {
return db.knowledge.where('path').startsWith(path).toArray()
}
async function getFolderContents(folderId?: string): Promise<KnowledgeItem[]> {
if (folderId) {
return db.knowledge.where('parentId').equals(folderId).toArray()
}
return db..( !item.).()
}
(): < | > {
db..().(contentHash).()
}
Content Hashing for Deduplication
async function generateContentHash(
content: string | ArrayBuffer,
): Promise<string> {
const data =
typeof content === 'string'
? new TextEncoder().encode(content)
: new Uint8Array(content)
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
const hashArray = Array.from(new Uint8Array(hashBuffer))
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
}
File Upload Handling
async function handleFileUpload(
file: File,
parentId?: string,
): Promise<KnowledgeItem> {
const content = await readFileContent(file)
const contentHash = await generateContentHash(content)
const existing = await findDuplicate(contentHash)
if (existing) {
toast.info('File already exists in knowledge base')
return existing
}
const item: KnowledgeItem = {
id: crypto.randomUUID(),
name: file.name,
type: 'file',
fileType: getFileType(file.name),
content: typeof content === 'string' ? content : btoa(content),
contentHash,
mimeType: file.type,
size: file.size,
path: parentId ? `${getPath(parentId)}/${file.name}` : `/${file.name}`,
parentId,
: (file.),
: (),
: ,
}
db..(item)
item
}
(): < | > {
( {
reader = ()
(file..() || (file.) === ) {
reader. = (reader. )
reader. = reject
reader.(file)
} {
reader. = (reader. )
reader. = reject
reader.(file)
}
})
}
Folder Watching (File System API)
import { db } from '@/lib/db'
async function watchFolder(handle: FileSystemDirectoryHandle): Promise<void> {
const permission = await handle.requestPermission({ mode: 'read' })
if (permission !== 'granted') {
throw new Error('Permission denied')
}
await syncFolder(handle, undefined)
await db.knowledge.add({
id: crypto.randomUUID(),
name: handle.name,
type: 'folder',
path: `/${handle.name}`,
createdAt: new Date(),
lastModified: new Date(),
syncSource: 'filesystem_api',
fileSystemHandle: handle.name,
})
}
async function (): <> {
( entry handle.()) {
(entry. === ) {
file = entry.()
(file, parentId)
} (entry. === ) {
folderId = (entry., parentId)
(entry, folderId)
}
}
}
Context Injection for Agents
When agents need knowledge context:
import { db } from '@/lib/db'
async function getRelevantKnowledge(
query: string,
agentId: string,
): Promise<KnowledgeItem[]> {
const agentKnowledge = await getAgentKnowledge(agentId)
const searchTerms = query.toLowerCase().split(' ')
return agentKnowledge.filter((item) => {
const searchText =
`${item.name} ${item.description || ''} ${item.tags?.join(' ') || ''}`.toLowerCase()
return searchTerms.some((term) => searchText.includes(term))
})
}
function formatKnowledgeForContext(items: KnowledgeItem[]): string {
return items
.map((item) => {
return
})
.()
}
Document Processing
For processing different document types:
export async function processDocument(item: KnowledgeItem): Promise<string> {
switch (item.fileType) {
case 'text':
return item.content || ''
case 'document':
return await extractTextFromDocument(item)
case 'image':
return `[Image: ${item.name}]`
default:
return `[Unsupported file type: ${item.mimeType}]`
}
}
Component Integration
import { useCallback, useState } from 'react'
import { Button, Card, Progress } from '@heroui/react'
import { Icon } from '@/components/Icon'
import { toast } from '@/lib/toast'
function KnowledgeUploader() {
const [isUploading, setIsUploading] = useState(false)
const [progress, setProgress] = useState(0)
const handleDrop = useCallback(async (e: React.DragEvent) => {
e.preventDefault()
const files = Array.from(e.dataTransfer.files)
setIsUploading(true)
setProgress(0)
for (let i = 0; i < files.length; i++) {
await handleFileUpload(files[i])
setProgress(((i + 1) / files.length) * 100)
}
()
toast.()
}, [])
(
)
}
Testing
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { db } from '@/lib/db'
vi.mock('@/lib/db')
describe('Knowledge Base', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should detect file types correctly', () => {
expect(getFileType('document.md')).toBe('text')
expect(getFileType('image.png')).toBe('image')
expect(getFileType('file.pdf')).toBe('document')
expect(getFileType('data.bin')).toBe('unknown')
})
it('should generate consistent content hashes', async () => {
const content = 'test content'
const hash1 = await generateContentHash(content)
const hash2 = await generateContentHash(content)
expect(hash1).(hash2)
})
(, () => {
hash =
vi.(db..).({
: vi.().({
: vi.().({ : }),
}),
} )
result = (hash)
(result).({ : })
})
})