2e104c8e76
## Summary - Add code interpreter tool that enables AI to execute Python code for data analysis, CSV processing, and chart generation - Support for both local (development) and E2B (sandboxed production) execution drivers - Real-time streaming of stdout/stderr and generated files - Frontend components for displaying code execution results with expandable sections ## Code Quality Improvements - Extract `getMimeType` to shared utility to reduce code duplication between drivers - Fix security issue: escape single quotes/backslashes in E2B driver env variable injection - Add `buildExecutionState` helper to reduce duplicated state object construction - Add `DEFAULT_CODE_INTERPRETER_TIMEOUT_MS` constant for consistency - Fix lingui linting warning and TypeScript theme errors in frontend ## Test Plan - [ ] Test code interpreter with local driver in development - [ ] Test code interpreter with E2B driver in production environment - [ ] Verify streaming output displays correctly in chat UI - [ ] Verify generated files (charts, CSVs) are uploaded and downloadable - [ ] Test file upload flow (CSV, Excel) triggers code interpreter <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Updates generated i18n catalogs for Polish and pseudo-English, adding strings for code execution/output (code interpreter) and various UI messages, with minor text adjustments. > > - **Localization**: > - **Generated catalogs**: Refresh `locales/generated/pl-PL.ts` and `locales/generated/pseudo-en.ts`. > - Add strings for code execution/output (e.g., code, copy code/output, running/waiting states, download files, generated files, Python code execution). > - Include new UI texts (errors, prompts, menus) and minor text corrections. > - No changes to `pt-BR`; other files unchanged functionally. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit befc13d02c21e5a6647bc1aa6daa2a89f60b7ef8. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import { type ToolUIPart } from 'ai';
|
|
import { type ExtendedUIMessagePart } from 'twenty-shared/ai';
|
|
|
|
import { type AgentMessagePartEntity } from 'src/engine/metadata-modules/ai/ai-agent-execution/entities/agent-message-part.entity';
|
|
|
|
const isToolPart = (part: ExtendedUIMessagePart): part is ToolUIPart => {
|
|
return part.type.includes('tool-') && 'toolCallId' in part;
|
|
};
|
|
|
|
export const mapUIMessagePartsToDBParts = (
|
|
uiMessageParts: ExtendedUIMessagePart[],
|
|
messageId: string,
|
|
): Partial<AgentMessagePartEntity>[] => {
|
|
return uiMessageParts
|
|
.map((part, index) => {
|
|
const basePart: Partial<AgentMessagePartEntity> = {
|
|
messageId,
|
|
orderIndex: index,
|
|
type: part.type,
|
|
};
|
|
|
|
switch (part.type) {
|
|
case 'text':
|
|
return {
|
|
...basePart,
|
|
textContent: part.text,
|
|
};
|
|
case 'reasoning':
|
|
return {
|
|
...basePart,
|
|
reasoningContent: part.text,
|
|
};
|
|
case 'file':
|
|
return {
|
|
...basePart,
|
|
fileMediaType: part.mediaType,
|
|
fileFilename: part.filename,
|
|
fileUrl: part.url,
|
|
};
|
|
case 'source-url':
|
|
return {
|
|
...basePart,
|
|
sourceUrlSourceId: part.sourceId,
|
|
sourceUrlUrl: part.url,
|
|
sourceUrlTitle: part.title,
|
|
providerMetadata: part.providerMetadata ?? null,
|
|
};
|
|
case 'source-document':
|
|
return {
|
|
...basePart,
|
|
sourceDocumentSourceId: part.sourceId,
|
|
sourceDocumentMediaType: part.mediaType,
|
|
sourceDocumentTitle: part.title,
|
|
sourceDocumentFilename: part.filename,
|
|
providerMetadata: part.providerMetadata ?? null,
|
|
};
|
|
case 'step-start':
|
|
return basePart;
|
|
case 'data-routing-status':
|
|
return {
|
|
...basePart,
|
|
textContent: part.data.text,
|
|
state: part.data.state,
|
|
};
|
|
case 'data-code-execution':
|
|
// Code execution parts are streamed during execution but don't need
|
|
// to be persisted - the final result is captured in the tool part
|
|
return null;
|
|
default:
|
|
{
|
|
if (isToolPart(part)) {
|
|
const { toolCallId, input, output, errorText, state } = part;
|
|
|
|
return {
|
|
...basePart,
|
|
toolCallId: toolCallId,
|
|
toolInput: input,
|
|
toolOutput: output,
|
|
errorMessage: errorText,
|
|
state,
|
|
};
|
|
}
|
|
}
|
|
throw new Error(`Unsupported part type: ${part.type}`);
|
|
}
|
|
})
|
|
.filter((part): part is Partial<AgentMessagePartEntity> => part !== null);
|
|
};
|