Toutes les modifications importantes de Synapse Core sont documentées dans ce fichier.
Les modifications importantes sont classées par catégorie :
AbstractAgent::useMasterPrompt() — opt-in Directive FondamentaleNouvelle méthode sur AbstractAgent (valeur par défaut : false). Les agents code sont désormais exemptés de la Directive Fondamentale (master prompt) par défaut — l'agent contrôle entièrement son propre prompt. Les agents qui ont besoin d'hériter des règles de sécurité de l'application hôte peuvent opt-in :
public function useMasterPrompt(): bool
{
return true;
}
Les agents BDD continuent de recevoir la directive fondamentale systématiquement. Le MasterPromptSubscriber lit le flag via $options['_skip_master_prompt'] injecté par ContextBuilderSubscriber.
run_agent_test — support des agents codeL'outil MCP run_agent_test résout maintenant les deux types d'agents : BDD (AgentRegistry) et code (CodeAgentRegistry). Le champ source ("db" ou "code") est ajouté à la réponse.
list_agents — fusion DB + codeListAgentsTool liste maintenant les agents code (non shadowés par un agent BDD) en plus des agents BDD. Les agents code exposent : systemPrompt ("(defined)" ou "(orchestrator)"), presetKey, allowedTools.
Avant ce fix, ContextBuilderSubscriber injectait la directive fondamentale sur tous les agents, y compris les agents code qui gèrent leur propre prompt. Correction : le flag _skip_master_prompt est injecté dans les options quand l'agent est un AbstractAgent avec useMasterPrompt() = false.
7 nouveaux outils MCP pour permettre aux clients MCP (Claude, etc.) de tester Synapse de manière autonome :
list_presets : Liste les presets disponibles avec provider, modèle, paramètres de générationcreate_sandbox_preset : Crée un preset temporaire avec choix de provider+modèle, validé via ModelCapabilityRegistrycreate_sandbox_agent : Crée un agent temporaire avec system prompt et preset assignécreate_sandbox_workflow : Assemble un pipeline multi-agents (format pivot JSON)run_workflow : Exécute via WorkflowRunner → MultiAgent → sous-agents via AgentResolverinspect_workflow_run : Inspecte status, tokens, output, durée d'un runcleanup_sandbox : Supprime toutes les entités sandbox (runs → workflows → agents → presets)Pattern sandbox : flag isSandbox (bool, default false) sur SynapseAgent, SynapseWorkflow, SynapseModelPreset. Filtré dans les queries admin (findAllActive, findAllOrdered, findAllPresets) mais résolvable pour l'exécution (findByKey non filtré). Migration Doctrine : 3 colonnes is_sandbox ajoutées.
ListAgentsTool enrichi : param includeSandbox + champ isSandbox + model dans l'output.
ConfiguredAgent::call() — message vide : quand le MultiAgent passe Input::ofStructured(...) à un agent configuré, getMessage() retournait une chaîne vide car seul le champ structured était peuplé. Ajout de buildMessageFromStructured() qui convertit l'input structuré en message texte (valeur unique = string directe, sinon JSON).ConfiguredAgent::call() — pas de token accounting : module/action manquants dans les options passées à ChatService::ask() → recordTokenUsage() court-circuitait. Fix : module='agent', action='agent_call' par défaut.SynapseDebugLogRepository::findRoots() — workflow steps invisibles : la condition WHERE parentRunId IS NULL excluait les logs de workflow steps (depth=1, origin=workflow) qui ont un parentRunId. Fix : OR (origin = 'workflow' AND depth = 1).synapse_label qui tente la traduction et fallback sur une version humanisée de la clé brute. Appliqué sur les pages debug et analytics pour éviter l'affichage de clés de traduction brutes pour les actions d'agents dynamiques.#[IsGranted]) par une vérification via PermissionCheckerInterface dans tous les contrôleurs (Admin & API).X-CSRF-Token pour une intégration fluide avec les frameworks frontend.canCreateConversation() pour protéger le point d'entrée du chat.Nouveau système de mémoire conversationnelle avec consentement explicite :
ProposeMemoryTool : Le LLM peut proposer de mémoriser un fait important via un AI Tool dédié. Il retourne un signal JSON (__synapse_action: memory_proposal) sans sauvegarder directement.MemoryManager : Service de haut niveau (remember, recall, forget, listForUser, update) avec vectorisation automatique via EmbeddingService.MemoryScope : Enum USER (souvenir permanent) / CONVERSATION (éphémère).MemoryApiController : Endpoints REST /synapse/api/memory/{confirm,reject,list,delete}.MemoryContextSubscriber : Injection automatique des souvenirs pertinents (score ≥ 0.7) dans le prompt avant chaque appel LLM.user_id imposé au niveau SQL dans DoctrineVectorStore — isolation totale garantie.SynapseVectorMemory enrichie : 5 nouvelles colonnes (user_id, scope, conversation_id, content, source_type).VectorStoreRegistry : Registre centralisé des implémentations de stockage.
DynamicVectorStore : Résolveur dynamique du moteur de stockage (sans redémarrage).
Interface Admin Embeddings : Sélection du Vector Store depuis l'interface.
Visualiseur de Chunking : Aperçu interactif avec mise à l'échelle adaptative (jusqu'à 20k chars).
Refactorisation majeure : ChatService utilise maintenant OpenAI Chat Completions comme format interne standard
Impact : Bundle maintenant 100% LLM-agnostique (prêt pour Mistral, Claude, Ollama, etc.)
Changement de format : Message système intégré comme premier élément de contents array
systemInstruction (string) + contents (array) séparéscontents avec role: 'system' en tête (format OpenAI)Clients LLM : Chacun devient un simple "traducteur"
ChatService : Zéro logique spécifique au provider (sécurité, safety settings, etc. gérés par les clients)
Chunk format : Changement de blocked_category → blocked_reason (raison lisible)
Sécurité Gemini : Toujours fonctionnelle, traduction déplacée du centre à la périphérie
Pour les développeurs créant des clients personnalisés : Voir le guide d'implémentation
synapse.encryption.key (clé 32 bytes base64 dans .env.local)api_key, service_account_json, private_keybase64(nonce_24bytes + ciphertext) en BDDpersonas_path)thinking.enabled, thinking.budgetSupport complet du multilingue pour les trois bundles :
synapse_admin, synapse_chat, synapse_core.TranslatorInterface dans les services de contexte et les contrôleurs API.Core/ : logique métier, orchestration LLMAdmin/ : contrôleurs et UI administrationStorage/ : persistance Doctrine, entités, repositoriesSecurity/ : chiffrement, permissionsContract/ : interfaces publiques (API du bundle)Shared/ : code réutilisable (enums, models, tools, utils)Infrastructure/ : DI, commandes CLI, ressources/viewsInfrastructure/config/models/ en premier, fallback sur Core/ROLE_ADMIN)synapse.yamlSi vous avez créé un client LLM personnalisé, vous devez mettre à jour ses signatures :
AVANT :
public function generateContent(
string $systemInstruction,
array $contents,
array $tools = [],
?string $model = null,
?array $thinkingConfigOverride = null,
array &$debugOut = [],
): array;
APRÈS :
public function generateContent(
array $contents, // ← Contient le système en [0]
array $tools = [],
?string $model = null,
?array $thinkingConfigOverride = null,
array &$debugOut = [],
): array;
AVANT : systèmeInstruction séparé + contents
$systemInstruction = "You are helpful";
$contents = [
['role' => 'user', 'content' => '...'],
['role' => 'assistant', 'content' => '...'],
];
APRÈS : Tout dans contents, système en premier
$contents = [
['role' => 'system', 'content' => 'You are helpful'], // ← PREMIER
['role' => 'user', 'content' => '...'],
['role' => 'assistant', 'content' => '...'],
];
AVANT : blocked_category (enum Gemini-spécifique)
return [
'blocked' => true,
'blocked_category' => 'HARM_CATEGORY_HATE_SPEECH', // ← Constante Gemini
];
APRÈS : blocked_reason (string lisible, provider-agnostique)
return [
'blocked' => true,
'blocked_reason' => 'discours haineux', // ← String lisible
];
Si vous aviez un client personnalisé, voici le pattern :
class MyLLMClient implements LlmClientInterface {
public function generateContent(
array $contents, // ← Nouvelle signature
array $tools = [],
?string $model = null,
?array $thinkingConfigOverride = null,
array &$debugOut = [],
): array {
// 1. Extraire le système si présent
$systemMessage = '';
$contentsWithoutSystem = $contents;
if (!empty($contents) && $contents[0]['role'] === 'system') {
$systemMessage = $contents[0]['content'];
$contentsWithoutSystem = array_slice($contents, 1);
}
// 2. Convertir au format de votre provider
$providerMessages = $this->toProviderFormat($contentsWithoutSystem);
// 3. Appeler votre API (avec ou sans système selon le provider)
$response = $this->callApi($systemMessage, $providerMessages, ...);
// 4. Normaliser la réponse
return $this->normalizeResponse($response);
}
}
Voir : le guide d'implémentation pour un guide complet.
php -r "echo base64_encode(random_bytes(32));".env.local : SYNAPSE_ENCRYPTION_KEY=<valeur_générée>synapse.yaml :
synapse:
encryption:
key: '%env(SYNAPSE_ENCRYPTION_KEY)%'
synapse.yamlHow can I help you explore Laravel packages today?