feat(ai): add view management tools for AI chat (#16495)
## Summary Adds a new **VIEW** tool category for the AI chat, enabling it to work with views: - **get-views**: List views in the workspace, optionally filtered by object metadata ID - **get-view-query-parameters**: Convert a view's filters and sorts into GraphQL query parameters that can be passed to existing `find_*` data tools - **create-view**, **update-view**, **delete-view**: CRUD operations for view management ### Key design decisions 1. **No pagination duplication**: Instead of creating a `find-records-from-view` tool that would duplicate pagination logic, `get-view-query-parameters` returns filter/sort parameters that the AI can pass to existing record-fetching tools. 2. **Permission model**: - Read tools (get-views, get-view-query-parameters) are available to all users - Write tools require the `VIEW` permission - UNLISTED views can only be modified by their creator 3. **Leverages existing utilities**: Uses `computeRecordGqlOperationFilter` from `twenty-shared` for filter conversion. ### Files changed - Added `ViewToolProvider`, `ViewToolsFactory`, and `ViewQueryParamsService` - Added `VIEW` to `ToolCategory` enum and tool registry - Updated `chat-execution.service.ts` to include view tools in the catalog and pass viewId in browsing context - Extracted shared `formatValidationErrors` utility to reduce duplication ## Test plan - [x] Unit tests for `ViewToolsFactory` - [x] Unit tests for `ViewQueryParamsService` - [x] Lint and typecheck pass
This commit is contained in:
+13
-3
@@ -233,15 +233,17 @@ export class ChatExecutionService {
|
||||
viewName: string;
|
||||
filterDescriptions: string[];
|
||||
}): string {
|
||||
const { objectNameSingular, viewName, filterDescriptions } =
|
||||
const { objectNameSingular, viewId, viewName, filterDescriptions } =
|
||||
browsingContext;
|
||||
|
||||
let context = `The user is viewing a list of ${objectNameSingular} records in a view called "${viewName}".`;
|
||||
let context = `The user is viewing a list of ${objectNameSingular} records in a view called "${viewName}" (viewId: ${viewId}).`;
|
||||
|
||||
if (filterDescriptions.length > 0) {
|
||||
context += `\nFilters applied: ${filterDescriptions.join(', ')}`;
|
||||
}
|
||||
|
||||
context += `\nUse get_view_query_parameters tool with this viewId to get the exact filter/sort parameters for querying records.`;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -323,7 +325,13 @@ ${preloadedTools.length > 0 ? preloadedTools.map((t) => `- \`${t}\` ✓`).join('
|
||||
|
||||
### Tool Catalog by Category`);
|
||||
|
||||
const categoryOrder = ['database', 'action', 'workflow', 'metadata'];
|
||||
const categoryOrder = [
|
||||
'database',
|
||||
'action',
|
||||
'workflow',
|
||||
'metadata',
|
||||
'view',
|
||||
];
|
||||
|
||||
for (const category of categoryOrder) {
|
||||
const tools = toolsByCategory.get(category);
|
||||
@@ -365,6 +373,8 @@ ${tools
|
||||
return 'Workflow Tools (create/manage workflows)';
|
||||
case 'metadata':
|
||||
return 'Metadata Tools (schema management)';
|
||||
case 'view':
|
||||
return 'View Tools (query views)';
|
||||
default:
|
||||
return category;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user