1 line
76 KiB
JSON
1 line
76 KiB
JSON
{"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"}}}} |