Claire 1.3.1 est une application web de chat IA construite avec Slim 4, Twig et Neuron AI. Elle fournit une interface web, un endpoint API, une integration Telegram et un runtime Docker base sur FrankenPHP/Caddy pour piloter des LLM compatibles OpenAI.
# 1. Cloner et installer les dépendances
git clone https://github.com/semhoun/claire-chatbot.git
cd claire-chatbot
composer install
# 2. Créer un fichier .env avec votre clé API
cat > .env <<EOF
OPENAPI_KEY=votre-clé-api
OPENAPI_URL=https://api.openai.com/v1
OPENAPI_MODEL=gpt-4o-mini
SESSION_JWT_SECRET=$(openssl rand -hex 32)
OTEL_LOGS_EXPORTER=console
OTEL_LOGS_PROCESSOR=simple
EOF
# 3. Initialiser la base de données
./console migrations:migrate
# 4. Lancer le serveur de développement
composer start
# Ouvrir http://localhost:8080Ou avec Docker:
Image Docker prete a l'emploi: semhoun/claire-chatbot
docker build -t claire:latest -f docker/Dockerfile .
docker run -d -p 8080:80 \
-e OPENAPI_KEY=votre-clé-api \
-e SESSION_JWT_SECRET=$(openssl rand -hex 32) \
-v claire_data:/data \
claire:latest- Interface web de chat avec horodatage, annulation du dernier echange, affichage instantane du message utilisateur, mode streaming par defaut, raccourci pour revenir en bas de conversation et ajustements visuels sur les themes et la mise en page.
- Endpoint API
POST /brain/chatpour envoyer un message et récupérer la réponse de l'agent. - Healthcheck
GET /health(JSON) pour la supervision. - Intégration d'un fournisseur LLM « OpenAI-like » (URL, clé et modèle configurables).
- Ajout automatique de la date et l'heure courantes dans le contexte système des agents.
- Memoire courte avec resume automatique pour contenir l'historique dans la fenetre de contexte.
- Recherche web optionnelle via SearXNG et RAG fichier via embeddings OpenAI-like.
- Génération d'images avec ComfyUI (optionnel).
- Support d'agents personnalisés via classes PHP et fichiers YAML.
- Intégration Telegram avec filtrage des balises
[OC], prise en charge des photos et documents, meilleur rendu MarkdownV2 des listes, et mise en queue des updates via un worker CLI Redis. - Observabilite via OpenTelemetry pour traces, metriques et logs, y compris l'instrumentation interne des agents.
- Historique de conversation avec separation entre messages LLM et messages affiches a l'utilisateur, y compris conservation du message d'ouverture dans l'affichage.
- PHP 8.4+
- Slim 4 (routing, middlewares)
- PHP-DI (container)
- Twig (templates)
- Monolog (logs)
- Doctrine ORM (persistance des données)
- Neuron AI (agent LLM)
- phptg/bot-api (bot Telegram)
- OpenTelemetry (observabilité)
- Symfony YAML (cerveaux personnalisés)
- FrankenPHP + Caddy (runtime HTTP principal en conteneur)
- Docker (déploiement containerisé avec Supervisor)
- PHP 8.4 ou supérieur avec les extensions:
ext-jsonext-sqlite3ou 'ext-mysql' ou 'ext-pgsql'ext-libxml
- Composer
- Ajustez
max_execution_timedans php.ini (ex:max_execution_time=300) car les appels LLM peuvent être longs.
- Cloner le dépôt puis installer les dépendances:
composer install
- (Optionnel) Si vous comptez utiliser les migrations/Doctrine, initialisez votre base de données selon vos besoins.
Les paramètres sont chargés depuis config/settings/*.php et complétés par des variables d’environnement. Les clés importantes:
-
LLM (voir
config/settings/llm.php):OPENAPI_KEY— clé d'API du fournisseur (compatible OpenAI)OPENAPI_URL— base URL de l'API (ex: https://api.openai.com/v1 ou un proxy type LiteLLM)OPENAPI_MODEL— identifiant du modèle par défaut (ex: gpt-4o-mini, gpt-5.1, etc.)OPENAPI_MODEL_SUMMARY— modèle dédié aux tâches de synthèse/résumé (optionnel; si absent,OPENAPI_MODELsera utilisé)OPENAPI_MODEL_EMBED— modèle dédié aux embeddings (optionnel; si absent, le RAG est désactivé)OPENAPI_CONTEXT_WINDOW— taille de fenêtre de contexte cible utilisée par l'agent (défaut:50000)llm.shortMemory.messageToKeep— nombre de messages recents conserves lors d'un resume automatiquellm.shortMemory.maxTokens— seuil de tokens a partir duquel la memoire courte compacte l'historiquellm.summary.minMessages/llm.summary.maxMessages— bornes utilisees pour declencher la generation du titre ou resume de conversationllm.workflow.timeout— duree maximale d'execution d'un workflow d'agentSEARXNG_URL— URL de l'instance SearXNG pour la recherche web (optionnel)
-
Telegram (voir
config/settings/telegram.php):TELEGRAM_BOT_TOKEN— Token de votre bot Telegram (obtenu via @BotFather).
-
Queue (voir
config/settings/queue.phpetconfig/settings/redis.php):REDIS_HOST— hote Redis (defaut:127.0.0.1).REDIS_PORT— port Redis (defaut:6379).REDIS_DATABASE— base Redis numerique (defaut:0).REDIS_PASSWORD— mot de passe Redis (optionnel).REDIS_TIMEOUT— timeout de connexion Redis en secondes.REDIS_PREFIX— prefixe des cles Redis pour la queue.
-
Mode et logs:
DEBUG_MODE=true|false(active un niveau de logs plus verbeux)ENABLE_LETSENCRYPT=true|false(active HTTPS automatique via Let's Encrypt dans le conteneur FrankenPHP/Caddy)ENABLE_ACCESS_LOGS=true|false(active les access logs HTTP sur la sortie standard du conteneur)ACME_EMAIL— email utilisé pour l'enregistrement ACME/Let's Encrypt (optionnel)
-
Stockage des fichiers et données:
DATA_PATH— chemin racine pour les données persistantes (base de données SQLite, fichiers, cache; par défaut:var/data)FILES_PATH— chemin vers le répertoire de stockage des fichiers (par défaut:var/filer)
-
Observabilité (OpenTelemetry — requis):
- Journalisation OpenTelemetry: les logs de l’application sont émis via l’intégration Monolog/OpenTelemetry.
- Pour afficher les logs dans la console en développement, définissez
OTEL_LOGS_EXPORTER=console(et éventuellementOTEL_LOGS_PROCESSOR=simple). - Variables d’environnement principales (par signal):
- Générales
OTEL_PHP_AUTOLOAD_ENABLED— active l’auto‑instrumentation PHP (true/false).OTEL_SERVICE_NAME— nom du service (utilisé par les 3 signaux).OTEL_RESOURCE_ATTRIBUTES— attributs ressource supplémentaires (ex:deployment.environment=dev,service.version=1.3.1).OTEL_PROPAGATORS— propagateurs de contexte (ex:baggage,tracecontext).
- Traces
OTEL_TRACES_EXPORTER— exporteur des traces (otlp,none).OTEL_TRACES_SAMPLER— stratégie d’échantillonnage (ex:parentbased_always_on,traceidratio).OTEL_TRACES_SAMPLER_ARG— paramètre du sampler (ex:0.1pour 10%).
- Metrics
OTEL_METRICS_EXPORTER— exporteur des métriques (otlp,none).
- Logs
OTEL_LOGS_EXPORTER— exporteur des logs (console,otlp,none).OTEL_LOGS_PROCESSOR— processeur des logs (simplepour affichage immédiat,batchpour production).
- Export OTLP (commun, et surcharges par signal)
OTEL_EXPORTER_OTLP_PROTOCOL— protocole (http/protobufrecommandé, ougrpc).OTEL_EXPORTER_OTLP_ENDPOINT— endpoint commun OTLP (optionnel, ex:http://collector:4318).OTEL_EXPORTER_OTLP_HEADERS— en‑têtes additionnels (optionnels, ex:authorization=Bearer <token>).OTEL_EXPORTER_OTLP_TRACES_ENDPOINT— endpoint traces (optionnel; surcharge de..._ENDPOINT).OTEL_EXPORTER_OTLP_METRICS_ENDPOINT— endpoint métriques (optionnel; surcharge de..._ENDPOINT).OTEL_EXPORTER_OTLP_LOGS_ENDPOINT— endpoint logs (optionnel; surcharge de..._ENDPOINT).
- Générales
Note: les endpoints OTLP et les headers sont optionnels. Si vous ne les définissez pas, l’exporteur appliquera ses valeurs par défaut. Par exemple, pour afficher les logs uniquement en console, il suffit de définir
OTEL_LOGS_EXPORTER=consolesans renseigner d’endpoint OTLP.
L'application permet de sélectionner différents « cerveaux » (avatars) pour l'agent (ex.: Claire, Einstein, Calliope). La sélection est mémorisée en session sous la clé brain_avatar.
- La logique de sélection est gérée par le registre
BrainRegistry. - Si une valeur invalide est fournie, l'application revient automatiquement sur l'avatar par défaut:
claire. - Vous pouvez exposer ce choix dans l'UI (ex.: select) ou via un paramètre de requête selon vos besoins.
- Cerveaux disponibles par défaut:
claire— Assistante généralisteeinstein— Expert scientifiquecalliope— Calliope la conteuse
Vous pouvez créer vos propres agents sans écrire de code PHP en plaçant des fichiers YAML dans le répertoire addons/agents/. Chaque fichier définit un nouveau cerveau avec ses propres caractéristiques.
Structure d'un fichier YAML:
name: "Nom de l'Assistant"
description: "Description courte affichée dans l'interface"
avatar: "data:image/png;base64,iVBORw0K......" # URL ou data URI de l'avatar
css: "" # Nom du fichier CSS dans public/css/ (optionnel)
css_inline: | # CSS inline directement dans le fichier (optionnel)
:root {
--accent: #FF6B6B;
}
.chat-header {
background: #ffffff;
}
welcomes:
- "Premier message d'accueil possible"
- "Deuxième message d'accueil possible"
- "Troisième message d'accueil possible"
instruction: |
Le prompt système complet de l'assistant.
Définissez ici son rôle, son ton, ses compétences.
Utilisez plusieurs lignes si nécessaire.Exemple de fichier addons/agents/coach.yaml:
name: "Coach Personnel"
description: "Un coach motivant pour vous aider à atteindre vos objectifs"
avatar: ""
css: ""
css_inline: |
:root {
--accent: #FF6B35;
--accent-light: #FF8C42;
}
.message--sent .message__bubble {
background: linear-gradient(135deg, var(--accent), var(--accent-light));
}
welcomes:
- "Prêt à relever de nouveaux défis aujourd'hui ?"
- "Bonjour champion ! Que veux-tu accomplir ?"
- "Salut ! Je suis là pour te motiver et te guider."
instruction: |
Tu es un coach personnel motivant et bienveillant.
Ton rôle est d'aider l'utilisateur à définir ses objectifs,
à rester motivé et à surmonter les obstacles.
Sois encourageant, pose des questions pertinentes et propose
des plans d'action concrets.Champs disponibles:
| Champ | Type | Description |
|---|---|---|
name |
string | Nom affiché de l'assistant (obligatoire) |
description |
string | Description courte pour la sélection (obligatoire) |
avatar |
string | URL ou data URI de l'image avatar (optionnel) |
css |
string | Nom d'un fichier CSS externe dans public/css/ (optionnel) |
css_inline |
string | CSS inline directement dans le YAML (optionnel, recommandé) |
welcomes |
array | Liste de messages d'accueil (un choisi aléatoirement) |
instruction |
string | Prompt système complet de l'assistant (obligatoire) |
Notes sur le CSS:
- Recommandé: Utilisez
css_inlinepour embarquer le CSS directement dans le fichier YAML - Alternative: Utilisez
csspour référencer un fichier externe (ex:css: "monbrain.css") - Vous pouvez utiliser les deux simultanément si nécessaire
- Le CSS inline est injecté via une balise
<style>dans le<head> - Voir
addons/agents/calliope.yamlpour un exemple complet avec CSS inline
Points importants:
- Le slug du cerveau est déterminé par le nom du fichier (ex:
coach.yaml→ slugcoach) - Les fichiers YAML sont chargés automatiquement au démarrage
- Un message d'accueil est choisi aléatoirement parmi la liste
welcomes - Les cerveaux YAML apparaissent automatiquement dans l'interface aux côtés des cerveaux PHP
- Voir le fichier exemple dans
addons/agents/calliope.yaml(exemple complet d'un cerveau YAML avec CSS inline)
Claire utilise des balises internes [OC]...[/OC] pour porter des instructions ou contenus de contrôle qui ne doivent pas être affichés à l'utilisateur final.
- Dans l'interface web, le filtre Twig
filter_oc_tagssupprime automatiquement ces blocs avant le rendu Markdown. - Dans Telegram, le même filtrage est appliqué avant conversion vers
MarkdownV2. - Si un message ne contient plus rien après filtrage, il n'est pas affiché ni envoyé.
- Le fichier
tmpl/partials/message.twigapplique ce filtrage avant rendu.
Les agents principaux ajoutent automatiquement la date et l'heure courantes à leurs instructions système.
- Cela améliore la contextualisation des réponses sensibles au temps.
- Le comportement est implémenté dans
App\Brain\AgentetApp\Brain\RAG. - Les messages de bienvenue générés par l'agent utilisent également un bloc
[OC]pour éviter d'exposer l'instruction interne.
L'application prend en charge la recherche web via SearXNG lorsque la variable d'environnement SEARXNG_URL est configurée. Cette fonctionnalité ajoute l'outil web_search aux agents, leur permettant d'effectuer des recherches sur internet pour enrichir leurs réponses.
- Configuration: définissez
SEARXNG_URLavec l'URL de votre instance SearXNG (ex:http://searxng:8080) - L'outil est automatiquement disponible pour tous les cerveaux lorsque la configuration est présente
L'application peut générer des images via ComfyUI lorsque la configuration appropriée est définie. Cette fonctionnalité ajoute l'outil generate_image aux agents, leur permettant de créer des images à partir de descriptions textuelles.
Configuration requise:
| Variable | Obligatoire | Description |
|---|---|---|
COMFYUI_ENABLED |
Oui | Active la génération d'images (true ou false) |
COMFYUI_URL |
Non | URL de l'instance ComfyUI (défaut: http://localhost:8188) |
COMFYUI_TIMEOUT |
Non | Timeout en secondes (défaut: 300) |
COMFYUI_DEFAULT_WORKFLOW |
Non | Slug du workflow ComfyUI à sélectionner par défaut s'il existe |
Exemple de configuration minimale:
# Activation de ComfyUI
export COMFYUI_ENABLED=true
export COMFYUI_URL=http://comfyui:8188
export COMFYUI_DEFAULT_WORKFLOW=sdxlWorkflows multiples via addons/comfyui/:
Vous pouvez définir plusieurs workflows ComfyUI, comme pour les agents YAML, en ajoutant un fichier .yaml, .yml ou .json dans addons/comfyui/.
Chaque workflow doit définir:
type: type de prompt attendu (sdxl,flux, etc.)label: libellé affiché dans l'interface web et sur Telegramworkflow: JSON ComfyUI avec le placeholder{{PROMPT}}
Exemple:
type: sdxl
label: Portrait SDXL
workflow: |
{
"6": {
"inputs": {
"text": "{{PROMPT}}"
},
"class_type": "CLIPTextEncode"
}
}Notes de fonctionnement:
- Le slug du workflow est déterminé par le nom du fichier, par exemple
portrait-sdxl.yaml→portrait-sdxl - Si
COMFYUI_DEFAULT_WORKFLOWest défini et correspond à un slug existant, il devient le workflow par défaut - Sinon, le premier fichier trouvé dans
addons/comfyui/devient le workflow par défaut - Le workflow sélectionné est mémorisé dans la session utilisateur et dans ses options persistées
- Le changement est disponible dans le menu web et via la commande Telegram
/comfyui - Ce système n'est actif que si
COMFYUI_ENABLED=true - Si aucun fichier n'est présent dans
addons/comfyui/, la génération d'image n'est pas disponible
Note: le workflow doit contenir un placeholder {{PROMPT}} qui sera remplacé par la description de l'image à générer.
Styles de prompts:
sdxl: Prompts optimisés pour SDXL (mots-clés séparés par des virgules)flux: Prompts en langage naturel pour Flux
Fonctionnement:
- Lorsqu'activé, l'outil
generate_imageest disponible pour tous les cerveaux - L'agent peut décider de générer une image en réponse à une demande utilisateur
- Les images générées sont stockées dans
var/generated_images/ - Les images sont servies via l'endpoint
/files/generated/{filename} - Le bot Telegram supporte l'envoi des images générées
L’application prend en charge une authentification via SSO OpenID Connect, si les paramètres sont définis sinon elle utilise un utilisateur par défaut. La configuration est lue dans config/settings/oidc.php et repose sur les variables d’environnement suivantes:
OPENID_WELLKNOWN_URL— URL du document de découverte OpenID Connect 1.0 (ex:https://votre-idp/.well-known/openid-configuration).OPENID_CLIENT_ID— identifiant du client OIDC (côté fournisseur).OPENID_CLIENT_SECRET— secret du client OIDC (côté fournisseur).OPENID_REDIRECT_URI_BASE— base d’URL publique de votre application (ex:https://claire.example.com). L’URI de redirection effective sera${OPENID_REDIRECT_URI_BASE}/auth/callbacket doit être enregistrée à l’identique dans la fiche du client côté IdP.
Autres détails:
- Scopes utilisés par défaut:
openid email profile(envoyés avec un séparateur espace, encodé en+dans l’URL d’autorisation). - Endpoints utilisés:
authorization_endpoint,token_endpointetuserinfo_endpointdécouverts via le document.well-known. - En cas d’échec d’authentification, l’application reste SSO‑only et ne propose pas de login/mot de passe.
Dépannage rapide:
- Erreur
invalid_clientlors de l’échange de code: vérifiez l’ID et le secret, et surtout que la méthode d’authentification configurée pour VOTRE client au token endpoint côté IdP correspond à celle attendue (souventclient_secret_basicouclient_secret_post). Assurez‑vous également que l’URI de redirection enregistrée correspond exactement àhttps://votre-domaine/auth/callback. - Erreur de redirection: vérifiez
OPENID_REDIRECT_URI_BASEet les règles de proxy/host (Traefik) afin que l’URL publique corresponde bien au host utilisé par les utilisateurs.
Claire utilise un système de session stateless basé sur JWT. Les données de session sont stockées dans un cookie JWT signé, éliminant le besoin de stockage côté serveur.
- Configuration:
config/settings/session.php - Variables d'environnement obligatoires:
SESSION_JWT_SECRET— Clé secrète pour la signature JWT (obligatoire)SESSION_JWT_ALGORITHM— Algorithme de signature (défaut: HS256)
- Routes publiques (contournent la session): configurées dans
config/settings/security.php
Pour plus de détails, voir src/Session/README.md.
Le projet inclut Doctrine ORM/DBAL et peut fonctionner avec SQLite (par défaut), MySQL/MariaDB ou PostgreSQL. La configuration est lue depuis config/settings/database.php et pilotée par des variables d’environnement prefixées DATABASE_.
-
Variables d’environnement supportées:
DATABASE_KIND— pilote de base de données. Valeurs possibles:sqlite,mysql,postgres(oupgsql).DATABASE_HOST— hôte (MySQL/PostgreSQL uniquement).DATABASE_PORT— port (MySQL/PostgreSQL uniquement; ex: 3306 pour MySQL, 5432 pour PostgreSQL).DATABASE_NAME— nom de la base (MySQL/PostgreSQL uniquement).DATABASE_USER— utilisateur (MySQL/PostgreSQL uniquement).DATABASE_PASSWORD— mot de passe (MySQL/PostgreSQL uniquement).
-
SQLite
- Si
DATABASE_KIND=sqlite, aucune autre variable n’est nécessaire. - Le fichier de base de données est créé/lu à l’emplacement par défaut:
var/database.sqlite(chemin défini dansconfig/settings/database.php). Assurez‑vous que le processus PHP a les droits d’écriture sur le dossiervar/.
- Si
-
MySQL / MariaDB
- Exemple minimal:
DATABASE_KIND=mysql DATABASE_HOST=localhost DATABASE_PORT=3306 DATABASE_NAME=claire DATABASE_USER=claire DATABASE_PASSWORD=change_me
- Exemple minimal:
-
PostgreSQL
- Exemple minimal:
DATABASE_KIND=postgres DATABASE_HOST=localhost DATABASE_PORT=5432 DATABASE_NAME=claire DATABASE_USER=claire DATABASE_PASSWORD=change_me
- Exemple minimal:
Migrations Doctrine
-
Pour initialiser la base de données et/ou la mettre à jour, exécutez:
./console migrations:migrate
-
Cette commande applique toutes les migrations disponibles (dossier
migrations/) et maintient la table de versiondb_versionconformément à la configuration définie dansconfig/settings/database.php.
Claire inclut une queue de fond minimaliste basee sur Redis pour deleguer certains traitements hors du cycle HTTP, en particulier les updates Telegram.
- Un job est retire de la queue des sa prise en charge par le worker.
- Le contrat des jobs est
App\Queue\QueueDoeravecmake(ContainerInterface $container)ethandle(array $payload): void. - Le contrat applicatif actuel suppose que
handle()absorbe ses erreurs et ne leve pas d'exception.
Lancer le worker:
./console queue:workTraiter une queue specifique:
./console queue:work --queue=telegramOptions utiles:
--once— traite un seul job puis quitte--sleep=1— attente quand la queue est vide--max-jobs=100— quitte apres N jobs--max-time=3600— quitte apres N secondes
Exemple:
export REDIS_HOST=127.0.0.1
export REDIS_PORT=6379
./console queue:work --queue=telegram- Exportez vos variables d’environnement (au besoin).
- Lancez le serveur de développement:
ou
composer start
php -S localhost:8080 -t public public/index.php
- Ouvrez http://localhost:8080
Claire fournit une image Docker complete basee sur FrankenPHP, Caddy et PHP 8.4.
docker build -t claire:latest -f docker/Dockerfile .L'extrait ci‑dessous présente une configuration Docker Compose de référence. Adaptez les variables d'environnement (OPENAPI_, OTEL_) et, le cas échéant, les labels Traefik à votre contexte.
services:
claire:
image: claire:latest
# Ou utilisez l'image de développement avec volumes montés:
# image: semhoun/webserver:8.4
# volumes:
# - .:/www
volumes:
- claire_data:/data # Persistance des données (base SQLite, fichiers uploadés, etc.)
ports:
- "8080:80"
environment:
SERVER_ADMIN: webmaster@example.com
SERVER_NAME: claire.example.com
DEBUG_MODE: "false"
ENABLE_ACCESS_LOGS: "true"
ENABLE_LETSENCRYPT: "false"
# ACME_EMAIL: admin@example.com
DATA_PATH: /data
# Session JWT (obligatoire)
SESSION_JWT_SECRET: ${SESSION_JWT_SECRET:?set_me}
# OpenTelemetry (requis)
OTEL_PHP_AUTOLOAD_ENABLED: "true"
OTEL_SERVICE_NAME: claire
OTEL_PHP_EXCLUDED_URLS: /health
OTEL_PROPAGATORS: baggage,tracecontext
OTEL_TRACES_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
# En développement, afficher les logs en console
OTEL_LOGS_EXPORTER: console
OTEL_LOGS_PROCESSOR: simple
# Optionnel: configuration OTLP commune (si vous envoyez vers un collecteur)
# OTEL_EXPORTER_OTLP_PROTOCOL: http/protobuf
# OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4318
# OTEL_EXPORTER_OTLP_HEADERS: authorization=Bearer <token>
# LLM (remplacez par vos valeurs / variables d'env)
OPENAPI_KEY: ${OPENAPI_KEY:?set_me}
OPENAPI_URL: https://api.openai.com/v1
OPENAPI_MODEL: gpt-5
# Optionnels (précisez si vous souhaitez des modèles dédiés)
OPENAPI_MODEL_SUMMARY: gpt-5-mini
OPENAPI_MODEL_EMBED: text-embedding-3-large
# NB: si `OPENAPI_MODEL_EMBED` est omis, le RAG est désactivé
# Optionnel
# SEARXNG_URL: http://searxng:8080
# ComfyUI (génération d'images, optionnel)
# COMFYUI_ENABLED: true
# COMFYUI_URL: http://comfyui:8188
# Telegram (optionnel)
# TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:?set_me}
# TELEGRAM_WEBHOOK_SECRET: ${TELEGRAM_WEBHOOK_SECRET:?set_me}
# OpenID Connect (SSO)
OPENID_WELLKNOWN_URL: https://auth.example.com/.well-known/openid-configuration
OPENID_CLIENT_ID: ${OPENID_CLIENT_ID:?set_me}
OPENID_CLIENT_SECRET: ${OPENID_CLIENT_SECRET:?set_me}
OPENID_REDIRECT_URI_BASE: https://claire.example.com
# Base de données
# Choix simple (par défaut): SQLite, aucune autre variable requise
DATABASE_KIND: sqlite
# Exemple MySQL/MariaDB (décommentez et ajustez)
# DATABASE_KIND: mysql
# DATABASE_HOST: mysql
# DATABASE_PORT: 3306
# DATABASE_NAME: claire
# DATABASE_USER: claire
# DATABASE_PASSWORD: change_me
# Exemple PostgreSQL (décommentez et ajustez)
# DATABASE_KIND: postgres
# DATABASE_HOST: postgres
# DATABASE_PORT: 5432
# DATABASE_NAME: claire
# DATABASE_USER: claire
# DATABASE_PASSWORD: change_me
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 60s
volumes:
claire_data:
networks:
internal:
external: true
name: internalNotes Docker/FrankenPHP:
- L'image Docker utilise FrankenPHP avec Caddy comme serveur HTTP integre.
- Si
ENABLE_LETSENCRYPT=true, le conteneur sert directement votre domaineSERVER_NAMEen HTTPS avec certificats Let's Encrypt automatiques. - Si
ENABLE_LETSENCRYPT=false, le conteneur reste en HTTP sur le port 80, ce qui est recommande derriere un reverse proxy TLS. ENABLE_ACCESS_LOGSpermet d'activer ou couper les logs d'acces HTTP sans rebuilder l'image.- Pour Let's Encrypt, le domaine doit etre publiquement resolvable vers le conteneur et les ports 80/443 doivent etre accessibles.
- Base: Debian Trixie Slim avec PHP 8.4
- Services: FrankenPHP + Caddy + Supervisor
- Extensions PHP: SQLite3, MySQL, PostgreSQL, GD, Imagick, Redis, Memcache, etc.
- OpenTelemetry: Extension pré-installée et configurée
- Modules Caddy:
transform-encoder, Mercure, Vulcain - Volumes:
/data— Données persistantes (base SQLite, fichiers uploadés, cache, etc.)/www— Code de l'application (pré-copié dans l'image, ou monté en dev)
- Healthcheck: Vérifie automatiquement l'endpoint
/health - Ports: Expose
80et443
# Démarrer la stack
docker compose up -d
# Voir les logs
docker compose logs -f claire
# Exécuter des commandes dans le conteneur
docker compose exec claire ./console migrations:migrate
docker compose exec claire ./console cache:clearPour un développement actif avec rechargement automatique, utilisez l'image semhoun/webserver:8.4 avec un volume monté:
services:
claire:
image: semhoun/webserver:8.4
volumes:
- .:/www
# ... reste de la configurationGET /health— endpoint JSON simple pour supervision (liveness/readiness).- Réponse 200 (exemple):
{ "version": "1.3.1", "date": "2025-01-01T12:34:56+00:00" } - Utilisation typique: sonde de conteneur/orchestrateur (Docker, Traefik, Kubernetes, etc.).
- Aucun corps requis, pas d’authentification par défaut.
- Réponse 200 (exemple):
GET /files/count— Nombre total de fichiers stockésGET /files/list— Liste des fichiers (JSON)POST /files/upload— Upload de fichier(s) standardPOST /files/upload_rag— Upload de fichier pour le RAG (vectorisation)DELETE /files/delete/{id}— Suppression d'un fichierGET /files/generated/{filename}— Récupération d'une image générée
GET /history/count— Nombre de conversationsGET /history/list— Liste des conversationsGET /history/open/{threadId}— Ouvrir une conversation existantePOST /history/new— Créer une nouvelle conversationDELETE /history/exchange/last— Annuler le dernier échange de la conversation couranteDELETE /history/delete/{threadId}— Supprimer une conversation
POST /config/chat_mode— Changer le mode de chat (sync/stream)POST /config/layout_mode— Changer le mode d'affichagePOST /config/brain_avatar— Changer l'avatar/cerveau actifPOST /config/telegram— Configurer TelegramGET /config/telegram_form— Formulaire de configuration Telegram
L’endpoint de streaming utilise le type text/event-stream et écrit au fil de l’eau (pas de buffer côté application). Cependant, certains reverse proxies / frontaux web peuvent bufferiser la réponse et empêcher l’affichage progressif côté navigateur.
Pour garantir un streaming fluide, désactivez la bufferisation au niveau du proxy. Exemple avec Apache quand PHP est servi via mod_proxy_fcgi:
# Assurez-vous que mod_proxy et mod_proxy_fcgi sont activés
# a2enmod proxy proxy_fcgi
# Votre mapping vers PHP-FPM (à adapter à votre environnement)
# ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/html/$1
# Désactiver la bufferisation pour le backend FastCGI
<Proxy "fcgi://localhost/" flushpackets=on flushwait=20>
</Proxy>Notes:
- Remplacez l’URL FastCGI (
fcgi://localhost/oufcgi://127.0.0.1:9000/…) par celle de votre instance PHP‑FPM. - Selon la version d’Apache, vous pouvez également utiliser
ProxySetà l’intérieur du bloc<Proxy>:<Proxy "fcgi://127.0.0.1:9000"> ProxySet flushpackets=on flushwait=20 </Proxy>
- Si vous utilisez un autre proxy (Nginx, Traefik, etc.), désactivez la bufferisation équivalente (ex. Nginx:
proxy_buffering off;sur l’emplacement concerné).
L’agent supporte deux modes de réponse:
- Mode « chat » (synchrone): la réponse complète est rendue côté serveur et renvoyée en une fois.
- Mode streaming SSE: la réponse est envoyée progressivement via un flux
text/stream(utilise une sortie non tamponnée côté application; veillez à désactiver la bufferisation côté proxy, voir plus haut).
La sélection du mode peut se faire via le champ mode dans le corps de la requête (chat par défaut, ou stream).
POST /brain/chat HTTP/1.1
Content-Type: application/json
{
"message": "Bonjour Claire !",
"mode": "chat"
}Réponses possibles:
- 200: corps HTML fragment (ex.: rendu Twig
partials/message.twig) ou contenu texte selon l’intégration front. - 422: si le champ
messageest vide.
POST /brain/chat HTTP/1.1
Accept: text/event-stream
Content-Type: application/json
{
"message": "Explique-moi la relativité en 3 points",
"mode": "stream"
}La réponse aura des en-têtes: content-type: text/stream, cache-control: no-cache. Les chunks contiendront du texte (et potentiellement des informations d’outillage) au fil de l’eau.
Deux mécanismes sont pris en charge côté serveur pour enrichir le contexte utilisateur:
- Upload direct de fichiers via
multipart/form-dataavec le champupload_files[]. - Référence à des fichiers déjà connus de l’application via une liste d’identifiants
file_ids(IDs de l’entitéApp\Entity\File).
Exemple multipart (upload direct):
POST /brain/chat HTTP/1.1
Content-Type: multipart/form-data; boundary=----BOUND
------BOUND
Content-Disposition: form-data; name="message"
Analyse ces documents, s'il te plaît.
------BOUND
Content-Disposition: form-data; name="mode"
chat
------BOUND
Content-Disposition: form-data; name="upload_files[]"; filename="notes.txt"
Content-Type: text/plain
(contenu du fichier)
------BOUND--Exemple JSON (fichiers déjà stockés):
POST /brain/chat HTTP/1.1
Content-Type: application/json
{
"message": "Utilise mes fichiers pour répondre.",
"mode": "chat",
"file_ids": ["e7a4f2d8-...", "6b0e9c1a-..."]
}Comportement serveur:
- Si
messageest vide ou absent → 422. - Les fichiers uploadés sont lus en mémoire et intégrés au contexte interne du message utilisateur.
- Les erreurs de lecture d’un fichier n’interrompent pas la requête: elles sont journalisées (best‑effort).
- Ne commitez jamais vos clés ou secrets (
OPENAPI_KEY, etc.). - En production, désactivez
DEBUG_MODEet vérifiez les permissions du répertoirevar/(cache, logs, tmp). - Si l’agent dispose d’outils web (lecture d’URL, recherche), restreignez l’accès public ou placez l’instance derrière une authentification/reverse proxy.
- Configurez le CORS en amont si vous exposez l’API à des origines externes.
Claire peut être utilisée comme bot Telegram avec deux modes de fonctionnement : webhook (recommandé pour la production) ou daemon (polling, utile pour le développement local).
L'intégration Telegram permet d'interagir avec Claire directement depuis l'application Telegram. Le bot supporte les messages texte, les photos et les documents, avec une historique de conversation persistante par utilisateur.
- Créez un bot via @BotFather sur Telegram.
- Récupérez le token API fourni (format:
123456789:ABCdefGHIjklMNOpqrsTUVwxyz).
Variable d'environnement (définie dans .env ou via Docker):
| Variable | Obligatoire | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Oui | Token du bot fourni par @BotFather |
TELEGRAM_WEBHOOK_SECRET |
Recommandé | Token secret pour sécuriser le webhook (générez une chaîne aléatoire de 32+ caractères) |
Le mode webhook permet à Telegram d'envoyer les mises à jour directement à votre serveur via HTTPS.
Configuration du webhook:
- Définissez
TELEGRAM_WEBHOOK_SECRETdans votre fichier.env(générez une chaîne aléatoire sécurisée) - Configurez le webhook:
# Option 1: Utiliser --domain (recommandé, HTTPS forcé)
./console telegram:webhook --domain=votre-domaine.com
# Option 2: Utiliser --url (URL complète personnalisée)
./console telegram:webhook --url=https://votre-domaine.com/webhook/telegram
# Vérifier le statut
./console telegram:webhook --info
# Supprimer le webhook (revenir au mode polling manuel)
./console telegram:webhook --deletePoints d'entrée:
- Endpoint webhook:
POST /webhook/telegram - Doit être accessible publiquement en HTTPS
- Vérifie le header
X-Telegram-Bot-Api-Secret-Token(siTELEGRAM_WEBHOOK_SECRETest configuré) - Le webhook enfile l'update Telegram dans la queue
telegram; le traitement effectif est fait parqueue:work
Exemple Docker Compose (webhook):
environment:
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:?set_me}
TELEGRAM_WEBHOOK_SECRET: ${TELEGRAM_WEBHOOK_SECRET:?set_me}Le mode daemon interroge périodiquement les serveurs Telegram pour récupérer les nouveaux messages.
Lancer le daemon:
# Mode simple
./console telegram:daemon
# Avec options personnalisées
./console telegram:daemon --timeout=60 --limit=50
# Arrêt gracieux: Ctrl+COptions disponibles:
--timeout: Timeout des requêtes long polling (défaut: 30s)--limit: Nombre maximum de messages à récupérer par requête (défaut: 100)
Configuration Docker Compose (daemon):
services:
claire:
# ... configuration web ...
environment:
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:?set_me}Les updates Telegram recus par webhook ou par daemon ne sont plus traites directement dans le process de reception.
TelegramServiceimplemente le contratQueueDoer.- Le webhook et le daemon enfilent
TelegramService::classdans la queuetelegramavec un payload JSON. - Le worker doit etre lance separement pour traiter les messages.
Exemple minimal:
./console queue:work --queue=telegramLe bot Telegram supporte:
- Messages texte: Dialogue standard avec historique persistant
- Photos: Envoi d'images avec analyse (si un modèle de vision est configuré)
- Documents: Upload de fichiers pour analyse
- Génération d'images: Création d'images via ComfyUI (si configuré)
- Changement de cerveau: Commande
/<nom_du_cerveau>pour basculer d'avatar - Commandes intégrées:
/start— Démarrer une nouvelle conversation/help— Afficher l'aide/list— Lister les personnalités/brain— Voir ou changer de personnalité/comfyui— Voir ou changer le workflow ComfyUI
Le bot Telegram nécessite une colonne supplémentaire dans la table des utilisateurs pour stocker l'identifiant Telegram:
./console migrations:migrateDémarrer une conversation:
/start
Bonjour Claire, peux-tu m'aider avec un problème de mathématiques?
Changer de cerveau:
/einstein
Quelle est la théorie de la relativité restreinte?
Envoyer une photo:
- Joindre une image avec ou sans légende
- Le bot analyse l'image si un modèle de vision est configuré
Envoyer un document:
- Joindre un fichier PDF, texte, etc.
- Le bot peut analyser le contenu du document
Demander une génération d'image:
Claire, peux-tu générer une image d'un chat sur la lune?
- L'agent génère l'image via ComfyUI et l'envoie sur Telegram
- Nécessite la configuration de ComfyUI (voir section dédiée)
-
Démarrer le serveur de développement:
composer start
-
Rector (modernisation PHP 8.4):
- Vérifier:
composer rector-check - Appliquer:
composer rector-fix
- Vérifier:
-
PHP Insights (qualité):
- Vérifier:
composer insights-check - Corriger:
composer insights-fix
- Vérifier:
-
Pre-commit (tous les checks):
composer pre-commit
Corrige les fins de ligne (LF), applique PHP Insights et Rector.
Le fichier ./console fournit des commandes pour la gestion du cache et des migrations:
-
Cache:
./console cache:clear— Vide le cache (conteneur, routes, etc.)./console cache:init— Initialise/régénère le cache./console generate:proxies— Génère les proxies Doctrine
-
Migrations Doctrine:
./console migrations:migrate— Applique les migrations./console migrations:diff— Génère une migration depuis les entités./console migrations:generate— Crée une migration vide./console migrations:status— Affiche le statut des migrations
-
Telegram:
./console telegram:webhook --url=https://...— Configure le webhook./console telegram:webhook --domain=...— Configure le webhook avec domaine (HTTPS forcé)./console telegram:webhook --info— Vérifie le statut du webhook./console telegram:webhook --delete— Supprime le webhook./console telegram:daemon— Lance le daemon (polling)./console telegram:set-commands— Configure le menu des commandes du bot Telegram (SetMyCommands)
-
Queue:
./console queue:work— Lance le worker de queue./console queue:work --queue=telegram— Traite la queue Telegram./console queue:work --once— Traite un seul job puis quitte
PHPUnit est configuré pour les tests unitaires:
vendor/bin/phpunit # Tous les tests
vendor/bin/phpunit test/Unit/Services/FooTest.php # Fichier spécifique
vendor/bin/phpunit --filter testMethodName # Méthode spécifique- Slim Tracy (debug console) est activable en mode debug via la configuration.
- Définissez
DISABLE_TRACY_BAR=falsepour activer la barre de debug.
- 500 au
GET /: vérifiez les permissions du dossier var/ (cache, logs, tmp). - 404 partout: vérifiez que le serveur pointe bien sur
public/index.phpet que vos règles de réécriture sont actives. - Pas de logs: les logs sont gérés par OpenTelemetry. Pour les voir dans la console, définissez
OTEL_LOGS_EXPORTER=console(etOTEL_LOGS_PROCESSOR=simplepour un affichage immédiat). En alternance, configurez un export OTLP (OTEL_LOGS_EXPORTER=otlp) vers un collecteur comme l’OTel Collector. - RAG inactif: assurez-vous que
OPENAPI_MODEL_EMBEDest défini. S’il est absent, le RAG est désactivé par conception. - Génération d'images non disponible: vérifiez que
COMFYUI_ENABLED=true, qu'au moins un workflow valide existe dansaddons/comfyui/, et que l'instance ComfyUI est accessible à l'URL définie dansCOMFYUI_URL.
Ce projet est distribué sous licence MIT. Voir le fichier LICENSE pour plus d’informations.