Skip to content

Upgrade Notes

Everything you need to review before upgrading to a new version of the bundle. The same notes ship with the package as UPGRADES.md in the repository root.

0.7.0

Sulu minimum version raised

The bundles now require Sulu ^2.6.25 (2.6 line) or ^3.0.8 (3.0 line). These versions ship the admin support for the configurable AI field types described below. Update your Sulu installation before upgrading the bundles.

symfony/ai packages upgraded to 0.10

Both bundles now require the symfony/ai-* packages in version ^0.10. If your application requires any symfony/ai-* package directly (for example symfony/ai-bundle or symfony/ai-agent), update its constraint to ^0.10 before upgrading.

New optional configuration

No action is required — the defaults match the previous behavior:

  • sulu_ai_platform.text_field_types (default ['text_line', 'text_area']) and sulu_ai_platform.html_field_types (default ['text_editor']) control which property types the AI features treat as plain-text respectively HTML content. Projects with custom property types containing translatable text should add them here — see the translator documentation.
  • sulu_ai_platform.intelligent_search.identifier optionally targets a specific intelligent search by UUID. When omitted, the project default is used.

0.6.0

New Sulu AI security contexts

The ai-platform-bundle now registers its own Sulu AI security category in the Sulu admin permission system.

Bundle users should review their roles and grant the new VIEW permissions for the AI features they want to expose:

  • sulu.ai.writing_assistant
  • sulu.ai.translate
  • sulu.ai.seo_generator
  • sulu.ai.excerpt_generator
  • sulu.ai.media_metadata_generator
  • sulu.ai.feedback

Without these permissions, the corresponding AI UI actions are hidden and the backend endpoints return 403 Access Denied.

Translation permissions are combined

The following translation-related permissions were combined into a single permission:

  • sulu.ai.translate_text
  • sulu.ai.media_metadata_translate
  • sulu.ai.category_translate
  • sulu.ai.full_content_translate

Use sulu.ai.translate instead.

SQL helper for existing roles

If you want to grant all new Sulu AI VIEW permissions to an existing role directly in SQL, you can use the following statement. Replace :ROLE_ID with the numeric role id from se_roles.id:

INSERT INTO se_permissions (`context`, `module`, `permissions`, `idRoles`)
VALUES
    ('sulu.ai.writing_assistant', NULL, 64, :ROLE_ID),
    ('sulu.ai.translate', NULL, 64, :ROLE_ID),
    ('sulu.ai.seo_generator', NULL, 64, :ROLE_ID),
    ('sulu.ai.excerpt_generator', NULL, 64, :ROLE_ID),
    ('sulu.ai.media_metadata_generator', NULL, 64, :ROLE_ID),
    ('sulu.ai.feedback', NULL, 64, :ROLE_ID)
ON DUPLICATE KEY UPDATE
    `permissions` = `permissions` | VALUES(`permissions`);

64 is the Sulu bitmask for VIEW. The ON DUPLICATE KEY UPDATE clause preserves any existing permission bits and only adds VIEW where needed.

0.5.0

Context objects replace individual parameters

All expert interfaces now use dedicated context objects instead of individual $locale and $resourceKey parameters. These context objects bundle locale, resourceKey, and the new webspaceKey together, enabling the platform to resolve profile metadata per webspace.

ai-bundle: WritingAssistantGeneratorInterface

The $locale parameter was replaced by WritingAssistantContext:

// Before
public function optimize(
    string $chatId,
    string $text,
    string $message,
    ?string $expertUuid,
    string $locale,
): WritingAssistantResponse;

// After
use Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistantContext;

public function optimize(
    string $chatId,
    string $text,
    string $message,
    ?string $expertUuid,
    WritingAssistantContext $context,
): WritingAssistantResponse;

The WritingAssistantContext provides locale, resourceId, resourceKey, schemeAndHttpHost, optional webspaceKey, and optional content data (data, dataPath).

ai-bundle: WritingAssistantContextProviderInterface

The $locale parameter was removed and parameters were reordered. The context object now carries the locale:

// Before
public function generateContext(
    string $text,
    string $message,
    string $locale,
    ?string $expertUuid,
): iterable;

// After
use Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistantContext;

public function generateContext(
    string $text,
    string $message,
    ?string $expertUuid,
    WritingAssistantContext $context,
): iterable;

ai-platform-bundle: SeoGeneratorInterface

The $locale and $resourceKey parameters were replaced by SeoGeneratorContext, and $expertUuid was moved:

// Before
public function generate(
    string $id,
    string $locale,
    string $expertUuid,
    ?array $currentSeoData,
    string $resourceKey,
): SeoGeneratorResponse;

// After
use Sulu\Bundle\AiPlatformBundle\Expert\SeoGenerator\SeoGeneratorContext;

public function generate(
    string $id,
    string $expertUuid,
    ?array $currentSeoData,
    SeoGeneratorContext $context,
): SeoGeneratorResponse;

ai-platform-bundle: SeoContextProviderInterface

The $locale parameter was replaced by SeoGeneratorContext, and $expertUuid was moved:

// Before
public function generateContext(
    ContentAdapterInterface $content,
    string $locale,
    string $expertUuid,
): iterable;

// After
use Sulu\Bundle\AiPlatformBundle\Expert\SeoGenerator\SeoGeneratorContext;

public function generateContext(
    ContentAdapterInterface $content,
    string $expertUuid,
    SeoGeneratorContext $context,
): iterable;

ai-platform-bundle: ExcerptGeneratorInterface

Same pattern as SeoGeneratorInterface:

// Before
public function generate(
    string $id,
    string $locale,
    string $expertUuid,
    ?array $currentExcerptData,
    string $resourceKey,
): ExcerptGeneratorResponse;

// After
use Sulu\Bundle\AiPlatformBundle\Expert\ExcerptGenerator\ExcerptGeneratorContext;

public function generate(
    string $id,
    string $expertUuid,
    ?array $currentExcerptData,
    ExcerptGeneratorContext $context,
): ExcerptGeneratorResponse;

ai-platform-bundle: ExcerptContextProviderInterface

Same pattern as SeoContextProviderInterface:

// Before
public function generateContext(
    ContentAdapterInterface $content,
    string $locale,
    string $expertUuid,
): iterable;

// After
use Sulu\Bundle\AiPlatformBundle\Expert\ExcerptGenerator\ExcerptGeneratorContext;

public function generateContext(
    ContentAdapterInterface $content,
    string $expertUuid,
    ExcerptGeneratorContext $context,
): iterable;

ai-bundle: TranslatorInterface new parameter

A new optional $webspaceKey parameter was added to translate():

// Before
public function translate(
    string $translateId,
    array $texts,
    string $targetLanguage,
    ?string $sourceLanguage = null,
    bool $html = true,
): TranslatorResponse;

// After
public function translate(
    string $translateId,
    array $texts,
    string $targetLanguage,
    ?string $sourceLanguage = null,
    bool $html = true,
    ?string $webspaceKey = null,
): TranslatorResponse;

Existing callers are unaffected. Implementations of this interface must add the new parameter.

New context factories

Context objects are now created via factories that handle webspace key resolution for articles:

  • Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistantContextFactory
  • Sulu\Bundle\AiPlatformBundle\Expert\SeoGenerator\SeoGeneratorContextFactory
  • Sulu\Bundle\AiPlatformBundle\Expert\ExcerptGenerator\ExcerptGeneratorContextFactory

If you extend WritingAssistantController, SeoGeneratorController, or ExcerptGeneratorController, note that their constructors now require the corresponding context factory as an additional parameter.

New WebspaceKeyResolverInterface

A new Sulu\Bundle\AiBundle\Compatibility\WebspaceKeyResolverInterface has been introduced. The ai-platform-bundle provides implementations for both Sulu 2.6 (Sulu26WebspaceKeyResolver) and Sulu 3.0 (Sulu3WebspaceKeyResolver) that resolve the webspaceKey for articles based on their main webspace.

New WritingAssistantMessageEnhancerInterface

A new WritingAssistantMessageEnhancerInterface has been introduced for enhancing the locale instruction message with additional content parts (e.g., media images). Implementations are tagged with sulu_ai.writing_assistant_message_enhancer.


0.4.0

Migration from ModelflowAi to Symfony/AI

Quick Navigation: - For Bundle Users - If you're using AI features in Sulu admin - For Advanced Users - If you implement custom experts or extend the bundles


For Bundle Users

If you're using the AI features in Sulu admin, follow these steps:

1. Update Dependencies

composer update sulu/ai-platform-bundle -W

Note: This bundle requires Symfony 7.3 or higher due to the Symfony AI component dependencies.

2. Update Bundle Configuration

You have to enable the Symfony AI bundle in your application while removing the ModelflowAi Bundle.

Update config/bundles.php:

return [
    // Remove this line:
    // ModelflowAi\Integration\Symfony\ModelflowAiBundle::class => ['all' => true],

    // Add Symfony AI bundle
    Symfony\AI\AiBundle\AiBundle::class => ['all' => true],

    // Keep these existing bundles:
    Sulu\Bundle\AiPlatformBundle\SuluAiPlatformBundle::class => ['all' => true],
    Sulu\Bundle\AiBundle\SuluAiBundle::class => ['all' => true],
];


For Advanced Users

If you implement custom experts or extend the bundles:

ExpertInterface Changes

Only relevant if you implement ExpertInterface yourself:

// Before
public function createThread(?string $expertUuid): ThreadInterface;
public function getVariants(): array;  // Returns objects

// After
public function createThread(?string $variantUuid = null): ThreadInterface;
public function getVariants(): array;  // Returns arrays

Variant access:

// Before: Objects
$variant->getUuid()

// After: Arrays
$variant['uuid']

Service Names

Only relevant if you inject expert services:

Expert services have been split into two distinct services following a consistent naming pattern:

Old Service ID New Service ID Type
modelflow_ai.experts.writing_assistant sulu_ai.writing_assistant_expert Expert (ExpertInterface)
sulu_ai.experts.writing_assistant sulu_ai.writing_assistant_generator WritingAssistantGenerator implementation
modelflow_ai.experts.seo_generator sulu_ai_platform.seo_expert Expert (ExpertInterface)
sulu_ai_platform.experts.seo_generator sulu_ai_platform.seo_generator SeoGenerator implementation
modelflow_ai.experts.excerpt_generator sulu_ai_platform.excerpt_expert Expert (ExpertInterface)
sulu_ai_platform.experts.excerpt_generator sulu_ai_platform.excerpt_generator ExcerptGenerator implementation
modelflow_ai.experts.media_metadata_generator sulu_ai_platform.media_metadata_expert Expert (ExpertInterface)
sulu_ai_platform.experts.media_metadata_generator sulu_ai_platform.media_metadata_generator MediaMetadataGenerator implementation
sulu_ai_platform.experts.translator sulu_ai_platform.translator Translator

Naming Pattern: - Services ending in _expert: Return ExpertVariant arrays for expert definitions - Services ending in _generator: Implementation services for programmatic use

Expert/Thread Architecture Refactoring

Only relevant if you extend our generators (WritingAssistantGenerator, SeoGenerator, ExcerptGenerator, MediaMetadataGenerator):

We've simplified the generators to use the Expert/Thread pattern. If you're extending any of the generators, please check your implementation.

Context Provider Interface Change

Only relevant if you implement a custom SeoContextProviderInterface or ExcerptContextProviderInterface:

The $document parameter of generateContext() changed from object to ConentAdapterInterface:

// Before
public function generateContext(
    StructureBehavior $document,
    string $locale,
    string $expertUuid,
): iterable;

// After
use Sulu\Bundle\AiPlatformBundle\Compatibility\ContentPersister\ConentAdapterInterface;

public function generateContext(
    ConentAdapterInterface $content,
    string $locale,
    string $expertUuid,
): iterable;

Update your implementations to use ConentAdapterInterface and its methods (getTemplateData(), getSeoData(), getExcerptData(), etc.) instead of checking for version-specific types.

Class renamings

Following classes have been renamed:

Old Class New Class
Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistant Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistantGenerator
Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistantInterface Sulu\Bundle\AiBundle\Expert\WritingAssistant\WritingAssistantGeneratorInterface

Testing

Only relevant if you test AI features:

// Before
use Sulu\Bundle\AiBundle\Testing\TestAdapter\TestChatAdapter;
$adapter = new TestChatAdapter([...]);

// After
use Sulu\Bundle\AiBundle\Testing\MockPlatform;
$mockPlatform = new MockPlatform(/*...*/);
$mockPlatform->addMockResponse(fn($m, $msg) => true, '{"result": "test"}');

Removed Classes

Unlikely you were using these, but they're removed: - Criteria\ExpertCriteria, Expert\ExpertFactory, Expert\ExpertFactoryInterface - Testing\TestAdapter\TestChatAdapter


Quick Checklist

For all users: - [ ] Update Symfony to ^7.3 - [ ] Update dependencies: composer update sulu/ai-platform-bundle -W - [ ] Remove ModelflowAi\Integration\Symfony\ModelflowAiBundle from bundles.php - [ ] Add Symfony\AI\AiBundle\AiBundle to bundles.php

For advanced users only: - [ ] Update custom expert implementations (if you have any) - [ ] Update service injection (if you inject expert services) - [ ] Update test code (if you test AI features) - [ ] Update generator extensions (if you extend generators) - [ ] Update custom context provider implementations (if you implement SeoContextProviderInterface or ExcerptContextProviderInterface)