Skip to content

Add an option to suggest improvements to paragraphs#1666

Merged
nygrenh merged 6 commits intomasterfrom
gutenberg-abilities
Mar 6, 2026
Merged

Add an option to suggest improvements to paragraphs#1666
nygrenh merged 6 commits intomasterfrom
gutenberg-abilities

Conversation

@nygrenh
Copy link
Member

@nygrenh nygrenh commented Mar 4, 2026

Summary by CodeRabbit

  • New Features

    • AI-powered paragraph editor toolbar: multiple writing actions, selectable suggestions dialog with diff preview, and apply-change flow.
    • Server-side AI suggestions endpoint and backend integration for editor suggestions.
  • Content Safety

    • Improved HTML/plain-text handling and robust sanitization for AI-generated paragraph content.
  • Localization

    • Added AI UI strings for Arabic, English, Finnish, Swedish, and Ukrainian.
  • Tests

    • New unit and system tests covering AI utilities and test teardowns.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Walkthrough

Adds end-to-end AI paragraph suggestion support: frontend Gutenberg toolbar and abilities, client backend call, paragraph HTML sanitization, server endpoint and Rust LLM integration, shared request/response types and guards, DB migration/seed, tests, and i18n strings across multiple locales.

Changes

Cohort / File(s) Summary
Package deps
services/cms/package.json
Added diff and dompurify dependencies.
Gutenberg editor hook
services/cms/src/components/editors/GutenbergEditor.tsx
Register editor AI abilities on mount and add BlockEdit filter for paragraph AI toolbar; cleanup on unmount.
AI framework types & registry
services/cms/src/utils/Gutenberg/ai/types.ts, services/cms/src/utils/Gutenberg/ai/registry.ts
New JSON-schema types, AbilityDefinition and category types; in-memory registry with register/get/execute and result validation.
AI abilities & menu catalog
services/cms/src/utils/Gutenberg/ai/abilities.ts, services/cms/src/utils/Gutenberg/ai/menu.ts
Concrete abilities (fix-spelling + placeholder abilities), request builders, ability registration, and data-driven action/menu/submenu catalogs with meta options.
Paragraph AI toolbar HOC & UI
services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx
HOC that augments core/paragraph BlockEdit: toolbar UI, ability execution flow, suggestion dialog with diff preview, and block content update on confirm.
Paragraph source & sanitization
services/cms/src/utils/Gutenberg/paragraphAiSource.ts, services/cms/src/utils/Gutenberg/paragraphHtmlSanitizer.ts
Utilities to extract/plain HTML detection and construct AI request payloads; robust DOMPurify-based HTML sanitizer preserving safe inline/block content and configurable allowed tags.
Client → backend
services/cms/src/services/backend/ai-suggestions.ts
New requestParagraphSuggestions function that posts paragraph payload to /ai-suggestions/paragraph and validates response.
Server controllers & routes
services/headless-lms/server/src/controllers/cms/ai_suggestions.rs, services/headless-lms/server/src/controllers/cms/mod.rs
New POST /api/v0/cms/ai-suggestions/paragraph controller (content/is_html, meta/context), auth checks, fetch LLM config, drop DB conn before LLM call, and route registration under /ai-suggestions.
Chatbot LLM integration (Rust)
services/headless-lms/chatbot/src/cms_ai_suggestion.rs, services/headless-lms/chatbot/src/lib.rs
New module to build prompts, enforce token/context limits, call LLM with JSON-schema output, parse and return suggestions; exported from crate.
DB migration, model, seeding
services/headless-lms/migrations/20260304120000_add_cms_paragraph_suggestion_task.*, services/headless-lms/models/src/application_task_default_language_models.rs, services/headless-lms/server/src/programs/seed/seed_application_task_llms.rs
Add cms-paragraph-suggestion application_task enum value, down migration adjustments, enum variant in model, and seed entry for default LLM with context_utilization 0.75.
Type bindings & runtime guards
services/headless-lms/server/src/ts_binding_generator.rs, shared-module/packages/common/src/bindings.ts, shared-module/packages/common/src/bindings.guard.ts
Export ParagraphSuggestion types to TypeScript bindings; add interfaces for meta/context/request/response and runtime type guards for them.
Locale additions
shared-module/packages/common/src/locales/{ar,en,fi,sv,uk}/cms.json
Add 50+ AI-related localization keys and translations (actions, groups, tones, submenus, toolbar/dialog labels).
Tests
services/cms/tests/utils/paragraphAiSource.test.ts, services/cms/tests/utils/paragraphHtmlSanitizer.test.ts, system-tests/src/tests/oauth/...
New unit tests for paragraph source and sanitizer (jsdom), plus added test.afterAll teardown hooks for redirect server in several system tests.
Misc small test data change
services/headless-lms/models/src/user_chapter_locking_statuses.rs
Seed data adjustment in a unit test setup.

Sequence Diagram

sequenceDiagram
    actor User
    participant Gutenberg as Gutenberg Editor
    participant Toolbar as Paragraph AI Toolbar HOC
    participant Registry as AI Abilities Registry
    participant CMSClient as CMS Backend Client
    participant Server as Headless-LMS Server
    participant Chatbot as Chatbot Service
    participant LLM as Language Model

    User->>Gutenberg: edit paragraph / trigger AI action
    Gutenberg->>Toolbar: open AI toolbar
    Toolbar->>Registry: executeAbility(name, { text, meta })
    Registry->>CMSClient: requestParagraphSuggestions(payload)
    CMSClient->>Server: POST /api/v0/cms/ai-suggestions/paragraph
    Server->>Server: auth, validate, fetch LLM config
    Server->>Chatbot: generate_paragraph_suggestions(input)
    Chatbot->>LLM: send JSON-schema guided request
    LLM-->>Chatbot: return structured suggestions
    Chatbot-->>Server: suggestions list
    Server-->>CMSClient: ParagraphSuggestionResponse
    CMSClient-->>Registry: ability result ({text, suggestions})
    Registry-->>Toolbar: deliver suggestions
    Toolbar->>User: show suggestion dialog (diff preview)
    User->>Toolbar: confirm selection
    Toolbar->>Gutenberg: update block content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Update deps 2026-03 #1665 — updates dependencies including diff and dompurify used by the new Gutenberg AI modules and sanitizer.

Suggested labels

feature, refactor, test

Poem

🐰 I hopped through blocks to bring AI cheer,
Buttons, diffs, and sanitized gear,
From prompt to suggestion, gentle and neat,
I offer choices, tidy and sweet.
Hooray — the editor's paws leave treats!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly reflects the main objective: adding AI-powered paragraph improvement suggestions to the Gutenberg editor. The change is specific and accurately describes the primary feature being introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gutenberg-abilities

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
services/cms/src/services/backend/ai-suggestions.ts (1)

10-15: Add a brief doc comment for this exported API helper.

Line 10 introduces a public function without a minimal intent/contract description.

♻️ Proposed change
+/**
+ * Requests AI-generated paragraph suggestions for a single paragraph action.
+ */
 export async function requestParagraphSuggestions(
   payload: ParagraphSuggestionRequest,
 ): Promise<ParagraphSuggestionResponse> {
   const response = await cmsClient.post("/ai-suggestions/paragraph", payload)
   return validateResponse(response, isParagraphSuggestionResponse)
 }

As per coding guidelines, "Require consistent, minimal function documentation... Public interfaces should have clearer and more comprehensive documentation."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/services/backend/ai-suggestions.ts` around lines 10 - 15,
Add a minimal doc comment above the exported function
requestParagraphSuggestions describing its purpose, expected input and output
contract: state that it sends a ParagraphSuggestionRequest to the
"/ai-suggestions/paragraph" endpoint, returns a validated
ParagraphSuggestionResponse (via validateResponse and
isParagraphSuggestionResponse), and note any errors it may throw (e.g.,
network/validation errors). Keep the comment concise (one to three lines) and
place it immediately above the requestParagraphSuggestions declaration.
services/cms/src/utils/Gutenberg/ai/registry.ts (1)

3-3: categories registry appears unused.

The categories Map and registerAbilityCategory function are defined but abilities.ts doesn't register any categories. If this is scaffolding for future use, consider adding a brief comment to clarify intent, or remove if not needed.

Also applies to: 6-9

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/ai/registry.ts` at line 3, The categories
Map (categories) and registerAbilityCategory function are defined but never used
by abilities.ts; either remove the unused registry or document it as intentional
scaffolding: if keeping, add a concise comment above const categories and
export/register code noting it's reserved for future ability grouping and
reference registerAbilityCategory and AbilityCategory so maintainers know why
it's present; if removing, delete the categories declaration and
registerAbilityCategory to avoid dead code and update any related exports in the
module.
services/headless-lms/chatbot/src/cms_ai_suggestion.rs (1)

17-17: Unused import.

BackendError is imported but not used in this file.

Suggested fix
-use headless_lms_utils::{ApplicationConfiguration, prelude::BackendError};
+use headless_lms_utils::ApplicationConfiguration;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/headless-lms/chatbot/src/cms_ai_suggestion.rs` at line 17, The
import BackendError in the use statement
(headless_lms_utils::{ApplicationConfiguration, prelude::BackendError}) is
unused; remove BackendError from that use declaration or, if you intended to
handle errors here, reference BackendError where appropriate (e.g., in function
signatures or error handling in functions like suggest_from_cms_ai). Update the
use line to only import ApplicationConfiguration or add the necessary
BackendError usage to avoid the unused-import lint.
services/cms/src/utils/Gutenberg/ai/abilities.ts (1)

128-132: Prefer for...of over forEach when not using return values.

Static analysis flagged that forEach callbacks shouldn't return values (even implicitly). Using for...of is cleaner here.

Suggested fix
 export function registerEditorAiAbilities(): void {
   registerAbility(fixSpellingAbility)
-  allPlaceholderAbilities.forEach((ability) => registerAbility(ability))
+  for (const ability of allPlaceholderAbilities) {
+    registerAbility(ability)
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/ai/abilities.ts` around lines 128 - 132, The
function registerEditorAiAbilities uses allPlaceholderAbilities.forEach(...)
which static analysis flags; replace the forEach call with an explicit for...of
loop to iterate over allPlaceholderAbilities and call registerAbility(ability)
for each item (i.e., in registerEditorAiAbilities iterate: for (const ability of
allPlaceholderAbilities) { registerAbility(ability) }) to avoid the forEach
callback return-value issue and make intent clearer.
services/headless-lms/server/src/controllers/cms/ai_suggestions.rs (1)

8-12: setting_type is currently a no-op in the request contract.

ParagraphSuggestionMeta.setting_type is accepted, but not forwarded into CmsParagraphSuggestionInput (only tone and language are passed). Either wire it through end-to-end or remove it for now to avoid a misleading API field.

Also applies to: 81-86

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/headless-lms/server/src/controllers/cms/ai_suggestions.rs` around
lines 8 - 12, ParagraphSuggestionMeta currently exposes setting_type but the
controller does not forward it into CmsParagraphSuggestionInput (only tone and
language are mapped); either propagate setting_type through the mapping and into
CmsParagraphSuggestionInput and any downstream types/handlers that consume it
(ensure field names and types align), or remove setting_type from
ParagraphSuggestionMeta to avoid a no-op API field; update any
serializers/deserializers and unit tests that reference ParagraphSuggestionMeta
or CmsParagraphSuggestionInput to reflect the chosen change.
services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx (2)

145-147: Replace hardcoded color literals with theme tokens.

This component currently mixes hardcoded colors/fallbacks (#ddd, white, rgba(...)). Please switch these to shared theme values for consistency.

As per coding guidelines: If a component uses colors, use colors from the theme if possible.

Also applies to: 304-307, 319-319

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx` around
lines 145 - 147, The component withParagraphAiToolbarAction is using hardcoded
color literals (e.g. "#ddd", "white", "rgba(...)") in its styled rules (see uses
withSubmenu/hasSubmenu for border-right and other styles around the toolbar
button, submenu and tooltip); replace those literals with the shared theme
tokens or CSS variables (e.g. theme colors or existing --wp-admin-* vars) by
referencing the theme object or CSS vars used elsewhere in the codebase, update
the styles in the styled wrapper and any button/submenu/tooltip style blocks
(the rules around hasSubmenu, the toolbar button styles, and submenu/tooltip
blocks) to use the appropriate theme tokens for border, background and
text/overlay colors, and ensure fallbacks use theme tokens rather than raw
hex/rgba values.

35-37: Define a narrow interface for the block props instead of any.

The component accesses props.name, props.attributes.content, and props.setAttributes. A typed interface would catch contract issues here without overhead, e.g.:

interface ParagraphBlockProps {
  name: string
  attributes: { content?: string }
  setAttributes: (attrs: Record<string, any>) => void
  // ... other BlockEdit props for spreading

Note: WordPress's createHigherOrderComponent doesn't export a standard BlockEdit props type, so you'd need to define this locally or derive it from block registration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx` around
lines 35 - 37, Replace the overly broad any on ParagraphWithAiToolbar props with
a narrow local interface (e.g., ParagraphBlockProps) that includes at least
name: string, attributes: { content?: string }, and setAttributes: (attrs:
Record<string, any>) => void; update the component signature to use that
interface and keep the rest of the incoming props via a spread type or index
signature to preserve other BlockEdit fields so usages of props.name,
props.attributes.content, and props.setAttributes are type-checked.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@services/headless-lms/server/src/controllers/cms/ai_suggestions.rs`:
- Around line 51-93: The DB connection acquired into conn is held across the
await to generate_paragraph_suggestions, starving the pool under load; after
you've finished all DB work (authorize(...) and
application_task_default_language_models::get_for_task(...) and creating
generator_input), explicitly release conn (e.g. drop(conn) or limit its scope)
before calling
headless_lms_chatbot::cms_ai_suggestion::generate_paragraph_suggestions so the
pool entry is returned; if you need DB after the LLM call, re-acquire a
connection then.

In `@shared-module/packages/common/src/locales/en/cms.json`:
- Line 132: Remove the unused "fix-spelling" locale key from this JSON (and
other locale files where it appears) so keys follow the ai-* convention; keep
and use the existing "ai-improve-fix-spelling-grammar" key referenced by
services/cms/src/utils/Gutenberg/ai/menu.ts (menu.ts line ~70) and other code
paths, ensuring no code references "fix-spelling" remain before deleting.

---

Nitpick comments:
In `@services/cms/src/services/backend/ai-suggestions.ts`:
- Around line 10-15: Add a minimal doc comment above the exported function
requestParagraphSuggestions describing its purpose, expected input and output
contract: state that it sends a ParagraphSuggestionRequest to the
"/ai-suggestions/paragraph" endpoint, returns a validated
ParagraphSuggestionResponse (via validateResponse and
isParagraphSuggestionResponse), and note any errors it may throw (e.g.,
network/validation errors). Keep the comment concise (one to three lines) and
place it immediately above the requestParagraphSuggestions declaration.

In `@services/cms/src/utils/Gutenberg/ai/abilities.ts`:
- Around line 128-132: The function registerEditorAiAbilities uses
allPlaceholderAbilities.forEach(...) which static analysis flags; replace the
forEach call with an explicit for...of loop to iterate over
allPlaceholderAbilities and call registerAbility(ability) for each item (i.e.,
in registerEditorAiAbilities iterate: for (const ability of
allPlaceholderAbilities) { registerAbility(ability) }) to avoid the forEach
callback return-value issue and make intent clearer.

In `@services/cms/src/utils/Gutenberg/ai/registry.ts`:
- Line 3: The categories Map (categories) and registerAbilityCategory function
are defined but never used by abilities.ts; either remove the unused registry or
document it as intentional scaffolding: if keeping, add a concise comment above
const categories and export/register code noting it's reserved for future
ability grouping and reference registerAbilityCategory and AbilityCategory so
maintainers know why it's present; if removing, delete the categories
declaration and registerAbilityCategory to avoid dead code and update any
related exports in the module.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx`:
- Around line 145-147: The component withParagraphAiToolbarAction is using
hardcoded color literals (e.g. "#ddd", "white", "rgba(...)") in its styled rules
(see uses withSubmenu/hasSubmenu for border-right and other styles around the
toolbar button, submenu and tooltip); replace those literals with the shared
theme tokens or CSS variables (e.g. theme colors or existing --wp-admin-* vars)
by referencing the theme object or CSS vars used elsewhere in the codebase,
update the styles in the styled wrapper and any button/submenu/tooltip style
blocks (the rules around hasSubmenu, the toolbar button styles, and
submenu/tooltip blocks) to use the appropriate theme tokens for border,
background and text/overlay colors, and ensure fallbacks use theme tokens rather
than raw hex/rgba values.
- Around line 35-37: Replace the overly broad any on ParagraphWithAiToolbar
props with a narrow local interface (e.g., ParagraphBlockProps) that includes at
least name: string, attributes: { content?: string }, and setAttributes: (attrs:
Record<string, any>) => void; update the component signature to use that
interface and keep the rest of the incoming props via a spread type or index
signature to preserve other BlockEdit fields so usages of props.name,
props.attributes.content, and props.setAttributes are type-checked.

In `@services/headless-lms/chatbot/src/cms_ai_suggestion.rs`:
- Line 17: The import BackendError in the use statement
(headless_lms_utils::{ApplicationConfiguration, prelude::BackendError}) is
unused; remove BackendError from that use declaration or, if you intended to
handle errors here, reference BackendError where appropriate (e.g., in function
signatures or error handling in functions like suggest_from_cms_ai). Update the
use line to only import ApplicationConfiguration or add the necessary
BackendError usage to avoid the unused-import lint.

In `@services/headless-lms/server/src/controllers/cms/ai_suggestions.rs`:
- Around line 8-12: ParagraphSuggestionMeta currently exposes setting_type but
the controller does not forward it into CmsParagraphSuggestionInput (only tone
and language are mapped); either propagate setting_type through the mapping and
into CmsParagraphSuggestionInput and any downstream types/handlers that consume
it (ensure field names and types align), or remove setting_type from
ParagraphSuggestionMeta to avoid a no-op API field; update any
serializers/deserializers and unit tests that reference ParagraphSuggestionMeta
or CmsParagraphSuggestionInput to reflect the chosen change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 57d7f1d7-022e-4d85-927d-bde5f1b1784e

📥 Commits

Reviewing files that changed from the base of the PR and between 7422fcd and af0313d.

⛔ Files ignored due to path filters (1)
  • services/cms/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (24)
  • services/cms/package.json
  • services/cms/src/components/editors/GutenbergEditor.tsx
  • services/cms/src/services/backend/ai-suggestions.ts
  • services/cms/src/utils/Gutenberg/ai/abilities.ts
  • services/cms/src/utils/Gutenberg/ai/menu.ts
  • services/cms/src/utils/Gutenberg/ai/registry.ts
  • services/cms/src/utils/Gutenberg/ai/types.ts
  • services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx
  • services/headless-lms/chatbot/src/cms_ai_suggestion.rs
  • services/headless-lms/chatbot/src/lib.rs
  • services/headless-lms/migrations/20260304120000_add_cms_paragraph_suggestion_task.down.sql
  • services/headless-lms/migrations/20260304120000_add_cms_paragraph_suggestion_task.up.sql
  • services/headless-lms/models/src/application_task_default_language_models.rs
  • services/headless-lms/server/src/controllers/cms/ai_suggestions.rs
  • services/headless-lms/server/src/controllers/cms/mod.rs
  • services/headless-lms/server/src/programs/seed/seed_application_task_llms.rs
  • services/headless-lms/server/src/ts_binding_generator.rs
  • shared-module/packages/common/src/bindings.guard.ts
  • shared-module/packages/common/src/bindings.ts
  • shared-module/packages/common/src/locales/ar/cms.json
  • shared-module/packages/common/src/locales/en/cms.json
  • shared-module/packages/common/src/locales/fi/cms.json
  • shared-module/packages/common/src/locales/sv/cms.json
  • shared-module/packages/common/src/locales/uk/cms.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@services/cms/src/utils/Gutenberg/ai/abilities.ts`:
- Around line 14-17: The paragraph request currently always sends context: null,
preventing page-specific auth; update the
ParagraphAbilityInput/ParagraphAbilityInputMeta usage so the page context is
forwarded instead of null: add/ensure a context field on
ParagraphAbilityInputMeta (or use existing meta.context) and change the two
callback sites that set context: null to use input.meta?.context (or the new
meta.context) so the backend can perform page-specific authorization; also
ensure callers that construct ParagraphAbilityInput populate meta.context with
the current page context.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx`:
- Around line 65-76: The handler in withParagraphAiToolbarAction (using
props.attributes?.content and calling executeAbility) sends/returns raw HTML
which strips inline formatting; before calling executeAbility extract plain text
from props.attributes.content (e.g., via a stripHTML/text-extraction util) and
send that to executeAbility, then either (a) map the AI's plain-text rewrite
back onto the original rich-text content to preserve inline markup (apply the
plain-text range mapping to props.attributes.content) or (b) if mapping is not
possible, reject the action when inline markup is present and show a message.
Also update ParagraphAiSuggestionDialog’s diff/preview (the component that
renders the suggestion) to render the extracted plain text or a rendered HTML
preview instead of showing raw HTML so users see formatted output. Ensure
references: props.attributes.content, executeAbility,
withParagraphAiToolbarAction, and ParagraphAiSuggestionDialog.

In `@services/headless-lms/chatbot/src/cms_ai_suggestion.rs`:
- Around line 77-91: The context-window guard currently computes used_tokens
from SYSTEM_PROMPT + USER_PROMPT_PREFIX + text but omits the dynamically-built
system_instructions (which includes action and meta_* fields); update the
token-counting logic to compute used_tokens from the final prompt that will be
sent (e.g., the full system_instructions string plus USER_PROMPT_PREFIX and
text) so large action or meta_* values are included in the check, and apply the
same fix to the second occurrence referenced around the later block (the other
place that builds system_instructions at lines ~113-117).

In `@services/headless-lms/server/src/controllers/cms/ai_suggestions.rs`:
- Around line 24-29: The ParagraphSuggestionRequest.action is an unchecked
string and must be validated before being embedded into the LLM prompt; update
the boundary in the handler (where generate_paragraph_suggestions is called) to
map/validate action against a shared enum or allowlist (e.g., define a
ParagraphSuggestionAction enum) and reject any unknown value with an early error
response instead of passing it to generate_paragraph_suggestions; apply the same
validation for the other occurrence mentioned (lines ~80-87) so only allowed
actions reach the system prompt.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e8e1f4bb-5d8d-4ba3-a7c4-6442da8a4740

📥 Commits

Reviewing files that changed from the base of the PR and between af0313d and 186459e.

📒 Files selected for processing (13)
  • services/cms/package.json
  • services/cms/src/components/editors/GutenbergEditor.tsx
  • services/cms/src/services/backend/ai-suggestions.ts
  • services/cms/src/utils/Gutenberg/ai/abilities.ts
  • services/cms/src/utils/Gutenberg/ai/registry.ts
  • services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx
  • services/headless-lms/chatbot/src/cms_ai_suggestion.rs
  • services/headless-lms/server/src/controllers/cms/ai_suggestions.rs
  • shared-module/packages/common/src/locales/ar/cms.json
  • shared-module/packages/common/src/locales/en/cms.json
  • shared-module/packages/common/src/locales/fi/cms.json
  • shared-module/packages/common/src/locales/sv/cms.json
  • shared-module/packages/common/src/locales/uk/cms.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • services/cms/src/components/editors/GutenbergEditor.tsx
  • services/cms/package.json

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​jsdom@​28.0.01001007487100
Added@​bytedance/​tar-wasm@​0.1.7741009082100
Updated@​types/​react-dom@​19.2.3 ⏵ 18.3.7100 +110076 +187100
Updated@​types/​react@​19.2.14 ⏵ 18.3.2810010079 +194100

View full report

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx (1)

422-427: Remove the empty object comment artifact.

Line 423 contains an empty object {} that appears to be a leftover artifact. This produces no output but adds noise.

🧹 Proposed cleanup
           >
-            {}
             <div
               dangerouslySetInnerHTML={{ __html: suggestionPreviewHtml || originalPreviewHtml }}
             />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx` around
lines 422 - 427, Remove the stray empty object JSX artifact `{}` inside the
render block near the div that uses dangerouslySetInnerHTML (the element
referencing suggestionPreviewHtml || originalPreviewHtml) in
withParagraphAiToolbarAction.tsx; simply delete the `{}` so the JSX contains
only the surrounding elements and the <div dangerouslySetInnerHTML={{ __html:
suggestionPreviewHtml || originalPreviewHtml }} /> to eliminate the noise.
services/headless-lms/chatbot/src/cms_ai_suggestion.rs (1)

70-77: Consider using the is_html flag.

The is_html field is extracted but not used. If this is intentional (e.g., for future use or logging), consider adding a brief comment explaining why. Alternatively, if the LLM should handle HTML vs plain text differently, you could incorporate this into the prompt construction.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/headless-lms/chatbot/src/cms_ai_suggestion.rs` around lines 70 - 77,
The destructured field is_html from CmsParagraphSuggestionInput is extracted but
never used; either incorporate it into the prompt or explicitly document why
it’s ignored. Update the code around the CmsParagraphSuggestionInput
destructuring (and any prompt builder like the function that creates the LLM
prompt for paragraph suggestions) to branch on is_html — e.g., adjust prompt
text or mark content as HTML vs plain text so the LLM handles formatting
correctly — or if omission is intentional, add a short inline comment next to
the is_html binding explaining it’s intentionally unused for now (or add a TODO
referencing future HTML handling).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx`:
- Around line 422-427: Remove the stray empty object JSX artifact `{}` inside
the render block near the div that uses dangerouslySetInnerHTML (the element
referencing suggestionPreviewHtml || originalPreviewHtml) in
withParagraphAiToolbarAction.tsx; simply delete the `{}` so the JSX contains
only the surrounding elements and the <div dangerouslySetInnerHTML={{ __html:
suggestionPreviewHtml || originalPreviewHtml }} /> to eliminate the noise.

In `@services/headless-lms/chatbot/src/cms_ai_suggestion.rs`:
- Around line 70-77: The destructured field is_html from
CmsParagraphSuggestionInput is extracted but never used; either incorporate it
into the prompt or explicitly document why it’s ignored. Update the code around
the CmsParagraphSuggestionInput destructuring (and any prompt builder like the
function that creates the LLM prompt for paragraph suggestions) to branch on
is_html — e.g., adjust prompt text or mark content as HTML vs plain text so the
LLM handles formatting correctly — or if omission is intentional, add a short
inline comment next to the is_html binding explaining it’s intentionally unused
for now (or add a TODO referencing future HTML handling).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bd421381-f314-462a-adde-55baeebbf7ec

📥 Commits

Reviewing files that changed from the base of the PR and between 186459e and a124a45.

⛔ Files ignored due to path filters (1)
  • services/cms/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • services/cms/package.json
  • services/cms/src/utils/Gutenberg/ai/abilities.ts
  • services/cms/src/utils/Gutenberg/paragraphAiSource.ts
  • services/cms/src/utils/Gutenberg/paragraphHtmlSanitizer.ts
  • services/cms/src/utils/Gutenberg/withParagraphAiToolbarAction.tsx
  • services/cms/tests/utils/paragraphAiSource.test.ts
  • services/cms/tests/utils/paragraphHtmlSanitizer.test.ts
  • services/headless-lms/chatbot/src/cms_ai_suggestion.rs
  • services/headless-lms/models/src/user_chapter_locking_statuses.rs
  • services/headless-lms/server/src/controllers/cms/ai_suggestions.rs
  • shared-module/packages/common/src/bindings.guard.ts
  • shared-module/packages/common/src/bindings.ts
  • system-tests/src/tests/oauth/authorize/code-issuance.spec.ts
  • system-tests/src/tests/oauth/discovery.spec.ts
  • system-tests/src/tests/oauth/introspect.spec.ts
  • system-tests/src/tests/oauth/revocation.spec.ts
  • system-tests/src/tests/oauth/token/refresh.spec.ts
  • system-tests/src/tests/oauth/userinfo/bearer.spec.ts
  • system-tests/src/tests/oauth/userinfo/dpop.spec.ts
  • system-tests/src/tests/oauth/userinfo/scopes.spec.ts
✅ Files skipped from review due to trivial changes (3)
  • system-tests/src/tests/oauth/userinfo/bearer.spec.ts
  • system-tests/src/tests/oauth/discovery.spec.ts
  • system-tests/src/tests/oauth/revocation.spec.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • shared-module/packages/common/src/bindings.guard.ts
  • services/cms/src/utils/Gutenberg/ai/abilities.ts
  • services/headless-lms/server/src/controllers/cms/ai_suggestions.rs

@nygrenh nygrenh merged commit 46763bf into master Mar 6, 2026
16 checks passed
@nygrenh nygrenh deleted the gutenberg-abilities branch March 6, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant