mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
more
This commit is contained in:
parent
3ca83f3a46
commit
f9f21602aa
9 changed files with 70 additions and 49 deletions
|
|
@ -0,0 +1,5 @@
|
|||
// Public search tool ids shared across orchestration layers. The runtime can
|
||||
// back these ids with provider-native or external implementations.
|
||||
// one export per file -- to be cleaned after the strategy is clear
|
||||
export const WEB_SEARCH_TOOL_ID = 'web_search';
|
||||
export const X_SEARCH_TOOL_ID = 'x_search';
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
export const SEARCH_TOOL_NAMES = {
|
||||
webSearch: 'web_search',
|
||||
xSearch: 'x_search',
|
||||
} as const;
|
||||
|
|
@ -9,7 +9,7 @@ jest.mock('ai', () => {
|
|||
|
||||
import { generateText, type ToolSet } from 'ai';
|
||||
|
||||
import { SEARCH_TOOL_NAMES } from 'src/engine/core-modules/tool-provider/constants/search-tool-names.const';
|
||||
import { WEB_SEARCH_TOOL_ID } from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
import { type ToolRegistryService } from 'src/engine/core-modules/tool-provider/services/tool-registry.service';
|
||||
import { type WebSearchService } from 'src/engine/core-modules/web-search/web-search.service';
|
||||
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
|
@ -296,7 +296,7 @@ describe('AgentAsyncExecutorService', () => {
|
|||
},
|
||||
} as unknown as ToolSet;
|
||||
const nativeModelTools = {
|
||||
[SEARCH_TOOL_NAMES.webSearch]: {
|
||||
[WEB_SEARCH_TOOL_ID]: {
|
||||
description: 'Native search the web',
|
||||
inputSchema: {},
|
||||
execute: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { type Repository } from 'typeorm';
|
|||
|
||||
import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard';
|
||||
import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
|
||||
import { SEARCH_TOOL_NAMES } from 'src/engine/core-modules/tool-provider/constants/search-tool-names.const';
|
||||
import { WEB_SEARCH_TOOL_ID } from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
import { type ToolProviderAgent } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-agent.type';
|
||||
import { ToolRegistryService } from 'src/engine/core-modules/tool-provider/services/tool-registry.service';
|
||||
import { WebSearchService } from 'src/engine/core-modules/web-search/web-search.service';
|
||||
|
|
@ -51,7 +51,7 @@ const toToolProviderAgent = (agent: AgentEntity): ToolProviderAgent => ({
|
|||
|
||||
const WORKFLOW_NO_ROLE_FALLBACK_TOOL_NAMES = [
|
||||
'code_interpreter',
|
||||
SEARCH_TOOL_NAMES.webSearch,
|
||||
WEB_SEARCH_TOOL_ID,
|
||||
] as const;
|
||||
const WORKFLOW_NO_ROLE_FALLBACK_TOOL_NAMES_SET: ReadonlySet<string> = new Set(
|
||||
WORKFLOW_NO_ROLE_FALLBACK_TOOL_NAMES,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type StepResult, type ToolSet } from 'ai';
|
||||
|
||||
import { SEARCH_TOOL_NAMES } from 'src/engine/core-modules/tool-provider/constants/search-tool-names.const';
|
||||
import { WEB_SEARCH_TOOL_ID } from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
|
||||
export const countNativeWebSearchCallsFromSteps = (
|
||||
steps: StepResult<ToolSet>[],
|
||||
|
|
@ -9,7 +9,7 @@ export const countNativeWebSearchCallsFromSteps = (
|
|||
(count, step) =>
|
||||
count +
|
||||
step.toolCalls.filter(
|
||||
(toolCall) => toolCall.toolName === SEARCH_TOOL_NAMES.webSearch,
|
||||
(toolCall) => toolCall.toolName === WEB_SEARCH_TOOL_ID,
|
||||
).length,
|
||||
0,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { type StepResult, type ToolSet } from 'ai';
|
||||
|
||||
import { SEARCH_TOOL_NAMES } from 'src/engine/core-modules/tool-provider/constants/search-tool-names.const';
|
||||
import { X_SEARCH_TOOL_ID } from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
|
||||
export const countNativeXSearchCallsFromSteps = (
|
||||
steps: StepResult<ToolSet>[],
|
||||
|
|
@ -9,7 +9,7 @@ export const countNativeXSearchCallsFromSteps = (
|
|||
(count, step) =>
|
||||
count +
|
||||
step.toolCalls.filter(
|
||||
(toolCall) => toolCall.toolName === SEARCH_TOOL_NAMES.xSearch,
|
||||
(toolCall) => toolCall.toolName === X_SEARCH_TOOL_ID,
|
||||
).length,
|
||||
0,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { CodeInterpreterService } from 'src/engine/core-modules/code-interpreter
|
|||
import { WorkspaceDomainsService } from 'src/engine/core-modules/domain/workspace-domains/services/workspace-domains.service';
|
||||
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
|
||||
import { COMMON_PRELOAD_TOOLS } from 'src/engine/core-modules/tool-provider/constants/common-preload-tools.const';
|
||||
import { SEARCH_TOOL_NAMES } from 'src/engine/core-modules/tool-provider/constants/search-tool-names.const';
|
||||
import { WEB_SEARCH_TOOL_ID } from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
import { wrapToolsWithOutputSerialization } from 'src/engine/core-modules/tool-provider/output-serialization/wrap-tools-with-output-serialization.util';
|
||||
import { ToolRegistryService } from 'src/engine/core-modules/tool-provider/services/tool-registry.service';
|
||||
import {
|
||||
|
|
@ -159,23 +159,18 @@ export class ChatExecutionService {
|
|||
registeredModel.modelId,
|
||||
);
|
||||
|
||||
const nativeSearchTools =
|
||||
this.aiModelConfigService.getChatNativeSearchTools(registeredModel, {
|
||||
useProviderNativeWebSearch: useNativeSearch,
|
||||
});
|
||||
const hasNativeWebSearch = Object.prototype.hasOwnProperty.call(
|
||||
nativeSearchTools,
|
||||
SEARCH_TOOL_NAMES.webSearch,
|
||||
);
|
||||
const hasNativeXSearch = Object.prototype.hasOwnProperty.call(
|
||||
nativeSearchTools,
|
||||
SEARCH_TOOL_NAMES.xSearch,
|
||||
);
|
||||
const {
|
||||
tools: nativeSearchTools,
|
||||
hasWebSearch: hasNativeWebSearch,
|
||||
hasXSearch: hasNativeXSearch,
|
||||
} = this.aiModelConfigService.getChatNativeSearchPlan(registeredModel, {
|
||||
useProviderNativeWebSearch: useNativeSearch,
|
||||
});
|
||||
|
||||
const toolNamesToPreload = [
|
||||
...COMMON_PRELOAD_TOOLS,
|
||||
...(!hasNativeWebSearch && externalWebSearchEnabled
|
||||
? [SEARCH_TOOL_NAMES.webSearch]
|
||||
? [WEB_SEARCH_TOOL_ID]
|
||||
: []),
|
||||
];
|
||||
|
||||
|
|
@ -196,10 +191,10 @@ export class ChatExecutionService {
|
|||
...Object.keys(nativeSearchTools),
|
||||
];
|
||||
const excludedChatToolNames = hasNativeWebSearch
|
||||
? new Set([SEARCH_TOOL_NAMES.webSearch])
|
||||
? new Set([WEB_SEARCH_TOOL_ID])
|
||||
: undefined;
|
||||
const toolCatalogForPrompt = hasNativeWebSearch
|
||||
? toolCatalog.filter((tool) => tool.name !== SEARCH_TOOL_NAMES.webSearch)
|
||||
? toolCatalog.filter((tool) => tool.name !== WEB_SEARCH_TOOL_ID)
|
||||
: toolCatalog;
|
||||
|
||||
// ToolSet is constant for the entire conversation — no mutation.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { AiModelConfigService } from './ai-model-config.service';
|
||||
|
||||
import {
|
||||
WEB_SEARCH_TOOL_ID,
|
||||
X_SEARCH_TOOL_ID,
|
||||
} from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
import {
|
||||
AI_SDK_OPENAI,
|
||||
AI_SDK_XAI,
|
||||
|
|
@ -26,7 +30,7 @@ describe('AiModelConfigService', () => {
|
|||
}),
|
||||
});
|
||||
|
||||
const result = service.getChatNativeSearchTools(
|
||||
const result = service.getChatNativeSearchPlan(
|
||||
{
|
||||
sdkPackage: AI_SDK_XAI,
|
||||
providerName: 'xai',
|
||||
|
|
@ -35,7 +39,11 @@ describe('AiModelConfigService', () => {
|
|||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
x_search: xSearchTool,
|
||||
tools: {
|
||||
[X_SEARCH_TOOL_ID]: xSearchTool,
|
||||
},
|
||||
hasWebSearch: false,
|
||||
hasXSearch: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -49,7 +57,7 @@ describe('AiModelConfigService', () => {
|
|||
}),
|
||||
});
|
||||
|
||||
const result = service.getChatNativeSearchTools(
|
||||
const result = service.getChatNativeSearchPlan(
|
||||
{
|
||||
sdkPackage: AI_SDK_XAI,
|
||||
providerName: 'xai',
|
||||
|
|
@ -58,8 +66,12 @@ describe('AiModelConfigService', () => {
|
|||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
web_search: webSearchTool,
|
||||
x_search: xSearchTool,
|
||||
tools: {
|
||||
[WEB_SEARCH_TOOL_ID]: webSearchTool,
|
||||
[X_SEARCH_TOOL_ID]: xSearchTool,
|
||||
},
|
||||
hasWebSearch: true,
|
||||
hasXSearch: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -68,7 +80,7 @@ describe('AiModelConfigService', () => {
|
|||
getRawOpenAIProvider: jest.fn(),
|
||||
});
|
||||
|
||||
const result = service.getChatNativeSearchTools(
|
||||
const result = service.getChatNativeSearchPlan(
|
||||
{
|
||||
sdkPackage: AI_SDK_OPENAI,
|
||||
providerName: 'openai',
|
||||
|
|
@ -76,6 +88,10 @@ describe('AiModelConfigService', () => {
|
|||
{ useProviderNativeWebSearch: false },
|
||||
);
|
||||
|
||||
expect(result).toEqual({});
|
||||
expect(result).toEqual({
|
||||
tools: {},
|
||||
hasWebSearch: false,
|
||||
hasXSearch: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ import { ToolSet } from 'ai';
|
|||
import { isAgentCapabilityEnabled } from 'twenty-shared/ai';
|
||||
|
||||
import { type ToolProviderAgent } from 'src/engine/core-modules/tool-provider/interfaces/tool-provider-agent.type';
|
||||
import { SEARCH_TOOL_NAMES } from 'src/engine/core-modules/tool-provider/constants/search-tool-names.const';
|
||||
import {
|
||||
WEB_SEARCH_TOOL_ID,
|
||||
X_SEARCH_TOOL_ID,
|
||||
} from 'src/engine/core-modules/tool-provider/constants/search-tool-ids.const';
|
||||
import { AGENT_CONFIG } from 'src/engine/metadata-modules/ai/ai-agent/constants/agent-config.const';
|
||||
import {
|
||||
AI_SDK_ANTHROPIC,
|
||||
|
|
@ -17,6 +20,11 @@ import { type RegisteredAiModel } from 'src/engine/metadata-modules/ai/ai-models
|
|||
import { SdkProviderFactoryService } from 'src/engine/metadata-modules/ai/ai-models/services/sdk-provider-factory.service';
|
||||
|
||||
type NativeSearchToolEntry = [string, ToolSet[string]];
|
||||
type ChatNativeSearchPlan = {
|
||||
tools: ToolSet;
|
||||
hasWebSearch: boolean;
|
||||
hasXSearch: boolean;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class AiModelConfigService {
|
||||
|
|
@ -60,16 +68,22 @@ export class AiModelConfigService {
|
|||
return Object.fromEntries(toolEntries) as ToolSet;
|
||||
}
|
||||
|
||||
getChatNativeSearchTools(
|
||||
getChatNativeSearchPlan(
|
||||
model: RegisteredAiModel,
|
||||
options: { useProviderNativeWebSearch: boolean },
|
||||
): ToolSet {
|
||||
): ChatNativeSearchPlan {
|
||||
const toolEntries = this.getNativeSearchToolEntries(model, {
|
||||
exposeWebSearch: options.useProviderNativeWebSearch,
|
||||
exposeTwitterSearch: model.sdkPackage === AI_SDK_XAI,
|
||||
});
|
||||
|
||||
return Object.fromEntries(toolEntries) as ToolSet;
|
||||
return {
|
||||
tools: Object.fromEntries(toolEntries) as ToolSet,
|
||||
hasWebSearch: toolEntries.some(
|
||||
([toolId]) => toolId === WEB_SEARCH_TOOL_ID,
|
||||
),
|
||||
hasXSearch: toolEntries.some(([toolId]) => toolId === X_SEARCH_TOOL_ID),
|
||||
};
|
||||
}
|
||||
|
||||
private getAnthropicProviderOptions(
|
||||
|
|
@ -129,10 +143,7 @@ export class AiModelConfigService {
|
|||
}
|
||||
|
||||
return [
|
||||
[
|
||||
SEARCH_TOOL_NAMES.webSearch,
|
||||
anthropicProvider.tools.webSearch_20250305(),
|
||||
],
|
||||
[WEB_SEARCH_TOOL_ID, anthropicProvider.tools.webSearch_20250305()],
|
||||
];
|
||||
}
|
||||
case AI_SDK_BEDROCK: {
|
||||
|
|
@ -150,7 +161,7 @@ export class AiModelConfigService {
|
|||
|
||||
return [
|
||||
[
|
||||
SEARCH_TOOL_NAMES.webSearch,
|
||||
WEB_SEARCH_TOOL_ID,
|
||||
bedrockProvider.tools.webSearch_20250305() as ToolSet[string],
|
||||
],
|
||||
];
|
||||
|
|
@ -168,9 +179,7 @@ export class AiModelConfigService {
|
|||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
[SEARCH_TOOL_NAMES.webSearch, openAiProvider.tools.webSearch()],
|
||||
];
|
||||
return [[WEB_SEARCH_TOOL_ID, openAiProvider.tools.webSearch()]];
|
||||
}
|
||||
case AI_SDK_XAI: {
|
||||
const xaiProvider = this.sdkProviderFactory.getRawXaiProvider(
|
||||
|
|
@ -185,14 +194,14 @@ export class AiModelConfigService {
|
|||
|
||||
if (options.exposeWebSearch) {
|
||||
toolEntries.push([
|
||||
SEARCH_TOOL_NAMES.webSearch,
|
||||
WEB_SEARCH_TOOL_ID,
|
||||
xaiProvider.tools.webSearch() as ToolSet[string],
|
||||
]);
|
||||
}
|
||||
|
||||
if (options.exposeTwitterSearch) {
|
||||
toolEntries.push([
|
||||
SEARCH_TOOL_NAMES.xSearch,
|
||||
X_SEARCH_TOOL_ID,
|
||||
xaiProvider.tools.xSearch() as ToolSet[string],
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue