feat: route AI requests through Drupal AI module#43
Conversation
Add AiChatController that proxies CKEditor AI Agent requests through the ai module's AiProviderPluginManager. When the ai module is available, getDynamicPluginConfig() and getCkEditorConfig() now pass endpointUrl with engine 'dxai' instead of exposing the apiKey to the browser. Closes #42
PR Review:
|
| Category | Issue | Severity |
|---|---|---|
| Security | access content permission too broad |
Critical |
| Security | No CSRF protection on POST endpoint | Critical |
| Security | No input validation on proxy passthrough | High |
| Architecture | Hard deps contradict fallback logic | High |
| Architecture | Provider hardcoded to 'dxpr' |
Medium |
| Architecture | Duplicate config logic in two classes | Medium |
| Reliability | ob_flush() warning when no buffering |
Low |
| Code quality | Hardcoded model default | Low |
| Code quality | Variable naming conventions | Low |
Recommendation: Do not merge until the security issues (permission, CSRF, input validation) and the hard-vs-soft dependency contradiction are resolved. The core approach is sound and well-structured, but it needs hardening before it's production-ready.
- Add dedicated 'use ckeditor ai agent' permission instead of 'access content' - Add CSRF request header token check on proxy route - Validate message roles, types, and model format in AiChatController - Type-check all passthrough fields (providers, allowed_html_tags, etc.) - Make ai/ai_provider_dxpr soft dependencies (suggest in composer, removed from info.yml) to match the existing fallback logic - Guard ob_flush() calls with ob_get_level() check - Fix variable naming to Drupal snake_case convention
|
Thanks for the thorough review! Pushed fixes in b139a39. Here's what I addressed and where I'm pushing back: Addressed1. Permission too permissive — Fixed. Added a dedicated 2. CSRF protection — Fixed. Added 3. Input validation — Fixed. The controller now:
4. Hard deps → soft deps — Fixed. Removed 8. 10. Variable naming — Fixed. Pushing back5. Hardcoded 6. Duplicate logic in two classes — The shared logic is 3 lines (module check + URL generation + engine assignment). 7. Rate limiting — This is a cross-cutting infrastructure concern, not specific to this endpoint. Drupal has contrib modules for rate limiting ( 9. Hardcoded model default 11. |
Use getDefaultProviderForOperationType('chat') to resolve whichever
provider the site admin has configured. Falls back to the default
model_id from ai module config instead of hardcoding 'kavya-m1'.
DXPR-specific passthrough fields are safely ignored by other providers.
|
Update (e5679ad): Corrected my pushback on issue #5 — the controller now uses the default configured provider instead of hardcoding $default = $this->aiProviderManager->getDefaultProviderForOperationType('chat');
$provider = $this->aiProviderManager->createInstance($default['provider_id']);This means any provider configured in The DXPR-specific passthrough fields ( |
Document the server-side proxy architecture, AI module setup steps, new 'Use CKEditor AI Agent' permission, and multi-provider support.
Resolve merge conflicts (README.md, ai-agent.js build, aiagent.js source) and address review findings from the original PR: - Remove the three adapter service classes (AiChatProviderGateway, AiChatProviderAdapter, AiChatOutputAdapter); the controller now injects AiProviderPluginManager directly, matching dxpr_builder's pattern. The ai module is a hard dependency, so the callable indirection was unnecessary. - Remove direct-API fallback branches from AiAgent.php and AiAgentConfigurationManager.php. Since ai is a hard dependency, all requests always route through the Drupal proxy. API keys are never sent to the browser. - Remove ModuleHandlerInterface from AiAgent plugin and config manager (no longer needed; the ai module is always present). - Change route path from /api/ckeditor-ai-agent/ai/chat to /ckeditor-ai-agent/ai/chat (Drupal convention). - Update hook_requirements() to check for a configured default AI chat provider instead of checking for the Key module and a direct API key. - Add provider setup guidance to hook_install(): if no default chat provider is configured, display a warning with links to provider modules and the AI settings page. - Update README: remove "without AI module" fallback instructions, simplify installation steps, remove key_provider/endpointUrl from the config table. - Remove ai_chat_provider_gateway service from services.yml, remove @module_handler from configuration_manager arguments.
Replace the single "no default provider" check with three levels: 1. No provider modules installed: lists popular providers to install (DXPR, OpenAI, Anthropic, Ollama) with drupal.org links. 2. Provider modules present but none usable for chat: shows which providers are installed and explains that authentication (API key) is likely missing, with a link to AI settings. 3. Usable providers exist but no default selected: shows the count and names of ready providers, with a link to select a default. The OK state now shows the active provider name and model in the requirements value column. hook_install() reuses the same check via the shared helper, so the warning message on install matches exactly what status report shows.
Add ignoreErrors for ProviderProxy magic method delegation with a runtime guard that verifies __call still exists. Enable set -e so any failing step aborts CI instead of silently continuing. Add AI module directory check and treatPhpDocTypesAsCertain: false.
|
Reviewed current head Important baseline: because AI is a core feature in Findings I would treat as urgent before merge:
The strong parts of this PR: server-side routing, CSRF protection, no browser API key exposure, default-provider lookup, SSE-compatible streaming, and provider diagnostics are all directionally consistent with the DXPR Builder AI integration. The remaining work is mostly making the admin/editor UX and config contract fully match that architecture. |
1. Replace legacy Connection & Model Settings form section with an AI provider status panel that shows the active provider and links to AI settings. Removes API Key, Engine/Model, and Endpoint URL fields that conflicted with the server-side proxy architecture. 2. Stop injecting model from plugin/global config into CKEditor JS. The AI module's default provider and model now win; no hardcoded fallback to openai:gpt-4o or kavya-m1 from stale config. 3. Gate editor UI by permission: getDynamicPluginConfig() returns an empty array when the current user lacks 'use ckeditor ai agent', so users without the permission never see AI controls. 4. Forward the full HTML allowlist contract from the JS client to the provider: allowed_html_styles, allowed_html_attributes, and the allows_all_* boolean flags were previously dropped by the proxy. 5. Update PR description to remove stale references to optional-AI fallback mode and direct API key setup.
Remove the AiAgentKeyService class and all references to it: the AI module now handles provider authentication, making this service unused. Remove stale config schema keys (key_provider, apiKey, model, ollamaModel, endpointUrl) that belonged to the pre-proxy architecture.
Cover 8 scenarios: settings page access (anonymous, no permission, admin), chat proxy POST access (anonymous, no permission, missing CSRF, valid access), and hook_requirements on the status report.
212fdcc to
452840f
Compare
The CI environment does not install PHPUnit or Behat/Mink, so PHPStan cannot resolve test class references. Exclude tests/ from analysis, matching standard Drupal module practice.
Review of latest commitsThe new commits have addressed the major issues from the earlier review: the hard dependency contradiction is resolved (ai module is now fully required, fallback code removed), the adapter indirection is gone (controller uses A few items that still apply: 1. Duplicate
|
Prepares for dxpr/ckeditor_ai_agent#43 which adds a new permission controlling access to the AI proxy endpoint.
- Extract duplicate getTokenizedProxyEndpointUrl() into ProxyEndpointUrlTrait, used by both AiAgent and AiAgentConfigurationManager. - Move AI provider check logic from procedural _ckeditor_ai_agent_check_ai_provider() into AiAgentConfigurationManager::checkAiProvider(), eliminating the loadInclude pattern in the form trait. - Reorder stream default condition for readability: default case first, override second. - Remove unused imports (Url in AiAgent.php and install file).
Remove key:key and markdownify:markdownify from dependencies: the AI module now handles provider authentication and neither package is referenced in the source code. Remove legacy keys (key_provider, apiKey, model, ollamaModel, endpointUrl) from the CKEditor5 plugin schema. Add update_9005 to clean orphaned config values from global settings and per-editor plugin config left over from the pre-AI-module architecture.
Remove moderationKey and moderationEnable config since moderation should not bypass the AI module by calling OpenAI directly from the browser. Update README and desc.html to remove stale Key/Markdownify references and align with AI module ecosystem conventions.
The checkAiProvider() method uses REQUIREMENT_OK/ERROR/WARNING constants which are defined in install.inc. This file is auto-loaded in .install context but not when the method is called from the settings form or other service contexts.
Summary
AiChatControllerthat proxies CKEditor AI Agent requests through theaimodule'sAiProviderPluginManager, eliminating API key exposure in the browsergetDynamicPluginConfig()andgetCkEditorConfig()to setendpointUrlwith enginedxaiinstead of passingapiKeywhen theaimodule is availableaiandai_provider_dxpras module and composer dependenciesArchitecture:
Frontend → Drupal Controller → ai module → ai_provider_dxpr → Kavya APIFollows the same pattern as dxpr/dxpr_builder#4061.
Closes #42
Files changed
src/Controller/AiChatController.phpckeditor_ai_agent.routing.yml/api/ckeditor-ai-agent/ai/chatrouteckeditor_ai_agent.info.ymlaiandai_provider_dxprdependenciescomposer.jsondrupal/aianddrupal/ai_provider_dxprsrc/Plugin/CKEditor5Plugin/AiAgent.phpendpointUrl+ enginedxaiinstead ofapiKeysrc/AiAgentConfigurationManager.phpgetCkEditorConfig()ckeditor_ai_agent.services.ymlmodule_handlerandurl_generatorto config managerTest plan
aiandai_provider_dxprmodulesaimodule is not installed (direct API key mode)