refactor: standardize AI acronym to Ai (PascalCase) across internal identifiers (#19837)
## Summary
The "AI" acronym was rendered inconsistently across the codebase. The
backend AI module had settled on PascalCase `Ai` (`AiAgentModule`,
`AiBillingService`, `AiChatModule`, `AiModelRegistryService`, etc.),
while frontend components, several DTOs, a few types, and shared
identifiers still used all-caps `AI` (`AIChatTab`,
`AISystemPromptPreviewDTO`, `SettingsPath.AIPrompts`, ...). CLAUDE.md
specifies PascalCase for classes; this PR normalizes everything internal
to `Ai`.
**This is a pure internal rename.** The GraphQL schema is untouched —
`@ObjectType` decorator string arguments, resolver method names (which
become Query/Mutation field names), gql template contents, and the
`generated-metadata/graphql.ts` file are preserved verbatim. The only
visible change is TypeScript identifiers and file names.
## Also folded in (adjacent cleanups)
- **`AgentModelConfigService` → `AiModelConfigService`**. Lives in
`ai-models/` and is used by multiple AI code paths, not just the Agent
entity. The "Agent" prefix was misleading.
- **`generate-text-input.dto.ts` → `generate-text.input.ts`**. The
`ai-agent/dtos/` folder already uses `<entity>.input.ts` convention for
Input classes (`create-agent.input.ts` etc.); the old path mixed
`.dto.ts` file extension with a class that has no DTO suffix. File
rename only; class stays `GenerateTextInput`.
- **Removed stale TODO** in `ai-model-config.type.ts` that asked for the
`AiModelConfig` rename that this PR performs.
## Rename methodology
Bulk rename via perl with anchored regex
`(?<!['"])(?<![A-Z.])AI([A-Z])(?=[a-z])/Ai$1/g`:
- **Lookbehind for non-uppercase** skips adjacent acronyms (`MOSAIC`,
`OIDCSSO`) and leaves `AIRBNB_ID` alone.
- **Lookbehind for non-quote** protects most string literals.
- **Lookahead for lowercase** restricts matches to PascalCase
identifiers (`AIChatTab`), leaving SCREAMING_SNAKE constants untouched.
Strict file-scope exclusions: `generated-metadata/**`, `generated/**`,
`locales/**`, `migrations/**`, `illustrations/**`, `halftone/**`, and
the two gql template files (`queries/getAISystemPromptPreview.ts`,
`mutations/uploadAIChatFile.ts`).
Post-rename reverts for identifiers where the regex was too eager:
- Backend resolver method names kept: `getAISystemPromptPreview`,
`uploadAIChatFile` (they are GraphQL field names).
- `@ObjectType('AdminAIModels')` / `('AISystemPromptPreview')` /
`('AISystemPromptSection')` kept as-is.
- Backend classes `ClientAIModelConfig` / `AdminAIModelConfig` kept
as-is (they use `@ObjectType()` with no argument, so the class name IS
the schema name).
- External-library symbols restored: `OpenAIProvider`,
`createOpenAICompatible`, `vercelAIIntegration`.
File renames use a two-step rename to work on macOS case-insensitive
filesystems: `git mv X.tsx X.tsx.tmp && git mv X.tsx.tmp renamed.tsx`.
## Diff audit
- 0 changes to migrations
- 0 changes to locale `.po` / `.ts` files
- 0 changes to `generated-metadata/graphql.ts`
- 0 changes to website illustration files (base64 blobs preserved)
- 0 renames inside user-facing translation strings (`t\`…\``,
`msg\`…\``, `<Trans>…</Trans>`)
## Test plan
- [x] `npx nx typecheck twenty-server` — PASS
- [x] `npx nx typecheck twenty-front` — PASS
- [x] `npx jest ai-model admin agent-role` — 79/79 PASS
- [x] `npx oxlint --type-aware` on 118 changed files — 0 errors
- [x] `npx prettier --check` on 118 changed files — clean
- [ ] CI
This commit is contained in:
@@ -23,7 +23,7 @@ import { AdminWorkspaceChatThreadDTO } from 'src/engine/core-modules/admin-panel
|
||||
import { AdminChatThreadMessagesDTO } from 'src/engine/core-modules/admin-panel/dtos/admin-chat-thread-messages.dto';
|
||||
import { ApplicationRegistrationEntity } from 'src/engine/core-modules/application/application-registration/application-registration.entity';
|
||||
import { ApplicationRegistrationService } from 'src/engine/core-modules/application/application-registration/application-registration.service';
|
||||
import { AdminAIModelsDTO } from 'src/engine/core-modules/client-config/client-config.entity';
|
||||
import { AdminAiModelsDTO } from 'src/engine/core-modules/client-config/client-config.entity';
|
||||
import { UsageBreakdownItemDTO } from 'src/engine/core-modules/usage/dtos/usage-breakdown-item.dto';
|
||||
import { UsageAnalyticsService } from 'src/engine/core-modules/usage/services/usage-analytics.service';
|
||||
import { AiModelRole } from 'src/engine/metadata-modules/ai/ai-models/types/ai-model-role.enum';
|
||||
@@ -211,8 +211,8 @@ export class AdminPanelResolver {
|
||||
}
|
||||
|
||||
@UseGuards(AdminPanelGuard)
|
||||
@Query(() => AdminAIModelsDTO)
|
||||
async getAdminAiModels(): Promise<AdminAIModelsDTO> {
|
||||
@Query(() => AdminAiModelsDTO)
|
||||
async getAdminAiModels(): Promise<AdminAiModelsDTO> {
|
||||
const resolvedProviders =
|
||||
this.aiModelRegistryService.getResolvedProvidersForAdmin();
|
||||
|
||||
|
||||
+8
-8
@@ -39,7 +39,7 @@ export class NativeModelCapabilities {
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class ClientAIModelConfig {
|
||||
export class ClientAiModelConfig {
|
||||
@Field(() => String)
|
||||
// Composite model id (`provider/modelName`) for this workspace; matches registry and admin APIs.
|
||||
modelId: ModelId;
|
||||
@@ -88,7 +88,7 @@ export class ClientAIModelConfig {
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class AdminAIModelConfig {
|
||||
export class AdminAiModelConfig {
|
||||
@Field(() => String)
|
||||
// Composite model id (`provider/modelName`) used for toggles, defaults, and registry lookups.
|
||||
modelId: string;
|
||||
@@ -143,10 +143,10 @@ export class AdminAIModelConfig {
|
||||
dataResidency?: string;
|
||||
}
|
||||
|
||||
@ObjectType('AdminAIModels')
|
||||
export class AdminAIModelsDTO {
|
||||
@Field(() => [AdminAIModelConfig])
|
||||
models: AdminAIModelConfig[];
|
||||
@ObjectType('AdminAiModels')
|
||||
export class AdminAiModelsDTO {
|
||||
@Field(() => [AdminAiModelConfig])
|
||||
models: AdminAiModelConfig[];
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
// Composite model id for the default “smart” role (`provider/modelName`).
|
||||
@@ -249,8 +249,8 @@ export class ClientConfig {
|
||||
@Field(() => Billing, { nullable: false })
|
||||
billing: Billing;
|
||||
|
||||
@Field(() => [ClientAIModelConfig])
|
||||
aiModels: ClientAIModelConfig[];
|
||||
@Field(() => [ClientAiModelConfig])
|
||||
aiModels: ClientAiModelConfig[];
|
||||
|
||||
@Field(() => Boolean)
|
||||
signInPrefilled: boolean;
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ import { SupportDriver } from 'src/engine/core-modules/twenty-config/interfaces/
|
||||
|
||||
import { MaintenanceModeService } from 'src/engine/core-modules/admin-panel/maintenance-mode.service';
|
||||
import {
|
||||
type ClientAIModelConfig,
|
||||
type ClientAiModelConfig,
|
||||
type ClientConfig,
|
||||
type NativeModelCapabilities,
|
||||
} from 'src/engine/core-modules/client-config/client-config.entity';
|
||||
@@ -76,7 +76,7 @@ export class ClientConfigService {
|
||||
? (resolvedProviders[providerName]?.label ?? providerName)
|
||||
: undefined;
|
||||
|
||||
const aiModels: ClientAIModelConfig[] = availableModels.map(
|
||||
const aiModels: ClientAiModelConfig[] = availableModels.map(
|
||||
(registeredModel) => {
|
||||
const modelConfig = this.aiModelRegistryService.getModelConfig(
|
||||
registeredModel.modelId,
|
||||
|
||||
+5
-5
@@ -1,14 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FileAIChatResolver } from 'src/engine/core-modules/file/file-ai-chat/resolvers/file-ai-chat.resolver';
|
||||
import { FileAIChatService } from 'src/engine/core-modules/file/file-ai-chat/services/file-ai-chat.service';
|
||||
import { FileAiChatResolver } from 'src/engine/core-modules/file/file-ai-chat/resolvers/file-ai-chat.resolver';
|
||||
import { FileAiChatService } from 'src/engine/core-modules/file/file-ai-chat/services/file-ai-chat.service';
|
||||
import { FileUrlModule } from 'src/engine/core-modules/file/file-url/file-url.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
|
||||
@Module({
|
||||
imports: [FileUrlModule, ApplicationModule, PermissionsModule],
|
||||
providers: [FileAIChatService, FileAIChatResolver],
|
||||
exports: [FileAIChatService],
|
||||
providers: [FileAiChatService, FileAiChatResolver],
|
||||
exports: [FileAiChatService],
|
||||
})
|
||||
export class FileAIChatModule {}
|
||||
export class FileAiChatModule {}
|
||||
|
||||
+5
-5
@@ -8,7 +8,7 @@ import type { FileUpload } from 'graphql-upload/processRequest.mjs';
|
||||
|
||||
import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
|
||||
import { FileWithSignedUrlDTO } from 'src/engine/core-modules/file/dtos/file-with-sign-url.dto';
|
||||
import { FileAIChatService } from 'src/engine/core-modules/file/file-ai-chat/services/file-ai-chat.service';
|
||||
import { FileAiChatService } from 'src/engine/core-modules/file/file-ai-chat/services/file-ai-chat.service';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
@@ -21,12 +21,12 @@ import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
@UsePipes(ResolverValidationPipe)
|
||||
@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter)
|
||||
@MetadataResolver()
|
||||
export class FileAIChatResolver {
|
||||
constructor(private readonly fileAIChatService: FileAIChatService) {}
|
||||
export class FileAiChatResolver {
|
||||
constructor(private readonly fileAiChatService: FileAiChatService) {}
|
||||
|
||||
@Mutation(() => FileWithSignedUrlDTO)
|
||||
@UseGuards(SettingsPermissionGuard(PermissionFlagType.UPLOAD_FILE))
|
||||
async uploadAIChatFile(
|
||||
async uploadAiChatFile(
|
||||
@AuthWorkspace()
|
||||
{ id: workspaceId }: WorkspaceEntity,
|
||||
@Args({ name: 'file', type: () => GraphQLUpload })
|
||||
@@ -35,7 +35,7 @@ export class FileAIChatResolver {
|
||||
const stream = createReadStream();
|
||||
const buffer = await streamToBuffer(stream);
|
||||
|
||||
return await this.fileAIChatService.uploadFile({
|
||||
return await this.fileAiChatService.uploadFile({
|
||||
file: buffer,
|
||||
filename,
|
||||
workspaceId,
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { FileUrlService } from 'src/engine/core-modules/file/file-url/file-url.s
|
||||
import { extractFileInfo } from 'src/engine/core-modules/file/utils/extract-file-info.utils';
|
||||
import { sanitizeFile } from 'src/engine/core-modules/file/utils/sanitize-file.utils';
|
||||
@Injectable()
|
||||
export class FileAIChatService {
|
||||
export class FileAiChatService {
|
||||
constructor(
|
||||
private readonly fileStorageService: FileStorageService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { FileStorageModule } from 'src/engine/core-modules/file-storage/file-storage.module';
|
||||
import { FileAIChatModule } from 'src/engine/core-modules/file/file-ai-chat/file-ai-chat.module';
|
||||
import { FileAiChatModule } from 'src/engine/core-modules/file/file-ai-chat/file-ai-chat.module';
|
||||
import { FilePathGuard } from 'src/engine/core-modules/file/guards/file-path-guard';
|
||||
import { FileDeletionJob } from 'src/engine/core-modules/file/jobs/file-deletion.job';
|
||||
import { FileWorkspaceFolderDeletionJob } from 'src/engine/core-modules/file/jobs/file-workspace-folder-deletion.job';
|
||||
@@ -34,7 +34,7 @@ import { FileService } from './services/file.service';
|
||||
FilesFieldModule,
|
||||
FileCorePictureModule,
|
||||
FileWorkflowModule,
|
||||
FileAIChatModule,
|
||||
FileAiChatModule,
|
||||
FileEmailAttachmentModule,
|
||||
SecureHttpClientModule,
|
||||
],
|
||||
@@ -53,7 +53,7 @@ import { FileService } from './services/file.service';
|
||||
FilesFieldModule,
|
||||
FileCorePictureModule,
|
||||
FileWorkflowModule,
|
||||
FileAIChatModule,
|
||||
FileAiChatModule,
|
||||
FileEmailAttachmentModule,
|
||||
],
|
||||
controllers: [FileController],
|
||||
|
||||
@@ -23,8 +23,8 @@ export enum MetricsKeys {
|
||||
WorkflowRunThrottled = 'workflow-run/throttled',
|
||||
WorkflowRunFailedToEnqueue = 'workflow-run/failed/to-enqueue',
|
||||
WorkflowRunSystemError = 'workflow-run/system-error',
|
||||
AIToolExecutionFailed = 'ai-tool-execution/failed',
|
||||
AIToolExecutionSucceeded = 'ai-tool-execution/succeeded',
|
||||
AiToolExecutionFailed = 'ai-tool-execution/failed',
|
||||
AiToolExecutionSucceeded = 'ai-tool-execution/succeeded',
|
||||
SchemaVersionMismatch = 'schema-version/mismatch',
|
||||
AppVersionMismatch = 'app-version/mismatch',
|
||||
CronJobDeletedWorkspace = 'cron-job/deleted-workspace',
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ import { type ToolProviderContext } from 'src/engine/core-modules/tool-provider/
|
||||
|
||||
import { ToolCategory } from 'twenty-shared/ai';
|
||||
import { WebSearchService } from 'src/engine/core-modules/web-search/web-search.service';
|
||||
import { AgentModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/agent-model-config.service';
|
||||
import { AiModelConfigService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-config.service';
|
||||
import { AiModelRegistryService } from 'src/engine/metadata-modules/ai/ai-models/services/ai-model-registry.service';
|
||||
|
||||
// SDK-native tools (anthropic webSearch, etc.) are opaque and not serializable.
|
||||
@@ -18,7 +18,7 @@ export class NativeModelToolProvider implements NativeToolProvider {
|
||||
readonly category = ToolCategory.NATIVE_MODEL;
|
||||
|
||||
constructor(
|
||||
private readonly agentModelConfigService: AgentModelConfigService,
|
||||
private readonly aiModelConfigService: AiModelConfigService,
|
||||
private readonly aiModelRegistryService: AiModelRegistryService,
|
||||
private readonly webSearchService: WebSearchService,
|
||||
) {}
|
||||
@@ -39,7 +39,7 @@ export class NativeModelToolProvider implements NativeToolProvider {
|
||||
const registeredModel =
|
||||
await this.aiModelRegistryService.resolveModelForAgent(context.agent);
|
||||
|
||||
return this.agentModelConfigService.getNativeModelTools(
|
||||
return this.aiModelConfigService.getNativeModelTools(
|
||||
registeredModel,
|
||||
context.agent,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user