feat(ai): add code interpreter for AI data analysis (#16559)
## 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 -->
This commit is contained in:
+4
@@ -15,6 +15,8 @@ import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system
|
||||
export type AgentActorContext = {
|
||||
actorContext: ActorMetadata;
|
||||
roleId: string;
|
||||
userId: string;
|
||||
userWorkspaceId: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -88,6 +90,8 @@ export class AgentActorContextService {
|
||||
return {
|
||||
actorContext,
|
||||
roleId,
|
||||
userId: userWorkspace.userId,
|
||||
userWorkspaceId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+71
-65
@@ -11,72 +11,78 @@ export const mapUIMessagePartsToDBParts = (
|
||||
uiMessageParts: ExtendedUIMessagePart[],
|
||||
messageId: string,
|
||||
): Partial<AgentMessagePartEntity>[] => {
|
||||
return uiMessageParts.map((part, index) => {
|
||||
const basePart: Partial<AgentMessagePartEntity> = {
|
||||
messageId,
|
||||
orderIndex: index,
|
||||
type: part.type,
|
||||
};
|
||||
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,
|
||||
};
|
||||
default:
|
||||
{
|
||||
if (isToolPart(part)) {
|
||||
const { toolCallId, input, output, errorText, state } = part;
|
||||
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,
|
||||
};
|
||||
return {
|
||||
...basePart,
|
||||
toolCallId: toolCallId,
|
||||
toolInput: input,
|
||||
toolOutput: output,
|
||||
errorMessage: errorText,
|
||||
state,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(`Unsupported part type: ${part.type}`);
|
||||
}
|
||||
});
|
||||
throw new Error(`Unsupported part type: ${part.type}`);
|
||||
}
|
||||
})
|
||||
.filter((part): part is Partial<AgentMessagePartEntity> => part !== null);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user