Search by

tmonier / sylius-b2b-sales-copilot-plugin

thibmonier

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

Statistics

Installs: 107

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.3.2 2026-11-01 11:52 UTC

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:read scope, modification tools require b2b:cart scope
    • 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

The widget on the account dashboard, after a recorded request

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.