Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/alexandrefradin-lab_praxitests.json

1 line
76 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "alexandrefradin-lab/praxitests", "files": {"app/Http/Controllers/Admin/SettingsController.php": {"8796a1b140d3e49424776adbca8afbe107d629a8": {"t": 1785745792.0599098, "n": 6740, "h": "dd8ef75f8341967a", "c": "<?php\n\nnamespace App\\Http\\Controllers\\Admin;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\Setting;\nuse Illuminate\\Http\\Request;\nuse Inertia\\Inertia;\n\nclass SettingsController extends Controller\n{\n /** Clés IA qui doivent être chiffrées en base. */\n private const ENCRYPTED_KEYS = [\n 'anthropic_api_key',\n 'openai_api_key',\n 'mistral_api_key',\n 'deepseek_api_key',\n ];\n\n /** Fournisseurs (drivers) sélectionnables par tâche. value = nom du driver. */\n private const PROVIDERS = [\n ['value' => '', 'label' => '(défaut config)'],\n ['value' => 'anthropic', 'label' => 'Claude — Sonnet (qualité)'],\n ['value' => 'anthropic_haiku', 'label' => 'Claude — Haiku (économique)'],\n ['value' => 'openai', 'label' => 'OpenAI'],\n ['value' => 'deepseek', 'label' => 'DeepSeek'],\n ['value' => 'mistral', 'label' => 'Mistral'],\n ];\n\n /** Tâches IA dont le modèle est pilotable en admin (clé => libellé). */\n private const TASKS = [\n 'profile_synthesis' => 'Synthèse par test',\n 'job_suggestions' => 'Suggestions de métiers (par test)',\n 'global_grimoire' => 'Grimoire — relecture (synthèse)',\n 'global_grimoire_voies' => 'Grimoire — pistes métiers',\n 'cv_extract' => 'Extraction du CV',\n 'email_personalization' => 'Personnalisation des emails',\n 'oracle_chat' => 'Oracle (chat)',\n ];\n\n /** Drivers autorisés pour une tâche (sécurité : pas de valeur arbitraire). */\n private function allowedDrivers(): array\n {\n return ['', 'anthropic', 'anthropic_haiku', 'openai', 'deepseek', 'mistral', 'ollama'];\n }\n\n public function show()\n {\n $raw = Setting::getGroup('ai');\n\n // Masquer les clés existantes pour l'affichage (on n'envoie jamais la valeur en clair)\n $masked = [];\n foreach ($raw as $key => $value) {\n $masked[$key] = (in_array($key, self::ENCRYPTED_KEYS, true) && $value)\n ? '••••••••' // juste indiquer qu'une clé est enregistrée\n : $value;\n }\n\n // Réglage courant par tâche (driver + modèle), avec repli sur la config.\n $taskConfig = [];\n foreach (self::TASKS as $task => $label) {\n $taskConfig[$task] = [\n 'label' => $label,\n 'driver' => $masked[\"task_{$task}_driver\"] ?? (config(\"ai.tasks.{$task}.driver\") ?? ''),\n 'model' => $masked[\"task_{$task}_model\"] ?? '',\n 'default_model' => config(\"ai.drivers.\" . (config(\"ai.tasks.{$task}.driver\") ?: config('ai.default')) . \".model\"),\n ];\n }\n\n return Inertia::render('Admin/Settings', [\n 'settings' => $masked,\n 'drivers' => array_keys(config('ai.drivers')),\n 'providers' => self::PROVIDERS,\n 'taskConfig' => $taskConfig,\n ]);\n }\n\n public function update(Request $request)\n {\n $allowed = $this->allowedDrivers();\n\n $rules = [\n 'default_driver' => ['required', 'string', 'in:anthropic,openai,mistral,ollama,deepseek'],\n 'anthropic_api_key' => ['nullable', 'string'],\n 'anthropic_model' => ['nullable', 'string', 'max:120'],\n 'anthropic_haiku_model' => ['nullable', 'string', 'max:120'],\n 'openai_api_key' => ['nullable', 'string'],\n 'openai_model' => ['nullable', 'string', 'max:120'],\n 'deepseek_api_key' => ['nullable', 'string'],\n 'deepseek_model' => ['nullable', 'string', 'max:120'],\n 'deepseek_base_url' => ['nullable', 'url', 'max:255'],\n 'mistral_api_key' => ['nullable', 'string'],\n 'mistral_model' => ['nullable', 'string', 'max:120'],\n 'ollama_base_url' => ['nullable', 'url', 'max:255'],\n 'ollama_model' => ['nullable', 'string', 'max:120'],\n ];\n\n // Champs dynamiques par tâche.\n foreach (array_keys(self::TASKS) as $task) {\n $rules[\"task_{$task}_driver\"] = ['nullable', 'string', 'in:' . implode(',', $allowed)];\n $rules[\"task_{$task}_model\"] = ['nullable', 'string', 'max:120'];\n }\n\n $data = $request->validate($rules);\n\n $changedKeys = [];\n foreach ($data as $key => $value) {\n // Ne pas écraser une clé chiffrée si l'user n'a rien saisi\n // (l'affichage renvoie '••••••••' mais on l'ignore)\n if (in_array($key, self::ENCRYPTED_KEYS, true)) {\n if (blank($value) || $value === '••••••••') continue;\n Setting::set('ai', $key, $value, true);\n $changedKeys[] = $key; // le nom de la clé, jamais sa valeur\n } else {\n // null = champ non envoyé → on ne touche pas ; '' = effacement explicite.\n if ($value !== null) {\n Setting::set('ai', $key, $value, false);\n $changedKeys[] = $key;\n }\n }\n }\n\n // Traçabilité : quels réglages ont changé (noms uniquement, pas les secrets).\n \\App\\Models\\AuditLog::record('settings.updated', null, ['keys' => $changedKeys]);\n\n return back()->with('success', 'Configuration IA enregistrée.');\n }\n\n /**\n * Teste la connexion au fournisseur IA sélectionné avec la configuration\n * réellement enregistrée (clé déchiffrée côté serveur, jamais exposée).\n * Une clé invalide se découvre ici, pas au premier candidat sans synthèse.\n */\n public function testConnection(Request $request, \\Praxis\\Core\\AI\\AIManager $ai)\n {\n $data = $request->validate([\n 'driver' => ['required', 'string', 'in:anthropic,anthropic_haiku,openai,deepseek,mistral,ollama'],\n ]);\n\n try {\n // Ping minimal : une génération d'un mot suffit à valider clé + modèle + réseau.\n $driver = $ai->driver($data['driver']);\n $response = $driver->generate('Réponds uniquement : OK', ['max_tokens' => 8]);\n\n return back()->with('success', sprintf(\n 'Connexion %s réussie (modèle %s) — réponse : « %s »',\n $data['driver'],\n $driver->model(),\n \\Illuminate\\Support\\Str::limit(trim($response), 40),\n ));\n } catch (\\Throwable $e) {\n report($e);\n return back()->with('error', sprintf(\n 'Échec de connexion %s : %s',\n $data['driver'],\n \\Illuminate\\Support\\Str::limit($e->getMessage(), 180),\n ));\n }\n }\n}\n"}, "d7c38c4853930ed2f1773c0b8a86c166bc2cc1c7": {"t": 1785756533.8331783, "n": 6740, "h": "dd8ef75f8341967a", "c": "<?php\n\nnamespace App\\Http\\Controllers\\Admin;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\Setting;\nuse Illuminate\\Http\\Request;\nuse Inertia\\Inertia;\n\nclass SettingsController extends Controller\n{\n /** Clés IA qui doivent être chiffrées en base. */\n private const ENCRYPTED_KEYS = [\n 'anthropic_api_key',\n 'openai_api_key',\n 'mistral_api_key',\n 'deepseek_api_key',\n ];\n\n /** Fournisseurs (drivers) sélectionnables par tâche. value = nom du driver. */\n private const PROVIDERS = [\n ['value' => '', 'label' => '(défaut config)'],\n ['value' => 'anthropic', 'label' => 'Claude — Sonnet (qualité)'],\n ['value' => 'anthropic_haiku', 'label' => 'Claude — Haiku (économique)'],\n ['value' => 'openai', 'label' => 'OpenAI'],\n ['value' => 'deepseek', 'label' => 'DeepSeek'],\n ['value' => 'mistral', 'label' => 'Mistral'],\n ];\n\n /** Tâches IA dont le modèle est pilotable en admin (clé => libellé). */\n private const TASKS = [\n 'profile_synthesis' => 'Synthèse par test',\n 'job_suggestions' => 'Suggestions de métiers (par test)',\n 'global_grimoire' => 'Grimoire — relecture (synthèse)',\n 'global_grimoire_voies' => 'Grimoire — pistes métiers',\n 'cv_extract' => 'Extraction du CV',\n 'email_personalization' => 'Personnalisation des emails',\n 'oracle_chat' => 'Oracle (chat)',\n ];\n\n /** Drivers autorisés pour une tâche (sécurité : pas de valeur arbitraire). */\n private function allowedDrivers(): array\n {\n return ['', 'anthropic', 'anthropic_haiku', 'openai', 'deepseek', 'mistral', 'ollama'];\n }\n\n public function show()\n {\n $raw = Setting::getGroup('ai');\n\n // Masquer les clés existantes pour l'affichage (on n'envoie jamais la valeur en clair)\n $masked = [];\n foreach ($raw as $key => $value) {\n $masked[$key] = (in_array($key, self::ENCRYPTED_KEYS, true) && $value)\n ? '••••••••' // juste indiquer qu'une clé est enregistrée\n : $value;\n }\n\n // Réglage courant par tâche (driver + modèle), avec repli sur la config.\n $taskConfig = [];\n foreach (self::TASKS as $task => $label) {\n $taskConfig[$task] = [\n 'label' => $label,\n 'driver' => $masked[\"task_{$task}_driver\"] ?? (config(\"ai.tasks.{$task}.driver\") ?? ''),\n 'model' => $masked[\"task_{$task}_model\"] ?? '',\n 'default_model' => config(\"ai.drivers.\" . (config(\"ai.tasks.{$task}.driver\") ?: config('ai.default')) . \".model\"),\n ];\n }\n\n return Inertia::render('Admin/Settings', [\n 'settings' => $masked,\n 'drivers' => array_keys(config('ai.drivers')),\n 'providers' => self::PROVIDERS,\n 'taskConfig' => $taskConfig,\n ]);\n }\n\n public function update(Request $request)\n {\n $allowed = $this->allowedDrivers();\n\n $rules = [\n 'default_driver' => ['required', 'string', 'in:anthropic,openai,mistral,ollama,deepseek'],\n 'anthropic_api_key' => ['nullable', 'string'],\n 'anthropic_model' => ['nullable', 'string', 'max:120'],\n 'anthropic_haiku_model' => ['nullable', 'string', 'max:120'],\n 'openai_api_key' => ['nullable', 'string'],\n 'openai_model' => ['nullable', 'string', 'max:120'],\n 'deepseek_api_key' => ['nullable', 'string'],\n 'deepseek_model' => ['nullable', 'string', 'max:120'],\n 'deepseek_base_url' => ['nullable', 'url', 'max:255'],\n 'mistral_api_key' => ['nullable', 'string'],\n 'mistral_model' => ['nullable', 'string', 'max:120'],\n 'ollama_base_url' => ['nullable', 'url', 'max:255'],\n 'ollama_model' => ['nullable', 'string', 'max:120'],\n ];\n\n // Champs dynamiques par tâche.\n foreach (array_keys(self::TASKS) as $task) {\n $rules[\"task_{$task}_driver\"] = ['nullable', 'string', 'in:' . implode(',', $allowed)];\n $rules[\"task_{$task}_model\"] = ['nullable', 'string', 'max:120'];\n }\n\n $data = $request->validate($rules);\n\n $changedKeys = [];\n foreach ($data as $key => $value) {\n // Ne pas écraser une clé chiffrée si l'user n'a rien saisi\n // (l'affichage renvoie '••••••••' mais on l'ignore)\n if (in_array($key, self::ENCRYPTED_KEYS, true)) {\n if (blank($value) || $value === '••••••••') continue;\n Setting::set('ai', $key, $value, true);\n $changedKeys[] = $key; // le nom de la clé, jamais sa valeur\n } else {\n // null = champ non envoyé → on ne touche pas ; '' = effacement explicite.\n if ($value !== null) {\n Setting::set('ai', $key, $value, false);\n $changedKeys[] = $key;\n }\n }\n }\n\n // Traçabilité : quels réglages ont changé (noms uniquement, pas les secrets).\n \\App\\Models\\AuditLog::record('settings.updated', null, ['keys' => $changedKeys]);\n\n return back()->with('success', 'Configuration IA enregistrée.');\n }\n\n /**\n * Teste la connexion au fournisseur IA sélectionné avec la configuration\n * réellement enregistrée (clé déchiffrée côté serveur, jamais exposée).\n * Une clé invalide se découvre ici, pas au premier candidat sans synthèse.\n */\n public function testConnection(Request $request, \\Praxis\\Core\\AI\\AIManager $ai)\n {\n $data = $request->validate([\n 'driver' => ['required', 'string', 'in:anthropic,anthropic_haiku,openai,deepseek,mistral,ollama'],\n ]);\n\n try {\n // Ping minimal : une génération d'un mot suffit à valider clé + modèle + réseau.\n $driver = $ai->driver($data['driver']);\n $response = $driver->generate('Réponds uniquement : OK', ['max_tokens' => 8]);\n\n return back()->with('success', sprintf(\n 'Connexion %s réussie (modèle %s) — réponse : « %s »',\n $data['driver'],\n $driver->model(),\n \\Illuminate\\Support\\Str::limit(trim($response), 40),\n ));\n } catch (\\Throwable $e) {\n report($e);\n return back()->with('error', sprintf(\n 'Échec de connexion %s : %s',\n $data['driver'],\n \\Illuminate\\Support\\Str::limit($e->getMessage(), 180),\n ));\n }\n }\n}\n"}, "5859f1c8e06973f9fee9a9b463376bd44be42ab7": {"t": 1785756536.3798978, "n": 5093, "h": "ad2e934fc52c66bd", "c": "<?php\n\nnamespace App\\Http\\Controllers\\Admin;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\Setting;\nuse Illuminate\\Http\\Request;\nuse Inertia\\Inertia;\n\nclass SettingsController extends Controller\n{\n /** Clés IA qui doivent être chiffrées en base. */\n private const ENCRYPTED_KEYS = [\n 'anthropic_api_key',\n 'openai_api_key',\n 'mistral_api_key',\n 'deepseek_api_key',\n ];\n\n /** Fournisseurs (drivers) sélectionnables par tâche. value = nom du driver. */\n private const PROVIDERS = [\n ['value' => '', 'label' => '(défaut config)'],\n ['value' => 'anthropic', 'label' => 'Claude — Sonnet (qualité)'],\n ['value' => 'anthropic_haiku', 'label' => 'Claude — Haiku (économique)'],\n ['value' => 'openai', 'label' => 'OpenAI'],\n ['value' => 'deepseek', 'label' => 'DeepSeek'],\n ['value' => 'mistral', 'label' => 'Mistral'],\n ];\n\n /** Tâches IA dont le modèle est pilotable en admin (clé => libellé). */\n private const TASKS = [\n 'profile_synthesis' => 'Synthèse par test',\n 'job_suggestions' => 'Suggestions de métiers (par test)',\n 'global_grimoire' => 'Grimoire — relecture (synthèse)',\n 'global_grimoire_voies' => 'Grimoire — pistes métiers',\n 'cv_extract' => 'Extraction du CV',\n 'email_personalization' => 'Personnalisation des emails',\n 'oracle_chat' => 'Oracle (chat)',\n ];\n\n /** Drivers autorisés pour une tâche (sécurité : pas de valeur arbitraire). */\n private function allowedDrivers(): array\n {\n return ['', 'anthropic', 'anthropic_haiku', 'openai', 'deepseek', 'mistral', 'ollama'];\n }\n\n public function show()\n {\n $raw = Setting::getGroup('ai');\n\n // Masquer les clés existantes pour l'affichage (on n'envoie jamais la valeur en clair)\n $masked = [];\n foreach ($raw as $key => $value) {\n $masked[$key] = (in_array($key, self::ENCRYPTED_KEYS, true) && $value)\n ? '••••••••' // juste indiquer qu'une clé est enregistrée\n : $value;\n }\n\n // Réglage courant par tâche (driver + modèle), avec repli sur la config.\n $taskConfig = [];\n foreach (self::TASKS as $task => $label) {\n $taskConfig[$task] = [\n 'label' => $label,\n 'driver' => $masked[\"task_{$task}_driver\"] ?? (config(\"ai.tasks.{$task}.driver\") ?? ''),\n 'model' => $masked[\"task_{$task}_model\"] ?? '',\n 'default_model' => config(\"ai.drivers.\" . (config(\"ai.tasks.{$task}.driver\") ?: config('ai.default')) . \".model\"),\n ];\n }\n\n return Inertia::render('Admin/Settings', [\n 'settings' => $masked,\n 'drivers' => array_keys(config('ai.drivers')),\n 'providers' => self::PROVIDERS,\n 'taskConfig' => $taskConfig,\n ]);\n }\n\n public function update(Request $request)\n {\n $allowed = $this->allowedDrivers();\n\n $rules = [\n 'default_driver' => ['required', 'string', 'in:anthropic,openai,mistral,ollama,deepseek'],\n 'anthropic_api_key' => ['nullable', 'string'],\n 'anthropic_model' => ['nullable', 'string', 'max:120'],\n 'anthropic_haiku_model' => ['nullable', 'string', 'max:120'],\n 'openai_api_key' => ['nullable', 'string'],\n 'openai_model' => ['nullable', 'string', 'max:120'],\n 'deepseek_api_key' => ['nullable', 'string'],\n 'deepseek_model' => ['nullable', 'string', 'max:120'],\n 'deepseek_base_url' => ['nullable', 'url', 'max:255'],\n 'mistral_api_key' => ['nullable', 'string'],\n 'mistral_model' => ['nullable', 'string', 'max:120'],\n 'ollama_base_url' => ['nullable', 'url', 'max:255'],\n 'ollama_model' => ['nullable', 'string', 'max:120'],\n ];\n\n // Champs dynamiques par tâche.\n foreach (array_keys(self::TASKS) as $task) {\n $rules[\"task_{$task}_driver\"] = ['nullable', 'string', 'in:' . implode(',', $allowed)];\n $rules[\"task_{$task}_model\"] = ['nullable', 'string', 'max:120'];\n }\n\n $data = $request->validate($rules);\n\n foreach ($data as $key => $value) {\n // Ne pas écraser une clé chiffrée si l'user n'a rien saisi\n // (l'affichage renvoie '••••••••' mais on l'ignore)\n if (in_array($key, self::ENCRYPTED_KEYS, true)) {\n if (blank($value) || $value === '••••••••') continue;\n Setting::set('ai', $key, $value, true);\n } else {\n // null = champ non envoyé → on ne touche pas ; '' = effacement explicite.\n if ($value !== null) {\n Setting::set('ai', $key, $value, false);\n }\n }\n }\n\n return back()->with('success', 'Configuration IA enregistrée.');\n }\n}\n"}, "10e0cf85652ddf131a261bf67637b26a8ac38d67": {"t": 1785756537.6058433, "n": 2336, "h": "3618e4ff3390d69b", "c": "<?php\n\nnamespace App\\Http\\Controllers\\Admin;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\Setting;\nuse Illuminate\\Http\\Request;\nuse Inertia\\Inertia;\n\nclass SettingsController extends Controller\n{\n /** Clés IA qui doivent être chiffrées en base. */\n private const ENCRYPTED_KEYS = [\n 'anthropic_api_key',\n 'openai_api_key',\n 'mistral_api_key',\n ];\n\n public function show()\n {\n $raw = Setting::getGroup('ai');\n\n // Masquer les clés existantes pour l'affichage (on n'envoie jamais la valeur en clair)\n $masked = [];\n foreach ($raw as $key => $value) {\n $masked[$key] = (in_array($key, self::ENCRYPTED_KEYS, true) && $value)\n ? '••••••••' // juste indiquer qu'une clé est enregistrée\n : $value;\n }\n\n return Inertia::render('Admin/Settings', [\n 'settings' => $masked,\n 'drivers' => array_keys(config('ai.drivers')),\n ]);\n }\n\n public function update(Request $request)\n {\n $data = $request->validate([\n 'default_driver' => ['required', 'string', 'in:anthropic,openai,mistral,ollama'],\n 'anthropic_api_key' => ['nullable', 'string'],\n 'anthropic_model' => ['nullable', 'string', 'max:120'],\n 'openai_api_key' => ['nullable', 'string'],\n 'openai_model' => ['nullable', 'string', 'max:120'],\n 'mistral_api_key' => ['nullable', 'string'],\n 'mistral_model' => ['nullable', 'string', 'max:120'],\n 'ollama_base_url' => ['nullable', 'url', 'max:255'],\n 'ollama_model' => ['nullable', 'string', 'max:120'],\n ]);\n\n foreach ($data as $key => $value) {\n // Ne pas écraser une clé chiffrée si l'user n'a rien saisi\n // (l'affichage renvoie '••••••••' mais on l'ignore)\n if (in_array($key, self::ENCRYPTED_KEYS, true)) {\n if (blank($value) || $value === '••••••••') continue;\n Setting::set('ai', $key, $value, true);\n } else {\n if ($value !== null) {\n Setting::set('ai', $key, $value, false);\n }\n }\n }\n\n return back()->with('success', 'Configuration IA enregistrée.');\n }\n}\n"}}, "config/ai.php": {"8796a1b140d3e49424776adbca8afbe107d629a8": {"t": 1785746169.8400085, "n": 5405, "h": "3073befabfa69f66", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n\n // Même fournisseur (Anthropic), modèle ÉCONOMIQUE (Haiku ≈ 3× moins cher que\n // Sonnet : 1$/5$ vs 3$/15$ par M tokens). Utilisé pour les tâches structurées\n // ou peu rédactionnelles (extraction CV, emails, suggestions, voies du Grimoire).\n 'anthropic_haiku' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_HAIKU_MODEL', 'claude-haiku-4-5-20251001'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n\n // DeepSeek : API compatible OpenAI → on réutilise OpenAiDriver avec un base_url\n // différent. Clé et modèle surchargeables en admin (Setting groupe 'ai').\n 'deepseek' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'key' => 'deepseek',\n 'api_key' => env('DEEPSEEK_API_KEY'),\n 'base_url' => env('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'),\n 'model' => env('DEEPSEEK_MODEL', 'deepseek-chat'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n // NB : 'driver' désigne une entrée de 'drivers' ci-dessus. null = défaut (anthropic/Sonnet).\n // 'anthropic_haiku' = même fournisseur, modèle économique (Haiku). Choix par tâche\n // pour ne payer Sonnet QUE sur le rédactionnel (synthèses, relecture, Oracle) et\n // basculer les tâches structurées/extraction vers Haiku. Sera surchargeable en admin (Phase 2).\n 'tasks' => [\n // Rédactionnel, qualité importante → Sonnet.\n 'profile_synthesis' => [\n 'driver' => null, // null = default (Sonnet)\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n // Liste structurée de métiers → Haiku suffit. count 30→15 (aligné sur le produit : \"15 idées de métiers\").\n 'job_suggestions' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n // Grimoire — SYNTHÈSE (relecture rédactionnelle) → Sonnet.\n 'global_grimoire' => [\n 'driver' => null, // = défaut (Sonnet)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n // Grimoire — VOIES (liste structurée compacte) → Haiku (3× moins cher).\n 'global_grimoire_voies' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Grimoire — IMPACT IA SUR LE MÉTIER (onglet « Ton métier face à l'IA ») → Haiku.\n // Relecture courte et structurée (Markdown) : analyse de l'exposition du métier\n // du candidat à l'IA. Générée en même temps que les voies (étape 2 du job).\n 'global_grimoire_ia_impact' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Extraction JSON pure → Haiku.\n 'cv_extract' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n // Personnalisation d'email → Haiku.\n 'email_personalization' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n // Plan d'action par piste métier → Haiku (structuré, compact, ~0,01€/plan).\n 'path_plan' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Oracle conversationnel → Sonnet (qualité), mais on réduit l'historique rejoué\n // (20→10) pour diviser par ~2 les tokens d'entrée sur les conversations longues.\n 'oracle_chat' => [\n 'driver' => null, // = défaut (Sonnet)\n 'history_limit' => 10, // nombre de messages (user+assistant) rejoués dans le prompt\n // Délai max (secondes) de l'appel LLM. L'Oracle est SYNCHRONE (il bloque\n // un worker PHP-FPM pendant toute la réponse — cf. SCALING_1000_USERS.md) :\n // on plafonne bien plus bas que les 120 s par défaut des jobs en arrière-plan.\n 'timeout' => (int) env('AI_ORACLE_CHAT_TIMEOUT', 30),\n ],\n ],\n];\n"}, "e2893d9b3fbe67665e5e71bd7ef85416f9b8f70f": {"t": 1785756534.2347565, "n": 5405, "h": "3073befabfa69f66", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n\n // Même fournisseur (Anthropic), modèle ÉCONOMIQUE (Haiku ≈ 3× moins cher que\n // Sonnet : 1$/5$ vs 3$/15$ par M tokens). Utilisé pour les tâches structurées\n // ou peu rédactionnelles (extraction CV, emails, suggestions, voies du Grimoire).\n 'anthropic_haiku' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_HAIKU_MODEL', 'claude-haiku-4-5-20251001'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n\n // DeepSeek : API compatible OpenAI → on réutilise OpenAiDriver avec un base_url\n // différent. Clé et modèle surchargeables en admin (Setting groupe 'ai').\n 'deepseek' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'key' => 'deepseek',\n 'api_key' => env('DEEPSEEK_API_KEY'),\n 'base_url' => env('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'),\n 'model' => env('DEEPSEEK_MODEL', 'deepseek-chat'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n // NB : 'driver' désigne une entrée de 'drivers' ci-dessus. null = défaut (anthropic/Sonnet).\n // 'anthropic_haiku' = même fournisseur, modèle économique (Haiku). Choix par tâche\n // pour ne payer Sonnet QUE sur le rédactionnel (synthèses, relecture, Oracle) et\n // basculer les tâches structurées/extraction vers Haiku. Sera surchargeable en admin (Phase 2).\n 'tasks' => [\n // Rédactionnel, qualité importante → Sonnet.\n 'profile_synthesis' => [\n 'driver' => null, // null = default (Sonnet)\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n // Liste structurée de métiers → Haiku suffit. count 30→15 (aligné sur le produit : \"15 idées de métiers\").\n 'job_suggestions' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n // Grimoire — SYNTHÈSE (relecture rédactionnelle) → Sonnet.\n 'global_grimoire' => [\n 'driver' => null, // = défaut (Sonnet)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n // Grimoire — VOIES (liste structurée compacte) → Haiku (3× moins cher).\n 'global_grimoire_voies' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Grimoire — IMPACT IA SUR LE MÉTIER (onglet « Ton métier face à l'IA ») → Haiku.\n // Relecture courte et structurée (Markdown) : analyse de l'exposition du métier\n // du candidat à l'IA. Générée en même temps que les voies (étape 2 du job).\n 'global_grimoire_ia_impact' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Extraction JSON pure → Haiku.\n 'cv_extract' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n // Personnalisation d'email → Haiku.\n 'email_personalization' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n // Plan d'action par piste métier → Haiku (structuré, compact, ~0,01€/plan).\n 'path_plan' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Oracle conversationnel → Sonnet (qualité), mais on réduit l'historique rejoué\n // (20→10) pour diviser par ~2 les tokens d'entrée sur les conversations longues.\n 'oracle_chat' => [\n 'driver' => null, // = défaut (Sonnet)\n 'history_limit' => 10, // nombre de messages (user+assistant) rejoués dans le prompt\n // Délai max (secondes) de l'appel LLM. L'Oracle est SYNCHRONE (il bloque\n // un worker PHP-FPM pendant toute la réponse — cf. SCALING_1000_USERS.md) :\n // on plafonne bien plus bas que les 120 s par défaut des jobs en arrière-plan.\n 'timeout' => (int) env('AI_ORACLE_CHAT_TIMEOUT', 30),\n ],\n ],\n];\n"}, "bc7bf6486e727ad9bb1f15be7c3628f61b56ebad": {"t": 1785756535.5143404, "n": 5072, "h": "ae0f65a62016ca53", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n\n // Même fournisseur (Anthropic), modèle ÉCONOMIQUE (Haiku ≈ 3× moins cher que\n // Sonnet : 1$/5$ vs 3$/15$ par M tokens). Utilisé pour les tâches structurées\n // ou peu rédactionnelles (extraction CV, emails, suggestions, voies du Grimoire).\n 'anthropic_haiku' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_HAIKU_MODEL', 'claude-haiku-4-5-20251001'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n\n // DeepSeek : API compatible OpenAI → on réutilise OpenAiDriver avec un base_url\n // différent. Clé et modèle surchargeables en admin (Setting groupe 'ai').\n 'deepseek' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'key' => 'deepseek',\n 'api_key' => env('DEEPSEEK_API_KEY'),\n 'base_url' => env('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'),\n 'model' => env('DEEPSEEK_MODEL', 'deepseek-chat'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n // NB : 'driver' désigne une entrée de 'drivers' ci-dessus. null = défaut (anthropic/Sonnet).\n // 'anthropic_haiku' = même fournisseur, modèle économique (Haiku). Choix par tâche\n // pour ne payer Sonnet QUE sur le rédactionnel (synthèses, relecture, Oracle) et\n // basculer les tâches structurées/extraction vers Haiku. Sera surchargeable en admin (Phase 2).\n 'tasks' => [\n // Rédactionnel, qualité importante → Sonnet.\n 'profile_synthesis' => [\n 'driver' => null, // null = default (Sonnet)\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n // Liste structurée de métiers → Haiku suffit. count 30→15 (aligné sur le produit : \"15 idées de métiers\").\n 'job_suggestions' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n // Grimoire — SYNTHÈSE (relecture rédactionnelle) → Sonnet.\n 'global_grimoire' => [\n 'driver' => null, // = défaut (Sonnet)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n // Grimoire — VOIES (liste structurée compacte) → Haiku (3× moins cher).\n 'global_grimoire_voies' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Grimoire — IMPACT IA SUR LE MÉTIER (onglet « Ton métier face à l'IA ») → Haiku.\n // Relecture courte et structurée (Markdown) : analyse de l'exposition du métier\n // du candidat à l'IA. Générée en même temps que les voies (étape 2 du job).\n 'global_grimoire_ia_impact' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Extraction JSON pure → Haiku.\n 'cv_extract' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n // Personnalisation d'email → Haiku.\n 'email_personalization' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n // Plan d'action par piste métier → Haiku (structuré, compact, ~0,01€/plan).\n 'path_plan' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Oracle conversationnel → Sonnet (qualité), mais on réduit l'historique rejoué\n // (20→10) pour diviser par ~2 les tokens d'entrée sur les conversations longues.\n 'oracle_chat' => [\n 'driver' => null, // = défaut (Sonnet)\n 'history_limit' => 10, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "c9167debe91e6e135593666aef37b18cd0b45d74": {"t": 1785756536.9198096, "n": 4908, "h": "b7516de8773b582c", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n\n // Même fournisseur (Anthropic), modèle ÉCONOMIQUE (Haiku ≈ 3× moins cher que\n // Sonnet : 1$/5$ vs 3$/15$ par M tokens). Utilisé pour les tâches structurées\n // ou peu rédactionnelles (extraction CV, emails, suggestions, voies du Grimoire).\n 'anthropic_haiku' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_HAIKU_MODEL', 'claude-haiku-4-5-20251001'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n\n // DeepSeek : API compatible OpenAI → on réutilise OpenAiDriver avec un base_url\n // différent. Clé et modèle surchargeables en admin (Setting groupe 'ai').\n 'deepseek' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'key' => 'deepseek',\n 'api_key' => env('DEEPSEEK_API_KEY'),\n 'base_url' => env('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'),\n 'model' => env('DEEPSEEK_MODEL', 'deepseek-chat'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n // NB : 'driver' désigne une entrée de 'drivers' ci-dessus. null = défaut (anthropic/Sonnet).\n // 'anthropic_haiku' = même fournisseur, modèle économique (Haiku). Choix par tâche\n // pour ne payer Sonnet QUE sur le rédactionnel (synthèses, relecture, Oracle) et\n // basculer les tâches structurées/extraction vers Haiku. Sera surchargeable en admin (Phase 2).\n 'tasks' => [\n // Rédactionnel, qualité importante → Sonnet.\n 'profile_synthesis' => [\n 'driver' => null, // null = default (Sonnet)\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n // Liste structurée de métiers → Haiku suffit. count 30→15 (aligné sur le produit : \"15 idées de métiers\").\n 'job_suggestions' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n // Grimoire — SYNTHÈSE (relecture rédactionnelle) → Sonnet.\n 'global_grimoire' => [\n 'driver' => null, // = défaut (Sonnet)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n // Grimoire — VOIES (liste structurée compacte) → Haiku (3× moins cher).\n 'global_grimoire_voies' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Grimoire — IMPACT IA SUR LE MÉTIER (onglet « Ton métier face à l'IA ») → Haiku.\n // Relecture courte et structurée (Markdown) : analyse de l'exposition du métier\n // du candidat à l'IA. Générée en même temps que les voies (étape 2 du job).\n 'global_grimoire_ia_impact' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Extraction JSON pure → Haiku.\n 'cv_extract' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n // Personnalisation d'email → Haiku.\n 'email_personalization' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n // Oracle conversationnel → Sonnet (qualité), mais on réduit l'historique rejoué\n // (20→10) pour diviser par ~2 les tokens d'entrée sur les conversations longues.\n 'oracle_chat' => [\n 'driver' => null, // = défaut (Sonnet)\n 'history_limit' => 10, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "5859f1c8e06973f9fee9a9b463376bd44be42ab7": {"t": 1785756538.3342283, "n": 4548, "h": "06bbcc6730464090", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n\n // Même fournisseur (Anthropic), modèle ÉCONOMIQUE (Haiku ≈ 3× moins cher que\n // Sonnet : 1$/5$ vs 3$/15$ par M tokens). Utilisé pour les tâches structurées\n // ou peu rédactionnelles (extraction CV, emails, suggestions, voies du Grimoire).\n 'anthropic_haiku' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_HAIKU_MODEL', 'claude-haiku-4-5-20251001'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n\n // DeepSeek : API compatible OpenAI → on réutilise OpenAiDriver avec un base_url\n // différent. Clé et modèle surchargeables en admin (Setting groupe 'ai').\n 'deepseek' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'key' => 'deepseek',\n 'api_key' => env('DEEPSEEK_API_KEY'),\n 'base_url' => env('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'),\n 'model' => env('DEEPSEEK_MODEL', 'deepseek-chat'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n // NB : 'driver' désigne une entrée de 'drivers' ci-dessus. null = défaut (anthropic/Sonnet).\n // 'anthropic_haiku' = même fournisseur, modèle économique (Haiku). Choix par tâche\n // pour ne payer Sonnet QUE sur le rédactionnel (synthèses, relecture, Oracle) et\n // basculer les tâches structurées/extraction vers Haiku. Sera surchargeable en admin (Phase 2).\n 'tasks' => [\n // Rédactionnel, qualité importante → Sonnet.\n 'profile_synthesis' => [\n 'driver' => null, // null = default (Sonnet)\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n // Liste structurée de métiers → Haiku suffit. count 30→15 (aligné sur le produit : \"15 idées de métiers\").\n 'job_suggestions' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n // Grimoire — SYNTHÈSE (relecture rédactionnelle) → Sonnet.\n 'global_grimoire' => [\n 'driver' => null, // = défaut (Sonnet)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n // Grimoire — VOIES (liste structurée compacte) → Haiku (3× moins cher).\n 'global_grimoire_voies' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Extraction JSON pure → Haiku.\n 'cv_extract' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n // Personnalisation d'email → Haiku.\n 'email_personalization' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n // Oracle conversationnel → Sonnet (qualité), mais on réduit l'historique rejoué\n // (20→10) pour diviser par ~2 les tokens d'entrée sur les conversations longues.\n 'oracle_chat' => [\n 'driver' => null, // = défaut (Sonnet)\n 'history_limit' => 10, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "dc52cd794fa22e3828af2d2504e47e537198ffdc": {"t": 1785756539.6419246, "n": 3966, "h": "89ec372df2ba787d", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n\n // Même fournisseur (Anthropic), modèle ÉCONOMIQUE (Haiku ≈ 3× moins cher que\n // Sonnet : 1$/5$ vs 3$/15$ par M tokens). Utilisé pour les tâches structurées\n // ou peu rédactionnelles (extraction CV, emails, suggestions, voies du Grimoire).\n 'anthropic_haiku' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_HAIKU_MODEL', 'claude-haiku-4-5-20251001'),\n 'temperature' => 0.6,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n // NB : 'driver' désigne une entrée de 'drivers' ci-dessus. null = défaut (anthropic/Sonnet).\n // 'anthropic_haiku' = même fournisseur, modèle économique (Haiku). Choix par tâche\n // pour ne payer Sonnet QUE sur le rédactionnel (synthèses, relecture, Oracle) et\n // basculer les tâches structurées/extraction vers Haiku. Sera surchargeable en admin (Phase 2).\n 'tasks' => [\n // Rédactionnel, qualité importante → Sonnet.\n 'profile_synthesis' => [\n 'driver' => null, // null = default (Sonnet)\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n // Liste structurée de métiers → Haiku suffit. count 30→15 (aligné sur le produit : \"15 idées de métiers\").\n 'job_suggestions' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n // Grimoire — SYNTHÈSE (relecture rédactionnelle) → Sonnet.\n 'global_grimoire' => [\n 'driver' => null, // = défaut (Sonnet)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n // Grimoire — VOIES (liste structurée compacte) → Haiku (3× moins cher).\n 'global_grimoire_voies' => [\n 'driver' => 'anthropic_haiku',\n ],\n // Extraction JSON pure → Haiku.\n 'cv_extract' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n // Personnalisation d'email → Haiku.\n 'email_personalization' => [\n 'driver' => 'anthropic_haiku',\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n // Oracle conversationnel → Sonnet (qualité), mais on réduit l'historique rejoué\n // (20→10) pour diviser par ~2 les tokens d'entrée sur les conversations longues.\n 'oracle_chat' => [\n 'driver' => null, // = défaut (Sonnet)\n 'history_limit' => 10, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "9e42c65a5365dfff279cfa5fd811bb776f8780b6": {"t": 1785756540.9905372, "n": 2294, "h": "80be92ec0c7e148e", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 30,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.1', // 1.1 = pistes compactes + génération progressive\n 'count' => 30, // nombre de \"Voies Possibles\" par défaut (max 50). Format compact => 30 tient en <30s.\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n 'oracle_chat' => [\n 'driver' => null, // = défaut (anthropic)\n 'history_limit' => 20, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "7b7dd845f8459a16d1bc9db153ceb3cbc5e10e52": {"t": 1785756542.2139575, "n": 2237, "h": "5cc84d7e590d7202", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 30,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.0',\n 'count' => 20, // nombre de \"Voies Possibles\" consolidées (max 50) — 20 par défaut pour OVH (<30s)\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n 'oracle_chat' => [\n 'driver' => null, // = défaut (anthropic)\n 'history_limit' => 20, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "aff715b928204594b7b1a454b680401c365c748f": {"t": 1785756545.5191288, "n": 2205, "h": "b98efa24532ba342", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 30,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.0',\n 'count' => 30, // nombre de \"Voies Possibles\" consolidées (max 50)\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n 'oracle_chat' => [\n 'driver' => null, // = défaut (anthropic)\n 'history_limit' => 20, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "78227b25997b7e04e580854d026cdf0fa4574df3": {"t": 1785756547.645182, "n": 2198, "h": "79dd6c56cbd1d7a7", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 100,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.0',\n 'count' => 100, // nombre de \"Voies Possibles\" consolidées\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n 'oracle_chat' => [\n 'driver' => null, // = défaut (anthropic)\n 'history_limit' => 20, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "8c19bec7294ac324ad158610ba6f3d99ba689c0d": {"t": 1785756548.992702, "n": 2196, "h": "ecab69ac198e4f22", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 30,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.0',\n 'count' => 30, // nombre de \"Voies Possibles\" consolidées\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n 'oracle_chat' => [\n 'driver' => null, // = défaut (anthropic)\n 'history_limit' => 20, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "a800c7b2b528507fbb19a9c2aa285f82d8dacbc0": {"t": 1785756550.4213412, "n": 2196, "h": "5f422686b61d5281", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.0',\n 'count' => 15, // nombre de \"Voies Possibles\" consolidées\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n 'oracle_chat' => [\n 'driver' => null, // = défaut (anthropic)\n 'history_limit' => 20, // nombre de messages (user+assistant) rejoués dans le prompt\n ],\n ],\n];\n"}, "0ea79845ad9ae7240a79344489d852934012de96": {"t": 1785756551.5852022, "n": 1990, "h": "1dfe8a7df30aa868", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n 'global_grimoire' => [\n 'driver' => null, // = défaut (anthropic)\n 'prompt_version' => '1.0',\n 'count' => 15, // nombre de \"Voies Possibles\" consolidées\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n ],\n];\n"}, "09ec99eb616ffad0017ef27d7105c3445c56fd92": {"t": 1785756552.8867157, "n": 1760, "h": "8d89302345535ec6", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n ],\n];\n"}, "10e0cf85652ddf131a261bf67637b26a8ac38d67": {"t": 1785756554.2083154, "n": 1759, "h": "344f5a7d1eec365c", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n // 'driver' => null signifie \"utiliser ai.default\"\n // Pour overrider le driver d'une tâche : 'driver' => 'openai'\n 'profile_synthesis' => ['driver' => null],\n 'job_suggestions' => ['driver' => null, 'count' => 15],\n 'cv_extract' => ['driver' => null],\n 'email_personalization' => ['driver' => null],\n ],\n];\n\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"}, "cb436748a971b7560363cfe5f0fb3f641c015481": {"t": 1785756555.3739996, "n": 1760, "h": "8d89302345535ec6", "c": "<?php\n\nreturn [\n 'default' => env('AI_DEFAULT_DRIVER', 'anthropic'),\n\n 'drivers' => [\n 'openai' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OpenAiDriver::class,\n 'api_key' => env('OPENAI_API_KEY'),\n 'model' => env('OPENAI_MODEL', 'gpt-4o-mini'),\n 'temperature' => 0.7,\n 'max_tokens' => 2000,\n ],\n 'anthropic' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\AnthropicDriver::class,\n 'api_key' => env('ANTHROPIC_API_KEY'),\n 'model' => env('ANTHROPIC_MODEL', 'claude-sonnet-4-6'),\n 'temperature' => 0.7,\n 'max_tokens' => 4000,\n ],\n 'mistral' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\MistralDriver::class,\n 'api_key' => env('MISTRAL_API_KEY'),\n 'model' => env('MISTRAL_MODEL', 'mistral-large-latest'),\n ],\n 'ollama' => [\n 'driver' => Praxis\\Core\\AI\\Drivers\\OllamaDriver::class,\n 'base_url' => env('OLLAMA_BASE_URL', 'http://localhost:11434'),\n 'model' => env('OLLAMA_MODEL', 'llama3.1'),\n ],\n ],\n\n 'tasks' => [\n 'profile_synthesis' => [\n 'driver' => null, // null = default\n 'system_prompt_view' => 'ai.prompts.synthesis-system',\n ],\n 'job_suggestions' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.jobs-system',\n 'count' => 15,\n ],\n 'cv_extract' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.cv-extract-system',\n ],\n 'email_personalization' => [\n 'driver' => null,\n 'system_prompt_view' => 'ai.prompts.email-system',\n ],\n ],\n];\n"}}}}