feat(dotAI): Implement new dotAI config page - #37048
Conversation
…y and configuration metadata - Added new model provider strategies for various AI services (Anthropic, Azure OpenAI, Bedrock, Google AI, OpenAI, OpenRouter, Vertex AI). - Introduced enum to define supported capabilities (CHAT, EMBEDDINGS, IMAGE). - Implemented and to manage configuration fields for each provider. - Created to aggregate provider capabilities and fields for dynamic form rendering. - Added REST endpoint to expose provider metadata for client consumption. - Updated OpenAPI documentation to reflect new endpoints and data structures. - Added unit tests for provider metadata functionality. This commit enhances the AI provider configuration system, allowing for more flexible and dynamic integration of various AI models.
|
Claude finished @KevinDavilaDotCMS's task in 2m 39s —— View job dotAI Config Page — Re-reviewRe-reviewed the latest commit ( New Issues
Resolved
ExistingThese were acknowledged by the author as out-of-scope follow-ups (reasonable, but tracking them here):
Semgrep's LangChain4j finding was correctly triaged as acceptable risk (fixed hardcoded test prompt, no untrusted input). Nothing blocking from my pass — the one new item (settings-card additional-property collision) is non-blocking but cheap to close for parity with the capability-card fix. |
…forms and additional properties - Refactored the AI configuration detail component to improve layout and user experience. - Introduced new components for capability cards, settings, and dynamic fields to support various AI provider configurations. - Added functionality for managing additional properties in a flexible key/value format. - Implemented loading states and error handling for better user feedback during configuration. - Created constants for capability metadata and settings fields to streamline configuration management. This commit significantly enhances the AI provider configuration interface, allowing for more intuitive and dynamic interactions.
…ocalization and error handling - Refactored the AI configuration detail component to utilize new localization keys for titles, subtitles, and button labels. - Enhanced error handling by integrating localized error messages for loading and saving configurations. - Updated constants for capability metadata and settings fields to support dynamic localization. - Improved user feedback with loading indicators and unsaved changes notifications using localized strings. - Added new localization keys to the Language.properties file to support the changes. This commit enhances the user experience by providing a more localized and informative interface for AI configuration management.
…ucture - Cleaned up import statements in the AI configuration detail component and its subcomponents for better readability and organization. - Moved the import to the appropriate location in the file. - Adjusted the import order in and to maintain consistency and improve clarity. These changes streamline the code structure, making it easier to navigate and maintain.
…entials - Added checks to ensure only CMS admins can test AI provider connections, enhancing security. - Updated OpenAPI documentation to reflect the new requirement for admin access. - Implemented a method to validate that masked credentials are only resolved when the posted provider and endpoint match the stored configuration, preventing potential credential exfiltration. - Introduced unit tests to cover various scenarios for masked credential resolution, ensuring robust functionality and security. These changes improve the security posture of the AI provider resource and ensure that sensitive information is handled appropriately.
🐳 PR Docker test imageLatest build for commit docker pull dotcms/dotcms-test:pr-37048-36826-dotai-provider-configuration-ui
docker pull dotcms/dotcms-test:pr-37048-36826-dotai-provider-configuration-ui_bf3b452 |
… AI configuration fields - Introduced unit tests for the function to validate field visibility rules based on requirements and types. - Refactored the AI capability card component to utilize the new visibility logic, ensuring required and specific optional fields are displayed above the Advanced panel. - Updated the HTML templates to reflect changes in field visibility, enhancing user experience by clearly distinguishing between visible and advanced fields. - Improved the organization of the AI configuration detail component for better maintainability. These changes enhance the functionality and reliability of the AI configuration interface, ensuring that users have a clearer understanding of which fields are essential and which are optional.
…d validation - Added a new function to enforce validation rules for fields that are conditionally required based on the presence of sibling fields. - Updated the AI configuration detail component and its associated tests to utilize the new validator, ensuring that fields like and are validated correctly based on each other's values. - Enhanced unit tests to cover various scenarios for the new validation logic, improving the robustness of the AI configuration interface. - Refactored related components to ensure proper integration of the new validation logic. These changes enhance the validation capabilities of the AI configuration, providing a more intuitive user experience by enforcing conditional requirements effectively.
rjvelazco
left a comment
There was a problem hiding this comment.
Issues
Important
1. onlySelf: true leaves the parent FormGroup stale-INVALID
- File:
dot-ai-capability-card.component.ts:~320(therequiredUnlessFieldsrecheck). - Mechanism:
modelis constructed beforedeploymentNameexists. - Result: its validator sees no parent, returns an error, and the group caches INVALID.
- The recheck flips
modelto VALID butonlySelf: trueskips parent recalculation. - User impact: an Azure config saved with only
deploymentNameloads as invalid. - Consequence: Save and Test Connection stay blocked until the user touches a field.
- Contradiction: the spec at line 254 asserts the opposite, so that test should be failing.
- Verify:
pnpm nx test dotcms-ui --testPathPattern=dot-ai-capability-card. - Fix: drop
onlySelf: true, keepemitEvent: false.
2. Bedrock and Vertex clients are never closed
- File:
ProviderConnectionTester.java:~87(test()). - Issue: each call builds a model wrapping an
AutoCloseableSDK client, then discards it. - Affected:
BedrockRuntimeClient/BedrockRuntimeAsyncClientandVertexAI. - Why it matters: repeated admin "Test connection" clicks accumulate connection pools and threads.
- Verify: whether LangChain4j closes these itself or registers a shutdown hook.
- Fix: close the client after the test call if it does not.
3. Test path uses lenient host resolution, save uses strict
- File:
AiProviderResource.java(testConnectioncallsAiHostResolver.resolveHost). - Behavior: an unresolvable
siteIdsilently falls back to the current host. - Effect: masked credentials then resolve against a different site's stored config.
- Risk: an admin gets a misleading "success" for a site they never tested.
- Fix: use
resolveHostStrictfor consistency withPUT /v1/ai/completions/config.
Minor
- Locale-sensitive enum parsing:
capability.toUpperCase()breaks undertr_TR. - Fix: pass
Locale.ROOTto bothtoUpperCase()andtoLowerCase(). - Dynamic field labels bypass i18n:
humanizeFieldName()derives English from field names. - Consequence: every provider field label ships untranslated, unlike the rest of the page.
- Raw provider errors reach the UI:
friendlyMessage()forwards SDK exception text verbatim. - Risk: some SDKs embed the request URL, which can carry the API key as a query param.
- Mixed message semantics:
testResult.messageholds i18n keys locally, raw English from the server. - Fragility: this only works because
DotMessageService.get()echoes unknown keys. - Unrelated file:
core-web/.sdkmanrcpins Java 25 inside the frontend directory. - Question: does this belong in this PR, or at the repo root?
- Stale subscriptions:
rebuildFieldsGroupre-subscribes on every provider switch. - Impact: harmless in practice, since discarded groups never emit again.
- Check unused imports in
CompletionsResourceafter the four private methods were removed.
Recommendations
- Add a spec for
dot-ai-config-detail.component.ts: dirty tracking and payload assembly are untested. - Confirm clearing behavior:
buildPayloadSectiondrops empty values from the payload. - Open question: can a user actually unset a previously-saved optional field like
temperature? - Consider a contract test asserting every strategy's
configFieldsthrows only for unsupported capabilities. - Untick items: the PR body still has placeholder text ("change 1", "original screenshot") and unchecked boxes.
zJaaal
left a comment
There was a problem hiding this comment.
Review: correctness pass on the new dotAI config page
Read every hunk plus surrounding context. 9 inline findings below, ordered roughly by severity - the two flagged HIGH are both silent-data-loss paths in the save flow, so they are the ones worth looking at first.
One more finding that has no good inline anchor:
LOW - dotCMS/src/main/webapp/WEB-INF/openapi/openapi.yaml (/v1/ai/providers/test/{capability}): the generated requestBody schema is type: string, taken from the String body parameter, even though the endpoint expects a JSON object. The @RequestBody(content = @Content(schema = @Schema(implementation = Map.class))) annotation is not winning over the parameter type, so clients generated from the spec will send a JSON-encoded string. A typed request DTO would fix both the spec and the CLAUDE.md "@Schema must match the actual type" rule.
Checked and confirmed not bugs (so they do not get re-litigated)
- The
updateValueAndValidity({ onlySelf: true })recheck inrebuildFieldsGroupdoes leave the parent group's aggregate status stale (verified with a throwaway jest probe: the child flips to valid, the group does not). It is harmless in practice becauseFormGroupDirective.addControlre-validates each control with parent propagation on render, and PrimeNG'sp-panelprojects its content even when collapsed - so everyrequiredUnlessfield is always registered and the group status is repaired before any user action. Worth knowing it depends on that, though. AiProviderResource:199does not leak the resolved secret through the Jackson 400 message: Jackson 2.16+ disablesINCLUDE_SOURCE_IN_LOCATIONby default (verified against 2.17.2, the version inbom/application/pom.xml), so the source snippet is redacted.API_ENDPOINT+/providersresolves correctly to/api/v1/ai/providers.pInputTextdoes expose aninvalidinput in PrimeNG 21.1.3, so the[invalid]bindings compile.- Every key in
SETTINGS_ADVANCED_FIELDSmatches anAppKeyssettingsKey. ProviderConfigMerger.containsMasked/containsMaskedCredentialexist with the semantics the new resource assumes, andProviderConfigis@JsonIgnoreProperties(ignoreUnknown = true), so the free-form additional properties do not break deserialization.
Automated review authored by Claude (Claude Code), posted from @zJaaal's account.
|
It looks good overall! In addition to Jal's feedback I found these: 🟡 The page no longer shows which site's configuration is being edited
Suggestion: surface the site name in the page heading 🟢 Non-blocking: Test Connection silently ignores additional properties The additional properties escape hatch lets users add keys the form doesn't model, and those keys are persisted on save. But the test endpoint deserializes the body into Suggestion: Just flagging it. Options if you want to address it later: reject unknown keys with a notice, or state in the UI that the test ignores them. |
- Clear additional properties on provider switch and stop a stray row from silently overriding a real field with the same name - Preserve non-string additional-property values (e.g. Vertex JSON credentials) instead of corrupting them via String() - Make capability parsing in the test-connection endpoint locale independent and use the strict host resolver, consistent with save - Apply a longer default timeout to image connection tests, since real generation often exceeds the chat/embeddings default - Make the requiredUnless cross-field validity fix explicit instead of relying on incidental Angular re-render timing - Re-mask a SECRET field as soon as its saved placeholder is edited, so a newly-typed secret is never shown in clear text - Show the site this configuration applies to - Block Save instead of silently saving an empty/default config when the initial load fails - Remove an unrelated core-web/.sdkmanrc picked up in this branch Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Went through the rest of the feedback and fixed everything actionable in the last commit (data-loss on load failure, additional-properties override/clear-on-switch, imageSize duplication, image test timeout, locale-sensitive parsing, secret re-masking, missing site name, host-resolution consistency). A few remaining items I'm intentionally leaving as-is for this PR — flagging why instead of silently skipping them: Bedrock/Vertex client leak on Test Connection (@rjvelazco, also flagged by the automated review) — real concern, but it's a pre-existing gap in how
Test Connection ignores additional properties (@ihoffmann-dot) — accurate: Mixed i18n semantics in |
0817.mov
Summary
Redesigns the dotAI provider configuration page (per-site Chat/Embeddings/Image capabilities plus shared settings) and adds a real "Test Connection" flow, driven entirely by provider metadata from the backend so new providers need no frontend changes.
Backend
POST /v1/ai/providers/test/{capability}endpoint (AiProviderResource) that builds the real LangChain4j model for a capability and issues one minimal live call.requiredUnlessfield metadata (ProviderField) so a provider can declare "either this field or that sibling field is required" (e.g. Azure'smodel/deploymentName) without any provider-specific frontend code.*****) to an attacker-controlledendpoint/providerduring connection testing (targetsStoredDestination).IMAGE, since real generation routinely exceeds the chat/embeddings default).AiHostResolver, used consistently by both the save and test-connection endpoints.Frontend
dot-ai-config-detailpage: white background, per-capability cards (Chat/Embeddings/Image), a shared settings card, and an "Additional properties" escape hatch for provider fields not yet modeled.dot-ai-dynamic-field) purely from provider metadata — text/number/secret inputs, required-field markers, and cross-fieldrequiredUnlessvalidation.SECRETfield with an already-saved value renders as read-only plain text (no reveal toggle, since the real secret never reaches the browser); editing it switches back to a masked password input so a newly-typed secret isn't shown in clear text.Language.properties(apps.ai.*keys).Fixes from review
Addressed the review feedback that was in-scope for this PR (see PR comments for full discussion): additional-properties no longer silently override a real field with the same name and are cleared on provider switch; non-string saved values (e.g. Vertex's JSON credentials) round-trip without
String()corruption; the connection-test capability parsing is locale-independent; therequiredUnlesscross-field validity fix no longer depends on incidental Angular re-render timing; the page now shows the site it's configuring; and the page blocks Save (instead of silently saving an empty/default config) when the initial load fails.Testing
dot-ai-capability-card,dot-ai-dynamic-field,dot-ai-config-detail, anddot-ai-config.constants.AiProviderResource,ProviderConnectionTester, and provider metadata.Checklist