adk-typescript
Version:
TypeScript port of Google's Agent Development Kit (ADK)
155 lines (154 loc) • 6.21 kB
JavaScript
;
/**
* Utility functions for code execution.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeExecutionUtils = void 0;
/**
* Utility functions for code execution.
*/
class CodeExecutionUtils {
/**
* Gets the file content as a base64-encoded string.
*
* @param data - The file content bytes.
* @returns The file content as a base64-encoded string.
*/
static getEncodedFileContent(data) {
const isBase64Encoded = (data) => {
try {
const decoded = Buffer.from(data.toString(), 'base64');
const reEncoded = Buffer.from(decoded).toString('base64');
return reEncoded === data.toString();
}
catch (error) {
return false;
}
};
return isBase64Encoded(data)
? data.toString()
: Buffer.from(data).toString('base64');
}
/**
* Extracts the first code block from the content and truncate everything after it.
*
* @param content - The mutable content to extract the code from.
* @param codeBlockDelimiters - The list of the enclosing delimiters to identify the code blocks.
* @returns The first code block if found, otherwise undefined.
*/
static extractCodeAndTruncateContent(content, codeBlockDelimiters) {
if (!content || !content.parts || content.parts.length === 0) {
return undefined;
}
// Extract the code from the executable code parts if there're no associated
// code execution result parts.
for (let idx = 0; idx < content.parts.length; idx++) {
const part = content.parts[idx];
if (part.executableCode &&
(idx === content.parts.length - 1 || !content.parts[idx + 1].codeExecutionResult)) {
content.parts = content.parts.slice(0, idx + 1);
return part.executableCode.code;
}
}
// Extract the code from the text parts.
const textParts = content.parts.filter(p => p.text !== undefined);
if (textParts.length === 0) {
return undefined;
}
const firstTextPart = { ...textParts[0] };
const responseText = textParts.map(p => p.text).join('\n');
// Find the first code block.
const leadingDelimiterPattern = codeBlockDelimiters.map(d => d[0]).join('|');
const trailingDelimiterPattern = codeBlockDelimiters.map(d => d[1]).join('|');
const pattern = new RegExp(`(?<prefix>.*?)(${leadingDelimiterPattern})(?<code>.*?)(${trailingDelimiterPattern})(?<suffix>.*?)$`, 's');
const patternMatch = pattern.exec(responseText);
if (!patternMatch || !patternMatch.groups) {
return undefined;
}
const codeStr = patternMatch.groups.code;
if (!codeStr) {
return undefined;
}
content.parts = [];
if (patternMatch.groups.prefix) {
firstTextPart.text = patternMatch.groups.prefix;
content.parts.push(firstTextPart);
}
content.parts.push(CodeExecutionUtils.buildExecutableCodePart(codeStr));
return codeStr;
}
/**
* Builds an executable code part with code string.
*
* @param code - The code string.
* @returns The constructed executable code part.
*/
static buildExecutableCodePart(code) {
return {
executableCode: {
code,
language: 'PYTHON',
},
};
}
/**
* Builds the code execution result part from the code execution result.
*
* @param codeExecutionResult - The code execution result.
* @returns The constructed code execution result part.
*/
static buildCodeExecutionResultPart(codeExecutionResult) {
if (codeExecutionResult.stderr) {
return {
codeExecutionResult: {
outcome: 'OUTCOME_FAILED',
output: codeExecutionResult.stderr,
},
};
}
const finalResult = [];
if (codeExecutionResult.stdout || codeExecutionResult.outputFiles.length === 0) {
finalResult.push(`Code execution result:\n${codeExecutionResult.stdout}\n`);
}
if (codeExecutionResult.outputFiles.length > 0) {
finalResult.push(`Saved artifacts:\n${codeExecutionResult.outputFiles.map(f => `\`${f.name}\``).join(',')}`);
}
return {
codeExecutionResult: {
outcome: 'OUTCOME_OK',
output: finalResult.join('\n\n'),
},
};
}
/**
* Converts the code execution parts to text parts in a Content.
*
* @param content - The mutable content to convert the code execution parts to text parts.
* @param codeBlockDelimiter - The delimiter to format the code block.
* @param executionResultDelimiters - The delimiter to format the code execution result.
*/
static convertCodeExecutionParts(content, codeBlockDelimiter, executionResultDelimiters) {
if (!content.parts || content.parts.length === 0) {
return;
}
// Handle the conversion of trailing executable code parts.
const lastPart = content.parts[content.parts.length - 1];
if (lastPart.executableCode) {
content.parts[content.parts.length - 1] = {
text: codeBlockDelimiter[0] + lastPart.executableCode.code + codeBlockDelimiter[1],
};
}
// Handle the conversion of trailing code execution result parts.
// Skip if the Content has multiple parts, which means the Content is
// likely generated by the model.
else if (content.parts.length === 1 && lastPart.codeExecutionResult) {
content.parts[content.parts.length - 1] = {
text: executionResultDelimiters[0] +
lastPart.codeExecutionResult.output +
executionResultDelimiters[1],
};
content.role = 'user';
}
}
}
exports.CodeExecutionUtils = CodeExecutionUtils;