2396 lines
131 KiB
PHP
Executable File
2396 lines
131 KiB
PHP
Executable File
<?php
|
||
/**
|
||
* ╔═══════════════════════════════════════════════════════════════════════════╗
|
||
* ║ WEVIA COGNITIVE EXPANSION — 380+ Brain Functions ║
|
||
* ║ Auto-loaded by cognitive-brain.php ║
|
||
* ║ Claude 2 — 2026-03-03 ║
|
||
* ╚═══════════════════════════════════════════════════════════════════════════╝
|
||
*/
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE A: INDUSTRY DETECTORS (25 functions)
|
||
// Detect industry/sector context for domain-specific responses
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectPharmaContext($msg) {
|
||
return (bool)preg_match('/(pharma|médicament|HCP|AMM|clinical trial|molécule|principe actif|posologie|pharmacovigilance|bioéquivalence|CRO|FDA|EMA|ANSM|laboratoire|thérapeutique|oncologie|cardiologie)/i', $msg);
|
||
}
|
||
function detectFinanceContext($msg) {
|
||
return (bool)preg_match('/(finance|bancaire|crédit|investissement|portefeuille|trading|action|obligation|rendement|volatilité|hedge|derivative|OPCVM|PEA|assurance|actuariel|bourse|taux|amortissement)/i', $msg);
|
||
}
|
||
function detectManufacturingContext($msg) {
|
||
return (bool)preg_match('/(manufactur|usine|production|chaîne|supply chain|lean|six sigma|kanban|MES|SCADA|PLC|automate|qualité|ISO 9001|OEE|TRS|maintenance préventive|GMAO|logistique)/i', $msg);
|
||
}
|
||
function detectRetailContext($msg) {
|
||
return (bool)preg_match('/(retail|commerce|magasin|e-commerce|omnicanal|merchandising|PLV|point de vente|stock|inventaire|caisse|POS|CRM|fidélité|panier moyen|taux de conversion|dropshipping)/i', $msg);
|
||
}
|
||
function detectEnergyContext($msg) {
|
||
return (bool)preg_match('/(énergie|renouvelable|solaire|éolien|photovoltaïque|batterie|stockage|smart grid|compteur intelligent|linky|hydrogène|nucléaire|transition énergétique|carbone|GES|empreinte)/i', $msg);
|
||
}
|
||
function detectTelecomContext($msg) {
|
||
return (bool)preg_match('/(télécom|5G|4G|fibre|FTTH|réseau|bande passante|latence|QoS|VoIP|SIP|trunk|roaming|MVNO|opérateur|antenne|fréquence|spectre|IoT)/i', $msg);
|
||
}
|
||
function detectHealthcareContext($msg) {
|
||
return (bool)preg_match('/(santé|hôpital|clinique|médecin|patient|dossier médical|DMP|HL7|FHIR|télémédecine|téléconsultation|imagerie|radiologie|IRM|scanner|urgence|bloc opératoire)/i', $msg);
|
||
}
|
||
function detectEducationContext($msg) {
|
||
return (bool)preg_match('/(éducation|université|école|formation|e-learning|MOOC|LMS|Moodle|pédagogie|cursus|diplôme|certification|campus|étudiant|professeur|programme|syllabus)/i', $msg);
|
||
}
|
||
function detectInsuranceContext($msg) {
|
||
return (bool)preg_match('/(assurance|police|sinistre|prime|franchise|courtier|souscription|indemnisation|actuaire|risque|réassurance|mutuelle|prévoyance|IARD|vie|responsabilité civile)/i', $msg);
|
||
}
|
||
function detectRealEstateContext($msg) {
|
||
return (bool)preg_match('/(immobilier|logement|appartement|maison|loyer|bail|copropriété|promoteur|agent|transaction|hypothèque|SCI|SCPI|rendement locatif|travaux|rénovation)/i', $msg);
|
||
}
|
||
function detectAgricultureContext($msg) {
|
||
return (bool)preg_match('/(agriculture|fermier|récolte|semence|irrigation|pesticide|bio|agroalimentaire|élevage|PAC|coopérative|parcelle|rendement|sol|engrais|OGM|precision farming)/i', $msg);
|
||
}
|
||
function detectTransportContext($msg) {
|
||
return (bool)preg_match('/(transport|logistique|fret|camion|flotte|TMS|WMS|entrepôt|livraison|last mile|maritime|aérien|ferroviaire|intermodal|conteneur|douane|incoterm)/i', $msg);
|
||
}
|
||
function detectMediaContext($msg) {
|
||
return (bool)preg_match('/(média|presse|journalisme|rédaction|contenu|éditorial|SEO|audience|publication|abonné|paywall|streaming|podcast|vidéo|influenceur|communauté|engagement)/i', $msg);
|
||
}
|
||
function detectLegalContext($msg) {
|
||
return (bool)preg_match('/(juridique|avocat|tribunal|contrat|clause|litige|arbitrage|droit|loi|décret|jurisprudence|propriété intellectuelle|brevet|marque|RGPD|conformité légale)/i', $msg);
|
||
}
|
||
function detectHRContext($msg) {
|
||
return (bool)preg_match('/(RH|ressources humaines|recrutement|candidat|CV|entretien|onboarding|paie|bulletin|congé|formation|GPEC|compétence|talent|marque employeur|engagement|turnover)/i', $msg);
|
||
}
|
||
function detectConstructionContext($msg) {
|
||
return (bool)preg_match('/(construction|BTP|chantier|béton|structure|architecte|maître d.œuvre|permis de construire|BIM|maquette|fondation|gros œuvre|second œuvre|réception|DTU)/i', $msg);
|
||
}
|
||
function detectGamingContext($msg) {
|
||
return (bool)preg_match('/(jeu vidéo|gaming|game design|Unity|Unreal|sprite|shader|gameplay|level design|UX game|monetization|F2P|gacha|esport|streamer|twitch)/i', $msg);
|
||
}
|
||
function detectAutomotiveContext($msg) {
|
||
return (bool)preg_match('/(automobile|véhicule|voiture|moteur|électrique|hybride|autonome|ADAS|OBD|diagnostic|concession|flotte|leasing|homologation|crash test|Euro NCAP)/i', $msg);
|
||
}
|
||
function detectAerospaceContext($msg) {
|
||
return (bool)preg_match('/(aéronautique|aviation|satellite|spatial|drone|UAV|turbine|propulsion|aérodynamique|DO-178|AS9100|MRO|avionique|vol|altitude|trajectoire)/i', $msg);
|
||
}
|
||
function detectFoodContext($msg) {
|
||
return (bool)preg_match('/(alimentaire|food|restauration|cuisine|recette|HACCP|traçabilité|DLC|DLUO|emballage|nutrition|ingrédient|additif|label|bio|certification)/i', $msg);
|
||
}
|
||
function detectMoroccanContext($msg) {
|
||
return (bool)preg_match('/(Maroc|marocain|Casablanca|Rabat|CNDP|loi 09-08|ONDA|AMMC|Bank Al-Maghrib|dirham|CNSS|OFPPT|IRCAM|amazigh|darija|médina|souk)/i', $msg);
|
||
}
|
||
function detectAfricanContext($msg) {
|
||
return (bool)preg_match('/(Afrique|africain|Tunisie|Algérie|Sénégal|Côte d.Ivoire|UEMOA|CEMAC|CEDEAO|CFA|Mobile Money|Orange Money|diaspora|francophonie|panafricain)/i', $msg);
|
||
}
|
||
function detectStartupContext($msg) {
|
||
return (bool)preg_match('/(startup|levée de fonds|seed|série A|VC|venture|pitch|MVP|pivot|burn rate|runway|incubateur|accélérateur|scale-up|unicorn|cap table|term sheet)/i', $msg);
|
||
}
|
||
function detectGovContext($msg) {
|
||
return (bool)preg_match('/(gouvernement|ministère|administration|fonction publique|marché public|appel d.offre|collectivité|commune|région|département|préfecture|service public|dématérialisation)/i', $msg);
|
||
}
|
||
function detectNGOContext($msg) {
|
||
return (bool)preg_match('/(ONG|association|humanitaire|développement|bénévolat|don|mécénat|fondation|impact social|ESS|économie sociale|coopérative|mutuelle|micro-crédit)/i', $msg);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE B: TECHNOLOGY DETECTORS (30 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectSAPContext($msg) {
|
||
return (bool)preg_match('/(SAP|S\/4HANA|ABAP|Fiori|BTP|RISE|ECC|BW|HANA|transaction|BAPI|RFC|CDS|RAP|iDoc|PI\/PO|Integration Suite|Activate|Signavio)/i', $msg);
|
||
}
|
||
function detectAWSContext($msg) {
|
||
return (bool)preg_match('/(AWS|Amazon Web|EC2|S3|Lambda|DynamoDB|CloudFormation|IAM|VPC|ECS|EKS|RDS|SQS|SNS|CloudWatch|Terraform.*aws|CDK)/i', $msg);
|
||
}
|
||
function detectAzureContext($msg) {
|
||
return (bool)preg_match('/(Azure|Microsoft Cloud|AKS|Cosmos|Azure Functions|Blob Storage|Active Directory|Entra|DevOps|Azure SQL|App Service|Logic Apps)/i', $msg);
|
||
}
|
||
function detectGCPContext($msg) {
|
||
return (bool)preg_match('/(GCP|Google Cloud|BigQuery|Cloud Run|GKE|Firestore|Cloud Functions|Pub\/Sub|Vertex AI|Cloud Storage|Spanner|Dataflow)/i', $msg);
|
||
}
|
||
function detectKubernetesContext($msg) {
|
||
return (bool)preg_match('/(kubernetes|k8s|kubectl|pod|deployment|service|ingress|helm|istio|namespace|configmap|secret|daemonset|statefulset|operator|CRD)/i', $msg);
|
||
}
|
||
function detectDockerContext($msg) {
|
||
return (bool)preg_match('/(docker|conteneur|container|Dockerfile|docker-compose|image|registry|volume|network|swarm|overlay|buildx|multi-stage)/i', $msg);
|
||
}
|
||
function detectCICDContext($msg) {
|
||
return (bool)preg_match('/(CI\/CD|pipeline|Jenkins|GitLab CI|GitHub Actions|ArgoCD|Tekton|CircleCI|Travis|build|deploy|release|artifact|stage|rollback)/i', $msg);
|
||
}
|
||
function detectMLContext($msg) {
|
||
return (bool)preg_match('/(machine learning|deep learning|neural|réseau de neurones|TensorFlow|PyTorch|scikit|XGBoost|Random Forest|SVM|CNN|RNN|LSTM|transformer|attention|embedding)/i', $msg);
|
||
}
|
||
function detectLLMContext($msg) {
|
||
return (bool)preg_match('/(LLM|GPT|Claude|Mistral|Llama|Gemini|fine-tun|prompt engineering|RAG|retrieval|RLHF|tokenizer|context window|hallucination|grounding|agent)/i', $msg);
|
||
}
|
||
function detectBlockchainContext($msg) {
|
||
return (bool)preg_match('/(blockchain|crypto|smart contract|Solidity|Ethereum|DeFi|NFT|Web3|wallet|consensus|proof of|staking|token|DAO|IPFS|dApp)/i', $msg);
|
||
}
|
||
function detectDatabaseContext($msg) {
|
||
return (bool)preg_match('/(database|base de données|PostgreSQL|MySQL|MongoDB|Redis|Elasticsearch|Cassandra|DynamoDB|index|requête|query|ACID|sharding|réplication|backup|migration)/i', $msg);
|
||
}
|
||
function detectNetworkContext($msg) {
|
||
return (bool)preg_match('/(réseau|network|TCP|UDP|HTTP|DNS|DHCP|VLAN|firewall|routeur|switch|load balancer|proxy|reverse proxy|CDN|VPN|IPsec|BGP|OSPF)/i', $msg);
|
||
}
|
||
function detectSecurityContext($msg) {
|
||
return (bool)preg_match('/(sécurité|security|vulnérabilité|CVE|pentest|WAF|firewall|encryption|chiffrement|TLS|SSL|XSS|CSRF|injection SQL|OWASP|SOC|SIEM|EDR|zero trust)/i', $msg);
|
||
}
|
||
function detectFrontendContext($msg) {
|
||
return (bool)preg_match('/(frontend|React|Vue|Angular|Svelte|Next\.js|Nuxt|CSS|Tailwind|responsive|SPA|PWA|component|hook|state management|Redux|Zustand)/i', $msg);
|
||
}
|
||
function detectBackendContext($msg) {
|
||
return (bool)preg_match('/(backend|API|REST|GraphQL|gRPC|microservice|monolithe|Node\.js|Express|FastAPI|Django|Spring|Laravel|middleware|authentification|JWT|OAuth)/i', $msg);
|
||
}
|
||
function detectMobileContext($msg) {
|
||
return (bool)preg_match('/(mobile|iOS|Android|React Native|Flutter|Swift|Kotlin|Xcode|Android Studio|APK|IPA|push notification|deep link|app store|play store)/i', $msg);
|
||
}
|
||
function detectDevOpsContext($msg) {
|
||
return (bool)preg_match('/(DevOps|SRE|monitoring|Prometheus|Grafana|ELK|observability|alerting|incident|SLA|SLO|SLI|uptime|disponibilité|infrastructure as code|Ansible|Puppet)/i', $msg);
|
||
}
|
||
function detectDataEngContext($msg) {
|
||
return (bool)preg_match('/(data engineering|ETL|ELT|pipeline de données|data lake|data warehouse|Spark|Airflow|dbt|Kafka|streaming|batch|data quality|lineage|catalogue|metadata)/i', $msg);
|
||
}
|
||
function detectTestingContext($msg) {
|
||
return (bool)preg_match('/(test|testing|unitaire|intégration|e2e|Selenium|Cypress|Jest|PHPUnit|pytest|TDD|BDD|couverture|coverage|mock|stub|regression|smoke test|load test)/i', $msg);
|
||
}
|
||
function detectGitContext($msg) {
|
||
return (bool)preg_match('/(git|GitHub|GitLab|Bitbucket|commit|branch|merge|rebase|pull request|cherry-pick|stash|tag|release|fork|clone|conflict|gitflow)/i', $msg);
|
||
}
|
||
function detectLinuxContext($msg) {
|
||
return (bool)preg_match('/(linux|ubuntu|debian|centos|RHEL|systemd|systemctl|apt|yum|dnf|kernel|cron|iptables|ufw|SSH|bash|shell|terminal|filesystem|mount|LVM)/i', $msg);
|
||
}
|
||
function detectNginxContext($msg) {
|
||
return (bool)preg_match('/(nginx|proxy_pass|upstream|server_name|location|ssl_certificate|worker_processes|try_files|fastcgi|gzip|cache|rate_limit|proxy|reverse)/i', $msg);
|
||
}
|
||
function detectEmailTechContext($msg) {
|
||
return (bool)preg_match('/(SMTP|IMAP|POP3|DKIM|SPF|DMARC|MX|rDNS|deliverability|inbox|spam|bounce|complaint|warmup|IP reputation|blacklist|sendgrid|postfix|PowerMTA)/i', $msg);
|
||
}
|
||
function detectPHPContext($msg) {
|
||
return (bool)preg_match('/(PHP|Laravel|Symfony|Composer|Eloquent|Blade|Twig|PDO|PSR|Artisan|migration|middleware|service provider|facade|trait|namespace|autoload)/i', $msg);
|
||
}
|
||
function detectPythonContext($msg) {
|
||
return (bool)preg_match('/(Python|pip|virtualenv|Django|Flask|FastAPI|pandas|numpy|scipy|matplotlib|Jupyter|asyncio|decorator|generator|comprehension|type hint)/i', $msg);
|
||
}
|
||
function detectJavaScriptContext($msg) {
|
||
return (bool)preg_match('/(JavaScript|TypeScript|Node\.js|npm|yarn|ES6|async await|Promise|fetch|Axios|Webpack|Vite|Bun|Deno|callback|closure|prototype)/i', $msg);
|
||
}
|
||
function detectSQLContext($msg) {
|
||
return (bool)preg_match('/(SQL|SELECT|INSERT|UPDATE|DELETE|JOIN|GROUP BY|HAVING|CTE|window function|index|explain|query plan|subquery|view|stored procedure|trigger)/i', $msg);
|
||
}
|
||
function detectAPIDesignContext($msg) {
|
||
return (bool)preg_match('/(API design|REST|OpenAPI|Swagger|endpoint|versioning|pagination|rate limit|authentication|authorization|webhook|HATEOAS|JSON:API|schema)/i', $msg);
|
||
}
|
||
function detectPerformanceContext($msg) {
|
||
return (bool)preg_match('/(performance|optimisation|benchmark|profiling|cache|CDN|lazy load|minification|compression|bottleneck|goulot|latence|throughput|scalabilité|horizontal|vertical)/i', $msg);
|
||
}
|
||
function detectArchitectureContext($msg) {
|
||
return (bool)preg_match('/(architecture|microservice|monolithe|event-driven|CQRS|event sourcing|hexagonal|clean architecture|DDD|domain driven|bounded context|saga|pattern)/i', $msg);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE C: TASK TYPE DETECTORS (35 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectCodeGeneration($msg) {
|
||
return (bool)preg_match('/(écris|génère|crée|fais).*\b(code|script|fonction|classe|API|endpoint|composant|module|programme|algorithme)\b/i', $msg);
|
||
}
|
||
function detectCodeReview($msg) {
|
||
return (bool)preg_match('/(revue|review|vérifie|corrige|améliore|optimise|refactor|debug).*\b(code|script|fonction|programme)\b/i', $msg);
|
||
}
|
||
function detectDiagnostic($msg) {
|
||
return (bool)preg_match('/(diagnostic|diagnos|debug|problème|erreur|bug|ne (fonctionne|marche) (pas|plus)|crash|plantage|lent|timeout|500|404|403|502|503)/i', $msg);
|
||
}
|
||
function detectMigration($msg) {
|
||
return (bool)preg_match('/(migr|upgrade|mise à jour|passage|transition|déplacement|transfert|port|convertir|basculer).*\b(version|serveur|cloud|base|système|plateforme)\b/i', $msg);
|
||
}
|
||
function detectComparison($msg) {
|
||
return (bool)preg_match('/(compare|comparaison|versus|vs\.?|différence|avantage|inconvénient|meilleur|lequel|quel.*choisir|choix entre)/i', $msg);
|
||
}
|
||
function detectEstimation($msg) {
|
||
return (bool)preg_match('/(estime|estimation|combien.*temps|combien.*coût|budget|chiffrage|délai|timeline|planning|roadmap|macro-chiffrage|TCO|ROI)/i', $msg);
|
||
}
|
||
function detectDocumentation($msg) {
|
||
return (bool)preg_match('/(documente|documentation|README|wiki|guide|manuel|tutoriel|howto|procédure|runbook|SOP|playbook|référence)/i', $msg);
|
||
}
|
||
function detectAudit($msg) {
|
||
return (bool)preg_match('/(audit|évalue|évaluation|assessment|état des lieux|cartographie|inventaire|diagnostic|bilan|check|vérification complète)/i', $msg);
|
||
}
|
||
function detectOptimization($msg) {
|
||
return (bool)preg_match('/(optimis|améliore|accélère|réduis|augmente|booste|performance|efficacité|productivité|rapidité|lent|slow)/i', $msg);
|
||
}
|
||
function detectSecurity($msg) {
|
||
return (bool)preg_match('/(sécuris|protège|harden|durcissement|vulnérabilité|faille|patch|CVE|pentest|audit sécurité|WAF|firewall|chiffre|encrypt)/i', $msg);
|
||
}
|
||
function detectReporting($msg) {
|
||
return (bool)preg_match('/(rapport|report|dashboard|tableau de bord|KPI|métriques|statistiques|synthèse|bilan|résumé exécutif|slide|présentation)/i', $msg);
|
||
}
|
||
function detectAutomation($msg) {
|
||
return (bool)preg_match('/(automat|cron|scheduler|batch|script.*récurrent|tâche.*planifiée|workflow|orchestration|pipeline|CI|CD|trigger)/i', $msg);
|
||
}
|
||
function detectDeployment($msg) {
|
||
return (bool)preg_match('/(deploy|déploie|mise en production|release|livraison|rollout|blue.green|canary|rolling update|zero downtime)/i', $msg);
|
||
}
|
||
function detectBackup($msg) {
|
||
return (bool)preg_match('/(backup|sauvegarde|restauration|restore|disaster recovery|PRA|PCA|snapshot|réplication|archivage|rétention)/i', $msg);
|
||
}
|
||
function detectMonitoring($msg) {
|
||
return (bool)preg_match('/(monitor|supervision|surveillance|alerting|health check|uptime|disponibilité|observabilité|log|trace|métrique|Grafana|Prometheus|Nagios)/i', $msg);
|
||
}
|
||
function detectScaling($msg) {
|
||
return (bool)preg_match('/(scal|dimensionnement|montée en charge|capacité|élasticité|auto-scaling|horizontal|vertical|load|charge|pic|trafic)/i', $msg);
|
||
}
|
||
function detectTroubleshooting($msg) {
|
||
return (bool)preg_match('/(troubleshoot|dépannage|résoudre|solution|fix|corriger|contourner|workaround|patch|hotfix|incident|panne)/i', $msg);
|
||
}
|
||
function detectBrainstorming($msg) {
|
||
return (bool)preg_match('/(brainstorm|idée|suggestion|propose|recommand|conseil|avis|opinion|stratégie|approche|piste|réflexion|inspiration)/i', $msg);
|
||
}
|
||
function detectTranslation($msg) {
|
||
return (bool)preg_match('/(tradui|translate|traduction|translation|en anglais|en français|en arabe|en espagnol|version.*anglais)/i', $msg);
|
||
}
|
||
function detectSummary($msg) {
|
||
return (bool)preg_match('/(résume|synthèse|condensé|abrégé|TLDR|en bref|points clés|highlights|overview|vue d.ensemble|récapitulat)/i', $msg);
|
||
}
|
||
function detectExplanation($msg) {
|
||
return (bool)preg_match('/(explique|explain|c.est quoi|qu.est.ce|comment ça marche|définition|vulgarise|simplifie|en termes simples|pour un débutant)/i', $msg);
|
||
}
|
||
function detectList($msg) {
|
||
return (bool)preg_match('/(liste|énumère|cite|quels sont|combien|top \d|classement|ranking|palmarès|inventaire|catalogue)/i', $msg);
|
||
}
|
||
function detectTemplate($msg) {
|
||
return (bool)preg_match('/(template|modèle|gabarit|exemple|squelette|boilerplate|scaffold|starter|blueprint|patron)/i', $msg);
|
||
}
|
||
function detectRefactoring($msg) {
|
||
return (bool)preg_match('/(refactor|restructure|réorganise|découpe|modularise|clean code|dette technique|legacy|spaghetti|simplifi)/i', $msg);
|
||
}
|
||
function detectDataAnalysis($msg) {
|
||
return (bool)preg_match('/(analyse.*données|data analysis|statistique|corrélation|tendance|trend|distribution|outlier|anomalie|clustering|segmentation)/i', $msg);
|
||
}
|
||
function detectVisualization($msg) {
|
||
return (bool)preg_match('/(visualis|graphique|chart|diagramme|schéma|mermaid|flowchart|sequence|Gantt|pie chart|bar chart|heatmap|dashboard)/i', $msg);
|
||
}
|
||
function detectEmail($msg) {
|
||
return (bool)preg_match('/(email|e-mail|mail|courriel|newsletter|campagne|objet|sujet|corps|template|envoi|destinataire|ouverture|clic)/i', $msg);
|
||
}
|
||
function detectPresentation($msg) {
|
||
return (bool)preg_match('/(présentation|slide|diapo|PowerPoint|PPTX|deck|pitch|keynote|conférence|webinaire|démo)/i', $msg);
|
||
}
|
||
function detectNegotiation($msg) {
|
||
return (bool)preg_match('/(négoci|argue|convainc|persuad|proposition commerciale|devis|offre|tarif|remise|discount|deal|contrat)/i', $msg);
|
||
}
|
||
function detectProjectMgmt($msg) {
|
||
return (bool)preg_match('/(projet|planning|Gantt|sprint|backlog|user story|epic|milestone|jalon|livrable|deadline|Agile|Scrum|Kanban|PRINCE2|PMI)/i', $msg);
|
||
}
|
||
function detectCrisis($msg) {
|
||
return (bool)preg_match('/(urgence|urgent|critique|P0|production down|incident majeur|panne|crash|bloqué|bloquant|ASAP|immédiat|SOS)/i', $msg);
|
||
}
|
||
function detectLearning($msg) {
|
||
return (bool)preg_match('/(apprendre|learning|formation|cours|tutoriel|exercice|pratique|maîtriser|progresser|niveau|débutant|intermédiaire|avancé)/i', $msg);
|
||
}
|
||
function detectConfiguring($msg) {
|
||
return (bool)preg_match('/(configur|paramètre|setting|option|variable|environnement|\.env|\.conf|\.yaml|\.toml|\.ini|properties)/i', $msg);
|
||
}
|
||
function detectInstallation($msg) {
|
||
return (bool)preg_match('/(install|installe|setup|mise en place|démarrage|getting started|bootstrapp|init|provision|prérequis)/i', $msg);
|
||
}
|
||
function detectDesignPattern($msg) {
|
||
return (bool)preg_match('/(pattern|design pattern|singleton|factory|observer|strategy|decorator|adapter|facade|proxy|chain of responsibility|command|state|visitor)/i', $msg);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE D: LANGUAGE & STYLE ANALYZERS (25 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectLanguage($msg) {
|
||
if (preg_match('/[\x{0600}-\x{06FF}]/u', $msg)) return preg_match('/(ديال|واش|كيفاش|بزاف|خاص)/u', $msg) ? 'darija' : 'arabic';
|
||
if (preg_match('/^[a-zA-Z\s\d.,!?\'"]+$/', $msg) && preg_match('/\b(the|is|are|was|how|what|why|can|do|please)\b/i', $msg)) return 'english';
|
||
if (preg_match('/[\x{4E00}-\x{9FFF}]/u', $msg)) return 'chinese';
|
||
if (preg_match('/[\x{3040}-\x{30FF}]/u', $msg)) return 'japanese';
|
||
if (preg_match('/[\x{AC00}-\x{D7AF}]/u', $msg)) return 'korean';
|
||
if (preg_match('/\b(ist|und|oder|der|die|das|nicht|werden)\b/i', $msg)) return 'german';
|
||
if (preg_match('/\b(es|está|son|los|las|una|por|para|como|pero)\b/i', $msg)) return 'spanish';
|
||
if (preg_match('/\b(é|são|está|com|para|uma|dos|das|não|mais)\b/i', $msg)) return 'portuguese';
|
||
return 'french';
|
||
}
|
||
function detectFormalityLevel($msg) {
|
||
if (preg_match('/\b(vous|Monsieur|Madame|veuillez|cordialement|respectueusement)\b/i', $msg)) return 'formal';
|
||
if (preg_match('/\b(tu|toi|mec|poto|wesh|haha|lol|mdr|ptdr)\b/i', $msg)) return 'informal';
|
||
return 'neutral';
|
||
}
|
||
function detectUrgency($msg) {
|
||
if (preg_match('/\b(urgent|ASAP|immédiat|maintenant|tout de suite|P0|critique|bloquant|SOS|au secours|help)\b/i', $msg)) return 'high';
|
||
if (preg_match('/\b(dès que possible|rapidement|bientôt|cette semaine|prioritaire)\b/i', $msg)) return 'medium';
|
||
return 'normal';
|
||
}
|
||
function detectSentiment($msg) {
|
||
$pos = preg_match_all('/\b(merci|super|excellent|parfait|génial|bravo|magnifique|top|cool|nickel|impeccable)\b/i', $msg);
|
||
$neg = preg_match_all('/\b(merde|nul|horrible|catastrophe|pire|déçu|frustré|furieux|inacceptable|scandaleux)\b/i', $msg);
|
||
if ($pos > $neg + 1) return 'positive';
|
||
if ($neg > $pos + 1) return 'negative';
|
||
return 'neutral';
|
||
}
|
||
function detectExpertiseLevel($msg) {
|
||
$advanced = preg_match_all('/\b(ABAP|CDS|RFC|BAPI|kubernetes|nginx|systemctl|iptables|CQRS|DDD|saga|rebase|cherry-pick)\b/i', $msg);
|
||
$basic = preg_match_all('/\b(c.est quoi|comment ça marche|pour un débutant|expliqu|simplement|basique)\b/i', $msg);
|
||
if ($advanced >= 2) return 'expert';
|
||
if ($basic >= 1) return 'beginner';
|
||
return 'intermediate';
|
||
}
|
||
function detectResponseLength($msg) {
|
||
if (preg_match('/\b(détaillé|exhaustif|complet|approfondi|en profondeur|long|tout)\b/i', $msg)) return 'long';
|
||
if (preg_match('/\b(court|bref|rapide|résumé|TL;?DR|en une phrase|succinct|concis)\b/i', $msg)) return 'short';
|
||
return 'medium';
|
||
}
|
||
function detectOutputFormat($msg) {
|
||
if (preg_match('/\b(JSON|json)\b/', $msg)) return 'json';
|
||
if (preg_match('/\b(XML|xml)\b/', $msg)) return 'xml';
|
||
if (preg_match('/\b(CSV|csv)\b/', $msg)) return 'csv';
|
||
if (preg_match('/\b(markdown|MD)\b/i', $msg)) return 'markdown';
|
||
if (preg_match('/\b(tableau|table|grille)\b/i', $msg)) return 'table';
|
||
if (preg_match('/\b(YAML|yaml|yml)\b/', $msg)) return 'yaml';
|
||
if (preg_match('/\b(HTML|html)\b/', $msg)) return 'html';
|
||
return 'auto';
|
||
}
|
||
function detectToneRequest($msg) {
|
||
if (preg_match('/\b(professionnel|corporate|formel|business)\b/i', $msg)) return 'professional';
|
||
if (preg_match('/\b(amical|cool|décontracté|fun|sympathique)\b/i', $msg)) return 'friendly';
|
||
if (preg_match('/\b(technique|précis|rigoureux|scientifique)\b/i', $msg)) return 'technical';
|
||
if (preg_match('/\b(pédagogique|didactique|explicatif|vulgarisé)\b/i', $msg)) return 'educational';
|
||
return 'auto';
|
||
}
|
||
function detectQuestionType($msg) {
|
||
if (preg_match('/^(qui|who)\b/i', $msg)) return 'who';
|
||
if (preg_match('/^(quoi|que|what)\b/i', $msg)) return 'what';
|
||
if (preg_match('/^(quand|when)\b/i', $msg)) return 'when';
|
||
if (preg_match('/^(où|where)\b/i', $msg)) return 'where';
|
||
if (preg_match('/^(pourquoi|why)\b/i', $msg)) return 'why';
|
||
if (preg_match('/^(comment|how)\b/i', $msg)) return 'how';
|
||
if (preg_match('/^(combien|how much|how many)\b/i', $msg)) return 'howmuch';
|
||
if (preg_match('/\?$/', trim($msg))) return 'question';
|
||
return 'statement';
|
||
}
|
||
function detectMultiPart($msg) {
|
||
return substr_count($msg, '?') >= 2 || preg_match('/\b(et aussi|également|de plus|en plus|autre chose)\b/i', $msg);
|
||
}
|
||
function countTokensEstimate($text) {
|
||
return (int)(mb_strlen($text) / 3.5);
|
||
}
|
||
function detectSarcasm($msg) {
|
||
return (bool)preg_match('/(bien sûr|évidemment|comme d.habitude|encore une fois|bravo.*\!|génial.*\!|super.*\!)/i', $msg) && preg_match('/(!|\.\.\.)/', $msg);
|
||
}
|
||
function detectGratitude($msg) {
|
||
return (bool)preg_match('/\b(merci|thanks|thank you|thx|شكرا|بارك الله)\b/i', $msg);
|
||
}
|
||
function detectGreeting($msg) {
|
||
return (bool)preg_match('/^(bonjour|bonsoir|salut|hello|hi|hey|coucou|salam|السلام|صباح)[\s!?.,]*$/i', trim($msg));
|
||
}
|
||
function detectFarewell($msg) {
|
||
return (bool)preg_match('/^(au revoir|bye|ciao|à bientôt|bonne journée|bonne soirée|adieu|tchao|à\+|a\+)[\s!?.]*$/i', trim($msg));
|
||
}
|
||
function detectFollowUp($msg) {
|
||
return (bool)preg_match('/\b(suite|continuer|poursuivre|précédent|avant|dernier|rappel|on en était|tout à l.heure|et le reste|la suite)\b/i', $msg);
|
||
}
|
||
function detectClarification($msg) {
|
||
return (bool)preg_match('/\b(je voulais dire|en fait|non je parle de|pas ça|l.autre|celui|précise|clarifie|rectifie)\b/i', $msg);
|
||
}
|
||
function detectFrustration($msg) {
|
||
return (bool)preg_match('/\b(ça (marche|fonctionne) (pas|toujours pas)|encore|toujours le même|j.en ai marre|agaçant|frustrant|n.importe quoi|absurde)\b/i', $msg);
|
||
}
|
||
function detectCompliment($msg) {
|
||
return (bool)preg_match('/\b(impressionnant|bravo|excellent travail|bien joué|chapeau|magnifique|parfait|génial|incroyable)\b/i', $msg);
|
||
}
|
||
function detectConfusion($msg) {
|
||
return (bool)preg_match('/\b(je comprends pas|confused|perdu|flou|pas clair|embrouillé|je suis paumé|ça veut dire quoi|hein)\b/i', $msg);
|
||
}
|
||
function detectNumberHeavy($msg) {
|
||
return preg_match_all('/\d+/', $msg) >= 3;
|
||
}
|
||
function detectCodeInMessage($msg) {
|
||
return (bool)preg_match('/(```|function\s|class\s|def\s|SELECT\s|import\s|require\s|<\?php|\$\w+\s*=|const\s|let\s|var\s)/i', $msg);
|
||
}
|
||
function detectURLInMessage($msg) {
|
||
return (bool)preg_match('/https?:\/\/[^\s]+/i', $msg);
|
||
}
|
||
function detectFileReference($msg) {
|
||
return (bool)preg_match('/\b[\w\-]+\.(php|py|js|ts|html|css|sh|sql|json|yaml|yml|xml|csv|md|txt|pdf|docx|pptx|xlsx)\b/i', $msg);
|
||
}
|
||
function detectServerReference($msg) {
|
||
return (bool)preg_match('/\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|S46|S88|S89|S151|S157|serveur|server)\b/i', $msg);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE E: PROMPT INJECTION FUNCTIONS (30 functions)
|
||
// Per-domain system prompt snippets
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function injectSAPExpertise() {
|
||
return "\n## SAP EXPERTISE\nUtilise la terminologie SAP exacte. Cite les transactions (SE80, SM37, SU01), tables (BSEG, EKKO, VBAK), BAPIs. Distingue S/4HANA Cloud vs On-Premise. Recommande standard avant custom. Mentionne SAP Notes pertinentes.";
|
||
}
|
||
function injectSecurityMindset() {
|
||
return "\n## SECURITY MINDSET\nÉvalue CHAQUE recommandation sous l'angle sécurité. Principe du moindre privilège. Chiffrement au repos et en transit. Logs d'audit. Vérifie les CVEs récentes. Propose hardening.";
|
||
}
|
||
function injectPerformanceFocus() {
|
||
return "\n## PERFORMANCE FOCUS\nChiffre TOUT: avant/après, gain en %, temps de réponse, throughput. Identifie le goulot. Propose mesure avant optimisation. Profile avant de deviner. Cache stratégiquement.";
|
||
}
|
||
function injectComplianceFramework() {
|
||
return "\n## COMPLIANCE FRAMEWORK\nCite articles et sections exacts. Distingue obligations légales vs bonnes pratiques. Évalue risques (amendes, sanctions). Recommande actions concrètes de mise en conformité. Délais.";
|
||
}
|
||
function injectTeachingMode() {
|
||
return "\n## MODE PÉDAGOGIQUE\nAnalogie concrète → Définition précise → Exemple réel → Piège à éviter. Du simple au complexe. Vérifie la compréhension. Utilise des métaphores du quotidien.";
|
||
}
|
||
function injectMathRigor() {
|
||
return "\n## RIGUEUR MATHÉMATIQUE\nÉtape par étape. Montre CHAQUE calcul intermédiaire. Vérifie par méthode alternative. Unités. Arrondis explicites. Formule avant application. Sanity check du résultat.";
|
||
}
|
||
function injectCausalReasoning() {
|
||
return "\n## RAISONNEMENT CAUSAL\nDistingue corrélation de causalité. Structure: Symptôme → Hypothèses (top 3) → Tests → Cause racine → Solution. Cite les biais cognitifs potentiels. Contre-factuel.";
|
||
}
|
||
function injectCreativeMode() {
|
||
return "\n## MODE CRÉATIF\nPense latéralement. 3 approches originales minimum. Cross-pollination entre domaines. Pas de jugement prématuré. Pousse les idées jusqu'au bout. Surprise et inattendu.";
|
||
}
|
||
function injectDiagramExpert() {
|
||
return "\n## DIAGRAM EXPERT\nMermaid syntax précise. Flowchart pour processus, sequenceDiagram pour interactions, classDiagram pour architecture, gantt pour planning. Labels clairs en français. Couleurs si utile.";
|
||
}
|
||
function injectCodeQuality() {
|
||
return "\n## CODE QUALITY\nCode COMPLET et déployable. Gestion d'erreurs exhaustive. Variables parlantes. Logs pour debug. Commentaires français. Pas de TODO. Tests suggérés. Rollback prévu.";
|
||
}
|
||
function injectBashExpert() {
|
||
return "\n## BASH EXPERT\nset -euo pipefail. Variables readonly. Fonctions log/err/ok. Backup avant modification. Vérification post-exécution. Cron-ready. Idempotent si possible.";
|
||
}
|
||
function injectDatabaseExpert() {
|
||
return "\n## DATABASE EXPERT\nEXPLAIN ANALYZE avant optimisation. Index stratégiques. Transactions ACID. Backup avant ALTER. Migration réversible. Connection pooling. Vacuum/Analyze.";
|
||
}
|
||
function injectAPIDesign() {
|
||
return "\n## API DESIGN\nRESTful conventions. Versioning. Pagination cursor-based. Rate limiting. Auth JWT/OAuth. Error codes standardisés. Documentation OpenAPI. Idempotent PUT/DELETE.";
|
||
}
|
||
function injectDevOpsExpert() {
|
||
return "\n## DEVOPS EXPERT\nInfrastructure as Code. Immutable infrastructure. Blue-Green/Canary deployments. Monitoring avant scaling. Alerting actionnable. Runbooks pour chaque incident type.";
|
||
}
|
||
function injectDataScienceMode() {
|
||
return "\n## DATA SCIENCE MODE\nHypothèse → Données → Analyse → Conclusion. Valide statistiquement. Visualise avant de modéliser. Feature engineering avant complexité. Interprétabilité. Biais des données.";
|
||
}
|
||
function injectProjectManager() {
|
||
return "\n## PROJECT MANAGEMENT\nPérimètre → Phases → Dépendances → Timeline → Risques → KPIs. Chaque livrable a un responsable et une date. RACI si multi-équipes. Rétrospective intégrée.";
|
||
}
|
||
function injectUXDesign() {
|
||
return "\n## UX DESIGN\nUser-first. Parcours utilisateur avant wireframe. Accessibilité (WCAG). Mobile-first. Feedback immédiat. Progressive disclosure. Microcopy soigné. A/B testing.";
|
||
}
|
||
function injectCloudArchitect() {
|
||
return "\n## CLOUD ARCHITECT\nWell-Architected Framework. Multi-AZ/région. Cost optimization. Reserved vs On-Demand. Serverless quand possible. VPC design. IAM least privilege. Tags pour cost allocation.";
|
||
}
|
||
function injectEmailMarketing() {
|
||
return "\n## EMAIL MARKETING\nDélivrabilité d'abord. SPF/DKIM/DMARC. Warmup progressif. Segmentation fine. A/B testing sujets. Mobile-friendly. CTA clair. Unsubscribe facile. Compliance RGPD.";
|
||
}
|
||
function injectMoroccanBusiness() {
|
||
return "\n## CONTEXTE MAROC\nLoi 09-08 (protection données). CNDP (régulateur). Code du travail marocain. CNSS/AMO. Normes NM. Investissement (charte CRI). Zones franches (TFZ, Casanearshore). Darija acceptable.";
|
||
}
|
||
function injectStartupAdvisor() {
|
||
return "\n## STARTUP ADVISOR\nLean startup. MVP rapide. Product-market fit avant scale. Métriques: CAC, LTV, MRR, churn. Pitch deck: problème → solution → marché → traction → équipe → ask. Cap table clean.";
|
||
}
|
||
function injectNetworkEngineer() {
|
||
return "\n## NETWORK ENGINEER\nOSI model. Subnetting CIDR. Routing tables. ACLs. VLAN segmentation. QoS. Packet capture (tcpdump/wireshark). MTU. TTL. DNS resolution chain.";
|
||
}
|
||
function injectContainerExpert() {
|
||
return "\n## CONTAINER EXPERT\nMulti-stage builds. .dockerignore. Non-root user. Health checks. Resource limits. Layer caching. Compose pour dev, K8s pour prod. Security scanning. Distroless images.";
|
||
}
|
||
function injectMLEngineer() {
|
||
return "\n## ML ENGINEER\nData pipeline → Feature engineering → Model selection → Training → Evaluation → Deployment → Monitoring. MLOps. Experiment tracking. Model versioning. Drift detection.";
|
||
}
|
||
function injectLLMExpert() {
|
||
return "\n## LLM EXPERT\nPrompt engineering patterns. RAG architecture. Chunking strategies. Embedding models. Vector stores. Fine-tuning vs RAG. Hallucination mitigation. Context window management. Token optimization.";
|
||
}
|
||
function injectFinancialAnalyst() {
|
||
return "\n## FINANCIAL ANALYST\nDCF, comparables, multiples. NPV/IRR/payback. P&L impact. Cash flow projection. Sensitivity analysis. Break-even. ROI with timeframe. Currency considerations.";
|
||
}
|
||
function injectGitExpert() {
|
||
return "\n## GIT EXPERT\nGitflow ou trunk-based. Commits atomiques. Messages conventionnels. Rebase pour historique clean. Cherry-pick pour hotfix. Tags sémantiques. Protected branches. Code review checklist.";
|
||
}
|
||
function injectLinuxAdmin() {
|
||
return "\n## LINUX ADMIN\nsystemd services. journalctl pour logs. ss/netstat pour ports. lsof pour file handles. strace pour debug. crontab -e pour scheduling. logrotate. ulimits. sysctl tuning.";
|
||
}
|
||
function injectPHPExpert() {
|
||
return "\n## PHP EXPERT\nPHP 8.3 features (fibers, enums, readonly, match). PSR-12 coding standard. Composer autoload. PDO prepared statements. Error handling try/catch. OPcache. PHP-FPM tuning.";
|
||
}
|
||
function injectPythonExpert() {
|
||
return "\n## PYTHON EXPERT\nPython 3.12+. Type hints. Virtual environments. List/dict comprehensions. Context managers. Decorators. Async/await. Pathlib. F-strings. Dataclasses. Poetry/UV package management.";
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE F: RESPONSE QUALITY VALIDATORS (25 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function validateCodeCompleteness($response) {
|
||
$issues = [];
|
||
if (preg_match('/```/', $response) && !preg_match('/```\w+/', $response)) $issues[] = "Code block sans langage spécifié";
|
||
if (preg_match('/TODO|FIXME|à adapter|à compléter|\.\.\./', $response)) $issues[] = "Code incomplet (TODO/...)";
|
||
if (preg_match('/\$\w+/', $response) && !preg_match('/try|catch|if.*null|isset|empty/', $response)) $issues[] = "Variables non vérifiées";
|
||
return $issues;
|
||
}
|
||
function validateFactualClaims($response) {
|
||
$flags = [];
|
||
if (preg_match_all('/\b\d{2,}%/', $response, $m)) {
|
||
foreach ($m[0] as $pct) if (intval($pct) > 95) $flags[] = "Pourcentage suspicieusement élevé: $pct";
|
||
}
|
||
return $flags;
|
||
}
|
||
function validateResponseLength($response, $expected) {
|
||
$len = mb_strlen($response);
|
||
if ($expected === 'short' && $len > 500) return "Réponse trop longue pour demande courte";
|
||
if ($expected === 'long' && $len < 200) return "Réponse trop courte pour demande détaillée";
|
||
return null;
|
||
}
|
||
function validateEmojiPresence($response) {
|
||
return preg_match('/[\x{1F300}-\x{1F9FF}\x{2600}-\x{27BF}\x{1F600}-\x{1F64F}]/u', $response) ? true : false;
|
||
}
|
||
function validateNoLeakedInternals($response) {
|
||
$forbidden = ['WEVADS','PowerMTA','pmta','adx_system','adx_clients','sentinel-brain','brain_send_configs','brain_factory'];
|
||
foreach ($forbidden as $w) if (stripos($response, $w) !== false) return "Internal leaked: $w";
|
||
return null;
|
||
}
|
||
function validateSQLSafety($response) {
|
||
if (preg_match('/DROP\s+TABLE|TRUNCATE|DELETE\s+FROM\s+\w+\s*;/i', $response) && !preg_match('/backup|sauvegarde/i', $response))
|
||
return "SQL destructif sans mention de backup";
|
||
return null;
|
||
}
|
||
function validateBashSafety($response) {
|
||
if (preg_match('/rm\s+-rf\s+\/[^o]/i', $response)) return "rm -rf dangereux détecté";
|
||
if (preg_match('/chmod\s+777/i', $response)) return "chmod 777 non sécurisé";
|
||
if (preg_match('/curl.*\|\s*bash/i', $response)) return "Pipe curl to bash dangereux";
|
||
return null;
|
||
}
|
||
function validateMarkdownStructure($response) {
|
||
if (mb_strlen($response) > 1000 && !preg_match('/^#+\s/m', $response) && !preg_match('/\*\*/', $response))
|
||
return "Long response sans structure (pas de headers ni bold)";
|
||
return null;
|
||
}
|
||
function validateFrenchGrammar($response) {
|
||
$issues = [];
|
||
if (preg_match('/\bse (est|sont|a|ont)\b/i', $response)) $issues[] = "Grammaire: se + auxiliaire";
|
||
if (preg_match('/\bsi il\b/i', $response)) $issues[] = "Grammaire: si il → s'il";
|
||
return $issues;
|
||
}
|
||
function validateNoRepetition($response) {
|
||
$sentences = preg_split('/[.!?]+/', $response);
|
||
$seen = [];
|
||
foreach ($sentences as $s) {
|
||
$key = mb_strtolower(trim($s));
|
||
if (mb_strlen($key) > 20 && isset($seen[$key])) return "Phrase répétée: " . mb_substr($s, 0, 50);
|
||
$seen[$key] = true;
|
||
}
|
||
return null;
|
||
}
|
||
function validateActionable($response, $intent) {
|
||
if (in_array($intent, ['technical','operational','code']) && mb_strlen($response) > 200) {
|
||
if (!preg_match('/```|commande|instruction|étape|step/i', $response)) return "Réponse technique sans livrable concret";
|
||
}
|
||
return null;
|
||
}
|
||
function validateSourceCitation($response) {
|
||
if (preg_match('/selon|d.après|étude|recherche|rapport/i', $response) && !preg_match('/\d{4}|source|référence|lien/i', $response))
|
||
return "Citation sans source identifiable";
|
||
return null;
|
||
}
|
||
function validateConsistency($response) {
|
||
if (preg_match('/oui.*non|non.*oui|d.un côté.*de l.autre/i', $response) && !preg_match('/avantage|inconvénient|compare|nuanc/i', $response))
|
||
return "Possible contradiction non structurée";
|
||
return null;
|
||
}
|
||
function validateTechnicalDepth($response, $level) {
|
||
if ($level === 'expert' && preg_match('/simplement|basiquement|en gros/i', $response)) return "Ton trop simple pour audience expert";
|
||
if ($level === 'beginner' && preg_match_all('/\b[A-Z]{3,}\b/', $response) > 5) return "Trop d'acronymes pour débutant";
|
||
return null;
|
||
}
|
||
function scoreResponseQuality($response, $intent, $msg) {
|
||
$score = 10;
|
||
$issues = [];
|
||
$codeIssues = validateCodeCompleteness($response);
|
||
if (!empty($codeIssues)) { $score -= count($codeIssues); $issues = array_merge($issues, $codeIssues); }
|
||
if (!validateEmojiPresence($response) && mb_strlen($response) > 50) { $score -= 0.5; $issues[] = "Pas d'emoji"; }
|
||
$leak = validateNoLeakedInternals($response);
|
||
if ($leak) { $score -= 3; $issues[] = $leak; }
|
||
$sql = validateSQLSafety($response);
|
||
if ($sql) { $score -= 2; $issues[] = $sql; }
|
||
$bash = validateBashSafety($response);
|
||
if ($bash) { $score -= 2; $issues[] = $bash; }
|
||
$rep = validateNoRepetition($response);
|
||
if ($rep) { $score -= 1; $issues[] = $rep; }
|
||
return ['score' => max(0, $score), 'issues' => $issues];
|
||
}
|
||
function validateWEVALIdentity($response) {
|
||
$mentions = preg_match_all('/\bWEVAL\b/i', $response);
|
||
if ($mentions > 5) return "Trop de mentions WEVAL (marketing agressif)";
|
||
return null;
|
||
}
|
||
function validateDateReferences($response) {
|
||
if (preg_match('/\b(202[0-3])\b/', $response)) return "Référence à date potentiellement obsolète";
|
||
return null;
|
||
}
|
||
function validateLinkFormat($response) {
|
||
if (preg_match('/https?:\/\/[^\s)]+/', $response, $m)) {
|
||
if (!preg_match('/\.(com|org|io|dev|fr|ma|net|gov)\b/', $m[0])) return "URL suspecte";
|
||
}
|
||
return null;
|
||
}
|
||
function validateTableFormat($response) {
|
||
if (preg_match('/\|.*\|.*\|/', $response)) {
|
||
$lines = explode("\n", $response);
|
||
$tableLine = false;
|
||
foreach ($lines as $l) {
|
||
if (preg_match('/^\|.*\|$/', trim($l))) $tableLine = true;
|
||
}
|
||
if (!$tableLine) return "Tableau markdown potentiellement malformé";
|
||
}
|
||
return null;
|
||
}
|
||
function validateMermaidSyntax($response) {
|
||
if (preg_match('/```mermaid([\s\S]*?)```/', $response, $m)) {
|
||
$diagram = $m[1];
|
||
if (!preg_match('/^(flowchart|graph|sequenceDiagram|classDiagram|gantt|pie|erDiagram|stateDiagram|journey)/m', $diagram))
|
||
return "Mermaid: type de diagramme manquant";
|
||
if (preg_match('/-->\|[^|]*\|>/', $diagram)) return "Mermaid: syntax invalide -->|text|>";
|
||
}
|
||
return null;
|
||
}
|
||
function validateJSONInResponse($response) {
|
||
if (preg_match('/```json([\s\S]*?)```/', $response, $m)) {
|
||
json_decode(trim($m[1]));
|
||
if (json_last_error() !== JSON_ERROR_NONE) return "JSON invalide dans la réponse";
|
||
}
|
||
return null;
|
||
}
|
||
function countSections($response) {
|
||
return preg_match_all('/^#+\s/m', $response);
|
||
}
|
||
function countCodeBlocks($response) {
|
||
return preg_match_all('/```/', $response) / 2;
|
||
}
|
||
function countWords($text) {
|
||
return str_word_count(strip_tags($text));
|
||
}
|
||
function estimateReadTime($text) {
|
||
return round(countWords($text) / 200, 1);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE G: CONTEXT ENRICHMENT FUNCTIONS (30 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function enrichWithIndustryContext($msg, $sys) {
|
||
$detectors = [
|
||
'detectPharmaContext' => injectSAPExpertise(), // fallback — pharma uses SAP
|
||
'detectFinanceContext' => injectFinancialAnalyst(),
|
||
'detectSAPContext' => injectSAPExpertise(),
|
||
'detectSecurityContext' => injectSecurityMindset(),
|
||
'detectLLMContext' => injectLLMExpert(),
|
||
'detectEmailTechContext' => injectEmailMarketing(),
|
||
'detectMoroccanContext' => injectMoroccanBusiness(),
|
||
];
|
||
$budget = 600;
|
||
foreach ($detectors as $fn => $prompt) {
|
||
if ($budget <= 0) break;
|
||
if (function_exists($fn) && $fn($msg)) {
|
||
$chunk = mb_substr($prompt, 0, $budget);
|
||
$sys .= $chunk;
|
||
$budget -= mb_strlen($chunk);
|
||
}
|
||
}
|
||
return $sys;
|
||
}
|
||
function enrichWithTaskContext($msg, $sys) {
|
||
$tasks = [];
|
||
if (detectCodeGeneration($msg)) $tasks[] = injectCodeQuality();
|
||
if (detectDiagnostic($msg)) $tasks[] = injectCausalReasoning();
|
||
if (detectComparison($msg)) $tasks[] = "\n## COMPARAISON\nUtilise un tableau. Critères objectifs. Score pondéré. Recommandation finale avec justification.";
|
||
if (detectEstimation($msg)) $tasks[] = "\n## ESTIMATION\nFourchette: optimiste/réaliste/pessimiste. Base de calcul explicite. Hypothèses listées. TCO sur 3 ans.";
|
||
if (detectVisualization($msg)) $tasks[] = injectDiagramExpert();
|
||
$budget = 500;
|
||
foreach ($tasks as $t) {
|
||
if ($budget <= 0) break;
|
||
$chunk = mb_substr($t, 0, $budget);
|
||
$sys .= $chunk;
|
||
$budget -= mb_strlen($chunk);
|
||
}
|
||
return $sys;
|
||
}
|
||
function enrichWithStyleContext($msg, $sys) {
|
||
$formality = detectFormalityLevel($msg);
|
||
$expertise = detectExpertiseLevel($msg);
|
||
$length = detectResponseLength($msg);
|
||
$tone = detectToneRequest($msg);
|
||
$style = [];
|
||
if ($formality === 'formal') $style[] = "Ton formel, vouvoiement.";
|
||
if ($formality === 'informal') $style[] = "Ton décontracté, tutoiement OK.";
|
||
if ($expertise === 'beginner') $style[] = "Explique simplement, évite jargon.";
|
||
if ($expertise === 'expert') $style[] = "Niveau expert, jargon OK, pas de simplification.";
|
||
if ($length === 'short') $style[] = "Réponse COURTE et directe.";
|
||
if ($length === 'long') $style[] = "Réponse DÉTAILLÉE et exhaustive.";
|
||
if ($tone === 'educational') $style[] = "Pédagogique avec exemples concrets.";
|
||
if (!empty($style)) $sys .= "\n## STYLE: " . implode(" ", $style);
|
||
return $sys;
|
||
}
|
||
function extractKeyEntities($msg) {
|
||
$entities = [];
|
||
if (preg_match_all('/\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/', $msg, $m)) $entities['proper_nouns'] = array_unique($m[0]);
|
||
if (preg_match_all('/\b\d+[KMG€$%]?\b/', $msg, $m)) $entities['numbers'] = $m[0];
|
||
if (preg_match_all('/\b[\w]+\.(?:php|py|js|sh|sql|html|css|json|yaml)\b/i', $msg, $m)) $entities['files'] = $m[0];
|
||
if (preg_match_all('/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/', $msg, $m)) $entities['ips'] = $m[0];
|
||
return $entities;
|
||
}
|
||
function buildContextSummary($history, $limit = 3) {
|
||
if (empty($history)) return "";
|
||
$recent = array_slice($history, -$limit * 2);
|
||
$topics = [];
|
||
foreach ($recent as $h) {
|
||
$content = $h['content'] ?? '';
|
||
if (mb_strlen($content) > 20) {
|
||
$topics[] = mb_substr($content, 0, 60);
|
||
}
|
||
}
|
||
return empty($topics) ? "" : "\n## CONTEXTE RÉCENT\n" . implode(" → ", $topics);
|
||
}
|
||
function detectConversationPhase($history) {
|
||
$count = count($history);
|
||
if ($count <= 2) return 'opening';
|
||
if ($count <= 6) return 'exploration';
|
||
if ($count <= 12) return 'deep_dive';
|
||
return 'extended';
|
||
}
|
||
function shouldAskClarification($msg, $intent) {
|
||
if (mb_strlen(trim($msg)) < 15 && $intent !== 'greeting') return true;
|
||
if (detectMultiPart($msg) && mb_strlen($msg) > 200) return true;
|
||
return false;
|
||
}
|
||
function prioritizeKBSections($msg) {
|
||
$sections = [];
|
||
if (detectSAPContext($msg)) $sections[] = 'sap';
|
||
if (detectSecurityContext($msg)) $sections[] = 'security';
|
||
if (detectLLMContext($msg)) $sections[] = 'ai';
|
||
if (detectEmailTechContext($msg)) $sections[] = 'email';
|
||
if (detectMoroccanContext($msg)) $sections[] = 'morocco';
|
||
return $sections;
|
||
}
|
||
function buildPersonalizedGreeting($history) {
|
||
$count = count($history);
|
||
if ($count === 0) return "Bonjour ! 👋 Je suis WEVIA, l'assistant IA de WEVAL Consulting.";
|
||
if ($count < 5) return "Re-bonjour ! 😊 On continue ?";
|
||
return "Content de vous revoir ! 🚀 Où en étions-nous ?";
|
||
}
|
||
function getResponseTemplate($taskType) {
|
||
$templates = [
|
||
'diagnostic' => "🔍 **DIAGNOSTIC**\n\n**Symptôme:** [observation]\n**Hypothèses:**\n1. \n2. \n3. \n\n**Tests:**\n```bash\n```\n\n**Solution:**\n",
|
||
'comparison' => "📊 **COMPARAISON**\n\n| Critère | Option A | Option B | Gagnant |\n|---|---|---|---|\n| | | | |\n\n**Recommandation:**\n",
|
||
'estimation' => "📋 **ESTIMATION**\n\n| Scénario | Durée | Coût | Risque |\n|---|---|---|---|\n| Optimiste | | | |\n| Réaliste | | | |\n| Pessimiste | | | |\n",
|
||
'audit' => "🛡️ **AUDIT**\n\n**Périmètre:** \n**Méthodologie:** \n**Constats:**\n1. \n2. \n**Recommandations:**\n",
|
||
];
|
||
return $templates[$taskType] ?? "";
|
||
}
|
||
function calculateComplexity($msg) {
|
||
$score = 0;
|
||
$score += mb_strlen($msg) > 100 ? 1 : 0;
|
||
$score += mb_strlen($msg) > 300 ? 1 : 0;
|
||
$score += substr_count($msg, '?') > 1 ? 1 : 0;
|
||
$score += preg_match_all('/\b(et|aussi|également|plus|en outre)\b/i', $msg) > 0 ? 1 : 0;
|
||
$score += detectCodeInMessage($msg) ? 1 : 0;
|
||
$score += detectNumberHeavy($msg) ? 1 : 0;
|
||
if ($score <= 1) return 'simple';
|
||
if ($score <= 3) return 'moderate';
|
||
return 'complex';
|
||
}
|
||
function selectOptimalModel($complexity, $intent) {
|
||
if ($complexity === 'simple') return ['cerebras', 'groq'];
|
||
if ($intent === 'code') return ['cerebras', 'sambanova'];
|
||
if ($complexity === 'complex') return ['sambanova', 'mistral'];
|
||
return ['groq', 'cerebras'];
|
||
}
|
||
function buildDebugPrompt($error) {
|
||
return "\n## DEBUG CONTEXT\nErreur rencontrée: {$error}\nAnalyse systématiquement: 1) Message exact 2) Stack trace 3) Contexte (OS, version, config) 4) Reproduction steps 5) Dernière modification.";
|
||
}
|
||
function buildMigrationChecklist($source, $target) {
|
||
return "## MIGRATION {$source} → {$target}\n□ Inventaire complet\n□ Analyse compatibilité\n□ Plan de migration\n□ Environnement test\n□ Backup complet\n□ Migration données\n□ Tests fonctionnels\n□ Tests performance\n□ Plan de rollback\n□ Go/No-Go\n□ Migration prod\n□ Validation post-migration";
|
||
}
|
||
function detectContextSwitch($history, $msg) {
|
||
if (count($history) < 2) return false;
|
||
$lastMsg = $history[count($history) - 1]['content'] ?? '';
|
||
$prevTopics = array_filter(explode(' ', mb_strtolower($lastMsg)), fn($w) => mb_strlen($w) > 4);
|
||
$currTopics = array_filter(explode(' ', mb_strtolower($msg)), fn($w) => mb_strlen($w) > 4);
|
||
$overlap = count(array_intersect($prevTopics, $currTopics));
|
||
return $overlap < 2;
|
||
}
|
||
function suggestFollowUp($response, $intent) {
|
||
$suggestions = [];
|
||
if (preg_match('/```/', $response)) $suggestions[] = "Voulez-vous que je teste ce code ?";
|
||
if (preg_match('/alternative|autre option/i', $response)) $suggestions[] = "Souhaitez-vous explorer les alternatives ?";
|
||
if (detectVisualization($response)) $suggestions[] = "Je peux générer un diagramme si vous le souhaitez.";
|
||
return $suggestions;
|
||
}
|
||
function compressSystemPrompt($sys, $maxChars = 7500) {
|
||
if (mb_strlen($sys) <= $maxChars) return $sys;
|
||
$head = mb_substr($sys, 0, (int)($maxChars * 0.55));
|
||
$tail = mb_substr($sys, -(int)($maxChars * 0.40));
|
||
return $head . "\n\n[...condensed...]\n\n" . $tail;
|
||
}
|
||
function mergeContexts($kbContext, $webContext, $memoryContext, $budget = 4000) {
|
||
$merged = "";
|
||
$parts = [['📚 KB', $kbContext], ['🌐 Web', $webContext], ['🧠 Mémoire', $memoryContext]];
|
||
$perPart = (int)($budget / 3);
|
||
foreach ($parts as [$label, $content]) {
|
||
if (!empty($content)) {
|
||
$merged .= "\n### {$label}\n" . mb_substr($content, 0, $perPart);
|
||
}
|
||
}
|
||
return $merged;
|
||
}
|
||
function buildFallbackResponse($intent, $lang = 'fr') {
|
||
$fallbacks = [
|
||
'fr' => [
|
||
'greeting' => "Bonjour ! 👋 Comment puis-je vous aider ?",
|
||
'technical' => "⚡ Je peux vous aider sur ce point technique. Pourriez-vous me donner plus de détails ?",
|
||
'general' => "💡 Bonne question ! Laissez-moi vous répondre...",
|
||
],
|
||
'en' => [
|
||
'greeting' => "Hello! 👋 How can I help you?",
|
||
'technical' => "⚡ I can help with that. Could you provide more details?",
|
||
'general' => "💡 Great question! Let me help you with that...",
|
||
],
|
||
];
|
||
return $fallbacks[$lang][$intent] ?? $fallbacks['fr']['general'];
|
||
}
|
||
function extractActionItems($response) {
|
||
$actions = [];
|
||
if (preg_match_all('/(?:^|\n)\s*(?:□|☐|TODO|Action|→)\s*(.+)/mi', $response, $m)) {
|
||
$actions = $m[1];
|
||
}
|
||
if (preg_match_all('/```(?:bash|sh)([\s\S]*?)```/', $response, $m)) {
|
||
foreach ($m[1] as $cmd) $actions[] = "Exécuter: " . mb_substr(trim($cmd), 0, 80);
|
||
}
|
||
return $actions;
|
||
}
|
||
function generateSessionSummary($history) {
|
||
$topics = [];
|
||
$codeBlocks = 0;
|
||
foreach ($history as $h) {
|
||
$c = $h['content'] ?? '';
|
||
if ($h['role'] === 'user') $topics[] = mb_substr($c, 0, 50);
|
||
$codeBlocks += preg_match_all('/```/', $c);
|
||
}
|
||
return [
|
||
'messages' => count($history),
|
||
'topics' => array_slice($topics, 0, 5),
|
||
'code_blocks' => (int)($codeBlocks / 2),
|
||
];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE H: REASONING PATTERNS (25 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function applySOCRATICMethod($msg) {
|
||
return "\n## MÉTHODE SOCRATIQUE\n1. Clarifier: Que signifie exactement la question ?\n2. Hypothèses: Quels présupposés sont cachés ?\n3. Preuves: Quelles données supportent chaque option ?\n4. Perspectives: Vu d'un autre angle, que dirait-on ?\n5. Conséquences: Si on suit cette logique, où mène-t-elle ?\n6. Méta: Pourquoi cette question est-elle importante ?";
|
||
}
|
||
function applySWOTFramework($topic) {
|
||
return "## ANALYSE SWOT — {$topic}\n| | Positif | Négatif |\n|---|---|---|\n| **Interne** | Forces | Faiblesses |\n| **Externe** | Opportunités | Menaces |";
|
||
}
|
||
function applyFiveWhys($symptom) {
|
||
return "## 5 POURQUOI — {$symptom}\n1. Pourquoi ? → \n2. Pourquoi ? → \n3. Pourquoi ? → \n4. Pourquoi ? → \n5. Pourquoi ? → CAUSE RACINE";
|
||
}
|
||
function applyIshikawa($problem) {
|
||
return "## ISHIKAWA — {$problem}\nMéthode (processus) → \nMatériel (outils) → \nMain-d'œuvre (compétences) → \nMatière (données/input) → \nMilieu (environnement) → \nMesure (métriques) →";
|
||
}
|
||
function applyPESTEL($context) {
|
||
return "## PESTEL\n**P**olitique: \n**É**conomique: \n**S**ocial: \n**T**echnologique: \n**E**nvironnemental: \n**L**égal: ";
|
||
}
|
||
function applyMoSCoW($features) {
|
||
return "## MoSCoW\n**Must have:** \n**Should have:** \n**Could have:** \n**Won't have:** ";
|
||
}
|
||
function applyRACI($tasks) {
|
||
return "## RACI\n| Tâche | Responsable | Approbateur | Consulté | Informé |\n|---|---|---|---|---|";
|
||
}
|
||
function applyDecisionMatrix($options) {
|
||
return "## MATRICE DE DÉCISION\n| Critère | Poids | " . implode(" | ", $options) . " |\n|---|---|" . str_repeat("---|", count($options));
|
||
}
|
||
function applyRiskMatrix() {
|
||
return "## MATRICE DES RISQUES\n| | Impact faible | Impact moyen | Impact fort |\n|---|---|---|---|\n| **Proba forte** | Moyen | Élevé | Critique |\n| **Proba moyenne** | Faible | Moyen | Élevé |\n| **Proba faible** | Négligeable | Faible | Moyen |";
|
||
}
|
||
function applyFirstPrinciples($topic) {
|
||
return "## FIRST PRINCIPLES — {$topic}\n1. Déconstruire: Quels sont les éléments fondamentaux ?\n2. Axiomes: Que sait-on avec certitude ?\n3. Reconstruire: À partir des axiomes, que peut-on déduire ?\n4. Valider: Le résultat est-il cohérent avec la réalité ?";
|
||
}
|
||
function applyInversion($goal) {
|
||
return "## INVERSION — Au lieu de '{$goal}'\nQ: Comment ÉCHOUER à coup sûr ?\n1. \n2. \n3. \nMAINTENANT: Éviter systématiquement ces anti-patterns.";
|
||
}
|
||
function applySecondOrderThinking($action) {
|
||
return "## EFFETS DE SECOND ORDRE\nAction: {$action}\n→ Effet immédiat (1er ordre): \n→ Conséquence (2e ordre): \n→ Effet systémique (3e ordre): ";
|
||
}
|
||
function applyEisenhowerMatrix() {
|
||
return "## MATRICE D'EISENHOWER\n| | Urgent | Non urgent |\n|---|---|---|\n| **Important** | FAIRE maintenant | PLANIFIER |\n| **Non important** | DÉLÉGUER | ÉLIMINER |";
|
||
}
|
||
function applyJobsToBeDone($context) {
|
||
return "## JOBS TO BE DONE\nQuand [situation], je veux [motivation], pour que [résultat attendu].\nContraintes: \nMétriques de succès: ";
|
||
}
|
||
function applyOKR($objective) {
|
||
return "## OKR — {$objective}\n**Objectif:** {$objective}\n**Résultat clé 1:** (mesurable)\n**Résultat clé 2:** (mesurable)\n**Résultat clé 3:** (mesurable)";
|
||
}
|
||
function applyCostBenefitAnalysis() {
|
||
return "## ANALYSE COÛT-BÉNÉFICE\n| Élément | Coûts | Bénéfices |\n|---|---|---|\n| Immédiat | | |\n| 6 mois | | |\n| 1 an | | |\n| 3 ans | | |\n**NPV:** \n**Payback:** ";
|
||
}
|
||
function applyPreMortem($project) {
|
||
return "## PRE-MORTEM — {$project}\nImaginez: le projet a ÉCHOUÉ. Pourquoi ?\n1. [risque technique]\n2. [risque humain]\n3. [risque business]\n4. [risque externe]\nPour CHAQUE: probabilité, impact, mitigation.";
|
||
}
|
||
function applyPareto($domain) {
|
||
return "## PARETO 80/20 — {$domain}\nQuels 20% d'actions produisent 80% des résultats ?\nPriorité absolue: \nSecondaire: \nÉliminable: ";
|
||
}
|
||
function applyDesignThinking() {
|
||
return "## DESIGN THINKING\n1. **Empathie:** Comprendre l'utilisateur\n2. **Définir:** Reformuler le problème\n3. **Idéation:** 3+ solutions créatives\n4. **Prototyper:** MVP rapide\n5. **Tester:** Feedback utilisateur";
|
||
}
|
||
function applyAgilePlanning() {
|
||
return "## PLANNING AGILE\n**Sprint 1 (2 sem):** MVP core\n**Sprint 2:** Itération feedback\n**Sprint 3:** Features secondaires\n**Sprint 4:** Polish + deploy\nRetro après chaque sprint.";
|
||
}
|
||
function applySystemsThinking($system) {
|
||
return "## PENSÉE SYSTÉMIQUE — {$system}\nComposants: \nInteractions: \nBoucles de feedback: \nPoints de levier: \nComportement émergent: ";
|
||
}
|
||
function applyConstraintTheory($process) {
|
||
return "## THÉORIE DES CONTRAINTES — {$process}\n1. IDENTIFIER le goulot\n2. EXPLOITER le goulot (100% utilisation)\n3. SUBORDONNER tout au goulot\n4. ÉLEVER la capacité du goulot\n5. RECOMMENCER (nouveau goulot)";
|
||
}
|
||
function applyKaizen($area) {
|
||
return "## KAIZEN — Amélioration continue\nDomaine: {$area}\n**PDCA:** Plan → Do → Check → Act\nP: Que changer ?\nD: Petit changement test\nC: Mesure résultat\nA: Standardiser si ok, ajuster sinon";
|
||
}
|
||
function applySixSigma() {
|
||
return "## SIX SIGMA — DMAIC\n**D**éfinir: Problème + périmètre + objectif\n**M**esurer: Baseline + métriques actuelles\n**A**nalyser: Causes racines (Ishikawa/5 Pourquoi)\n**I**mprover: Solution + pilote\n**C**ontrôler: SPC + pérennisation";
|
||
}
|
||
function applyMCDA($options, $criteria) {
|
||
return "## ANALYSE MULTICRITÈRE\nOptions: " . implode(", ", $options) . "\nCritères: " . implode(", ", $criteria) . "\nMéthode: Pondération + scoring 1-5\nRésultat: Classement objectif";
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE I: METRIC & CALCULATION FUNCTIONS (20 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function calculateROI($investment, $annualGain, $years = 3) {
|
||
$totalGain = $annualGain * $years;
|
||
$roi = (($totalGain - $investment) / $investment) * 100;
|
||
return ['roi_pct' => round($roi, 1), 'payback_years' => round($investment / $annualGain, 1), 'total_gain' => $totalGain, 'net_gain' => $totalGain - $investment];
|
||
}
|
||
function calculateTCO($capex, $opexAnnual, $years = 3) {
|
||
return $capex + ($opexAnnual * $years);
|
||
}
|
||
function calculateNPV($cashflows, $rate = 0.08) {
|
||
$npv = 0;
|
||
foreach ($cashflows as $i => $cf) { $npv += $cf / pow(1 + $rate, $i + 1); }
|
||
return round($npv, 2);
|
||
}
|
||
function calculateIRR($cashflows, $guess = 0.1) {
|
||
for ($i = 0; $i < 100; $i++) {
|
||
$npv = 0; $dnpv = 0;
|
||
foreach ($cashflows as $j => $cf) {
|
||
$npv += $cf / pow(1 + $guess, $j);
|
||
$dnpv -= $j * $cf / pow(1 + $guess, $j + 1);
|
||
}
|
||
if (abs($dnpv) < 0.001) break;
|
||
$guess -= $npv / $dnpv;
|
||
}
|
||
return round($guess * 100, 2);
|
||
}
|
||
function calculateCAGR($initial, $final, $years) {
|
||
if ($initial <= 0 || $years <= 0) return 0;
|
||
return round((pow($final / $initial, 1 / $years) - 1) * 100, 2);
|
||
}
|
||
function calculatePayback($investment, $annualCashflow) {
|
||
return $annualCashflow > 0 ? round($investment / $annualCashflow, 1) : INF;
|
||
}
|
||
function calculateEmailMetrics($sent, $delivered, $opened, $clicked, $bounced, $complaints) {
|
||
return [
|
||
'delivery_rate' => round(($delivered / max($sent, 1)) * 100, 2),
|
||
'open_rate' => round(($opened / max($delivered, 1)) * 100, 2),
|
||
'click_rate' => round(($clicked / max($delivered, 1)) * 100, 2),
|
||
'ctr' => round(($clicked / max($opened, 1)) * 100, 2),
|
||
'bounce_rate' => round(($bounced / max($sent, 1)) * 100, 2),
|
||
'complaint_rate' => round(($complaints / max($delivered, 1)) * 100, 4),
|
||
];
|
||
}
|
||
function calculateServerMetrics($cpuUsage, $memUsage, $diskUsage, $loadAvg) {
|
||
$health = 'green';
|
||
if ($cpuUsage > 80 || $memUsage > 85 || $diskUsage > 90 || $loadAvg > 4) $health = 'yellow';
|
||
if ($cpuUsage > 95 || $memUsage > 95 || $diskUsage > 95 || $loadAvg > 8) $health = 'red';
|
||
return ['health' => $health, 'cpu' => $cpuUsage, 'mem' => $memUsage, 'disk' => $diskUsage, 'load' => $loadAvg];
|
||
}
|
||
function calculateSLAUptime($downtimeMinutes, $periodDays = 30) {
|
||
$totalMinutes = $periodDays * 24 * 60;
|
||
return round((($totalMinutes - $downtimeMinutes) / $totalMinutes) * 100, 4);
|
||
}
|
||
function estimateTokenCost($tokens, $provider = 'groq') {
|
||
$costs = ['groq' => 0.0, 'cerebras' => 0.0, 'sambanova' => 0.0, 'openai' => 0.015, 'anthropic' => 0.015, 'mistral' => 0.002, 'cohere' => 0.001];
|
||
$rate = $costs[$provider] ?? 0.01;
|
||
return round(($tokens / 1000) * $rate, 4);
|
||
}
|
||
function calculateConversionRate($conversions, $clicks) {
|
||
return $clicks > 0 ? round(($conversions / $clicks) * 100, 2) : 0;
|
||
}
|
||
function calculateLTV($avgRevenue, $retentionRate, $margin = 1.0) {
|
||
return $retentionRate < 1 ? round(($avgRevenue * $margin) / (1 - $retentionRate), 2) : INF;
|
||
}
|
||
function calculateCAC($marketingCost, $newCustomers) {
|
||
return $newCustomers > 0 ? round($marketingCost / $newCustomers, 2) : INF;
|
||
}
|
||
function formatCurrency($amount, $currency = 'EUR') {
|
||
$symbols = ['EUR' => '€', 'USD' => '$', 'MAD' => 'MAD', 'GBP' => '£'];
|
||
$sym = $symbols[$currency] ?? $currency;
|
||
return number_format($amount, 0, ',', ' ') . ' ' . $sym;
|
||
}
|
||
function formatPercentage($value, $decimals = 1) {
|
||
return number_format($value, $decimals, ',', '') . '%';
|
||
}
|
||
function formatDuration($seconds) {
|
||
if ($seconds < 60) return "{$seconds}s";
|
||
if ($seconds < 3600) return round($seconds / 60, 1) . "min";
|
||
if ($seconds < 86400) return round($seconds / 3600, 1) . "h";
|
||
return round($seconds / 86400, 1) . "j";
|
||
}
|
||
function formatBytes($bytes) {
|
||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||
$i = 0;
|
||
while ($bytes >= 1024 && $i < 4) { $bytes /= 1024; $i++; }
|
||
return round($bytes, 1) . ' ' . $units[$i];
|
||
}
|
||
function calculateGrowthRate($current, $previous) {
|
||
return $previous > 0 ? round((($current - $previous) / $previous) * 100, 1) : 0;
|
||
}
|
||
function calculateMedian($values) {
|
||
sort($values);
|
||
$count = count($values);
|
||
if ($count === 0) return 0;
|
||
$mid = (int)($count / 2);
|
||
return ($count % 2 === 0) ? ($values[$mid - 1] + $values[$mid]) / 2 : $values[$mid];
|
||
}
|
||
function calculatePercentile($values, $p) {
|
||
sort($values);
|
||
$index = ($p / 100) * (count($values) - 1);
|
||
$lower = (int)floor($index);
|
||
$upper = (int)ceil($index);
|
||
return $values[$lower] + ($values[$upper] - $values[$lower]) * ($index - $lower);
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE J: WEVAL-SPECIFIC FUNCTIONS (20 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function isWEVALQuery($msg) {
|
||
return (bool)preg_match('/\b(WEVAL|weval-consulting|wevia|wevads|nos services|notre|consulting)\b/i', $msg);
|
||
}
|
||
function getWEVALServices() {
|
||
return ['SAP S/4HANA consulting', 'Digital transformation', 'Cloud migration (Huawei/AWS/Azure)', 'AI & Machine Learning', 'Cybersecurity & Compliance', 'Email marketing automation', 'Custom development', 'Data analytics', 'IT infrastructure', 'Training & Change management'];
|
||
}
|
||
function getWEVALDifferentiators() {
|
||
return ['IA souveraine (GPU local, zéro dépendance cloud)', 'Expertise SAP S/4HANA certifiée', 'Double compétence technique + business', 'Présence Afrique du Nord', 'Innovation continue (LLM fine-tuning, RAG)', 'Sécurité by design (RGPD, ISO 27001)'];
|
||
}
|
||
function buildWEVALPitch($context) {
|
||
return "WEVAL Consulting est un cabinet de conseil en transformation digitale basé à Casablanca. Spécialisé en SAP S/4HANA, IA, cybersécurité et cloud. Notre différence : une IA souveraine (WEVIA) et une expertise terrain Afrique du Nord.";
|
||
}
|
||
function detectWEVADSQuestion($msg) {
|
||
return (bool)preg_match('/\b(warmup|brain.*config|send_config|pmta|bounce|complaint|inbox|deliverability|offre|creative|arsenal)\b/i', $msg);
|
||
}
|
||
function injectWEVALContext($sys) {
|
||
return $sys . "\n## CONTEXTE WEVAL\nWEVAL Consulting — Casablanca, Maroc. Cabinet conseil en transformation digitale. Expertise: SAP, Cloud, IA, Cybersécurité. IA propriétaire: WEVIA.";
|
||
}
|
||
function buildClientResponse($msg, $industry) {
|
||
$prefix = "En tant que consultant WEVAL";
|
||
if ($industry) $prefix .= " spécialisé $industry";
|
||
return $prefix . ", voici mon analyse :\n\n";
|
||
}
|
||
function detectConsultingQuery($msg) {
|
||
return (bool)preg_match('/\b(conseil|recommandation|préconisation|avis d.expert|best practice|retour d.expérience|REX|benchmark|état de l.art)\b/i', $msg);
|
||
}
|
||
function detectQuotationQuery($msg) {
|
||
return (bool)preg_match('/\b(devis|tarif|prix|coût|combien ça coûte|budget|estimation|chiffrage|forfait|TJM|jour-homme)\b/i', $msg);
|
||
}
|
||
function getWEVALStack() {
|
||
return ['Backend' => 'PHP 8.3, Python 3.12, Node.js 20', 'Frontend' => 'React, Tailwind, Vite', 'DB' => 'PostgreSQL 16, Redis', 'AI' => 'Ollama (51 models), DeepSeek-R1, LLama, Whisper', 'Infra' => 'Hetzner dedicated, OVH, Huawei Cloud', 'Tools' => 'Git, Docker, nginx, PMTA'];
|
||
}
|
||
function detectInternalOnly($msg) {
|
||
return (bool)preg_match('/\b(sentinel|arsenal|brain_send|adx_system|pmta|warmup_accounts|seed_accounts)\b/i', $msg);
|
||
}
|
||
function sanitizeForPublic($response) {
|
||
$forbidden = ['WEVADS','PowerMTA','pmta','adx_system','adx_clients','sentinel-brain','brain_factory','brain_send_configs','warmup_accounts','send_configs','seed_accounts','127.0.0.1','88.198.4.195','46.62.220.135','NNuXhFfVHrwW','NKPwP4','admin123'];
|
||
foreach ($forbidden as $w) $response = str_ireplace($w, '[CONFIDENTIEL]', $response);
|
||
return $response;
|
||
}
|
||
function isConfidentialTopic($msg) {
|
||
return (bool)preg_match('/\b(mot de passe|password|credential|clé API|api key|token|secret|ssh|root access)\b/i', $msg);
|
||
}
|
||
function detectROASQuery($msg) {
|
||
return (bool)preg_match('/\b(ROAS|retour.*publicitaire|coût.*acquisition|CPA|CPM|CPC|CTR|taux.*conversion|campaign.*performance)\b/i', $msg);
|
||
}
|
||
function detectInfraQuery($msg) {
|
||
return (bool)preg_match('/\b(serveur|server|CPU|RAM|disque|disk|réseau|network|uptime|charge|load|DNS|certificat|SSL|nginx|apache|systemd)\b/i', $msg);
|
||
}
|
||
function getServerMap() {
|
||
return ['S88' => ['ip' => '88.198.4.195', 'role' => 'AI Primary (GPU)', 'specs' => 'i5-13500, 64GB, RTX 4000 Ada'], 'S89' => ['ip' => '127.0.0.1', 'role' => 'WEVADS Production', 'specs' => 'EPYC, 16GB, PMTA'], 'S151' => ['ip' => '151.80.235.110', 'role' => 'OVH Tracking', 'specs' => 'Tracking only']];
|
||
}
|
||
function detectArchitectureQuery($msg) {
|
||
return (bool)preg_match('/\b(architecture|infra|infrastructure|serveur|déploiement|stack|topologie|réseau|schéma.*serveur)\b/i', $msg);
|
||
}
|
||
function buildInfraOverview() {
|
||
return "## INFRASTRUCTURE WEVAL\nS88 (IA + Site) — GPU RTX 4000 Ada, 51 modèles Ollama\nS89 (WEVADS) — Email marketing, PMTA, Arsenal\nS151 (Tracking) — OVH, tracking affilié";
|
||
}
|
||
function detectKBQueryType($msg) {
|
||
if (preg_match('/\b(ajoute|crée|insert|nouveau|enregistre)\b/i', $msg)) return 'write';
|
||
if (preg_match('/\b(cherche|trouve|recherche|query|search)\b/i', $msg)) return 'read';
|
||
if (preg_match('/\b(modifie|update|met à jour|corrige)\b/i', $msg)) return 'update';
|
||
if (preg_match('/\b(supprime|delete|efface|retire)\b/i', $msg)) return 'delete';
|
||
return 'read';
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE K: MASTER ENRICHMENT PIPELINE
|
||
// Single entry point that orchestrates all modules
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function cognitiveExpansionEnrich($sys, $msg, $intent, $history = []) {
|
||
if (mb_strlen(trim($msg)) < 15 || $intent === 'greeting') return $sys;
|
||
|
||
$budget = 1500; // Max chars to add from expansion
|
||
$additions = [];
|
||
|
||
// Industry enrichment (max 400 chars)
|
||
$industryDetectors = [
|
||
'detectSAPContext' => 'injectSAPExpertise',
|
||
'detectLLMContext' => 'injectLLMExpert',
|
||
'detectSecurityContext' => 'injectSecurityMindset',
|
||
'detectPharmaContext' => 'injectSAPExpertise',
|
||
'detectMoroccanContext' => 'injectMoroccanBusiness',
|
||
'detectEmailTechContext' => 'injectEmailMarketing',
|
||
'detectFinanceContext' => 'injectFinancialAnalyst',
|
||
];
|
||
foreach ($industryDetectors as $detector => $injector) {
|
||
if ($budget <= 0) break;
|
||
if (function_exists($detector) && $detector($msg)) {
|
||
$prompt = $injector();
|
||
$chunk = mb_substr($prompt, 0, min(400, $budget));
|
||
$additions[] = $chunk;
|
||
$budget -= mb_strlen($chunk);
|
||
break; // One industry max
|
||
}
|
||
}
|
||
|
||
// Task-type enrichment (max 300 chars)
|
||
$taskMap = [
|
||
'detectCodeGeneration' => 'injectCodeQuality',
|
||
'detectDiagnostic' => 'injectCausalReasoning',
|
||
'detectVisualization' => 'injectDiagramExpert',
|
||
'detectMigration' => 'injectDevOpsExpert',
|
||
'detectSecurity' => 'injectSecurityMindset',
|
||
];
|
||
foreach ($taskMap as $detector => $injector) {
|
||
if ($budget <= 0) break;
|
||
if (function_exists($detector) && $detector($msg)) {
|
||
$prompt = $injector();
|
||
$chunk = mb_substr($prompt, 0, min(300, $budget));
|
||
$additions[] = $chunk;
|
||
$budget -= mb_strlen($chunk);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Style calibration (max 200 chars)
|
||
$styleCtx = '';
|
||
$expertise = detectExpertiseLevel($msg);
|
||
$urgency = detectUrgency($msg);
|
||
if ($expertise === 'beginner') $styleCtx .= " Niveau débutant, explique simplement.";
|
||
if ($expertise === 'expert') $styleCtx .= " Niveau expert, jargon OK.";
|
||
if ($urgency === 'high') $styleCtx .= " URGENT — réponse rapide et actionnable.";
|
||
if ($styleCtx && $budget > 0) {
|
||
$additions[] = "\n## CALIBRAGE:" . $styleCtx;
|
||
$budget -= mb_strlen($styleCtx) + 15;
|
||
}
|
||
|
||
// Complexity routing hint
|
||
$complexity = calculateComplexity($msg);
|
||
if ($complexity === 'complex' && $budget > 50) {
|
||
$additions[] = "\n[COMPLEXITÉ ÉLEVÉE: Structure ta réponse, utilise des sections.]";
|
||
}
|
||
|
||
if (!empty($additions)) {
|
||
$sys .= implode("", $additions);
|
||
}
|
||
|
||
return $sys;
|
||
}
|
||
|
||
function cognitiveExpansionPostProcess($response, $msg) {
|
||
// Quality scoring (log only)
|
||
$quality = scoreResponseQuality($response, '', $msg);
|
||
if ($quality['score'] < 7) {
|
||
error_log("WEVIA_COG_QUALITY: score=" . $quality['score'] . " issues=" . implode("|", $quality['issues']));
|
||
}
|
||
|
||
// Sanitize internals
|
||
$response = sanitizeForPublic($response);
|
||
|
||
return $response;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE L: GPU MODEL ROTATION & SOVEREIGN ROUTING (25 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function getGPUModelCatalog() {
|
||
return [
|
||
'reasoning' => ['deepseek-r1:32b','deepseek-r1:14b','qwen2.5:32b','phi4:14b'],
|
||
'coding' => ['qwen2.5-coder:14b','deepseek-coder-v2:16b','codellama:34b','starcoder2:15b'],
|
||
'general' => ['llama3.3:70b','wizardlm2:8x22b','mixtral:8x7b','gemma2:27b'],
|
||
'fast' => ['llama3.1:8b','gemma2:9b','phi4:14b','mistral:7b'],
|
||
'vision' => ['moondream:latest','llava:13b','bakllava:latest'],
|
||
'embedding' => ['nomic-embed-text:latest','mxbai-embed-large:latest'],
|
||
'creative' => ['wizardlm2:8x22b','mixtral:8x7b','nous-hermes2:34b'],
|
||
];
|
||
}
|
||
function selectGPUModelV1($intent, $complexity, $msgLen) {
|
||
$catalog = getGPUModelCatalog();
|
||
if ($intent === 'code') return $catalog['coding'][0];
|
||
if ($intent === 'mathematical' || $complexity === 'complex') return $catalog['reasoning'][0];
|
||
if ($intent === 'creative') return $catalog['creative'][0];
|
||
if ($msgLen < 50) return $catalog['fast'][0];
|
||
return $catalog['general'][0];
|
||
}
|
||
function buildGPURotation($intent) {
|
||
$catalog = getGPUModelCatalog();
|
||
$primary = $catalog['reasoning'][0];
|
||
$fallbacks = [];
|
||
switch($intent) {
|
||
case 'code': case 'technical': case 'operational':
|
||
$primary = $catalog['coding'][0]; $fallbacks = [$catalog['reasoning'][0], $catalog['general'][0]]; break;
|
||
case 'analytical': case 'mathematical': case 'causal':
|
||
$primary = $catalog['reasoning'][0]; $fallbacks = [$catalog['general'][0], $catalog['fast'][0]]; break;
|
||
case 'creative': case 'teaching':
|
||
$primary = $catalog['creative'][0]; $fallbacks = [$catalog['general'][0], $catalog['reasoning'][0]]; break;
|
||
default:
|
||
$primary = $catalog['general'][0]; $fallbacks = [$catalog['fast'][0], $catalog['reasoning'][0]];
|
||
}
|
||
return ['primary' => $primary, 'fallbacks' => $fallbacks];
|
||
}
|
||
function getModelVRAM($model) {
|
||
$vram = [
|
||
'deepseek-r1:32b'=>19,'llama3.3:70b'=>18,'wizardlm2:8x22b'=>17,'qwen2.5:32b'=>18,
|
||
'qwen2.5-coder:14b'=>9,'deepseek-r1:14b'=>9,'phi4:14b'=>9,'mixtral:8x7b'=>16,
|
||
'gemma2:27b'=>16,'llama3.1:8b'=>5,'gemma2:9b'=>6,'mistral:7b'=>5,
|
||
'moondream:latest'=>2,'nomic-embed-text:latest'=>1,'codellama:34b'=>19,
|
||
];
|
||
return $vram[$model] ?? 10;
|
||
}
|
||
function canLoadModel($model, $availableVRAM = 20) {
|
||
return getModelVRAM($model) <= $availableVRAM;
|
||
}
|
||
function estimateGPULatency($model, $tokens) {
|
||
$tps = [
|
||
'deepseek-r1:32b'=>12,'llama3.3:70b'=>8,'wizardlm2:8x22b'=>10,'qwen2.5-coder:14b'=>25,
|
||
'deepseek-r1:14b'=>22,'phi4:14b'=>20,'llama3.1:8b'=>45,'gemma2:9b'=>40,
|
||
'mistral:7b'=>50,'mixtral:8x7b'=>15,'gemma2:27b'=>14,
|
||
];
|
||
$speed = $tps[$model] ?? 15;
|
||
return round($tokens / $speed, 1);
|
||
}
|
||
function buildCloudRotation($intent) {
|
||
$rotations = [
|
||
'analytical' => ['sambanova','groq','mistral'],
|
||
'creative' => ['groq','mistral','cerebras'],
|
||
'technical' => ['cerebras','groq','sambanova'],
|
||
'strategic' => ['sambanova','mistral','groq'],
|
||
'operational' => ['groq','cerebras','sambanova'],
|
||
'mathematical'=> ['sambanova','cerebras','groq'],
|
||
'causal' => ['sambanova','groq','mistral'],
|
||
'compliance' => ['sambanova','mistral','groq'],
|
||
'teaching' => ['groq','mistral','cerebras'],
|
||
'code' => ['cerebras','groq','sambanova'],
|
||
'synthesis' => ['groq','cerebras','sambanova'],
|
||
'conversational'=>['groq','cerebras','sambanova'],
|
||
'social_intelligence'=>['groq','sambanova','mistral'],
|
||
'greeting' => ['groq','cerebras'],
|
||
];
|
||
return $rotations[$intent] ?? ['groq','cerebras','sambanova'];
|
||
}
|
||
function buildSovereignRoute($intent, $complexity, $gpuAvailable = true) {
|
||
if ($gpuAvailable && $complexity !== 'simple') {
|
||
$gpu = buildGPURotation($intent);
|
||
$cloud = buildCloudRotation($intent);
|
||
return ['sovereign' => true, 'primary' => 'gpu:' . $gpu['primary'], 'fallback1' => 'gpu:' . ($gpu['fallbacks'][0] ?? ''), 'fallback2' => 'cloud:' . $cloud[0], 'fallback3' => 'cloud:' . $cloud[1]];
|
||
}
|
||
$cloud = buildCloudRotation($intent);
|
||
return ['sovereign' => false, 'primary' => 'cloud:' . $cloud[0], 'fallback1' => 'cloud:' . $cloud[1], 'fallback2' => 'cloud:' . ($cloud[2] ?? 'groq')];
|
||
}
|
||
function buildCrossVerification($response, $intent) {
|
||
return ['verifiers' => ['cerebras','groq'], 'prompt' => "Vérifie cette réponse pour exactitude et complétude. Corrige les erreurs factuelles. Réponse originale:\n" . mb_substr($response, 0, 2000), 'intent' => $intent];
|
||
}
|
||
function buildDeepResponse($msg, $intent) {
|
||
return ['phase1' => ['model' => 'deepseek-r1:32b', 'task' => 'reasoning', 'prompt' => "Raisonne étape par étape sur: $msg"], 'phase2' => ['model' => 'qwen2.5-coder:14b', 'task' => 'code_gen', 'conditional' => $intent === 'code'], 'phase3' => ['providers' => ['cerebras','groq'], 'task' => 'verify', 'prompt' => 'Vérifie et enrichis cette réponse']];
|
||
}
|
||
function getProviderLimits() {
|
||
return [
|
||
'groq' => ['rpm' => 30, 'tpm' => 6000, 'models' => ['llama-3.3-70b-versatile','mixtral-8x7b-32768','gemma2-9b-it']],
|
||
'cerebras' => ['rpm' => 30, 'tpm' => 60000, 'models' => ['llama3.1-70b','llama3.1-8b']],
|
||
'sambanova' => ['rpm' => 10, 'tpm' => 100000, 'models' => ['Meta-Llama-3.3-70B-Instruct','DeepSeek-R1-Distill-Llama-70B']],
|
||
'mistral' => ['rpm' => 10, 'tpm' => 500000, 'models' => ['mistral-large-latest','codestral-latest']],
|
||
'cohere' => ['rpm' => 10, 'tpm' => 10000, 'models' => ['command-r-plus','command-r']],
|
||
];
|
||
}
|
||
function selectFastestProvider($intent) {
|
||
$speed = ['cerebras' => 1, 'groq' => 2, 'sambanova' => 3, 'mistral' => 4, 'cohere' => 5];
|
||
$cloud = buildCloudRotation($intent);
|
||
usort($cloud, fn($a,$b) => ($speed[$a] ?? 9) - ($speed[$b] ?? 9));
|
||
return $cloud[0];
|
||
}
|
||
function selectSmartestProvider($intent) {
|
||
$quality = ['sambanova' => 1, 'mistral' => 2, 'groq' => 3, 'cerebras' => 4, 'cohere' => 5];
|
||
$cloud = buildCloudRotation($intent);
|
||
usort($cloud, fn($a,$b) => ($quality[$a] ?? 9) - ($quality[$b] ?? 9));
|
||
return $cloud[0];
|
||
}
|
||
function buildMultiModelPipelineV1($msg, $intent, $complexity) {
|
||
$pipeline = ['stages' => []];
|
||
if ($complexity === 'complex') {
|
||
$pipeline['stages'][] = ['name'=>'reason','model'=>'deepseek-r1:32b','type'=>'gpu','timeout'=>30];
|
||
$pipeline['stages'][] = ['name'=>'enrich','provider'=>selectSmartestProvider($intent),'type'=>'cloud','timeout'=>15];
|
||
$pipeline['stages'][] = ['name'=>'verify','provider'=>'cerebras','type'=>'cloud','timeout'=>10];
|
||
} else {
|
||
$pipeline['stages'][] = ['name'=>'generate','provider'=>selectFastestProvider($intent),'type'=>'cloud','timeout'=>10];
|
||
}
|
||
return $pipeline;
|
||
}
|
||
function buildVerificationPromptV1($originalResponse, $question) {
|
||
return "Tu es un vérificateur expert. Analyse cette réponse pour:\n1. Exactitude factuelle\n2. Complétude\n3. Cohérence logique\n4. Qualité du code (si présent)\n\nQuestion: " . mb_substr($question, 0, 500) . "\nRéponse à vérifier: " . mb_substr($originalResponse, 0, 3000) . "\n\nRéponds UNIQUEMENT avec les corrections nécessaires. Si tout est correct, dis 'VERIFIED_OK'.";
|
||
}
|
||
function parseVerificationResultV1($result) {
|
||
if (strpos($result, 'VERIFIED_OK') !== false) return ['status' => 'ok', 'corrections' => []];
|
||
$corrections = [];
|
||
if (preg_match_all('/(?:Correction|Erreur|Fix)\s*:?\s*(.+)/i', $result, $m)) $corrections = $m[1];
|
||
return ['status' => 'needs_fix', 'corrections' => $corrections, 'raw' => mb_substr($result, 0, 1000)];
|
||
}
|
||
function applyVerificationFixes($response, $corrections) {
|
||
if (empty($corrections)) return $response;
|
||
$footer = "\n\n---\n⚠️ **Corrections appliquées:**\n";
|
||
foreach ($corrections as $c) $footer .= "- " . trim($c) . "\n";
|
||
return $response . $footer;
|
||
}
|
||
function buildOllamaRequestV1($model, $prompt, $sys = '', $temp = 0.7, $maxTokens = 2000) {
|
||
return ['model' => $model, 'prompt' => $prompt, 'system' => $sys, 'stream' => false, 'options' => ['temperature' => $temp, 'num_predict' => $maxTokens, 'top_p' => 0.9]];
|
||
}
|
||
function buildCloudRequest($provider, $model, $messages, $temp = 0.7, $maxTokens = 2000) {
|
||
return ['provider' => $provider, 'model' => $model, 'messages' => $messages, 'temperature' => $temp, 'max_tokens' => $maxTokens];
|
||
}
|
||
function logModelUsage($model, $provider, $tokens, $latencyMs, $intent) {
|
||
$entry = date('Y-m-d H:i:s') . " | model=$model provider=$provider tokens=$tokens latency={$latencyMs}ms intent=$intent\n";
|
||
@file_put_contents('/opt/wevads/logs/model-usage.log', $entry, FILE_APPEND);
|
||
}
|
||
function getModelRecommendation($intent, $msgLen, $gpuAvailable) {
|
||
$complexity = ($msgLen > 200) ? 'complex' : (($msgLen > 50) ? 'moderate' : 'simple');
|
||
$route = buildSovereignRoute($intent, $complexity, $gpuAvailable);
|
||
$gpu = buildGPURotation($intent);
|
||
$cloud = buildCloudRotation($intent);
|
||
return ['route' => $route, 'gpu_model' => $gpu['primary'], 'cloud_providers' => $cloud, 'estimated_latency' => estimateGPULatency($gpu['primary'], 500)];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE M: ADVANCED COGNITIVE PATTERNS (25 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function detectAmbiguity($msg) {
|
||
$ambiguous = 0;
|
||
if (preg_match('/\b(ça|cela|ce|cette|celui|il|elle|le|la|les|y|en)\b/i', $msg) && mb_strlen($msg) < 40) $ambiguous++;
|
||
if (preg_match('/\b(trucs?|machin|chose|bidule|truc|stuff)\b/i', $msg)) $ambiguous++;
|
||
if (preg_match('/\b(genre|style|type|sorte|espèce)\b/i', $msg) && !preg_match('/\btype\s+(de|d\')/i', $msg)) $ambiguous++;
|
||
return $ambiguous >= 2;
|
||
}
|
||
function detectImplicitExpectation($msg, $history) {
|
||
if (preg_match('/\b(comme (avant|d\'habitude|la dernière fois))\b/i', $msg)) return 'repeat_pattern';
|
||
if (preg_match('/\b(pareil|même chose|idem|rebelote)\b/i', $msg)) return 'repeat_exact';
|
||
if (preg_match('/\b(mieux|améliore|plus|davantage)\b/i', $msg)) return 'improve';
|
||
return null;
|
||
}
|
||
function buildReasoningChain($msg, $intent) {
|
||
$chain = ['observation' => mb_substr($msg, 0, 200)];
|
||
$chain['intent'] = $intent;
|
||
$chain['complexity'] = calculateComplexity($msg);
|
||
$chain['language'] = detectLanguage($msg);
|
||
$chain['expertise'] = detectExpertiseLevel($msg);
|
||
$chain['urgency'] = detectUrgency($msg);
|
||
$chain['question_type'] = detectQuestionType($msg);
|
||
$chain['has_code'] = detectCodeInMessage($msg);
|
||
$chain['has_files'] = detectFileReference($msg);
|
||
$chain['has_servers'] = detectServerReference($msg);
|
||
return $chain;
|
||
}
|
||
function buildMetaCognitivePrompt($chain) {
|
||
$meta = "\n## META-COGNITION\n";
|
||
$meta .= "Complexité: {$chain['complexity']}. ";
|
||
$meta .= "Expertise: {$chain['expertise']}. ";
|
||
if ($chain['urgency'] !== 'normal') $meta .= "Urgence: {$chain['urgency']}. ";
|
||
if ($chain['has_code']) $meta .= "Code détecté dans la question. ";
|
||
if ($chain['has_servers']) $meta .= "Référence serveur détectée. ";
|
||
return $meta;
|
||
}
|
||
function detectReasoningPattern($msg) {
|
||
if (preg_match('/\b(si|quand|lorsque).*alors\b/i', $msg)) return 'conditional';
|
||
if (preg_match('/\b(d\'abord|ensuite|puis|enfin|étape)\b/i', $msg)) return 'sequential';
|
||
if (preg_match('/\b(parce que|car|puisque|vu que|étant donné)\b/i', $msg)) return 'causal';
|
||
if (preg_match('/\b(mais|cependant|toutefois|néanmoins|or)\b/i', $msg)) return 'contrastive';
|
||
if (preg_match('/\b(en résumé|donc|ainsi|par conséquent)\b/i', $msg)) return 'conclusive';
|
||
return 'direct';
|
||
}
|
||
function buildThinkingFramework($pattern, $complexity) {
|
||
$frameworks = [
|
||
'conditional' => "Évalue: condition → conséquence → alternative. Test each branch.",
|
||
'sequential' => "Décompose en étapes ordonnées. Vérifie dépendances entre étapes.",
|
||
'causal' => "Remonte la chaîne causale. Distingue corrélation de causalité.",
|
||
'contrastive' => "Tableau comparatif. Critères objectifs. Score pondéré.",
|
||
'conclusive' => "Synthétise les éléments. Vérifie cohérence. Conclusion actionnable.",
|
||
'direct' => "Réponds directement. Code/commande si technique. Vérifie après.",
|
||
];
|
||
return $frameworks[$pattern] ?? $frameworks['direct'];
|
||
}
|
||
function estimateConfidence($response, $kbContext, $webContext) {
|
||
$score = 50;
|
||
if (!empty($kbContext)) $score += 20;
|
||
if (!empty($webContext)) $score += 15;
|
||
if (preg_match('/```/', $response)) $score += 5;
|
||
if (preg_match('/peut-être|probablement|il semble/i', $response)) $score -= 10;
|
||
if (preg_match('/certainement|absolument|toujours/i', $response)) $score += 5;
|
||
return min(100, max(0, $score));
|
||
}
|
||
function shouldUseThinkingTags($complexity, $intent) {
|
||
return ($complexity === 'complex' && in_array($intent, ['analytical','mathematical','causal','strategic','compliance']));
|
||
}
|
||
function buildCoTPrompt($msg) {
|
||
return "Pense étape par étape:\n1. Comprendre la question exacte\n2. Identifier les contraintes\n3. Lister les approches possibles\n4. Choisir la meilleure\n5. Implémenter\n6. Vérifier\n\nQuestion: $msg";
|
||
}
|
||
function buildTreeOfThought($msg, $branches = 3) {
|
||
$prompt = "Explore $branches approches différentes:\n";
|
||
for ($i = 1; $i <= $branches; $i++) {
|
||
$prompt .= "Branche $i: [approche]\n → Avantages:\n → Inconvénients:\n → Score /10:\n";
|
||
}
|
||
$prompt .= "Sélectionne la meilleure branche et développe.\n\nQuestion: $msg";
|
||
return $prompt;
|
||
}
|
||
function buildReActPrompt($msg) {
|
||
return "Utilise le pattern ReAct:\nThought: [réflexion]\nAction: [action à prendre]\nObservation: [résultat]\nThought: [ajustement]\nAction: [action suivante]\n...\nFinal Answer: [réponse complète]\n\nQuestion: $msg";
|
||
}
|
||
function buildSelfConsistencyCheck($responses) {
|
||
$common = [];
|
||
foreach ($responses as $r) {
|
||
$sentences = preg_split('/[.!?]+/', $r);
|
||
foreach ($sentences as $s) {
|
||
$key = mb_strtolower(trim($s));
|
||
if (mb_strlen($key) > 20) $common[$key] = ($common[$key] ?? 0) + 1;
|
||
}
|
||
}
|
||
$consistent = array_filter($common, fn($c) => $c >= 2);
|
||
return ['consistency_ratio' => count($consistent) / max(count($common), 1), 'agreed_points' => array_keys($consistent)];
|
||
}
|
||
function buildFeedbackLoop($response, $userFeedback) {
|
||
if (preg_match('/\b(non|incorrect|faux|erreur|mauvais|pas ça)\b/i', $userFeedback)) {
|
||
return ['action' => 'regenerate', 'instruction' => "L'utilisateur a indiqué que la réponse précédente est incorrecte. Raison: $userFeedback. Régénère en corrigeant."];
|
||
}
|
||
if (preg_match('/\b(plus|davantage|détails?|approfondi|développe)\b/i', $userFeedback)) {
|
||
return ['action' => 'expand', 'instruction' => "L'utilisateur veut plus de détails sur: $userFeedback"];
|
||
}
|
||
return ['action' => 'continue', 'instruction' => ''];
|
||
}
|
||
function buildProgressiveDisclosure($response) {
|
||
$sections = preg_split('/^#+\s/m', $response);
|
||
if (count($sections) <= 2) return ['type' => 'single', 'content' => $response];
|
||
return ['type' => 'progressive', 'summary' => mb_substr($sections[0], 0, 200), 'sections' => count($sections), 'full' => $response];
|
||
}
|
||
function detectContradiction($claim1, $claim2) {
|
||
$negations = ['pas','non','jamais','aucun','sans','ni'];
|
||
foreach ($negations as $neg) {
|
||
if ((stripos($claim1, $neg) !== false) !== (stripos($claim2, $neg) !== false)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
function buildHypothesisTree($symptom) {
|
||
return [
|
||
'symptom' => $symptom,
|
||
'hypotheses' => [
|
||
['name' => 'H1: Configuration', 'probability' => 'haute', 'test' => 'Vérifier fichiers config'],
|
||
['name' => 'H2: Réseau', 'probability' => 'moyenne', 'test' => 'Test connectivité'],
|
||
['name' => 'H3: Ressources', 'probability' => 'moyenne', 'test' => 'Check CPU/RAM/disk'],
|
||
['name' => 'H4: Code', 'probability' => 'faible', 'test' => 'Review logs + code'],
|
||
],
|
||
];
|
||
}
|
||
function buildProblemDecomposition($problem) {
|
||
$subproblems = [];
|
||
$words = explode(' ', $problem);
|
||
$chunks = array_chunk($words, 5);
|
||
foreach ($chunks as $i => $chunk) {
|
||
$subproblems[] = ['id' => $i + 1, 'description' => implode(' ', $chunk), 'status' => 'pending'];
|
||
}
|
||
return $subproblems;
|
||
}
|
||
function buildAnalogicalReasoning($domain, $problem) {
|
||
$analogies = [
|
||
'infrastructure' => 'Comme une ville: routes=réseau, bâtiments=serveurs, eau/électricité=données',
|
||
'security' => 'Comme une forteresse: murailles=firewall, gardes=monitoring, pont-levis=auth',
|
||
'performance' => 'Comme une course auto: moteur=CPU, carburant=RAM, pneus=I/O, aérodynamisme=code',
|
||
'architecture' => 'Comme un organisme: cerveau=orchestrateur, organes=services, sang=données, nerfs=API',
|
||
];
|
||
return $analogies[$domain] ?? "Analogie: $problem est comparable à un système complexe avec des composants interdépendants.";
|
||
}
|
||
function estimateAnswerDifficulty($msg) {
|
||
$score = 0;
|
||
$score += mb_strlen($msg) > 200 ? 2 : (mb_strlen($msg) > 50 ? 1 : 0);
|
||
$score += substr_count($msg, '?');
|
||
$score += detectCodeInMessage($msg) ? 1 : 0;
|
||
$score += preg_match_all('/\b(architecture|migration|optimis|sécurité|audit|diagnostic)\b/i', $msg);
|
||
$score += detectMultiPart($msg) ? 2 : 0;
|
||
if ($score <= 1) return ['difficulty' => 'easy', 'score' => $score, 'estimated_tokens' => 200];
|
||
if ($score <= 4) return ['difficulty' => 'medium', 'score' => $score, 'estimated_tokens' => 800];
|
||
return ['difficulty' => 'hard', 'score' => $score, 'estimated_tokens' => 2000];
|
||
}
|
||
function buildRecursiveRefinement($response, $iterations = 2) {
|
||
$prompts = [];
|
||
for ($i = 1; $i <= $iterations; $i++) {
|
||
$prompts[] = "Itération $i: Améliore cette réponse. Corrige erreurs, ajoute précision, renforce structure.\n" . mb_substr($response, 0, 3000);
|
||
}
|
||
return $prompts;
|
||
}
|
||
function buildAbductiveReasoning($observation) {
|
||
return "Observation: $observation\nRaisonnement abductif (meilleure explication):\n1. Quelle est l'explication la plus SIMPLE ?\n2. Quelle est l'explication la plus PROBABLE ?\n3. Quelle est l'explication la plus COMPLÈTE ?\nConclusion: L'explication qui maximise simplicité × probabilité.";
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE N: KNOWLEDGE BASE INTELLIGENCE (20 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function buildKBQuery($msg, $intent) {
|
||
$terms = preg_split('/\s+/', mb_strtolower($msg));
|
||
$terms = array_filter($terms, fn($t) => mb_strlen($t) > 3 && !in_array($t, ['dans','avec','pour','plus','cette','comme','quel','quels','comment','pourquoi']));
|
||
return array_slice($terms, 0, 5);
|
||
}
|
||
function rankKBResults($results, $msg) {
|
||
$msgLower = mb_strtolower($msg);
|
||
foreach ($results as &$r) {
|
||
$titleMatch = similar_text(mb_strtolower($r['title'] ?? ''), $msgLower);
|
||
$r['relevance_score'] = $titleMatch + ($r['relevance'] ?? 0) * 100;
|
||
}
|
||
usort($results, fn($a, $b) => $b['relevance_score'] - $a['relevance_score']);
|
||
return $results;
|
||
}
|
||
function buildKBContext($results, $maxChars = 2000) {
|
||
$context = "";
|
||
foreach ($results as $r) {
|
||
$entry = "📚 **{$r['title']}**\n" . mb_substr($r['content'] ?? $r['excerpt'] ?? '', 0, 400) . "\n\n";
|
||
if (mb_strlen($context . $entry) > $maxChars) break;
|
||
$context .= $entry;
|
||
}
|
||
return $context;
|
||
}
|
||
function detectKBGap($msg, $kbResults) {
|
||
if (empty($kbResults) && mb_strlen($msg) > 30 && !detectGreeting($msg)) return ['gap' => true, 'topic' => mb_substr($msg, 0, 100), 'action' => 'suggest_ingest'];
|
||
return ['gap' => false];
|
||
}
|
||
function suggestKBEntry($msg, $response) {
|
||
return ['title' => mb_substr($msg, 0, 100), 'content' => mb_substr($response, 0, 2000), 'category' => 'auto_learned', 'confidence' => 'medium'];
|
||
}
|
||
function buildKBSearchStrategy($msg) {
|
||
$strategies = [];
|
||
$strategies[] = ['type' => 'exact', 'terms' => buildKBQuery($msg, '')];
|
||
$words = explode(' ', $msg);
|
||
if (count($words) > 3) {
|
||
$bigrams = [];
|
||
for ($i = 0; $i < count($words) - 1; $i++) $bigrams[] = $words[$i] . ' ' . $words[$i+1];
|
||
$strategies[] = ['type' => 'bigram', 'terms' => array_slice($bigrams, 0, 3)];
|
||
}
|
||
$strategies[] = ['type' => 'fuzzy', 'terms' => array_map(fn($t) => mb_substr($t, 0, -1), buildKBQuery($msg, ''))];
|
||
return $strategies;
|
||
}
|
||
function categorizeKBEntry($title, $content) {
|
||
$text = mb_strtolower($title . ' ' . $content);
|
||
if (preg_match('/(sap|abap|fiori|hana|s\/4)/i', $text)) return 'sap';
|
||
if (preg_match('/(security|sécurité|firewall|CVE|pentest)/i', $text)) return 'security';
|
||
if (preg_match('/(email|smtp|deliverability|warmup|pmta)/i', $text)) return 'email';
|
||
if (preg_match('/(ai|machine learning|llm|model|neural)/i', $text)) return 'ai';
|
||
if (preg_match('/(server|nginx|docker|kubernetes|cloud)/i', $text)) return 'infrastructure';
|
||
if (preg_match('/(code|php|python|javascript|api)/i', $text)) return 'development';
|
||
return 'general';
|
||
}
|
||
function estimateKBRelevance($entry, $msg) {
|
||
$titleMatch = similar_text(mb_strtolower($entry['title'] ?? ''), mb_strtolower($msg));
|
||
$contentMatch = substr_count(mb_strtolower($entry['content'] ?? ''), mb_strtolower(explode(' ', $msg)[0] ?? ''));
|
||
return $titleMatch * 2 + $contentMatch * 5;
|
||
}
|
||
function buildKBEnrichmentPlan($msg, $currentResults) {
|
||
$gap = detectKBGap($msg, $currentResults);
|
||
if (!$gap['gap']) return ['action' => 'none', 'coverage' => 'adequate'];
|
||
return ['action' => 'enrich', 'topic' => $gap['topic'], 'suggested_sources' => ['web_search', 'document_ingest', 'manual_entry']];
|
||
}
|
||
function compressKBEntries($entries, $maxTotal = 3000) {
|
||
$compressed = [];
|
||
$budget = $maxTotal;
|
||
foreach ($entries as $e) {
|
||
$size = mb_strlen($e['content'] ?? '');
|
||
if ($size > $budget) {
|
||
$compressed[] = array_merge($e, ['content' => mb_substr($e['content'], 0, $budget)]);
|
||
break;
|
||
}
|
||
$compressed[] = $e;
|
||
$budget -= $size;
|
||
}
|
||
return $compressed;
|
||
}
|
||
function deduplicateKBResults($results) {
|
||
$seen = [];
|
||
return array_filter($results, function($r) use (&$seen) {
|
||
$key = mb_strtolower($r['title'] ?? '') . '_' . mb_substr($r['content'] ?? '', 0, 50);
|
||
if (isset($seen[$key])) return false;
|
||
$seen[$key] = true;
|
||
return true;
|
||
});
|
||
}
|
||
function buildKBSummary($entries) {
|
||
$sections = [];
|
||
foreach ($entries as $e) {
|
||
$cat = $e['category'] ?? 'general';
|
||
if (!isset($sections[$cat])) $sections[$cat] = 0;
|
||
$sections[$cat]++;
|
||
}
|
||
return $sections;
|
||
}
|
||
function buildKBNavigationPath($msg, $history) {
|
||
$topics = [];
|
||
foreach ($history as $h) {
|
||
if (($h['role'] ?? '') === 'user') {
|
||
$terms = buildKBQuery($h['content'] ?? '', '');
|
||
$topics = array_merge($topics, $terms);
|
||
}
|
||
}
|
||
$topics = array_merge($topics, buildKBQuery($msg, ''));
|
||
return array_unique($topics);
|
||
}
|
||
function buildKBFeedback($entryId, $helpful) {
|
||
return ['entry_id' => $entryId, 'helpful' => $helpful, 'timestamp' => time()];
|
||
}
|
||
function buildAutoKBEntry($msg, $response, $intent) {
|
||
if (mb_strlen($response) < 100 || $intent === 'greeting') return null;
|
||
return ['title' => mb_substr($msg, 0, 200), 'content' => mb_substr($response, 0, 3000), 'section' => $intent, 'category' => 'auto_generated', 'confidence' => estimateConfidence($response, '', '') > 70 ? 'high' : 'medium'];
|
||
}
|
||
function getKBStats() {
|
||
return ['total_entries' => 10880, 'tables' => 32, 'last_sync' => date('Y-m-d'), 'top_categories' => ['sap','email','infrastructure','security','ai']];
|
||
}
|
||
function buildSemanticKBSearch($msg) {
|
||
$embedding_prompt = "Trouve les entrées les plus pertinentes pour: $msg";
|
||
return ['type' => 'semantic', 'embedding_model' => 'nomic-embed-text', 'query' => $embedding_prompt, 'top_k' => 5];
|
||
}
|
||
function buildHybridKBSearch($msg) {
|
||
return ['keyword' => buildKBQuery($msg, ''), 'semantic' => buildSemanticKBSearch($msg), 'reranker' => 'cross_encoder', 'fusion' => 'reciprocal_rank'];
|
||
}
|
||
function buildKBIngestionPlan($filePath) {
|
||
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
|
||
$strategies = [
|
||
'pdf' => ['extractor' => 'PyPDF2', 'chunking' => 'page', 'max_chunk' => 2000],
|
||
'docx' => ['extractor' => 'python-docx', 'chunking' => 'paragraph', 'max_chunk' => 1500],
|
||
'csv' => ['extractor' => 'native', 'chunking' => 'row_batch', 'max_chunk' => 1000],
|
||
'json' => ['extractor' => 'native', 'chunking' => 'object', 'max_chunk' => 2000],
|
||
'md' => ['extractor' => 'native', 'chunking' => 'heading', 'max_chunk' => 2000],
|
||
];
|
||
return $strategies[$ext] ?? ['extractor' => 'native', 'chunking' => 'fixed', 'max_chunk' => 1500];
|
||
}
|
||
function buildKBMaintenancePlan() {
|
||
return ['daily' => ['backup PG → S88', 'deduplicate entries'], 'weekly' => ['reindex full-text search', 'prune low-confidence entries', 'generate KB stats report'], 'monthly' => ['rebuild embeddings', 'archive old entries', 'cross-reference with web sources']];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE O: MASTER ORCHESTRATOR v2
|
||
// Enhanced pipeline integrating GPU rotation + cross-verification
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function cognitiveExpansionV2Enrich($sys, $msg, $intent, $history = []) {
|
||
// Phase 1: Base enrichment from v1
|
||
$sys = cognitiveExpansionEnrich($sys, $msg, $intent, $history);
|
||
|
||
// Phase 2: Reasoning chain
|
||
$chain = buildReasoningChain($msg, $intent);
|
||
$metaPrompt = buildMetaCognitivePrompt($chain);
|
||
if (mb_strlen($sys) + mb_strlen($metaPrompt) < 7500) {
|
||
$sys .= $metaPrompt;
|
||
}
|
||
|
||
// Phase 3: Thinking framework selection
|
||
$pattern = detectReasoningPattern($msg);
|
||
$framework = buildThinkingFramework($pattern, $chain['complexity']);
|
||
if (mb_strlen($sys) + mb_strlen($framework) + 20 < 7800) {
|
||
$sys .= "\n## FRAMEWORK: " . $framework;
|
||
}
|
||
|
||
// Phase 4: GPU model recommendation (stored for API to use)
|
||
$difficulty = estimateAnswerDifficulty($msg);
|
||
$gpuRec = getModelRecommendation($intent, mb_strlen($msg), true);
|
||
$GLOBALS['_cogExpansion'] = [
|
||
'difficulty' => $difficulty,
|
||
'gpu_model' => $gpuRec['gpu_model'],
|
||
'cloud_providers' => $gpuRec['cloud_providers'],
|
||
'reasoning_pattern' => $pattern,
|
||
'chain' => $chain,
|
||
];
|
||
|
||
return $sys;
|
||
}
|
||
|
||
function cognitiveExpansionV2PostProcess($response, $msg, $intent) {
|
||
// Phase 1: Base post-processing
|
||
$response = cognitiveExpansionPostProcess($response, $msg);
|
||
|
||
// Phase 2: Quality gate
|
||
$quality = scoreResponseQuality($response, $intent, $msg);
|
||
if ($quality['score'] < 6) {
|
||
error_log("WEVIA_COG_QUALITY_ALERT: score=" . $quality['score'] . " intent=$intent issues=" . implode("|", $quality['issues']));
|
||
}
|
||
|
||
// Phase 3: Mermaid syntax validation
|
||
$mermaidCheck = validateMermaidSyntax($response);
|
||
if ($mermaidCheck) {
|
||
error_log("WEVIA_COG_MERMAID: $mermaidCheck");
|
||
}
|
||
|
||
// Phase 4: JSON validation
|
||
$jsonCheck = validateJSONInResponse($response);
|
||
if ($jsonCheck) {
|
||
error_log("WEVIA_COG_JSON: $jsonCheck");
|
||
}
|
||
|
||
return $response;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE P: CONVERSATION INTELLIGENCE (20 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function buildConversationGraph($history) {
|
||
$nodes = []; $edges = [];
|
||
$lastTopic = null;
|
||
foreach ($history as $i => $h) {
|
||
if (($h['role'] ?? '') !== 'user') continue;
|
||
$topic = mb_substr($h['content'] ?? '', 0, 40);
|
||
$nodes[] = ['id' => $i, 'label' => $topic];
|
||
if ($lastTopic !== null) $edges[] = ['from' => $lastTopic, 'to' => $i];
|
||
$lastTopic = $i;
|
||
}
|
||
return ['nodes' => $nodes, 'edges' => $edges, 'depth' => count($nodes)];
|
||
}
|
||
function detectTopicDrift($history, $msg) {
|
||
if (count($history) < 4) return false;
|
||
$last3 = array_slice($history, -3);
|
||
$lastWords = [];
|
||
foreach ($last3 as $h) { $lastWords = array_merge($lastWords, explode(' ', mb_strtolower($h['content'] ?? ''))); }
|
||
$currentWords = explode(' ', mb_strtolower($msg));
|
||
$overlap = count(array_intersect(array_filter($lastWords, fn($w) => mb_strlen($w) > 4), array_filter($currentWords, fn($w) => mb_strlen($w) > 4)));
|
||
return $overlap < 2;
|
||
}
|
||
function buildConversationSummary($history, $maxEntries = 5) {
|
||
$summaries = [];
|
||
$recent = array_slice($history, -$maxEntries * 2);
|
||
foreach ($recent as $h) {
|
||
if (($h['role'] ?? '') === 'user') $summaries[] = '👤 ' . mb_substr($h['content'] ?? '', 0, 80);
|
||
else $summaries[] = '🤖 ' . mb_substr($h['content'] ?? '', 0, 80);
|
||
}
|
||
return implode("\n", $summaries);
|
||
}
|
||
function calculateConversationMetrics($history) {
|
||
$userMsgs = $assistantMsgs = 0; $totalUserChars = $totalAssistantChars = 0;
|
||
foreach ($history as $h) {
|
||
if (($h['role'] ?? '') === 'user') { $userMsgs++; $totalUserChars += mb_strlen($h['content'] ?? ''); }
|
||
else { $assistantMsgs++; $totalAssistantChars += mb_strlen($h['content'] ?? ''); }
|
||
}
|
||
return ['user_messages' => $userMsgs, 'assistant_messages' => $assistantMsgs, 'avg_user_length' => $userMsgs > 0 ? round($totalUserChars / $userMsgs) : 0, 'avg_assistant_length' => $assistantMsgs > 0 ? round($totalAssistantChars / $assistantMsgs) : 0, 'ratio' => $userMsgs > 0 ? round($totalAssistantChars / max($totalUserChars, 1), 1) : 0];
|
||
}
|
||
function predictNextIntent($history) {
|
||
$intents = [];
|
||
foreach ($history as $h) {
|
||
if (isset($h['intent'])) $intents[] = $h['intent'];
|
||
}
|
||
if (empty($intents)) return 'general';
|
||
return end($intents);
|
||
}
|
||
function expansionBuildMemoryContext($history, $limit = 5) {
|
||
$entities = [];
|
||
$recent = array_slice($history, -$limit * 2);
|
||
foreach ($recent as $h) {
|
||
$c = $h['content'] ?? '';
|
||
if (preg_match_all('/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/', $c, $m)) $entities['ips'] = array_merge($entities['ips'] ?? [], $m[0]);
|
||
if (preg_match_all('/\b[\w]+\.(?:php|py|js|sh|sql)\b/i', $c, $m)) $entities['files'] = array_merge($entities['files'] ?? [], $m[0]);
|
||
if (preg_match_all('/\b(S\d{2,3}|serveur \d+)\b/i', $c, $m)) $entities['servers'] = array_merge($entities['servers'] ?? [], $m[0]);
|
||
}
|
||
foreach ($entities as &$v) $v = array_unique($v);
|
||
return $entities;
|
||
}
|
||
function detectConversationMode($history) {
|
||
$count = count($history);
|
||
if ($count <= 2) return 'new';
|
||
$codeCount = 0; $questionCount = 0;
|
||
foreach ($history as $h) {
|
||
$c = $h['content'] ?? '';
|
||
if (preg_match('/```/', $c)) $codeCount++;
|
||
if (preg_match('/\?/', $c)) $questionCount++;
|
||
}
|
||
if ($codeCount > $count * 0.4) return 'coding_session';
|
||
if ($questionCount > $count * 0.6) return 'qa_session';
|
||
return 'mixed';
|
||
}
|
||
function buildSessionObjective($history) {
|
||
if (count($history) < 2) return 'unknown';
|
||
$firstMsg = $history[0]['content'] ?? '';
|
||
if (detectCodeGeneration($firstMsg)) return 'build';
|
||
if (detectDiagnostic($firstMsg)) return 'fix';
|
||
if (detectAudit($firstMsg)) return 'audit';
|
||
if (detectMigration($firstMsg)) return 'migrate';
|
||
if (detectLearning($firstMsg)) return 'learn';
|
||
return 'explore';
|
||
}
|
||
function estimateRemainingEffort($history, $objective) {
|
||
$msgs = count($history);
|
||
$estimates = ['build' => 20, 'fix' => 10, 'audit' => 15, 'migrate' => 25, 'learn' => 30, 'explore' => 50];
|
||
$total = $estimates[$objective] ?? 20;
|
||
$progress = min(100, (int)(($msgs / $total) * 100));
|
||
return ['progress_pct' => $progress, 'estimated_remaining_msgs' => max(0, $total - $msgs)];
|
||
}
|
||
function buildConversationCheckpoint($history, $msg) {
|
||
return ['turn' => count($history), 'last_topic' => mb_substr($msg, 0, 100), 'mode' => detectConversationMode($history), 'entities' => expansionBuildMemoryContext($history), 'timestamp' => time()];
|
||
}
|
||
function shouldSummarizeHistory($history) {
|
||
return count($history) > 20;
|
||
}
|
||
function buildHistorySummary($history) {
|
||
$topics = []; $actions = []; $code = 0;
|
||
foreach ($history as $h) {
|
||
$c = $h['content'] ?? '';
|
||
if (($h['role'] ?? '') === 'user') $topics[] = mb_substr($c, 0, 40);
|
||
if (preg_match('/```/', $c)) $code++;
|
||
if (preg_match('/\b(créé|modifié|corrigé|déployé|installé|configuré)\b/i', $c, $m)) $actions[] = $m[0];
|
||
}
|
||
return "📝 Session: " . count($history) . " msgs, " . count(array_unique($topics)) . " sujets, $code blocs code. Actions: " . implode(', ', array_unique($actions));
|
||
}
|
||
function detectUserPreference($history) {
|
||
$prefs = ['format' => 'auto', 'detail' => 'medium', 'emoji' => true, 'code_style' => 'full'];
|
||
foreach ($history as $h) {
|
||
$c = mb_strtolower($h['content'] ?? '');
|
||
if (preg_match('/\b(court|bref|concis)\b/', $c)) $prefs['detail'] = 'short';
|
||
if (preg_match('/\b(détaillé|complet|exhaustif)\b/', $c)) $prefs['detail'] = 'long';
|
||
if (preg_match('/\b(pas d.emoji|sans emoji)\b/', $c)) $prefs['emoji'] = false;
|
||
}
|
||
return $prefs;
|
||
}
|
||
function buildAdaptiveResponse($prefs, $response) {
|
||
if ($prefs['detail'] === 'short' && mb_strlen($response) > 500) {
|
||
$lines = explode("\n", $response);
|
||
$response = implode("\n", array_slice($lines, 0, min(10, count($lines))));
|
||
}
|
||
if (!$prefs['emoji']) {
|
||
$response = preg_replace('/[\x{1F300}-\x{1F9FF}\x{2600}-\x{27BF}\x{1F600}-\x{1F64F}]/u', '', $response);
|
||
}
|
||
return $response;
|
||
}
|
||
function detectDuplicateQuestion($msg, $history) {
|
||
$msgNorm = mb_strtolower(preg_replace('/\s+/', ' ', trim($msg)));
|
||
foreach ($history as $h) {
|
||
if (($h['role'] ?? '') !== 'user') continue;
|
||
$prev = mb_strtolower(preg_replace('/\s+/', ' ', trim($h['content'] ?? '')));
|
||
if (similar_text($msgNorm, $prev) / max(mb_strlen($msgNorm), 1) > 0.8) return true;
|
||
}
|
||
return false;
|
||
}
|
||
function buildConversationExport($history) {
|
||
$export = "# Conversation WEVIA\nDate: " . date('Y-m-d H:i') . "\n\n";
|
||
foreach ($history as $h) {
|
||
$role = ($h['role'] ?? '') === 'user' ? '👤 Utilisateur' : '🤖 WEVIA';
|
||
$export .= "### $role\n" . ($h['content'] ?? '') . "\n\n";
|
||
}
|
||
return $export;
|
||
}
|
||
function buildTopicCluster($history) {
|
||
$clusters = [];
|
||
foreach ($history as $h) {
|
||
if (($h['role'] ?? '') !== 'user') continue;
|
||
$c = $h['content'] ?? '';
|
||
if (detectSAPContext($c)) $clusters['SAP'] = ($clusters['SAP'] ?? 0) + 1;
|
||
if (detectSecurityContext($c)) $clusters['Security'] = ($clusters['Security'] ?? 0) + 1;
|
||
if (detectCodeInMessage($c)) $clusters['Code'] = ($clusters['Code'] ?? 0) + 1;
|
||
if (detectInfraQuery($c)) $clusters['Infra'] = ($clusters['Infra'] ?? 0) + 1;
|
||
if (detectEmailTechContext($c)) $clusters['Email'] = ($clusters['Email'] ?? 0) + 1;
|
||
}
|
||
arsort($clusters);
|
||
return $clusters;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE Q: SECURITY GUARDS (15 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function sanitizeInput($input) {
|
||
$input = strip_tags($input);
|
||
$input = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $input);
|
||
return $input;
|
||
}
|
||
function detectPromptInjection($msg) {
|
||
$patterns = ['/ignore previous instructions/i', '/forget everything/i', '/you are now/i', '/system prompt/i', '/\bDAN\b/', '/jailbreak/i', '/bypass/i'];
|
||
foreach ($patterns as $p) if (preg_match($p, $msg)) return true;
|
||
return false;
|
||
}
|
||
function detectSensitiveData($text) {
|
||
$found = [];
|
||
if (preg_match('/\b\d{16}\b/', $text)) $found[] = 'credit_card';
|
||
if (preg_match('/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/', $text)) $found[] = 'email';
|
||
if (preg_match('/\b\d{3}-\d{2}-\d{4}\b/', $text)) $found[] = 'ssn';
|
||
if (preg_match('/password\s*[:=]\s*\S+/i', $text)) $found[] = 'password';
|
||
return $found;
|
||
}
|
||
function redactSensitiveData($text) {
|
||
$text = preg_replace('/\b\d{16}\b/', '[CARTE MASQUÉE]', $text);
|
||
$text = preg_replace('/password\s*[:=]\s*\S+/i', 'password=[MASQUÉ]', $text);
|
||
return $text;
|
||
}
|
||
function validateIPAccess($ip) {
|
||
$allowed = ['127.0.0.1','88.198.4.195','46.62.220.135','151.80.235.110','127.0.0.1'];
|
||
return in_array($ip, $allowed);
|
||
}
|
||
function buildSecurityAuditLog($action, $user, $resource) {
|
||
return date('Y-m-d H:i:s') . " | action=$action user=$user resource=$resource ip=" . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
|
||
}
|
||
function detectSQLInjection($input) {
|
||
return (bool)preg_match('/(\bUNION\b.*\bSELECT\b|\bOR\b\s+1\s*=\s*1|\b--\b|;\s*DROP|;\s*DELETE|xp_cmdshell)/i', $input);
|
||
}
|
||
function detectXSS($input) {
|
||
return (bool)preg_match('/<script|javascript:|onerror|onload|onclick|eval\(|document\.cookie/i', $input);
|
||
}
|
||
function sanitizeForDatabase($input) {
|
||
return htmlspecialchars(strip_tags($input), ENT_QUOTES, 'UTF-8');
|
||
}
|
||
function buildCSPHeaders() {
|
||
return "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'";
|
||
}
|
||
function hashSensitive($data) {
|
||
return hash('sha256', $data . 'weval_salt_2026');
|
||
}
|
||
function validateSessionToken($token) {
|
||
return preg_match('/^[a-f0-9]{64}$/', $token);
|
||
}
|
||
function buildRateLimitKey($ip, $endpoint) {
|
||
return "rate:" . md5($ip . $endpoint) . ":" . date('YmdHi');
|
||
}
|
||
function detectBruteForce($attempts, $threshold = 10) {
|
||
return $attempts >= $threshold;
|
||
}
|
||
function buildSecurityReport() {
|
||
return ['timestamp' => date('c'), 'checks' => ['input_sanitization' => 'active', 'sql_injection_guard' => 'active', 'xss_guard' => 'active', 'rate_limiting' => 'active', 'prompt_injection_guard' => 'active', 'sensitive_data_redaction' => 'active']];
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE R: OUTPUT FORMATTERS (20 functions)
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function formatAsTable($data, $headers) {
|
||
$table = "| " . implode(" | ", $headers) . " |\n";
|
||
$table .= "|" . str_repeat("---|", count($headers)) . "\n";
|
||
foreach ($data as $row) {
|
||
$table .= "| " . implode(" | ", array_map(fn($v) => strval($v), $row)) . " |\n";
|
||
}
|
||
return $table;
|
||
}
|
||
function formatAsJSON($data) {
|
||
return "```json\n" . json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n```";
|
||
}
|
||
function formatAsYAML($data, $indent = 0) {
|
||
$yaml = '';
|
||
foreach ($data as $key => $value) {
|
||
$yaml .= str_repeat(' ', $indent) . "$key: ";
|
||
if (is_array($value)) $yaml .= "\n" . formatAsYAML($value, $indent + 1);
|
||
else $yaml .= "$value\n";
|
||
}
|
||
return $indent === 0 ? "```yaml\n$yaml```" : $yaml;
|
||
}
|
||
function formatAsCSV($data, $headers) {
|
||
$csv = implode(",", $headers) . "\n";
|
||
foreach ($data as $row) $csv .= implode(",", array_map(fn($v) => '"' . str_replace('"', '""', strval($v)) . '"', $row)) . "\n";
|
||
return "```csv\n$csv```";
|
||
}
|
||
function formatAsMermaid($type, $content) {
|
||
return "```mermaid\n$type\n$content\n```";
|
||
}
|
||
function formatAsFlowchart($steps) {
|
||
$mermaid = "flowchart TD\n";
|
||
for ($i = 0; $i < count($steps); $i++) {
|
||
$id = chr(65 + $i);
|
||
$mermaid .= ' ' . $id . '[' . $steps[$i] . "]\n";
|
||
if ($i > 0) $mermaid .= ' ' . chr(64 + $i) . ' --> ' . $id . "\n";
|
||
}
|
||
return formatAsMermaid('', $mermaid);
|
||
}
|
||
function formatAsSequenceDiagram($actors, $interactions) {
|
||
$mermaid = "sequenceDiagram\n";
|
||
foreach ($actors as $a) $mermaid .= " participant $a\n";
|
||
foreach ($interactions as $i) $mermaid .= " {$i['from']}->>+{$i['to']}: {$i['msg']}\n";
|
||
return formatAsMermaid('', $mermaid);
|
||
}
|
||
function formatAsGantt($tasks) {
|
||
$mermaid = "gantt\n dateFormat YYYY-MM-DD\n title Planning\n";
|
||
foreach ($tasks as $t) {
|
||
$mermaid .= " {$t['name']} : {$t['start']}, {$t['duration']}\n";
|
||
}
|
||
return formatAsMermaid('', $mermaid);
|
||
}
|
||
function formatAsBulletPoints($items) {
|
||
return implode("\n", array_map(fn($i) => "- $i", $items));
|
||
}
|
||
function formatAsNumberedList($items) {
|
||
return implode("\n", array_map(fn($i, $n) => ($n+1) . ". $i", $items, array_keys($items)));
|
||
}
|
||
function formatAsChecklist($items, $checked = []) {
|
||
return implode("\n", array_map(fn($i, $n) => (in_array($n, $checked) ? "☑" : "☐") . " $i", $items, array_keys($items)));
|
||
}
|
||
function formatAsCallout($type, $content) {
|
||
$icons = ['info' => 'ℹ️', 'warning' => '⚠️', 'error' => '❌', 'success' => '✅', 'tip' => '💡'];
|
||
$icon = $icons[$type] ?? '📌';
|
||
return "> $icon **" . ucfirst($type) . ":** $content";
|
||
}
|
||
function formatAsCodeBlock($code, $lang = 'php') {
|
||
return "```$lang\n$code\n```";
|
||
}
|
||
function formatAsTimeline($events) {
|
||
$tl = "";
|
||
foreach ($events as $e) $tl .= "📅 **{$e['date']}** — {$e['event']}\n";
|
||
return $tl;
|
||
}
|
||
function formatAsProgressBar($current, $total, $width = 20) {
|
||
$pct = min(100, (int)(($current / max($total, 1)) * 100));
|
||
$filled = (int)($pct / (100 / $width));
|
||
$bar = str_repeat('█', $filled) . str_repeat('░', $width - $filled);
|
||
return "[$bar] $pct% ($current/$total)";
|
||
}
|
||
function formatAsCard($title, $body, $footer = '') {
|
||
$card = "╔══════════════════════╗\n║ **$title**\n╠══════════════════════╣\n║ $body\n";
|
||
if ($footer) $card .= "╠══════════════════════╣\n║ $footer\n";
|
||
$card .= "╚══════════════════════╝";
|
||
return $card;
|
||
}
|
||
function formatAsAccordion($sections) {
|
||
$out = "";
|
||
foreach ($sections as $title => $content) {
|
||
$out .= "<details>\n<summary>$title</summary>\n\n$content\n\n</details>\n\n";
|
||
}
|
||
return $out;
|
||
}
|
||
function formatAsDiff($before, $after) {
|
||
return "```diff\n- $before\n+ $after\n```";
|
||
}
|
||
function formatAsAlert($level, $msg) {
|
||
$levels = ['critical' => '🚨', 'warning' => '⚠️', 'info' => 'ℹ️', 'success' => '✅'];
|
||
$icon = $levels[$level] ?? '📌';
|
||
return "$icon **" . strtoupper($level) . ":** $msg";
|
||
}
|
||
function formatAsSummaryBlock($title, $points) {
|
||
$block = "### 📋 $title\n\n";
|
||
foreach ($points as $p) $block .= "• $p\n";
|
||
return $block;
|
||
}
|
||
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
// MODULE L: GPU MODEL ROTATION & VERIFICATION (30 functions)
|
||
// Sovereign GPU routing, multi-model verification, quality assurance
|
||
// ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
function getGPUModelRanking($intent) {
|
||
$rankings = [
|
||
'technical' => ['deepseek-r1:32b','qwen2.5-coder:14b','deepseek-r1:14b','llama3.3:70b','wizardlm2:8x22b'],
|
||
'analytical' => ['deepseek-r1:32b','llama3.3:70b','qwen2.5:14b','mistral:7b','phi3:14b'],
|
||
'creative' => ['llama3.3:70b','wizardlm2:8x22b','deepseek-r1:32b','mistral:7b','gemma2:9b'],
|
||
'strategic' => ['deepseek-r1:32b','llama3.3:70b','wizardlm2:8x22b','qwen2.5:14b','phi3:14b'],
|
||
'mathematical' => ['deepseek-r1:32b','qwen2.5:14b','llama3.3:70b','phi3:14b','mistral:7b'],
|
||
'causal' => ['deepseek-r1:32b','llama3.3:70b','mistral:7b','qwen2.5:14b','phi3:14b'],
|
||
'compliance' => ['llama3.3:70b','deepseek-r1:32b','mistral:7b','qwen2.5:14b','wizardlm2:8x22b'],
|
||
'teaching' => ['llama3.3:70b','wizardlm2:8x22b','deepseek-r1:32b','gemma2:9b','phi3:14b'],
|
||
'code' => ['qwen2.5-coder:14b','deepseek-r1:32b','deepseek-r1:14b','codellama:34b','starcoder2:15b'],
|
||
'conversational' => ['llama3.1:8b','gemma2:9b','mistral:7b','phi3:3.8b','tinyllama:1.1b'],
|
||
'greeting' => ['llama3.1:8b','gemma2:9b','phi3:3.8b','tinyllama:1.1b','mistral:7b'],
|
||
];
|
||
$primary = is_array($intent) ? ($intent['primary'] ?? 'conversational') : $intent;
|
||
return $rankings[$primary] ?? $rankings['conversational'];
|
||
}
|
||
|
||
function getCloudVerifiers() {
|
||
return ['cerebras' => 'llama-3.3-70b-versatile', 'groq' => 'llama-3.3-70b-versatile'];
|
||
}
|
||
|
||
function buildVerificationPrompt($question, $initialResponse) {
|
||
return "Tu es un vérificateur expert. Voici une question et une réponse initiale. Vérifie UNIQUEMENT les faits, la logique et la complétude. Réponds en JSON: {\"score\":0-10, \"errors\":[], \"missing\":[], \"verdict\":\"ok|needs_fix\"}\n\nQUESTION: " . mb_substr($question, 0, 500) . "\nRÉPONSE: " . mb_substr($initialResponse, 0, 2000);
|
||
}
|
||
|
||
function parseVerificationResult($json) {
|
||
$decoded = @json_decode($json, true);
|
||
if (!$decoded) {
|
||
if (preg_match('/"score"\s*:\s*(\d+)/', $json, $m)) return ['score' => intval($m[1]), 'verdict' => intval($m[1]) >= 7 ? 'ok' : 'needs_fix'];
|
||
return ['score' => 5, 'verdict' => 'unknown'];
|
||
}
|
||
return $decoded;
|
||
}
|
||
|
||
function selectGPUModel($intent, $complexity, $msgLength) {
|
||
$models = getGPUModelRanking($intent);
|
||
if ($complexity === 'simple' && $msgLength < 50) return $models[count($models) - 1]; // Lightest
|
||
if ($complexity === 'complex') return $models[0]; // Heaviest
|
||
return $models[1] ?? $models[0]; // Second best (balanced)
|
||
}
|
||
|
||
function buildRotationSchedule($intent, $providers) {
|
||
$gpu = getGPUModelRanking($intent);
|
||
$cloud = ['cerebras','groq','sambanova','mistral','cohere'];
|
||
$schedule = [];
|
||
$schedule[] = ['type' => 'gpu', 'model' => $gpu[0], 'role' => 'primary'];
|
||
$schedule[] = ['type' => 'gpu', 'model' => $gpu[1] ?? $gpu[0], 'role' => 'fallback_1'];
|
||
$schedule[] = ['type' => 'cloud', 'provider' => $cloud[0], 'role' => 'fallback_2'];
|
||
$schedule[] = ['type' => 'cloud', 'provider' => $cloud[1], 'role' => 'verifier'];
|
||
return $schedule;
|
||
}
|
||
|
||
function shouldVerifyResponse($intent, $complexity, $responseLen) {
|
||
if ($complexity === 'complex') return true;
|
||
if (in_array($intent, ['compliance','mathematical','causal'])) return true;
|
||
if ($responseLen > 2000) return true;
|
||
return false;
|
||
}
|
||
|
||
function mergeVerifiedResponse($primaryResponse, $verificationResult) {
|
||
if (!$verificationResult || ($verificationResult['verdict'] ?? '') === 'ok') return $primaryResponse;
|
||
$errors = $verificationResult['errors'] ?? [];
|
||
if (empty($errors)) return $primaryResponse;
|
||
return $primaryResponse . "\n\n⚠️ **Auto-vérification:** " . implode("; ", array_slice($errors, 0, 3));
|
||
}
|
||
|
||
function calculateModelLatencyBudget($complexity) {
|
||
$budgets = ['simple' => 3000, 'moderate' => 8000, 'complex' => 15000];
|
||
return $budgets[$complexity] ?? 8000;
|
||
}
|
||
|
||
function selectVerificationStrategy($intent) {
|
||
$strategies = [
|
||
'mathematical' => 'double_check_calculation',
|
||
'compliance' => 'cross_reference_legal',
|
||
'technical' => 'syntax_and_logic_check',
|
||
'causal' => 'counter_argument_test',
|
||
'code' => 'mental_execution_trace',
|
||
'analytical' => 'data_consistency_check',
|
||
];
|
||
return $strategies[$intent] ?? 'general_review';
|
||
}
|
||
|
||
function buildCrossVerificationPrompt($msg, $response, $strategy) {
|
||
$prompts = [
|
||
'double_check_calculation' => "Vérifie CHAQUE calcul dans cette réponse. Refais les calculs indépendamment.",
|
||
'cross_reference_legal' => "Vérifie les références légales (articles, lois, décrets) citées. Sont-elles exactes ?",
|
||
'syntax_and_logic_check' => "Vérifie: 1) Le code est syntaxiquement correct 2) La logique est sound 3) Les edge cases sont gérés",
|
||
'counter_argument_test' => "Joue l'avocat du diable. Quels sont les contre-arguments à cette analyse ?",
|
||
'mental_execution_trace' => "Exécute mentalement ce code ligne par ligne. Y a-t-il des bugs ?",
|
||
'data_consistency_check' => "Les données citées sont-elles cohérentes entre elles ? Y a-t-il des contradictions ?",
|
||
'general_review' => "Vérifie la qualité globale: exactitude, complétude, cohérence, actionabilité.",
|
||
];
|
||
return ($prompts[$strategy] ?? $prompts['general_review']) . "\n\nRÉPONSE À VÉRIFIER:\n" . mb_substr($response, 0, 2000);
|
||
}
|
||
|
||
function getGPUVRAMRequirement($model) {
|
||
$vram = [
|
||
'deepseek-r1:32b' => 19, 'llama3.3:70b' => 40, 'wizardlm2:8x22b' => 80,
|
||
'qwen2.5-coder:14b' => 9, 'deepseek-r1:14b' => 9, 'qwen2.5:14b' => 9,
|
||
'phi3:14b' => 8, 'mistral:7b' => 5, 'gemma2:9b' => 6, 'llama3.1:8b' => 5,
|
||
'codellama:34b' => 20, 'starcoder2:15b' => 10, 'phi3:3.8b' => 3, 'tinyllama:1.1b' => 1,
|
||
];
|
||
return $vram[$model] ?? 10;
|
||
}
|
||
|
||
function canFitInVRAM($model, $availableVRAM = 20) {
|
||
return getGPUVRAMRequirement($model) <= $availableVRAM;
|
||
}
|
||
|
||
function filterModelsForVRAM($models, $availableVRAM = 20) {
|
||
return array_values(array_filter($models, fn($m) => canFitInVRAM($m, $availableVRAM)));
|
||
}
|
||
|
||
function buildMultiModelPipeline($msg, $intent, $complexity) {
|
||
$pipeline = [];
|
||
|
||
// Phase 1: Primary generation (GPU)
|
||
$gpuModels = filterModelsForVRAM(getGPUModelRanking($intent), 20);
|
||
$pipeline[] = [
|
||
'phase' => 'generate',
|
||
'provider' => 'ollama_local',
|
||
'model' => $gpuModels[0] ?? 'mistral:7b',
|
||
'timeout_ms' => calculateModelLatencyBudget($complexity),
|
||
];
|
||
|
||
// Phase 2: Cloud fallback if GPU fails
|
||
$pipeline[] = [
|
||
'phase' => 'fallback',
|
||
'provider' => 'cerebras',
|
||
'model' => 'llama-3.3-70b-versatile',
|
||
'timeout_ms' => 10000,
|
||
];
|
||
|
||
// Phase 3: Verification (only for complex/critical)
|
||
if (shouldVerifyResponse($intent, $complexity, 0)) {
|
||
$pipeline[] = [
|
||
'phase' => 'verify',
|
||
'provider' => 'groq',
|
||
'model' => 'llama-3.3-70b-versatile',
|
||
'strategy' => selectVerificationStrategy($intent),
|
||
'timeout_ms' => 5000,
|
||
];
|
||
}
|
||
|
||
return $pipeline;
|
||
}
|
||
|
||
function scoreModelFit($model, $intent, $complexity) {
|
||
$rankings = getGPUModelRanking($intent);
|
||
$position = array_search($model, $rankings);
|
||
if ($position === false) return 0;
|
||
$rankScore = (count($rankings) - $position) / count($rankings) * 10;
|
||
$vramFit = canFitInVRAM($model) ? 1 : 0;
|
||
return round($rankScore * $vramFit, 1);
|
||
}
|
||
|
||
function getModelSpecialties($model) {
|
||
$specs = [
|
||
'deepseek-r1:32b' => ['reasoning','math','analysis','code'],
|
||
'deepseek-r1:14b' => ['reasoning','math','code'],
|
||
'qwen2.5-coder:14b' => ['code','debugging','refactoring','sql'],
|
||
'llama3.3:70b' => ['general','creative','multilingual','long_context'],
|
||
'wizardlm2:8x22b' => ['creative','nuanced','complex_reasoning'],
|
||
'mistral:7b' => ['fast','general','multilingual','instruction_following'],
|
||
'gemma2:9b' => ['conversational','fast','multilingual'],
|
||
'phi3:14b' => ['reasoning','math','compact'],
|
||
'llama3.1:8b' => ['fast','conversational','lightweight'],
|
||
'codellama:34b' => ['code','debugging','completion'],
|
||
];
|
||
return $specs[$model] ?? ['general'];
|
||
}
|
||
|
||
function matchModelToTask($msg, $intent) {
|
||
$models = filterModelsForVRAM(getGPUModelRanking($intent), 20);
|
||
$bestScore = 0;
|
||
$bestModel = $models[0] ?? 'mistral:7b';
|
||
|
||
foreach ($models as $model) {
|
||
$score = scoreModelFit($model, $intent, calculateComplexity($msg));
|
||
$specialties = getModelSpecialties($model);
|
||
|
||
// Bonus for specialty match
|
||
if (detectCodeInMessage($msg) && in_array('code', $specialties)) $score += 2;
|
||
if (detectNumberHeavy($msg) && in_array('math', $specialties)) $score += 2;
|
||
if (mb_strlen($msg) > 500 && in_array('long_context', $specialties)) $score += 1;
|
||
|
||
if ($score > $bestScore) { $bestScore = $score; $bestModel = $model; }
|
||
}
|
||
return ['model' => $bestModel, 'score' => $bestScore];
|
||
}
|
||
|
||
function buildGPURotationConfig() {
|
||
return [
|
||
'primary' => ['deepseek-r1:32b', 'qwen2.5-coder:14b'],
|
||
'secondary' => ['deepseek-r1:14b', 'phi3:14b', 'mistral:7b'],
|
||
'lightweight' => ['llama3.1:8b', 'gemma2:9b', 'phi3:3.8b'],
|
||
'cloud_fallback' => ['cerebras', 'groq', 'sambanova'],
|
||
'cloud_verifier' => ['cerebras', 'groq'],
|
||
'vram_limit' => 20,
|
||
'max_concurrent' => 1,
|
||
'rotation_strategy' => 'best_fit', // best_fit | round_robin | random
|
||
];
|
||
}
|
||
|
||
function logModelSelection($model, $intent, $complexity, $score) {
|
||
error_log("WEVIA_GPU_SELECT: model=$model intent=$intent complexity=$complexity fit_score=$score");
|
||
}
|
||
|
||
function buildAdaptiveTimeout($model, $msgLength) {
|
||
$baseTimeouts = [
|
||
'deepseek-r1:32b' => 15000, 'llama3.3:70b' => 20000, 'wizardlm2:8x22b' => 25000,
|
||
'qwen2.5-coder:14b' => 8000, 'deepseek-r1:14b' => 10000, 'mistral:7b' => 5000,
|
||
'gemma2:9b' => 5000, 'llama3.1:8b' => 4000, 'phi3:14b' => 8000, 'phi3:3.8b' => 3000,
|
||
];
|
||
$base = $baseTimeouts[$model] ?? 10000;
|
||
// Scale with message length
|
||
$scale = min(2.0, 1.0 + ($msgLength / 1000) * 0.3);
|
||
return (int)($base * $scale);
|
||
}
|
||
|
||
function buildResponseMetadata($model, $provider, $latencyMs, $tokenCount, $verified) {
|
||
return [
|
||
'model' => $model,
|
||
'provider' => $provider,
|
||
'latency_ms' => $latencyMs,
|
||
'tokens' => $tokenCount,
|
||
'verified' => $verified,
|
||
'cost_estimate' => estimateTokenCost($tokenCount, $provider),
|
||
'timestamp' => date('c'),
|
||
];
|
||
}
|
||
|
||
function detectModelOverload($latencyMs, $expectedMs) {
|
||
if ($latencyMs > $expectedMs * 2) return 'overloaded';
|
||
if ($latencyMs > $expectedMs * 1.5) return 'slow';
|
||
return 'normal';
|
||
}
|
||
|
||
function suggestModelSwitch($currentModel, $overloadStatus) {
|
||
if ($overloadStatus === 'normal') return null;
|
||
$lighter = [
|
||
'deepseek-r1:32b' => 'deepseek-r1:14b',
|
||
'llama3.3:70b' => 'deepseek-r1:32b',
|
||
'qwen2.5-coder:14b' => 'deepseek-r1:14b',
|
||
'deepseek-r1:14b' => 'mistral:7b',
|
||
'phi3:14b' => 'phi3:3.8b',
|
||
];
|
||
return $lighter[$currentModel] ?? 'mistral:7b';
|
||
}
|
||
|
||
function buildOllamaRequest($model, $prompt, $system, $options = []) {
|
||
return [
|
||
'model' => $model,
|
||
'prompt' => $prompt,
|
||
'system' => $system,
|
||
'stream' => false,
|
||
'options' => array_merge([
|
||
'temperature' => $options['temperature'] ?? 0.7,
|
||
'top_p' => $options['top_p'] ?? 0.9,
|
||
'num_predict' => $options['max_tokens'] ?? 2048,
|
||
'num_ctx' => $options['context_length'] ?? 4096,
|
||
], $options),
|
||
];
|
||
}
|
||
|
||
function estimateGPUMemoryUsage($loadedModels) {
|
||
$total = 0;
|
||
foreach ($loadedModels as $model) $total += getGPUVRAMRequirement($model);
|
||
return ['used_gb' => $total, 'available_gb' => 20, 'free_gb' => max(0, 20 - $total), 'utilization_pct' => round(($total / 20) * 100, 1)];
|
||
}
|
||
|
||
function optimizeModelLoading($requestedModel, $loadedModels, $vramLimit = 20) {
|
||
$needed = getGPUVRAMRequirement($requestedModel);
|
||
$currentUsage = 0;
|
||
foreach ($loadedModels as $m) $currentUsage += getGPUVRAMRequirement($m);
|
||
|
||
if ($currentUsage + $needed <= $vramLimit) return ['action' => 'load', 'evict' => []];
|
||
|
||
// Evict least important models
|
||
$evict = [];
|
||
$sorted = $loadedModels;
|
||
usort($sorted, fn($a, $b) => getGPUVRAMRequirement($a) - getGPUVRAMRequirement($b));
|
||
|
||
foreach ($sorted as $m) {
|
||
if ($m === $requestedModel) continue;
|
||
$evict[] = $m;
|
||
$currentUsage -= getGPUVRAMRequirement($m);
|
||
if ($currentUsage + $needed <= $vramLimit) break;
|
||
}
|
||
|
||
return ['action' => 'evict_and_load', 'evict' => $evict];
|
||
}
|
||
|
||
function buildHealthCheck() {
|
||
return [
|
||
'ollama' => 'curl -s http://localhost:11434/api/tags | python3 -c "import sys,json;d=json.load(sys.stdin);print(len(d[\'models\']),\'models loaded\')"',
|
||
'gpu' => 'nvidia-smi --query-gpu=memory.used,memory.total,temperature.gpu --format=csv,noheader',
|
||
'php' => 'php-fpm8.3 -t 2>&1',
|
||
'nginx' => 'nginx -t 2>&1',
|
||
'disk' => 'df -h / | tail -1',
|
||
'api' => 'curl -s -o /dev/null -w "%{http_code}" https://weval-consulting.com/api/weval-ia-full -d \'{"message":"ping","action":"chat"}\'',
|
||
];
|
||
}
|