tmonier / sylius-b2b-sales-copilot-plugin
AI-powered B2B purchasing assistant for Sylius 2: the buyer formulates their purchase request in natural language from their account, the copilot prepares the cart from past orders and quotes
Package info
github.com/thibmonier/sylius-b2b-sales-copilot-plugin
Type:sylius-plugin
pkg:composer/tmonier/sylius-b2b-sales-copilot-plugin
Requires
- php: ^8.2
- sylius/sylius: ^2.2.9 || ^2.3
- symfony/clock: ^6.4 || ^7.4 || ^8.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpstan/phpstan-doctrine: ^2.0
- phpstan/phpstan-webmozart-assert: ^2.0
- phpunit/phpunit: ^10.5
- rector/rector: ^2.0
- sylius-labs/coding-standard: ^4.4
- sylius/test-application: ^2.0.0@alpha
- symfony/ai-platform: ^0.14
- symfony/browser-kit: ^6.4 || ^7.4 || ^8.0
- symfony/css-selector: ^6.4 || ^7.4 || ^8.0
- symfony/debug-bundle: ^6.4 || ^7.4 || ^8.0
- symfony/dom-crawler: ^6.4 || ^7.4 || ^8.0
- symfony/dotenv: ^6.4 || ^7.4 || ^8.0
- symfony/http-client: ^6.4 || ^7.4 || ^8.0
- symfony/intl: ^6.4 || ^7.4 || ^8.0
- symfony/rate-limiter: ^7.4
- symfony/runtime: ^6.4 || ^7.4 || ^8.0
- symfony/web-profiler-bundle: ^6.4 || ^7.4 || ^8.0
- symfony/webpack-encore-bundle: ^2.2
Suggests
- symfony/ai-platform: Required for real LLM calls (ai.enabled: true)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-01 11:53:08 UTC
README
English | Français
Conversational purchasing assistant skeleton for Sylius 2 B2B: a widget in the customer account sends requests to a copilot model behind a port (CopilotModelInterface). This version ships the replay adapter only: answers come from pre-recorded cassettes, with no call to an LLM API.
Demo: https://sylius-b2b.thibaut-monier.fr
Author: Thibaut Monier
Features
- Conversational widget in the customer account dashboard (Stimulus, keyboard accessible, answers announced by a live region)
- Replay mode: recorded requests return their recorded answer; any other request returns « scénario non enregistré en démo »
- Access guard: the chat endpoint refuses anonymous visitors (401) and channels outside
enabled_channels(403); the widget is only rendered on enabled channels - Functional guardrails:
- Tool whitelist: only approved tools can be called
- Scope-based authorization: read tools require
b2b:readscope, modification tools requireb2b:cartscope - Rate limiting: maximum requests per member per day (configurable, default 50)
- Audit logging: all tool executions are logged to a dedicated channel
- Automatic refusal: checkout and payment operations are refused by design (security)
Natural-language actions on the store (reorder, quick add, quotes) through MCP tools are planned for a later version.
Requirements
- PHP
^8.2 - Sylius
^2.2.9 || ^2.3 - Symfony
^7.4 || ^8.0
Installation
composer require tmonier/sylius-b2b-sales-copilot-plugin
Enable the bundle in config/bundles.php:
return [ // ... Tmonier\SyliusB2bSalesCopilotPlugin\TmonierSyliusB2bSalesCopilotPlugin::class => ['all' => true], ];
Import the routes in config/routes/tmonier_sylius_b2b_sales_copilot.yaml:
tmonier_sylius_b2b_sales_copilot: resource: "@TmonierSyliusB2bSalesCopilotPlugin/config/routes.yaml"
Register the widget controller in your shop assets entrypoint:
import '@vendor/tmonier/sylius-b2b-sales-copilot-plugin/assets/shop/entrypoint';
Configuration
List the channels where the copilot is available (B2B channels):
# config/packages/tmonier_sylius_b2b_sales_copilot.yaml tmonier_sylius_b2b_sales_copilot: enabled_channels: ['B2B_WEB'] # Optional: tool whitelist (default includes safe tools) tool_whitelist: - view_cart - get_cart - search_products - list_my_orders - add_to_cart - remove_from_cart - update_cart_quantity - reorder # Optional: rate limit per member per day (default: 50) daily_limit: 50
Recorded answers live in config/cassettes.jsonl (JSONL, one recorded request per line).
Audit logs are written to var/log/copilot_audit.log.
LLM integration (optional)
The plugin can call a real LLM via Symfony AI Platform. When the LLM is unavailable, the plugin falls back to replay mode transparently.
composer require symfony/ai-platform symfony/ai-anthropic
tmonier_sylius_b2b_sales_copilot: ai: enabled: true model: 'claude-sonnet-4-5' daily_cost_limit_cents: 500 # 0 = unlimited
Set your API key via environment variable as documented by Symfony AI.
When ai.enabled is false (default), only the replay adapter is used — no LLM dependency is needed.
Example cassette
{"version":"1.0","intent":"état de mon panier","normalized":{"action":"get_cart"},"timestamp":"2026-10-31T10:00:00Z","tools":[{"name":"get_checkout_link","args":{},"result":"success"}],"response":"Votre panier contient 47 articles pour un total de 963,50 € HT. Voulez-vous passer au checkout ?"}
Usage
A signed-in customer on an enabled channel sees the copilot widget on the account dashboard and can send a request. A recorded request (for example « état de mon panier ») returns its recorded answer.
The widget is hooked into sylius_shop.account.dashboard.index.content.main.section. A theme that replaces the dashboard can include it directly: {% include '@TmonierSyliusB2bSalesCopilotPlugin/account/copilot_widget.html.twig' %} (it renders nothing outside the enabled channels).
Screenshots
Development
Running tests
composer install vendor/bin/phpunit --testsuite=all
Code quality
vendor/bin/phpstan analyse vendor/bin/ecs check vendor/bin/rector process --dry-run
License
MIT. See LICENSE file.
Author
Thibaut Monier
LinkedIn: https://www.linkedin.com/in/thibautmonier/
Contributing
Contributions are welcome. Please open an issue or pull request on GitHub.
