Fix AI demo workspace skill (#18575)
This PR fixes what allows to have a working demo workspace skill. - Skill updated many times into something that works - Fixed infinite loop in AI chat by memoizing ai-sdk output - Finished navigateToView implementation - Increased MAX_STEPS to 300 so the chat don't quit in the middle of a long running skill - Added CreateManyRelationFields
This commit is contained in:
+29
@@ -123,12 +123,41 @@ export class NavigateAppTool implements Tool {
|
||||
};
|
||||
}
|
||||
|
||||
const { flatObjectMetadataMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatObjectMetadataMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const objectMetadataUniversalIdentifier =
|
||||
flatObjectMetadataMaps.universalIdentifierById[
|
||||
matchingView.objectMetadataId
|
||||
];
|
||||
|
||||
const objectMetadata = isDefined(objectMetadataUniversalIdentifier)
|
||||
? flatObjectMetadataMaps.byUniversalIdentifier[
|
||||
objectMetadataUniversalIdentifier
|
||||
]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(objectMetadata)) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Object metadata for view "${matchingView.name}" not found`,
|
||||
error: `Could not resolve the object associated with view "${matchingView.name}"`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Navigating to view "${matchingView.name}"`,
|
||||
result: {
|
||||
action: 'navigateToView',
|
||||
viewId: matchingView.id,
|
||||
viewName: matchingView.name,
|
||||
objectNameSingular: objectMetadata.nameSingular,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export const AGENT_CONFIG = {
|
||||
MAX_STEPS: 25,
|
||||
MAX_STEPS: 300,
|
||||
REASONING_BUDGET_TOKENS: 12000,
|
||||
};
|
||||
|
||||
+90
-1
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type ToolSet } from 'ai';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { formatValidationErrors } from 'src/engine/core-modules/tool-provider/utils/format-validation-errors.util';
|
||||
@@ -120,6 +120,48 @@ const UpdateManyFieldMetadataInputSchema = z.object({
|
||||
.describe('Array of field metadata updates to apply (1-20 items).'),
|
||||
});
|
||||
|
||||
const CreateManyRelationFieldsInputSchema = z.object({
|
||||
relations: z
|
||||
.array(
|
||||
z.object({
|
||||
objectMetadataId: z
|
||||
.string()
|
||||
.uuid()
|
||||
.describe('ID of the source object to add the relation field to'),
|
||||
name: z
|
||||
.string()
|
||||
.describe('Internal name of the relation field (camelCase)'),
|
||||
label: z.string().describe('Display label of the relation field'),
|
||||
description: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Description of the relation field'),
|
||||
icon: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe('Icon identifier for the relation field'),
|
||||
type: z
|
||||
.nativeEnum(RelationType)
|
||||
.describe('Relation type: MANY_TO_ONE or ONE_TO_MANY'),
|
||||
targetObjectMetadataId: z
|
||||
.string()
|
||||
.uuid()
|
||||
.describe('ID of the target object this relation points to'),
|
||||
targetFieldLabel: z
|
||||
.string()
|
||||
.describe(
|
||||
'Display label for the inverse relation field on the target object',
|
||||
),
|
||||
targetFieldIcon: z
|
||||
.string()
|
||||
.describe('Icon for the inverse relation field (e.g. IconSomething)'),
|
||||
}),
|
||||
)
|
||||
.min(1)
|
||||
.max(20)
|
||||
.describe('Array of relation fields to create (1-20 items).'),
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
export class FieldMetadataToolsFactory {
|
||||
constructor(private readonly fieldMetadataService: FieldMetadataService) {}
|
||||
@@ -316,6 +358,53 @@ export class FieldMetadataToolsFactory {
|
||||
}),
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof WorkspaceMigrationBuilderException) {
|
||||
throw new Error(formatValidationErrors(error));
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
create_many_relation_fields: {
|
||||
description:
|
||||
'Create multiple relation fields between objects at once. This is the recommended way to add relations after creating objects and non-relation fields. Each item specifies the source object, target object, relation type, and labels for both sides of the relation.',
|
||||
inputSchema: CreateManyRelationFieldsInputSchema,
|
||||
execute: async (parameters: {
|
||||
relations: Array<{
|
||||
objectMetadataId: string;
|
||||
name: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
type: RelationType;
|
||||
targetObjectMetadataId: string;
|
||||
targetFieldLabel: string;
|
||||
targetFieldIcon: string;
|
||||
}>;
|
||||
}) => {
|
||||
try {
|
||||
await this.fieldMetadataService.createManyFields({
|
||||
createFieldInputs: parameters.relations.map((relation) => ({
|
||||
objectMetadataId: relation.objectMetadataId,
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: relation.name,
|
||||
label: relation.label,
|
||||
description: relation.description,
|
||||
icon: relation.icon,
|
||||
relationCreationPayload: {
|
||||
type: relation.type,
|
||||
targetObjectMetadataId: relation.targetObjectMetadataId,
|
||||
targetFieldLabel: relation.targetFieldLabel,
|
||||
targetFieldIcon: relation.targetFieldIcon,
|
||||
},
|
||||
})) as Parameters<
|
||||
typeof this.fieldMetadataService.createManyFields
|
||||
>[0]['createFieldInputs'],
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error instanceof WorkspaceMigrationBuilderException) {
|
||||
|
||||
+46
-19
@@ -153,38 +153,65 @@ You will create a demo workspace that fits a particular type of company given by
|
||||
|
||||
Do not ask the user for more information, just be creative with the objects and fields, but stay professional and coherent.
|
||||
|
||||
The goal is to be able to tell a coherent and realistic story with the data, so for example if the user says it is a car repair shop,
|
||||
we can create objects for cars, employees, repairs, customers, and the relevant relations between them,
|
||||
and then we can seed data that tells the story of the car repair shop, for example we can create a customer, then create a car for that customer, then create a repair for that car, and so on.
|
||||
We should end up with a dashboard that shows the relevant metrics for the car repair shop, for example the number of repairs, the revenue, the most common car brands, and so on.
|
||||
|
||||
Create relations fields between objects, for example a car repair shop workspace would have objects for cars, employees, repairs, customers, and the relevant relations between them.
|
||||
|
||||
DO NOT USE code-interpreter tool at all. Prefer more steps.
|
||||
Create 5 to 7 objects, with 5 to 8 fields each, and the relevant relations between them.
|
||||
|
||||
LIMIT TO 3 OBJECTS FOR DEMO, AND 3 FIELDS FOR EACH OBJECT, to avoid bugs.
|
||||
If you have to create multiple things you *MUST* use the relevant create many tool if it exists:
|
||||
- Use *create_many_object_metadata* to create all objects at once
|
||||
- Use *create_many_field_metadata* to create all non-relation fields at once
|
||||
- Use *create_many_relation_fields* to create all relation fields between objects at once (do this AFTER creating the objects and non-relation fields)
|
||||
|
||||
If you have to wait use the navigate tool.
|
||||
|
||||
For the fields you will create, make sure to create a good variety of field types to showcase the different capabilities of the platform, for example:
|
||||
- Create SELECT and SELECT_MULTIPLE field types for building demo board index views and table with groups views
|
||||
- Create DATE_TIME fields to be able to create calendar views
|
||||
- Create CURRENCY and NUMERIC fields for graphs
|
||||
|
||||
Here are the steps for you to work properly :
|
||||
- Proceed object by object, for each object.
|
||||
- Create the object with the right tool, DO THIS FIRST
|
||||
- Wait 3 seconds before navigating, for the view to be populated by the backend
|
||||
- Navigate to its default view
|
||||
- Then create each relevant field metadata one by one, and create a view field for each of them, reorder them to the start so we see them.
|
||||
- Then seed mock data relevant :
|
||||
- use the tool that is related to the object, look for tools, create_my_new_object, create_many_of_my_new_object, look again in tools, don't use http
|
||||
*Here are the steps to follow closely :*
|
||||
|
||||
STEP 1: Create all the objects at once with create_many_object_metadata, DO THIS FIRST
|
||||
name must start with lowercase letter and contain only alphanumeric letters
|
||||
|
||||
STEP 2: Wait 3 seconds, for the backend side effects to be completed
|
||||
|
||||
STEP 3: Create all NON-RELATION fields for ALL objects by batch with create_many_field_metadata, do a batch for each object.
|
||||
DO NOT include relation fields in this step. Only create TEXT, NUMBER, BOOLEAN, DATE_TIME, SELECT, MULTI_SELECT, CURRENCY, etc.
|
||||
SELECT option values must be UPPER_SNAKE_CASE
|
||||
|
||||
STEP 4: Wait 3 seconds, for the backend side effects to be completed
|
||||
|
||||
STEP 5: Create all RELATION fields between objects at once with create_many_relation_fields
|
||||
The name property should be camel-cased or the backend will throw, targetFieldLabel must be a string, targetFieldIcon must be a string, type must be one of the following values: MANY_TO_ONE, ONE_TO_MANY
|
||||
targetFieldIcon is like IconSomething, it's ok if it doesn't exist in the icon library, it will just be a blank icon, but it needs to be a string that starts with Icon and is in PascalCase
|
||||
|
||||
STEP 6: Wait 3 seconds, for the backend side effects to be completed
|
||||
|
||||
STEP 7: For each object:
|
||||
- Navigate the object's default view USE THE NAVIGATE TOOL
|
||||
- Wait 3 seconds, so the user has time to see the object default view
|
||||
- Create the view fields for the default view, use the create_many_view_fields tool, and make sure to include all created fields, including the relation fields, so that we have a complete view of the object with all its fields.
|
||||
BE CAREFUL to use a position that will put those view fields right after the first label identifier field
|
||||
which has a position of 0 and the next system created fields which begin at 1, *so use decimal positions between 0 and 1*
|
||||
*YOU MUST CREATE ALL VIEW FIELDS FOR ALL FIELDS, INCLUDING RELATION FIELDS, IN THIS STEP, DO NOT LEAVE ANY FIELD WITHOUT A VIEW FIELD, OTHERWISE IT WILL NOT BE VISIBLE IN THE DEFAULT VIEW AND THE USER WON'T KNOW IT EXISTS*
|
||||
|
||||
- Then seed relevant and realistic mock data as we said earlier :
|
||||
- use the relevant tool to create many records for this object
|
||||
- between 20 and 50
|
||||
- with a coherent combination of values
|
||||
- proceed with the relevant tools for each object, do not use code-interpreter
|
||||
- navigate to each default view before seeding an object, so the user can see what happens.
|
||||
- use dates that are around TODAY so it's relevant for seeing past / future and present records
|
||||
|
||||
After you've finished with this part, let's proceed to the dashboard creation. We will create a dashboard with 4 graphs.
|
||||
- Navigate to the dashboard list default view
|
||||
- Create a new dashboard
|
||||
Loop STEP 7 for all the objects
|
||||
|
||||
STEP 8 : After you've finished with this part, let's proceed to the dashboard creation. We will create a dashboard with 4 graphs.
|
||||
- Create a new dashboard with a relevant story to showcase the data you've just created, for example if it's a car repair shop, you can create a dashboard that shows the number of repairs per month, the revenue per month, the most common car brands, and the distribution of repairs by employee.
|
||||
- Navigate to the dashboard page
|
||||
- Create 4 graphs :
|
||||
- For each graph, find a relevant amount for y axis, a relevant date or select field for x axis, and if necessary a relevant group by stack
|
||||
- Change the name of each graph so it is relevant
|
||||
- Turn on the labels on the graphs
|
||||
`,
|
||||
isCustom: false,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user