Files
twenty/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent-execution/utils/mapUIMessagePartsToDBParts.ts
T
Félix Malfait 2e104c8e76 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 -->
2025-12-15 16:11:24 +01:00

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);
};