fix(ai) - optim (#21233)
1. tool-registry.service.ts, Pass precomputed catalog to resolveSchemas() resolveSchemas() now accepts an optional precomputedCatalog parameter. Both getToolsByName() and getToolInfo() pass the catalog they already fetched, eliminating a redundant getCatalog() rebuild inside resolveSchemas(). 2. database-tool.provider.ts, Skip field lookup when schemas=false When building the catalog index (includeSchemas=false), getFlatFieldsFromFlatObjectMetadata() is no longer called for each of the 25 objects. The hasGroupByToolInputSchema() check is also skipped, group_by tools are always included in the index, with the real eligibility check deferred to learn_tools time. --> 100/150ms gain on learn/execute_tool execution
This commit is contained in:
+7
-7
@@ -113,13 +113,11 @@ export class DatabaseToolProvider implements ToolProvider {
|
||||
continue;
|
||||
}
|
||||
|
||||
const objectMetadata = {
|
||||
...flatObject,
|
||||
fields: getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObject,
|
||||
flatFieldMetadataMaps,
|
||||
),
|
||||
};
|
||||
const fields = includeSchemas
|
||||
? getFlatFieldsFromFlatObjectMetadata(flatObject, flatFieldMetadataMaps)
|
||||
: [];
|
||||
|
||||
const objectMetadata = { ...flatObject, fields };
|
||||
|
||||
const restrictedFields = permission.restrictedFields;
|
||||
const canBeManagedByAutomation = canObjectBeManagedByAutomation({
|
||||
@@ -171,7 +169,9 @@ export class DatabaseToolProvider implements ToolProvider {
|
||||
const groupBySchema = shouldGenerateGroupBy
|
||||
? generateGroupByToolInputSchema(objectMetadata, restrictedFields)
|
||||
: null;
|
||||
|
||||
const hasGroupBySchema =
|
||||
!includeSchemas ||
|
||||
groupBySchema !== null ||
|
||||
hasGroupByToolInputSchema(objectMetadata, restrictedFields);
|
||||
|
||||
|
||||
+9
-6
@@ -84,12 +84,15 @@ export class ToolIndexResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
const schemas = await this.toolRegistryService.resolveSchemas([toolName], {
|
||||
workspaceId: workspace.id,
|
||||
roleId,
|
||||
rolePermissionConfig: { unionOf: [roleId] },
|
||||
userId: user?.id,
|
||||
userWorkspaceId,
|
||||
const schemas = await this.toolRegistryService.resolveSchemas({
|
||||
toolNames: [toolName],
|
||||
context: {
|
||||
workspaceId: workspace.id,
|
||||
roleId,
|
||||
rolePermissionConfig: { unionOf: [roleId] },
|
||||
userId: user?.id,
|
||||
userWorkspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
return schemas.get(toolName) ?? null;
|
||||
|
||||
+25
-17
@@ -2,8 +2,8 @@ import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { type ToolSet, jsonSchema } from 'ai';
|
||||
|
||||
import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface';
|
||||
import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type';
|
||||
import { type ToolProvider } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider.interface';
|
||||
import { type ToolRetrievalOptions } from 'src/engine/core-modules/tool-provider/interfaces/tool-retrieval-options.type';
|
||||
|
||||
import { TOOL_PROVIDERS } from 'src/engine/core-modules/tool-provider/constants/tool-providers.token';
|
||||
@@ -31,9 +31,6 @@ export class ToolRegistryService {
|
||||
private readonly toolExecutorService: ToolExecutorService,
|
||||
) {}
|
||||
|
||||
// Returns ToolIndexEntry[] (lightweight, no schemas).
|
||||
// Underlying data (metadata, permissions) is already cached by WorkspaceCacheService.
|
||||
// Providers run in parallel since they are independent.
|
||||
async getCatalog(context: ToolProviderContext): Promise<ToolIndexEntry[]> {
|
||||
const results = await Promise.all(
|
||||
this.providers.map(async (provider) => {
|
||||
@@ -50,16 +47,19 @@ export class ToolRegistryService {
|
||||
return results.flat();
|
||||
}
|
||||
|
||||
// On-demand schema generation for specific tools
|
||||
async resolveSchemas(
|
||||
toolNames: string[],
|
||||
context: ToolProviderContext,
|
||||
): Promise<Map<string, object>> {
|
||||
const index = await this.getCatalog(context);
|
||||
async resolveSchemas({
|
||||
toolNames,
|
||||
context,
|
||||
precomputedCatalog,
|
||||
}: {
|
||||
toolNames: string[];
|
||||
context: ToolProviderContext;
|
||||
precomputedCatalog?: ToolIndexEntry[];
|
||||
}): Promise<Map<string, object>> {
|
||||
const index = precomputedCatalog ?? (await this.getCatalog(context));
|
||||
const nameSet = new Set(toolNames);
|
||||
const matchingEntries = index.filter((entry) => nameSet.has(entry.name));
|
||||
|
||||
// Group matching entries by provider category
|
||||
const byCategory = new Map<string, ToolIndexEntry[]>();
|
||||
|
||||
for (const entry of matchingEntries) {
|
||||
@@ -176,11 +176,15 @@ export class ToolRegistryService {
|
||||
): Promise<ToolSet> {
|
||||
const fullContext = this.buildContextFromToolContext(context);
|
||||
|
||||
const index = await this.getCatalog(fullContext);
|
||||
const catalog = await this.getCatalog(fullContext);
|
||||
const nameSet = new Set(names);
|
||||
const matchingEntries = index.filter((entry) => nameSet.has(entry.name));
|
||||
const matchingEntries = catalog.filter((entry) => nameSet.has(entry.name));
|
||||
|
||||
const schemas = await this.resolveSchemas(names, fullContext);
|
||||
const schemas = await this.resolveSchemas({
|
||||
toolNames: names,
|
||||
context: fullContext,
|
||||
precomputedCatalog: catalog,
|
||||
});
|
||||
|
||||
const descriptors: ToolDescriptor[] = matchingEntries
|
||||
.filter((entry) => schemas.has(entry.name))
|
||||
@@ -204,14 +208,18 @@ export class ToolRegistryService {
|
||||
> {
|
||||
const fullContext = this.buildContextFromToolContext(context);
|
||||
|
||||
const index = await this.getCatalog(fullContext);
|
||||
const catalog = await this.getCatalog(fullContext);
|
||||
const nameSet = new Set(names);
|
||||
const matchingEntries = index.filter((entry) => nameSet.has(entry.name));
|
||||
const matchingEntries = catalog.filter((entry) => nameSet.has(entry.name));
|
||||
|
||||
let schemas: Map<string, object> | undefined;
|
||||
|
||||
if (aspects.includes('schema')) {
|
||||
schemas = await this.resolveSchemas(names, fullContext);
|
||||
schemas = await this.resolveSchemas({
|
||||
toolNames: names,
|
||||
context: fullContext,
|
||||
precomputedCatalog: catalog,
|
||||
});
|
||||
}
|
||||
|
||||
return matchingEntries.map((entry) => {
|
||||
|
||||
Reference in New Issue
Block a user