Skip to content

Commit 3b928c4

Browse files
authored
Merge branch 'v2-develop' into minor-log-api-disp-fix
2 parents d50dd95 + a4e3b51 commit 3b928c4

File tree

7 files changed

+827
-113
lines changed

7 files changed

+827
-113
lines changed

packages/cli/src/server/api/agent.ts

+152-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { Agent, Character, Content, IAgentRuntime, Memory, UUID } from '@elizaos/core';
22
import {
33
ChannelType,
4-
EventType,
54
ModelType,
65
composePrompt,
76
composePromptFromState,
87
createUniqueUuid,
98
logger,
109
messageHandlerTemplate,
1110
validateUuid,
11+
MemoryType,
1212
} from '@elizaos/core';
1313
import express from 'express';
1414
import fs from 'node:fs';
@@ -21,13 +21,17 @@ import { upload } from '../loader';
2121
* @interface CustomRequest
2222
* @extends express.Request
2323
* @property {Express.Multer.File} [file] - Optional property representing a file uploaded with the request
24+
* @property {Express.Multer.File[]} [files] - Optional property representing multiple files uploaded with the request
2425
* @property {Object} params - Object representing parameters included in the request
2526
* @property {string} params.agentId - The unique identifier for the agent associated with the request
27+
* @property {string} [params.knowledgeId] - Optional knowledge ID parameter
2628
*/
2729
interface CustomRequest extends express.Request {
2830
file?: Express.Multer.File;
31+
files?: Express.Multer.File[];
2932
params: {
3033
agentId: string;
34+
knowledgeId?: string;
3135
};
3236
}
3337

@@ -1403,9 +1407,12 @@ export function agentRouter(
14031407
return;
14041408
}
14051409

1410+
// Get tableName from query params, default to "messages"
1411+
const tableName = (req.query.tableName as string) || 'messages';
1412+
14061413
const memories = await runtime.getMemories({
14071414
agentId,
1408-
tableName: 'messages',
1415+
tableName,
14091416
});
14101417

14111418
const cleanMemories = memories.map((memory) => {
@@ -1611,5 +1618,148 @@ export function agentRouter(
16111618
}
16121619
});
16131620

1621+
// Knowledge management routes
1622+
router.post('/:agentId/memories/upload-knowledge', upload.array('files'), async (req, res) => {
1623+
const agentId = validateUuid(req.params.agentId);
1624+
1625+
if (!agentId) {
1626+
res.status(400).json({
1627+
success: false,
1628+
error: {
1629+
code: 'INVALID_ID',
1630+
message: 'Invalid agent ID format',
1631+
},
1632+
});
1633+
return;
1634+
}
1635+
1636+
const runtime = agents.get(agentId);
1637+
1638+
if (!runtime) {
1639+
res.status(404).json({
1640+
success: false,
1641+
error: {
1642+
code: 'NOT_FOUND',
1643+
message: 'Agent not found',
1644+
},
1645+
});
1646+
return;
1647+
}
1648+
1649+
const files = req.files as Express.Multer.File[];
1650+
1651+
if (!files || files.length === 0) {
1652+
res.status(400).json({
1653+
success: false,
1654+
error: {
1655+
code: 'NO_FILES',
1656+
message: 'No files uploaded',
1657+
},
1658+
});
1659+
return;
1660+
}
1661+
1662+
try {
1663+
const results = [];
1664+
1665+
for (const file of files) {
1666+
try {
1667+
// Read file content
1668+
const content = fs.readFileSync(file.path, 'utf8');
1669+
1670+
// Format the content with Path: prefix like in the devRel/index.ts example
1671+
const relativePath = file.originalname;
1672+
const formattedContent = `Path: ${relativePath}\n\n${content}`;
1673+
1674+
// Create knowledge item with proper metadata
1675+
const knowledgeId = createUniqueUuid(runtime, `knowledge-${Date.now()}`);
1676+
const fileExt = file.originalname.split('.').pop()?.toLowerCase() || '';
1677+
const filename = file.originalname;
1678+
const title = filename.replace(`.${fileExt}`, '');
1679+
1680+
const knowledgeItem = {
1681+
id: knowledgeId,
1682+
content: {
1683+
text: formattedContent,
1684+
},
1685+
metadata: {
1686+
type: MemoryType.DOCUMENT,
1687+
timestamp: Date.now(),
1688+
filename: filename,
1689+
fileExt: fileExt,
1690+
title: title,
1691+
path: relativePath,
1692+
fileType: file.mimetype,
1693+
fileSize: file.size,
1694+
source: 'upload',
1695+
},
1696+
};
1697+
1698+
// Add knowledge to agent
1699+
await runtime.addKnowledge(knowledgeItem, {
1700+
targetTokens: 3000,
1701+
overlap: 200,
1702+
modelContextSize: 4096,
1703+
});
1704+
1705+
// Clean up temp file immediately after successful processing
1706+
if (file.path && fs.existsSync(file.path)) {
1707+
fs.unlinkSync(file.path);
1708+
}
1709+
1710+
results.push({
1711+
id: knowledgeId,
1712+
filename: relativePath,
1713+
type: file.mimetype,
1714+
size: file.size,
1715+
uploadedAt: Date.now(),
1716+
preview:
1717+
formattedContent.length > 0
1718+
? `${formattedContent.substring(0, 150)}${formattedContent.length > 150 ? '...' : ''}`
1719+
: 'No preview available',
1720+
});
1721+
} catch (fileError) {
1722+
logger.error(`[KNOWLEDGE POST] Error processing file ${file.originalname}: ${fileError}`);
1723+
// Clean up this file if it exists
1724+
if (file.path && fs.existsSync(file.path)) {
1725+
fs.unlinkSync(file.path);
1726+
}
1727+
// Continue with other files even if one fails
1728+
}
1729+
}
1730+
1731+
res.json({
1732+
success: true,
1733+
data: results,
1734+
});
1735+
} catch (error) {
1736+
logger.error(`[KNOWLEDGE POST] Error uploading knowledge: ${error}`);
1737+
1738+
// Clean up any remaining files
1739+
if (files) {
1740+
for (const file of files) {
1741+
if (file.path && fs.existsSync(file.path)) {
1742+
try {
1743+
fs.unlinkSync(file.path);
1744+
} catch (cleanupError) {
1745+
logger.error(
1746+
`[KNOWLEDGE POST] Error cleaning up file ${file.originalname}: ${cleanupError}`
1747+
);
1748+
}
1749+
}
1750+
}
1751+
}
1752+
1753+
res.status(500).json({
1754+
success: false,
1755+
error: {
1756+
code: 500,
1757+
message: 'Failed to upload knowledge',
1758+
details: error.message,
1759+
},
1760+
});
1761+
}
1762+
});
1763+
16141764
return router;
16151765
}

0 commit comments

Comments
 (0)