@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera
243 lines (242 loc) • 7.56 kB
JavaScript
class MCPContentProcessor {
constructor(contentStorage, logger) {
this.contentStorage = contentStorage;
this.logger = logger;
}
analyzeResponse(response) {
const contents = [];
let totalSize = 0;
this.extractContentFromResponse(response, contents);
totalSize = contents.reduce((sum, content) => sum + content.sizeBytes, 0);
const largestContentSize = contents.reduce((max, content) => Math.max(max, content.sizeBytes), 0);
const shouldProcess = contents.some(
(content) => this.contentStorage.shouldUseReference(
typeof content.content === "string" ? content.content : JSON.stringify(content.content)
)
);
return {
shouldProcess,
contents,
totalSize,
largestContentSize
};
}
async processResponse(response, serverName, toolName) {
try {
const analysis = this.analyzeResponse(response);
if (!analysis.shouldProcess) {
return {
content: response,
wasProcessed: false
};
}
const processedResponse = await this.createReferencedResponse(
response,
analysis,
serverName,
toolName
);
return processedResponse;
} catch (error) {
this.logger.error("Error processing MCP response:", error);
return {
content: response,
wasProcessed: false,
errors: [error instanceof Error ? error.message : "Unknown processing error"]
};
}
}
extractContentFromResponse(obj, contents) {
if (obj === null || obj === void 0) {
return;
}
if (Array.isArray(obj)) {
obj.forEach((item) => this.extractContentFromResponse(item, contents));
return;
}
if (typeof obj === "object") {
const record = obj;
if (record.type === "text" && typeof record.text === "string") {
contents.push({
content: record.text,
type: "text",
sizeBytes: Buffer.byteLength(record.text, "utf8"),
mimeType: this.detectMimeType(record.text)
});
return;
}
if (record.type === "image" && typeof record.data === "string") {
contents.push({
content: record.data,
type: "image",
sizeBytes: Math.ceil(record.data.length * 0.75),
mimeType: record.mimeType || "image/jpeg"
});
return;
}
if (record.type === "resource" && record.resource) {
const resourceStr = JSON.stringify(record.resource);
contents.push({
content: resourceStr,
type: "resource",
sizeBytes: Buffer.byteLength(resourceStr, "utf8"),
mimeType: "application/json"
});
return;
}
Object.values(record).forEach((value) => this.extractContentFromResponse(value, contents));
return;
}
if (typeof obj === "string") {
if (obj.length > 1e3) {
contents.push({
content: obj,
type: "text",
sizeBytes: Buffer.byteLength(obj, "utf8"),
mimeType: this.detectMimeType(obj)
});
}
}
}
async createReferencedResponse(originalResponse, analysis, serverName, toolName) {
const processedResponse = this.deepClone(originalResponse);
const errors = [];
let referenceCreated = false;
let totalReferenceSize = 0;
for (const contentInfo of analysis.contents) {
if (this.contentStorage.shouldUseReference(
typeof contentInfo.content === "string" ? contentInfo.content : JSON.stringify(contentInfo.content)
)) {
try {
const contentBuffer = Buffer.from(
typeof contentInfo.content === "string" ? contentInfo.content : JSON.stringify(contentInfo.content),
"utf8"
);
const contentType = this.mapMimeTypeToContentType(contentInfo.mimeType);
const metadata = {
contentType,
source: "mcp_tool",
mcpToolName: `${serverName}::${toolName}`,
tags: ["mcp_response", serverName, toolName]
};
if (contentInfo.mimeType !== void 0) {
metadata.mimeType = contentInfo.mimeType;
}
const reference = await this.contentStorage.storeContentIfLarge(
contentBuffer,
metadata
);
if (reference) {
this.replaceContentInResponse(
processedResponse,
contentInfo.content,
this.createLightweightReference(reference)
);
referenceCreated = true;
totalReferenceSize += contentBuffer.length;
}
} catch (error) {
errors.push(`Failed to create reference: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
}
const result = {
content: processedResponse,
wasProcessed: true,
referenceCreated,
originalSize: totalReferenceSize
};
if (errors.length > 0) {
result.errors = errors;
}
return result;
}
createLightweightReference(reference) {
return {
type: "content_reference",
referenceId: reference.referenceId,
preview: reference.preview,
size: reference.metadata.sizeBytes,
contentType: reference.metadata.contentType,
format: "ref://{id}",
_isReference: true
};
}
replaceContentInResponse(obj, oldContent, newContent) {
if (obj === null || obj === void 0) {
return;
}
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
if (obj[i] === oldContent) {
obj[i] = newContent;
} else {
this.replaceContentInResponse(obj[i], oldContent, newContent);
}
}
return;
}
if (typeof obj === "object") {
const record = obj;
if (record.type === "text" && record.text === oldContent) {
for (const key of Object.keys(record)) {
delete record[key];
}
for (const key of Object.keys(newContent)) {
record[key] = newContent[key];
}
return;
}
for (const key in record) {
if (record[key] === oldContent) {
record[key] = newContent;
} else {
this.replaceContentInResponse(record[key], oldContent, newContent);
}
}
}
}
detectMimeType(content) {
if (content.trim().startsWith("{") || content.trim().startsWith("[")) {
return "application/json";
}
if (content.includes("<html>") || content.includes("<!DOCTYPE")) {
return "text/html";
}
if (content.includes("# ") || content.includes("## ")) {
return "text/markdown";
}
return "text/plain";
}
mapMimeTypeToContentType(mimeType) {
if (!mimeType) return "text";
if (mimeType.startsWith("text/plain")) return "text";
if (mimeType === "application/json") return "json";
if (mimeType === "text/html") return "html";
if (mimeType === "text/markdown") return "markdown";
if (mimeType.startsWith("text/")) return "text";
return "binary";
}
deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (Array.isArray(obj)) {
return obj.map((item) => this.deepClone(item));
}
const cloned = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = this.deepClone(obj[key]);
}
}
return cloned;
}
}
export {
MCPContentProcessor
};
//# sourceMappingURL=index27.js.map