[AI] Collapse NativeToolBinder to a single bind() entry (#20051)

The binder doesnt need an agent or full tool context -- just a model and
options.
Single `bind(model, options)` entry!


Builds on #20022.
This commit is contained in:
nitin
2026-04-27 20:05:59 +05:30
committed by GitHub
parent 13aaea597e
commit bb5f294c5a
6 changed files with 13 additions and 44 deletions
@@ -2,7 +2,6 @@ import { type ActorMetadata } from 'twenty-shared/types';
import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
import { type CodeExecutionStreamEmitter } from 'src/engine/core-modules/tool-provider/interfaces/code-execution-stream-emitter.type';
import { type FlatAgentWithRoleId } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
export type ToolProviderContext = {
@@ -13,6 +12,5 @@ export type ToolProviderContext = {
actorContext?: ActorMetadata;
userId?: string;
userWorkspaceId?: string;
agent?: FlatAgentWithRoleId | null;
onCodeExecutionUpdate?: CodeExecutionStreamEmitter;
};
@@ -1,6 +1,7 @@
import { type ToolSet } from 'ai';
import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type';
import { type RegisteredAiModel } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
import { type NativeModelToolOptions } from 'src/engine/metadata-modules/ai/ai-models/types/native-model-tool-options.type';
// Parallel to ToolProvider, not a variant of it. A binder produces SDK-native
// tool objects (Anthropic webSearch, OpenAI webSearch, etc.) that the AI SDK
@@ -9,5 +10,5 @@ import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/
// executed by ToolExecutorService. They're merged directly into the ToolSet
// handed to streamText.
export interface NativeToolBinder {
bind(context: ToolProviderContext): Promise<ToolSet>;
bind(model: RegisteredAiModel, options: NativeModelToolOptions): ToolSet;
}
@@ -3,37 +3,16 @@ import { Injectable } from '@nestjs/common';
import { type ToolSet } from 'ai';
import { type NativeToolBinder } from 'src/engine/core-modules/tool-provider/native/native-tool-binder.interface';
import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-context.type';
import { AiModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-config.service';
import {
AiModelRegistryService,
type RegisteredAiModel,
} from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
import { type RegisteredAiModel } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
import { type NativeModelToolOptions } from 'src/engine/metadata-modules/ai/ai-models/types/native-model-tool-options.type';
@Injectable()
export class NativeToolBinderService implements NativeToolBinder {
constructor(
private readonly aiModelConfigService: AiModelConfigService,
private readonly aiModelRegistryService: AiModelRegistryService,
) {}
constructor(private readonly aiModelConfigService: AiModelConfigService) {}
async bind(context: ToolProviderContext): Promise<ToolSet> {
if (!context.agent) {
return {};
}
const registeredModel =
await this.aiModelRegistryService.resolveModelForAgent(context.agent);
return this.aiModelConfigService.getNativeModelToolsForAgent(
registeredModel,
context.agent,
);
}
bindForModel(
bind(
model: RegisteredAiModel,
options: NativeModelToolOptions = {},
): ToolSet {
@@ -150,7 +150,6 @@ export class AgentAsyncExecutorService {
rolePermissionConfig: effectiveRoleConfig ?? { unionOf: [] },
authContext,
actorContext,
agent: agent as unknown as ToolProviderContext['agent'],
userId:
isDefined(authContext) && isUserAuthContext(authContext)
? authContext.user.id
@@ -169,8 +168,10 @@ export class AgentAsyncExecutorService {
},
);
const nativeTools =
await this.nativeToolBinder.bind(toolProviderContext);
const nativeTools = this.nativeToolBinder.bind(registeredModel, {
webSearchEnabled:
agent.modelConfiguration?.webSearch?.enabled === true,
});
tools = {
...registryTools,
@@ -157,10 +157,9 @@ export class ChatExecutionService {
registeredModel.modelId,
);
const nativeModelTools = this.nativeToolBinder.bindForModel(
registeredModel,
{ webSearchEnabled: true },
);
const nativeModelTools = this.nativeToolBinder.bind(registeredModel, {
webSearchEnabled: true,
});
// Tools the model can call directly: preloaded registry tools (already
// serialized by the hydrator) plus SDK-native tools (opaque, never
@@ -79,15 +79,6 @@ export class AiModelConfigService {
return tools;
}
getNativeModelToolsForAgent(
model: RegisteredAiModel,
agent: FlatAgentWithRoleId,
): ToolSet {
return this.getNativeModelTools(model, {
webSearchEnabled: agent.modelConfiguration?.webSearch?.enabled === true,
});
}
private getXaiProviderOptions(agent: FlatAgentWithRoleId): ProviderOptions {
if (
!agent.modelConfiguration ||