diff --git a/packages/twenty-front/src/modules/ai/components/AIChatAssistantMessageRenderer.tsx b/packages/twenty-front/src/modules/ai/components/AIChatAssistantMessageRenderer.tsx index 5c5680924f..d7586bc0de 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatAssistantMessageRenderer.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatAssistantMessageRenderer.tsx @@ -82,15 +82,7 @@ export const AIChatAssistantMessageRenderer = ({ default: { if (isToolUIPart(part)) { - const { output, input, type } = part; - return ( - - ); + return ; } } return null; diff --git a/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx b/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx index a954e7d07f..5892d5c250 100644 --- a/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx +++ b/packages/twenty-front/src/modules/ai/components/ToolStepRenderer.tsx @@ -3,11 +3,10 @@ import styled from '@emotion/styled'; import { useState } from 'react'; import { IconChevronDown, IconChevronUp } from 'twenty-ui/display'; -import { AnimatedExpandableContainer } from 'twenty-ui/layout'; import { JsonTree } from 'twenty-ui/json-visualizer'; +import { AnimatedExpandableContainer } from 'twenty-ui/layout'; import { ShimmeringText } from '@/ai/components/ShimmeringText'; -import { type ToolInput } from '@/ai/types/ToolInput'; import { getToolIcon } from '@/ai/utils/getToolIcon'; import { getToolDisplayMessage } from '@/ai/utils/getWebSearchToolDisplayMessage'; import { useLingui } from '@lingui/react/macro'; @@ -96,26 +95,22 @@ const StyledJsonContainer = styled.div` type TabType = 'output' | 'input'; -export const ToolStepRenderer = ({ - input, - output, - toolName, -}: { - input: ToolInput; - output: ToolUIPart['output']; - toolName: string; -}) => { +export const ToolStepRenderer = ({ toolPart }: { toolPart: ToolUIPart }) => { const { t } = useLingui(); const theme = useTheme(); const { copyToClipboard } = useCopyToClipboard(); const [isExpanded, setIsExpanded] = useState(false); const [activeTab, setActiveTab] = useState('output'); - const isExpandable = isDefined(output); + const { input, output, type, errorText } = toolPart; + const toolName = type.split('-')[1]; + + const hasError = isDefined(errorText); + const isExpandable = isDefined(output) || hasError; const isTwoFirstDepths = ({ depth }: { depth: number }) => depth < 2; - if (!output) { + if (!output && !hasError) { return ( @@ -129,11 +124,12 @@ export const ToolStepRenderer = ({ ); } - const displayMessage = - output && - typeof output === 'object' && - 'message' in output && - typeof output.message === 'string' + const displayMessage = hasError + ? 'Tool execution failed' + : output && + typeof output === 'object' && + 'message' in output && + typeof output.message === 'string' ? output.message : getToolDisplayMessage(input, toolName, true); @@ -165,33 +161,41 @@ export const ToolStepRenderer = ({ {isExpandable && ( - - setActiveTab('output')} - > - Output - - setActiveTab('input')} - > - Input - - + {hasError ? ( + {errorText} + ) : ( + <> + + setActiveTab('output')} + > + Output + + setActiveTab('input')} + > + Input + + - - - + + + + + )} )} diff --git a/packages/twenty-front/src/modules/ai/utils/mapDBPartToUIMessagePart.ts b/packages/twenty-front/src/modules/ai/utils/mapDBPartToUIMessagePart.ts index 72e702c5eb..f3414bebd0 100644 --- a/packages/twenty-front/src/modules/ai/utils/mapDBPartToUIMessagePart.ts +++ b/packages/twenty-front/src/modules/ai/utils/mapDBPartToUIMessagePart.ts @@ -59,7 +59,7 @@ export const mapDBPartToUIMessagePart = ( return { type: part.type as `tool-${string}`, toolCallId: part.toolCallId!, - input: part.toolInput, + input: part.toolInput ?? {}, output: part.toolOutput, errorText: part.errorMessage!, state: part.state, diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts index 12cdfd9315..bcbf9d9393 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/create-record.service.ts @@ -73,8 +73,12 @@ export class CreateRecordService { }); const validObjectRecord = Object.fromEntries( - Object.entries(objectRecord).filter(([key]) => - isDefined(objectMetadataItemWithFieldsMaps.fieldIdByName[key]), + Object.entries(objectRecord).filter( + ([key]) => + isDefined(objectMetadataItemWithFieldsMaps.fieldIdByName[key]) || + isDefined( + objectMetadataItemWithFieldsMaps.fieldIdByJoinColumnName[key], + ), ), ); diff --git a/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts b/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts index 466b58b3d5..4b385cd534 100644 --- a/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts +++ b/packages/twenty-server/src/engine/core-modules/record-crud/services/upsert-record.service.ts @@ -92,9 +92,13 @@ export class UpsertRecordService { }); const uniqueFieldsToUpdate = fieldsToUpdateArray - .map((field) => objectMetadataItemWithFieldsMaps.fieldIdByName[field]) + .map( + (field) => + objectMetadataItemWithFieldsMaps.fieldIdByName[field] || + objectMetadataItemWithFieldsMaps.fieldIdByJoinColumnName[field], + ) .map((fieldId) => objectMetadataItemWithFieldsMaps.fieldsById[fieldId]) - .filter((field) => field.isUnique || field.name === 'id'); + .filter((field) => field && (field.isUnique || field.name === 'id')); const conflictPathsUniqueFieldsToUpdate = uniqueFieldsToUpdate.flatMap( (field) => {