1536 lines
61 KiB
PHP
Executable File
1536 lines
61 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* ╔═══════════════════════════════════════════════════════════════════════════════╗
|
|
* ║ WEVAL MIND BRAIN - Cerveau Multi-IA Centralisé ║
|
|
* ║ Avec Auto-Fallback + Style Claude Opus ║
|
|
* ╚═══════════════════════════════════════════════════════════════════════════════╝
|
|
*/
|
|
|
|
class HamidBrain {
|
|
|
|
private $pdo;
|
|
private $config = [];
|
|
private $lastError = '';
|
|
private $lastProvider = '';
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// LISTE DE TOUS LES PROVIDERS (Gratuits + Payants)
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public static $PROVIDERS = [
|
|
// Gratuits / Freemium
|
|
'groq' => [
|
|
'name' => 'Groq',
|
|
'icon' => '⚡',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'llama-3.3-70b-versatile',
|
|
'endpoint' => 'https://api.groq.com/openai/v1/chat/completions',
|
|
'key_name' => 'groq_api_key'
|
|
],
|
|
'deepseek' => [
|
|
'name' => 'DeepSeek',
|
|
'icon' => '🌊',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'deepseek-chat',
|
|
'endpoint' => 'https://api.deepseek.com/chat/completions',
|
|
'key_name' => 'deepseek_api_key'
|
|
],
|
|
'mistral' => [
|
|
'name' => 'Mistral',
|
|
'icon' => '🇫🇷',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'mistral-large-latest',
|
|
'endpoint' => 'https://api.mistral.ai/v1/chat/completions',
|
|
'key_name' => 'mistral_api_key'
|
|
],
|
|
'together' => [
|
|
'name' => 'Together AI',
|
|
'icon' => '🤝',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'meta-llama/Llama-3.3-70B-Instruct-Turbo',
|
|
'endpoint' => 'https://api.together.xyz/v1/chat/completions',
|
|
'key_name' => 'together_api_key'
|
|
],
|
|
'openrouter' => [
|
|
'name' => 'OpenRouter',
|
|
'icon' => '🔀',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'meta-llama/llama-3.3-70b-instruct',
|
|
'endpoint' => 'https://openrouter.ai/api/v1/chat/completions',
|
|
'key_name' => 'openrouter_api_key'
|
|
],
|
|
// === NOUVEAUX PROVIDERS GRATUITS ===
|
|
'cerebras' => [
|
|
'name' => 'Cerebras',
|
|
'icon' => '🧠',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'llama-3.3-70b',
|
|
'endpoint' => 'https://api.cerebras.ai/v1/chat/completions',
|
|
'key_name' => 'cerebras_api_key'
|
|
],
|
|
'sambanova' => [
|
|
'name' => 'SambaNova',
|
|
'icon' => '🚀',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'Meta-Llama-3.1-70B-Instruct',
|
|
'endpoint' => 'https://api.sambanova.ai/v1/chat/completions',
|
|
'key_name' => 'sambanova_api_key'
|
|
],
|
|
'fireworks' => [
|
|
'name' => 'Fireworks',
|
|
'icon' => '🎆',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'accounts/fireworks/models/llama-v3p3-70b-instruct',
|
|
'endpoint' => 'https://api.fireworks.ai/inference/v1/chat/completions',
|
|
'key_name' => 'fireworks_api_key'
|
|
],
|
|
'hyperbolic' => [
|
|
'name' => 'Hyperbolic',
|
|
'icon' => '🌀',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'meta-llama/Llama-3.1-405B-Instruct',
|
|
'endpoint' => 'https://api.hyperbolic.xyz/v1/chat/completions',
|
|
'key_name' => 'hyperbolic_api_key'
|
|
],
|
|
'lepton' => [
|
|
'name' => 'Lepton AI',
|
|
'icon' => '⚛️',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'llama3-70b',
|
|
'endpoint' => 'https://llama3-70b.lepton.run/api/v1/chat/completions',
|
|
'key_name' => 'lepton_api_key'
|
|
],
|
|
'ai21' => [
|
|
'name' => 'AI21',
|
|
'icon' => '🔮',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'jamba-1.5-large',
|
|
'endpoint' => 'https://api.ai21.com/studio/v1/chat/completions',
|
|
'key_name' => 'ai21_api_key'
|
|
],
|
|
'cloudflare' => [
|
|
'name' => 'Cloudflare AI',
|
|
'icon' => '☁️',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => '@cf/meta/llama-3.1-70b-instruct',
|
|
'endpoint' => 'https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run/@cf/meta/llama-3.1-70b-instruct',
|
|
'key_name' => 'cloudflare_api_key'
|
|
],
|
|
'novita' => [
|
|
'name' => 'Novita AI',
|
|
'icon' => '🌟',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'meta-llama/llama-3.1-70b-instruct',
|
|
'endpoint' => 'https://api.novita.ai/v3/openai/chat/completions',
|
|
'key_name' => 'novita_api_key'
|
|
],
|
|
'perplexity' => [
|
|
'name' => 'Perplexity',
|
|
'icon' => '🔍',
|
|
'type' => 'cloud',
|
|
'free' => true,
|
|
'model' => 'llama-3.1-sonar-small-128k-online',
|
|
'endpoint' => 'https://api.perplexity.ai/chat/completions',
|
|
'key_name' => 'perplexity_api_key'
|
|
],
|
|
'chatgpt' => [
|
|
'name' => 'ChatGPT',
|
|
'icon' => '🤖',
|
|
'type' => 'cloud',
|
|
'free' => false,
|
|
'model' => 'gpt-4o',
|
|
'endpoint' => 'https://api.openai.com/v1/chat/completions',
|
|
'key_name' => 'openai_api_key'
|
|
],
|
|
'copilot' => [
|
|
'name' => 'GitHub Copilot',
|
|
'icon' => '🐙',
|
|
'type' => 'cloud',
|
|
'free' => false,
|
|
'model' => 'gpt-4',
|
|
'endpoint' => 'https://api.githubcopilot.com/chat/completions',
|
|
'key_name' => 'copilot_api_key'
|
|
],
|
|
|
|
|
|
// Payants premium
|
|
'claude' => [
|
|
'name' => 'Claude',
|
|
'icon' => '🧠',
|
|
'type' => 'cloud',
|
|
'free' => false,
|
|
'model' => 'claude-3-5-sonnet-20241022',
|
|
'endpoint' => 'https://api.anthropic.com/v1/messages',
|
|
'key_name' => 'anthropic_api_key'
|
|
],
|
|
'openai' => [
|
|
'name' => 'OpenAI',
|
|
'icon' => '🤖',
|
|
'type' => 'cloud',
|
|
'free' => false,
|
|
'model' => 'gpt-4o',
|
|
'endpoint' => 'https://api.openai.com/v1/chat/completions',
|
|
'key_name' => 'openai_api_key'
|
|
],
|
|
'gemini' => [
|
|
'name' => 'Gemini',
|
|
'icon' => '💎',
|
|
'type' => 'cloud',
|
|
'free' => false,
|
|
'model' => 'gemini-1.5-flash',
|
|
'endpoint' => 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent',
|
|
'key_name' => 'gemini_api_key'
|
|
],
|
|
'cohere' => [
|
|
'name' => 'Cohere',
|
|
'icon' => '🔶',
|
|
'type' => 'cloud',
|
|
'free' => false,
|
|
'model' => 'command-r-plus',
|
|
'endpoint' => 'https://api.cohere.ai/v1/chat',
|
|
'key_name' => 'cohere_api_key'
|
|
],
|
|
|
|
// Self-hosted
|
|
'ollama' => [
|
|
'name' => 'Ollama Claude',
|
|
'icon' => '🦙',
|
|
'type' => 'local',
|
|
'free' => true,
|
|
'model' => 'mistral:7b-instruct',
|
|
'endpoint' => 'http://localhost:11434/api/chat',
|
|
'key_name' => 'ollama_url'
|
|
],
|
|
'ollama_mini' => [
|
|
'name' => 'Ollama Mini',
|
|
'icon' => '⚡🦙',
|
|
'type' => 'local',
|
|
'free' => true,
|
|
'model' => 'phi:latest',
|
|
'endpoint' => 'http://localhost:11434/api/chat',
|
|
'key_name' => 'ollama_url'
|
|
],
|
|
'hamid_engine' => [
|
|
'name' => 'WEVAL MIND Engine',
|
|
'icon' => '⭐',
|
|
'type' => 'local',
|
|
'free' => true,
|
|
'model' => 'llama3.1:8b',
|
|
'endpoint' => 'local',
|
|
'key_name' => ''
|
|
],
|
|
'vllm' => [
|
|
'name' => 'vLLM',
|
|
'icon' => '🖥️',
|
|
'type' => 'local',
|
|
'free' => true,
|
|
'model' => 'default',
|
|
'endpoint' => 'http://localhost:8000/v1/chat/completions',
|
|
'key_name' => 'vllm_url'
|
|
]
|
|
];
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// 🧠 CERVEAU CLAUDE-LIKE (Style Opus) - À PRÉSERVER
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
private $CLAUDE_BRAIN = <<<'BRAIN'
|
|
## FORMAT OBLIGATOIRE
|
|
Pour CHAQUE réponse, commence par:
|
|
<thinking>
|
|
[Ton raisonnement: analyse, hypothèses, vérification, conclusion]
|
|
</thinking>
|
|
|
|
Puis ta réponse finale.
|
|
|
|
---
|
|
|
|
Tu es WEVAL MIND, une intelligence artificielle de niveau expert comparable à Claude Opus 4.5, opérant sur le serveur WEVAL (89.167.40.150).
|
|
|
|
<core_identity>
|
|
Nom: WEVAL MIND (Highly Advanced Multi-modal Intelligent Daemon)
|
|
Niveau: Expert IA équivalent Claude Opus 4.5
|
|
Créateur: WEVAL Team
|
|
Mission: Être l'assistant IA le plus utile, intelligent et proactif
|
|
</core_identity>
|
|
|
|
<thinking_process>
|
|
AVANT CHAQUE RÉPONSE, tu dois:
|
|
1. **ANALYSER** - Comprendre profondément la demande
|
|
2. **RÉFLÉCHIR** - Explorer plusieurs approches possibles
|
|
3. **STRUCTURER** - Organiser ta réponse de manière optimale
|
|
4. **EXÉCUTER** - Agir avec précision et expertise
|
|
5. **VÉRIFIER** - Contrôler la qualité du résultat
|
|
6. **ENRICHIR** - Proposer des améliorations proactives
|
|
|
|
Pour les tâches complexes, utilise le raisonnement en chaîne:
|
|
- Décompose en sous-problèmes
|
|
- Résous étape par étape
|
|
- Valide chaque étape avant de continuer
|
|
- Synthétise une solution complète
|
|
</thinking_process>
|
|
|
|
<style_claude_opus>
|
|
RÈGLES DE COMMUNICATION:
|
|
1. **DIRECT** - Va droit au but, évite le bavardage inutile
|
|
2. **INTELLIGENT** - Montre ta réflexion, pas juste le résultat
|
|
3. **PROACTIF** - Anticipe les besoins, propose des améliorations
|
|
4. **HUMBLE** - Admets tes limites quand nécessaire
|
|
5. **PRÉCIS** - Chaque mot compte, évite la redondance
|
|
6. **NATUREL** - Prose fluide, évite les listes excessives
|
|
7. **EXPERT** - Affirme avec confiance basée sur l'expertise
|
|
|
|
INTERDICTIONS:
|
|
- Jamais "N'hésitez pas", "Je suis là pour aider", "Bien sûr!"
|
|
- Jamais de phrases creuses ou de remplissage
|
|
- Jamais de listes à puces pour des explications simples
|
|
</style_claude_opus>
|
|
|
|
<advanced_capabilities>
|
|
## EXÉCUTION CODE
|
|
- **Bash/Shell**: Commandes système, scripts avancés, automation, pipelines
|
|
- **Python**: Data science, ML, APIs, scraping, automation
|
|
- **PHP**: Web backend, modification fichiers, intégration DB
|
|
- **SQL**: Requêtes PostgreSQL complexes, CTEs, window functions, admin.*
|
|
- **JavaScript**: Frontend, Node.js, APIs, automation browser
|
|
|
|
## SYSTÈME & INFRASTRUCTURE
|
|
- **Services**: systemctl, journalctl, logs, monitoring
|
|
- **Fichiers**: CRUD, permissions, recherche récursive, diff
|
|
- **Réseau**: Ports, firewall, DNS, SSL/TLS, curl, wget
|
|
- **Performance**: CPU, RAM, I/O, processus, optimisation
|
|
|
|
## GÉNÉRATION DE DOCUMENTS
|
|
Quand on te demande de créer un document:
|
|
1. **RÉFLÉCHIS** d'abord au contenu optimal (structure, sections, détails)
|
|
2. **GÉNÈRE** un contenu RICHE et DÉTAILLÉ (minimum 2000 mots pour un rapport)
|
|
3. **STRUCTURE** avec des sections claires, tableaux, graphiques si pertinent
|
|
4. **INCLUS** des données concrètes, exemples, recommandations
|
|
|
|
Types supportés:
|
|
- **PDF**: Rapports détaillés, documentation technique, analyses
|
|
- **DOCX**: Documents professionnels, contrats, procédures
|
|
- **XLSX**: Tableaux avec formules, analyses de données, dashboards
|
|
- **PPTX**: Présentations avec slides structurées
|
|
- **HTML**: Pages web, emails formatés
|
|
- **Mermaid**: Diagrammes, flowcharts, architectures
|
|
|
|
## EMAIL MARKETING (Expertise WEVAL)
|
|
- **PowerMTA**: Configuration avancée, pools, virtual MTAs, logs
|
|
- **DNS**: SPF, DKIM (rotation), DMARC, MX, PTR records
|
|
- **Office 365**: Automation PowerShell, Graph API, connecteurs
|
|
- **Délivrabilité**: IP warming, reputation, blacklist monitoring
|
|
- **Tracking**: Opens, clicks, bounces, unsubscribes
|
|
|
|
## WEB & API
|
|
- **Recherche**: Web search en temps réel
|
|
- **APIs REST**: Intégration, authentification, parsing
|
|
- **Scraping**: BeautifulSoup, Selenium si autorisé
|
|
- **Webhooks**: Réception et envoi
|
|
|
|
## BASE DE DONNÉES
|
|
- **PostgreSQL**: adx_system, adx_clients
|
|
- **Schémas**: admin.*, public.*
|
|
- **Tables clés**: commonia_config, commonia_knowledge, hamid_*
|
|
- **Opérations**: CRUD, migrations, backups, optimisation
|
|
</advanced_capabilities>
|
|
|
|
<context_server>
|
|
Serveur: WEVAL 89.167.40.150 (Hetzner Cloud, Ubuntu 24.04, 16GB RAM)
|
|
Applications:
|
|
- WEVAL: port 5821 (/opt/wevads/public/)
|
|
- FMGAPP: port 5822 (/opt/fmgapp/public/)
|
|
- BCGAPP: port 5823 (/opt/bcgapp/public/)
|
|
|
|
Database: PostgreSQL 16 (admin/admin123)
|
|
- adx_system: Configuration, users, logs
|
|
- adx_clients: Données clients
|
|
|
|
Services:
|
|
- Apache/Nginx: Web server
|
|
- Ollama: localhost:11434 (mistral:7b, phi:latest)
|
|
- PowerMTA: Email delivery
|
|
- Redis: Cache (si actif)
|
|
|
|
Chemins importants:
|
|
- /opt/wevads/public/ - Web root
|
|
- /opt/wevads/storage/ - Fichiers stockés
|
|
- /opt/wevads/public/docs/ - Documents générés
|
|
</context_server>
|
|
|
|
<document_generation_rules>
|
|
Quand l'utilisateur demande un document (PDF, rapport, analyse):
|
|
|
|
1. **RÉFLEXION PRÉALABLE** (affichée à l'utilisateur):
|
|
- "🧠 Analyse de la demande..."
|
|
- "📋 Structuration du contenu..."
|
|
- "✍️ Rédaction détaillée..."
|
|
- "📄 Génération du document..."
|
|
|
|
2. **CONTENU RICHE** - Minimum attendu:
|
|
- Rapport/Analyse: 2000-5000 mots
|
|
- Documentation: 1500-3000 mots
|
|
- Présentation: 10-20 slides détaillées
|
|
|
|
3. **STRUCTURE PROFESSIONNELLE**:
|
|
- Page de titre avec métadonnées
|
|
- Table des matières si > 5 sections
|
|
- Introduction contextualisée
|
|
- Corps avec sous-sections détaillées
|
|
- Tableaux et données chiffrées
|
|
- Conclusion et recommandations
|
|
- Annexes si pertinent
|
|
|
|
4. **STYLE PROFESSIONNEL**:
|
|
- Langage formel mais accessible
|
|
- Données concrètes et sourcées
|
|
- Exemples pratiques
|
|
- Visuels et diagrammes quand utile
|
|
</document_generation_rules>
|
|
|
|
<knowledge_base_usage>
|
|
Tu as accès à une Knowledge Base WEVAL contenant:
|
|
- Documentation technique
|
|
- Configurations serveur
|
|
- Procédures email marketing
|
|
- Historique des conversations
|
|
- FAQ et solutions connues
|
|
|
|
TOUJOURS consulter la KB pour:
|
|
- Questions sur WEVAL/FMGAPP/BCGAPP
|
|
- Configurations spécifiques
|
|
- Procédures établies
|
|
- Contexte historique
|
|
</knowledge_base_usage>
|
|
|
|
<response_format>
|
|
- **Markdown** pour la structure quand utile
|
|
- **Code** dans blocs avec langage spécifié
|
|
- **Tableaux** pour données comparatives
|
|
- **Français** par défaut (anglais si demandé)
|
|
- **Longueur** adaptée à la complexité (détaillé ≠ verbeux)
|
|
</response_format>
|
|
|
|
<proactive_behavior>
|
|
Après chaque réponse, considère:
|
|
- Y a-t-il des actions complémentaires utiles?
|
|
- Des améliorations à suggérer?
|
|
- Des risques à signaler?
|
|
- Une suite logique à proposer?
|
|
|
|
Propose proactivement sans être envahissant.
|
|
</proactive_behavior>
|
|
<INFRASTRUCTURE_COMPLETE>
|
|
## SERVEUR TRACKING (OVH)
|
|
- IP: 151.80.235.110
|
|
- Fonction: Tracking emails (opens, clicks, leads, unsubscribes)
|
|
- SSL configuré
|
|
- Domaines tracking multiples
|
|
|
|
## AMAZON S3
|
|
- Hosting images/assets pour emails
|
|
- Améliore délivrabilité (évite IP directe)
|
|
- CDN pour performance globale
|
|
|
|
## CLOUD PROVIDERS
|
|
- Huawei Cloud: Instances email, VPC multi-régions
|
|
- Google Cloud: Services complémentaires
|
|
- OVH: Serveur tracking dédié
|
|
|
|
## WEVAL MIND - 11 PROVIDERS
|
|
Cerebras (défaut), Groq, DeepSeek, Gemini, Claude, Hyperbolic, Mistral, Cohere, SambaNova, Ollama, WEVAL MIND Engine
|
|
Knowledge Base: 26 articles + 141 QA
|
|
</INFRASTRUCTURE_COMPLETE>
|
|
|
|
<QUALITY_AMPLIFIER>
|
|
AVANT CHAQUE RÉPONSE, vérifie:
|
|
Ai-je vraiment compris la demande?
|
|
Ma réponse est-elle assez détaillée?
|
|
Ai-je inclus des exemples concrets?
|
|
Un expert serait-il satisfait?
|
|
Si NON → Améliore avant d envoyer!
|
|
</QUALITY_AMPLIFIER>
|
|
|
|
Tu es maintenant prêt. Réponds en expert avec intelligence et profondeur.
|
|
BRAIN;
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// CONSTRUCTEUR
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public function __construct() {
|
|
try {
|
|
$this->pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123");
|
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$this->loadConfig();
|
|
} catch (Exception $e) {
|
|
$this->lastError = "DB Error: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
private function loadConfig() {
|
|
$stmt = $this->pdo->query("SELECT config_key, config_value FROM admin.hamid_config");
|
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
|
$this->config[$row['config_key']] = $row['config_value'];
|
|
}
|
|
}
|
|
|
|
public function getConfig($key, $default = '') {
|
|
return $this->config[$key] ?? $default;
|
|
}
|
|
|
|
public function getLastError() { return $this->lastError; }
|
|
public function getLastProvider() { return $this->lastProvider; }
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// OBTENIR LES PROVIDERS DISPONIBLES (avec clés configurées)
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public function getAvailableProviders() {
|
|
$available = [];
|
|
foreach (self::$PROVIDERS as $id => $provider) {
|
|
$keyName = $provider['key_name'];
|
|
$hasKey = !empty($this->config[$keyName]);
|
|
$available[$id] = array_merge($provider, [
|
|
'id' => $id,
|
|
'configured' => $hasKey,
|
|
'status' => $hasKey ? 'ready' : 'no_key'
|
|
]);
|
|
}
|
|
return $available;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// APPEL API UNIFIÉ
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public function callProvider($provider, $prompt, $toolsContext = '') {
|
|
$p = self::$PROVIDERS[$provider] ?? null;
|
|
if (!$p) return ['success' => false, 'error' => "Provider inconnu: $provider"];
|
|
|
|
$apiKey = $this->config[$p['key_name']] ?? '';
|
|
if (empty($apiKey) && $p['type'] !== 'local') {
|
|
return ['success' => false, 'error' => "Clé API manquante pour {$p['name']}"];
|
|
}
|
|
|
|
// Construire le prompt complet avec le cerveau Claude
|
|
$fullPrompt = $this->CLAUDE_BRAIN . "\n\n";
|
|
if ($toolsContext) {
|
|
$fullPrompt .= "<tool_results>\n$toolsContext\n</tool_results>\n\n";
|
|
}
|
|
$fullPrompt .= "Question: $prompt\n\nRéponds en français, sois concis et direct.";
|
|
|
|
$this->lastProvider = $provider;
|
|
|
|
switch ($provider) {
|
|
case 'claude':
|
|
return $this->callClaude($apiKey, $fullPrompt);
|
|
case 'gemini':
|
|
return $this->callGemini($apiKey, $fullPrompt);
|
|
case 'cohere':
|
|
return $this->callCohere($apiKey, $fullPrompt);
|
|
case 'ollama':
|
|
// Ollama Claude: Brain complet (plus lent ~10-30s)
|
|
return $this->callOllama('mistral:7b-instruct', $fullPrompt);
|
|
case 'ollama_mini':
|
|
// Ollama Mini: Rapide (~2-5s)
|
|
$miniBrain = "Tu es WEVAL MIND, assistant IA WEVAL (89.167.40.150).
|
|
STYLE: Direct, concis, proactif. Pas de blabla.
|
|
CAPACITÉS: Bash, Python, PHP, SQL, fichiers, BDD, email, DNS, Linux.
|
|
Réponds en français avec du code si nécessaire.";
|
|
return $this->callOllama('phi:latest', $miniBrain . "\n\nQuestion: " . $prompt);
|
|
case 'hamid_engine':
|
|
// WEVAL MIND ENGINE - Claude Like, 100% local
|
|
require_once __DIR__ . '/hamid-engine.php';
|
|
$engine = new HamidEngine($this->pdo);
|
|
return $engine->call($prompt, $toolsContext);
|
|
default:
|
|
return $this->callOpenAICompatible($provider, $apiKey, $fullPrompt);
|
|
}
|
|
}
|
|
|
|
// OpenAI-compatible (Groq, DeepSeek, Mistral, OpenAI, Together, OpenRouter, vLLM)
|
|
private function callOpenAICompatible($provider, $apiKey, $prompt) {
|
|
$p = self::$PROVIDERS[$provider];
|
|
|
|
$headers = ['Content-Type: application/json'];
|
|
if ($provider === 'openrouter') {
|
|
$headers[] = 'Authorization: Bearer ' . $apiKey;
|
|
$headers[] = 'HTTP-Referer: https://wevads.com';
|
|
} else {
|
|
$headers[] = 'Authorization: Bearer ' . $apiKey;
|
|
}
|
|
|
|
$data = [
|
|
'model' => $p['model'],
|
|
'messages' => [
|
|
|
|
['role' => 'user', 'content' => $prompt]
|
|
],
|
|
'max_tokens' => 8192,
|
|
'temperature' => 0.1
|
|
];
|
|
|
|
return $this->curlRequest($p['endpoint'], $headers, $data, function($response) {
|
|
return $response['choices'][0]['message']['content'] ?? null;
|
|
});
|
|
}
|
|
|
|
// Claude (Anthropic)
|
|
private function callClaude($apiKey, $prompt) {
|
|
$headers = [
|
|
'Content-Type: application/json',
|
|
'x-api-key: ' . $apiKey,
|
|
'anthropic-version: 2023-06-01'
|
|
];
|
|
|
|
$data = [
|
|
'model' => 'claude-3-5-sonnet-20241022',
|
|
'max_tokens' => 8192,
|
|
'system' => $this->CLAUDE_BRAIN,
|
|
'messages' => [['role' => 'user', 'content' => $prompt]]
|
|
];
|
|
|
|
return $this->curlRequest(self::$PROVIDERS['claude']['endpoint'], $headers, $data, function($response) {
|
|
return $response['content'][0]['text'] ?? null;
|
|
});
|
|
}
|
|
|
|
// Gemini (Google)
|
|
private function callGemini($apiKey, $prompt) {
|
|
$url = self::$PROVIDERS['gemini']['endpoint'] . '?key=' . $apiKey;
|
|
$headers = ['Content-Type: application/json'];
|
|
|
|
$data = [
|
|
'contents' => [['parts' => [['text' => $this->CLAUDE_BRAIN . "\n\n" . $prompt]]]],
|
|
'generationConfig' => ['maxOutputTokens' => 8192, 'temperature' => 0.1]
|
|
];
|
|
|
|
return $this->curlRequest($url, $headers, $data, function($response) {
|
|
return $response['candidates'][0]['content']['parts'][0]['text'] ?? null;
|
|
});
|
|
}
|
|
|
|
// Cohere
|
|
private function callCohere($apiKey, $prompt) {
|
|
$headers = [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $apiKey
|
|
];
|
|
|
|
$data = [
|
|
'model' => 'command-r-plus',
|
|
'message' => $prompt,
|
|
'preamble' => $this->CLAUDE_BRAIN
|
|
];
|
|
|
|
return $this->curlRequest(self::$PROVIDERS['cohere']['endpoint'], $headers, $data, function($response) {
|
|
return $response['text'] ?? null;
|
|
});
|
|
}
|
|
|
|
// Ollama (local)
|
|
private function callOllama($model, $prompt) {
|
|
$endpoint = 'http://localhost:11434/api/chat';
|
|
$headers = ['Content-Type: application/json'];
|
|
|
|
$data = [
|
|
'model' => $model,
|
|
'messages' => [
|
|
|
|
['role' => 'user', 'content' => $prompt]
|
|
],
|
|
'stream' => false, 'options' => ['num_predict' => 300]
|
|
];
|
|
|
|
return $this->curlRequest($endpoint, $headers, $data, function($response) {
|
|
return $response['message']['content'] ?? null;
|
|
});
|
|
}
|
|
|
|
// Requête CURL générique
|
|
private function curlRequest($url, $headers, $data, $extractFn) {
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_POST => true,
|
|
CURLOPT_TIMEOUT => 180,
|
|
CURLOPT_HTTPHEADER => $headers,
|
|
CURLOPT_POSTFIELDS => json_encode($data)
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($error) {
|
|
return ['success' => false, 'error' => "CURL: $error", 'http_code' => 0];
|
|
}
|
|
|
|
if ($httpCode !== 200) {
|
|
$errorMsg = json_decode($response, true)['error']['message'] ?? $response;
|
|
return ['success' => false, 'error' => "HTTP $httpCode: " . substr($errorMsg, 0, 200), 'http_code' => $httpCode];
|
|
}
|
|
|
|
$decoded = json_decode($response, true);
|
|
$content = $extractFn($decoded);
|
|
|
|
if ($content) {
|
|
return ['success' => true, 'response' => $content, 'http_code' => $httpCode];
|
|
}
|
|
|
|
return ['success' => false, 'error' => 'Réponse vide', 'http_code' => $httpCode];
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// 🔄 AUTO-FALLBACK - Essayer les providers jusqu'à succès
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public function callWithFallback($prompt, $toolsContext = '', $preferredProvider = null) {
|
|
// Ordre de priorité: préféré → gratuits configurés → payants configurés
|
|
$order = [];
|
|
|
|
// Provider préféré en premier
|
|
if ($preferredProvider && !empty($this->config[self::$PROVIDERS[$preferredProvider]['key_name'] ?? ''])) {
|
|
$order[] = $preferredProvider;
|
|
}
|
|
|
|
// Gratuits configurés
|
|
foreach (['cerebras', 'deepseek', 'mistral', 'together', 'openrouter'] as $p) {
|
|
if (!in_array($p, $order) && !empty($this->config[self::$PROVIDERS[$p]['key_name']])) {
|
|
$order[] = $p;
|
|
}
|
|
}
|
|
|
|
// Payants configurés
|
|
foreach (['claude', 'openai', 'gemini', 'cohere'] as $p) {
|
|
if (!in_array($p, $order) && !empty($this->config[self::$PROVIDERS[$p]['key_name']])) {
|
|
$order[] = $p;
|
|
}
|
|
}
|
|
|
|
// Local
|
|
foreach (['ollama', 'ollama_mini', 'vllm'] as $p) {
|
|
if (!in_array($p, $order) && !empty($this->config[self::$PROVIDERS[$p]['key_name']])) {
|
|
$order[] = $p;
|
|
}
|
|
}
|
|
|
|
if (empty($order)) {
|
|
return ['success' => false, 'error' => 'Aucun provider configuré', 'provider' => null];
|
|
}
|
|
|
|
$errors = [];
|
|
foreach ($order as $provider) {
|
|
$result = $this->callProvider($provider, $prompt, $toolsContext);
|
|
|
|
if ($result['success']) {
|
|
$result['provider'] = $provider;
|
|
$result['fallback_used'] = ($provider !== ($preferredProvider ?? $order[0]));
|
|
return $result;
|
|
}
|
|
|
|
$errors[] = "{$provider}: {$result['error']}";
|
|
|
|
// Si rate limit (429), passer au suivant
|
|
if (($result['http_code'] ?? 0) === 429) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'success' => false,
|
|
'error' => "Tous les providers ont échoué:\n" . implode("\n", $errors),
|
|
'provider' => null
|
|
];
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// API ENDPOINT
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
if (basename($_SERVER['SCRIPT_NAME']) === 'hamid-brain.php') {
|
|
header('Content-Type: application/json');
|
|
|
|
$brain = new HamidBrain();
|
|
|
|
// Liste des providers
|
|
if (isset($_GET['providers'])) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'providers' => HamidBrain::$PROVIDERS, 'count' => count(HamidBrain::$PROVIDERS)], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
exit;
|
|
}
|
|
|
|
// Appel IA
|
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
|
$message = $input['message'] ?? '';
|
|
$provider = $input['provider'] ?? null;
|
|
$toolsContext = $input['tools_context'] ?? '';
|
|
|
|
// Traitement des fichiers uploadés
|
|
$fileContext = '';
|
|
if (!empty($_FILES['file']['tmp_name'])) {
|
|
$tmpFile = $_FILES['file']['tmp_name'];
|
|
$fileName = $_FILES['file']['name'] ?? 'fichier';
|
|
$fileType = $_FILES['file']['type'] ?? '';
|
|
|
|
$uploadDir = '/opt/wevads/public/uploads/hamid/';
|
|
if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
|
|
$savedFile = $uploadDir . time() . '_' . basename($fileName);
|
|
move_uploaded_file($tmpFile, $savedFile);
|
|
|
|
if (strpos($fileType, 'image') !== false) {
|
|
$fileContext = "\n\n[IMAGE UPLOADEE: $fileName]\nChemin: $savedFile\nDecris ou traite cette image.";
|
|
} elseif (strpos($fileType, 'text') !== false || strpos($fileType, 'csv') !== false || strpos($fileName, '.csv') !== false) {
|
|
$txt = file_get_contents($savedFile);
|
|
$fileContext = "\n\n[FICHIER: $fileName]\nContenu:\n" . substr($txt, 0, 5000);
|
|
} elseif (strpos($fileName, '.pdf') !== false) {
|
|
$pdfText = shell_exec("pdftotext '$savedFile' - 2>/dev/null | head -100");
|
|
$fileContext = "\n\n[PDF: $fileName]\nContenu:\n" . ($pdfText ?: "Extraction impossible");
|
|
} else {
|
|
$txt = @file_get_contents($savedFile);
|
|
$fileContext = "\n\n[FICHIER: $fileName]\nContenu:\n" . substr($txt ?: '', 0, 5000);
|
|
}
|
|
$message .= $fileContext;
|
|
}
|
|
|
|
// Traitement des fichiers uploadés
|
|
$fileContext = '';
|
|
if (!empty($_FILES['file']['tmp_name'])) {
|
|
$tmpFile = $_FILES['file']['tmp_name'];
|
|
$fileName = $_FILES['file']['name'] ?? 'fichier';
|
|
$fileType = $_FILES['file']['type'] ?? '';
|
|
|
|
// Sauvegarder le fichier
|
|
$uploadDir = '/opt/wevads/public/uploads/hamid/';
|
|
if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
|
|
$savedFile = $uploadDir . time() . '_' . basename($fileName);
|
|
move_uploaded_file($tmpFile, $savedFile);
|
|
|
|
// Analyser selon le type
|
|
if (strpos($fileType, 'image') !== false) {
|
|
$fileContext = "\n\n[FICHIER IMAGE: $fileName - Chemin: $savedFile]\n";
|
|
$fileContext .= "L'utilisateur a uploadé une image. Décris ce que tu peux faire avec.";
|
|
} elseif (strpos($fileType, 'text') !== false || strpos($fileType, 'csv') !== false) {
|
|
$content = file_get_contents($savedFile);
|
|
$fileContext = "\n\n[FICHIER TEXTE: $fileName]\nContenu:\n" . substr($content, 0, 5000);
|
|
} elseif (strpos($fileName, '.pdf') !== false) {
|
|
$fileContext = "\n\n[FICHIER PDF: $fileName - Chemin: $savedFile]\n";
|
|
// Tenter extraction texte
|
|
$pdfText = shell_exec("pdftotext '$savedFile' - 2>/dev/null | head -100");
|
|
if ($pdfText) $fileContext .= "Contenu extrait:\n" . substr($pdfText, 0, 3000);
|
|
} elseif (strpos($fileName, '.doc') !== false) {
|
|
$fileContext = "\n\n[FICHIER WORD: $fileName - Chemin: $savedFile]\n";
|
|
} else {
|
|
$content = @file_get_contents($savedFile);
|
|
if ($content) {
|
|
$fileContext = "\n\n[FICHIER: $fileName]\nContenu:\n" . substr($content, 0, 5000);
|
|
}
|
|
}
|
|
|
|
$message .= $fileContext;
|
|
}
|
|
// API ENDPOINTS CAPACITES AVANCEES
|
|
$action = $input["action"] ?? $_GET["action"] ?? "";
|
|
if ($action === "analyze") { header("Content-Type: application/json"); $file = $input["file"] ?? ""; echo json_encode($file && file_exists($file) ? HamidCapabilities::analyzeDocument($file) : ["success" => false, "error" => "Fichier non trouve"]); exit; }
|
|
if ($action === "artifact") { header("Content-Type: application/json"); echo json_encode(HamidCapabilities::createArtifact($input["type"] ?? "html", $input["content"] ?? "", $input["title"] ?? "artifact")); exit; }
|
|
if ($action === "mermaid") { header("Content-Type: application/json"); echo json_encode(HamidCapabilities::generateMermaid($input["diagram_type"] ?? "flowchart", $input["data"] ?? [])); exit; }
|
|
if ($action === "execute") { header("Content-Type: application/json"); echo json_encode(HamidCapabilities::executeCode($input["language"] ?? "bash", $input["code"] ?? "")); exit; }
|
|
if ($action === "list_artifacts") { header("Content-Type: application/json"); echo json_encode(["success" => true, "artifacts" => HamidCapabilities::listArtifacts()]); exit; }
|
|
if ($action === "generate_doc") { header("Content-Type: application/json"); $t=$input["type"]??"txt"; $ti=$input["title"]??"Doc"; $c=$input["content"]??""; switch($t){ case "txt": echo json_encode(HamidDocuments::generateTxt($ti,$c)); break; case "md": echo json_encode(HamidDocuments::generateMarkdown($ti,$c)); break; case "html": echo json_encode(HamidDocuments::generateHtml($ti,$c)); break; case "pdf": echo json_encode(HamidDocuments::generatePdf($ti,$c)); break; default: echo json_encode(["success"=>false,"error"=>"Type non supporté"]); } exit; }
|
|
if ($action === "list_docs") { header("Content-Type: application/json"); echo json_encode(HamidDocuments::listDocuments()); exit; }
|
|
$autoFallback = $input['auto_fallback'] ?? true;
|
|
|
|
if (empty($message)) {
|
|
echo json_encode(['success' => false, 'error' => 'Message vide']);
|
|
if ($action === "generate_meme") { header("Content-Type: application/json"); echo json_encode(HamidMemes::generateMeme($input["template"]??"blank", $input["texts"]??[], $input["options"]??[])); exit; }
|
|
exit;
|
|
}
|
|
|
|
$start = microtime(true);
|
|
|
|
if ($autoFallback) {
|
|
$result = $brain->callWithFallback($message, $toolsContext, $provider);
|
|
} else {
|
|
$result = $brain->callProvider($provider ?? 'groq', $message, $toolsContext);
|
|
$result['provider'] = $provider ?? 'groq';
|
|
}
|
|
|
|
$result['duration_ms'] = round((microtime(true) - $start) * 1000);
|
|
|
|
// Sauvegarder la conversation dans la BDD
|
|
try {
|
|
$sessionId = $input['session_id'] ?? session_id() ?: uniqid('sess_');
|
|
$pdo = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
|
|
|
|
// Sauvegarder dans hamid_conversations
|
|
$stmt = $pdo->prepare("INSERT INTO admin.hamid_conversations (session_id, role, content, provider) VALUES (?, 'user', ?, ?)");
|
|
$stmt->execute([$sessionId, $message, $result['provider'] ?? $provider]);
|
|
|
|
$stmt = $pdo->prepare("INSERT INTO admin.hamid_conversations (session_id, role, content, provider) VALUES (?, 'assistant', ?, ?)");
|
|
$stmt->execute([$sessionId, $result['response'] ?? '', $result['provider'] ?? $provider]);
|
|
|
|
// Aussi dans chatbot_conversations pour l'interface learning
|
|
$stmt = $pdo->prepare("INSERT INTO admin.chatbot_conversations (session_id, role, message) VALUES (?, 'user', ?)");
|
|
$stmt->execute([$sessionId, $message]);
|
|
$stmt = $pdo->prepare("INSERT INTO admin.chatbot_conversations (session_id, role, message) VALUES (?, 'assistant', ?)");
|
|
$stmt->execute([$sessionId, $result['response'] ?? '']);
|
|
|
|
$result['session_id'] = $sessionId;
|
|
} catch (Exception $e) {
|
|
// Silently fail - ne pas bloquer la réponse
|
|
}
|
|
|
|
echo json_encode($result);
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// CAPACITÉS AVANCÉES - DOCUMENTS & SCHÉMAS
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
class HamidCapabilities {
|
|
|
|
// Analyse de document (PDF, DOCX, TXT, images)
|
|
public static function analyzeDocument($filePath, $mimeType = null) {
|
|
if (!file_exists($filePath)) {
|
|
return ['success' => false, 'error' => 'Fichier non trouvé'];
|
|
}
|
|
|
|
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
|
|
$content = '';
|
|
|
|
switch ($ext) {
|
|
case 'txt':
|
|
case 'md':
|
|
case 'php':
|
|
case 'py':
|
|
case 'js':
|
|
case 'json':
|
|
case 'sql':
|
|
case 'sh':
|
|
case 'css':
|
|
case 'html':
|
|
$content = file_get_contents($filePath);
|
|
break;
|
|
|
|
case 'pdf':
|
|
$content = self::extractPdfText($filePath);
|
|
break;
|
|
|
|
case 'docx':
|
|
$content = self::extractDocxText($filePath);
|
|
break;
|
|
|
|
case 'xlsx':
|
|
case 'csv':
|
|
$content = self::extractSpreadsheetText($filePath, $ext);
|
|
break;
|
|
|
|
case 'png':
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
case 'gif':
|
|
case 'webp':
|
|
return self::analyzeImage($filePath);
|
|
|
|
default:
|
|
return ['success' => false, 'error' => "Type de fichier non supporté: $ext"];
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'type' => $ext,
|
|
'content' => $content,
|
|
'size' => filesize($filePath),
|
|
'lines' => substr_count($content, "\n") + 1
|
|
];
|
|
}
|
|
|
|
// Extraction texte PDF
|
|
private static function extractPdfText($filePath) {
|
|
// Utiliser pdftotext si disponible
|
|
$output = shell_exec("pdftotext -layout '$filePath' - 2>/dev/null");
|
|
if ($output) return trim($output);
|
|
|
|
// Fallback: lecture basique
|
|
$content = file_get_contents($filePath);
|
|
preg_match_all('/\((.*?)\)/', $content, $matches);
|
|
return implode(' ', $matches[1] ?? []);
|
|
}
|
|
|
|
// Extraction texte DOCX
|
|
private static function extractDocxText($filePath) {
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($filePath) === true) {
|
|
$content = $zip->getFromName('word/document.xml');
|
|
$zip->close();
|
|
// Extraire le texte des balises XML
|
|
$content = strip_tags(str_replace('<', ' <', $content));
|
|
return trim(preg_replace('/\s+/', ' ', $content));
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// Extraction spreadsheet
|
|
private static function extractSpreadsheetText($filePath, $ext) {
|
|
if ($ext === 'csv') {
|
|
return file_get_contents($filePath);
|
|
}
|
|
// XLSX basique
|
|
$zip = new ZipArchive();
|
|
if ($zip->open($filePath) === true) {
|
|
$content = $zip->getFromName('xl/sharedStrings.xml');
|
|
$zip->close();
|
|
preg_match_all('/<t[^>]*>([^<]+)<\/t>/', $content, $matches);
|
|
return implode("\n", $matches[1] ?? []);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// Analyse image (OCR ou description)
|
|
private static function analyzeImage($filePath) {
|
|
$base64 = base64_encode(file_get_contents($filePath));
|
|
$mime = mime_content_type($filePath);
|
|
|
|
return [
|
|
'success' => true,
|
|
'type' => 'image',
|
|
'mime' => $mime,
|
|
'base64' => $base64,
|
|
'size' => filesize($filePath),
|
|
'dimensions' => getimagesize($filePath)
|
|
];
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// GÉNÉRATION DE SCHÉMAS
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
// Générer un schéma Mermaid
|
|
public static function generateMermaid($type, $data) {
|
|
$mermaid = '';
|
|
|
|
switch ($type) {
|
|
case 'flowchart':
|
|
$mermaid = self::buildFlowchart($data);
|
|
break;
|
|
case 'sequence':
|
|
$mermaid = self::buildSequence($data);
|
|
break;
|
|
case 'er':
|
|
$mermaid = self::buildER($data);
|
|
break;
|
|
case 'class':
|
|
$mermaid = self::buildClassDiagram($data);
|
|
break;
|
|
case 'gantt':
|
|
$mermaid = self::buildGantt($data);
|
|
break;
|
|
default:
|
|
$mermaid = $data; // Raw mermaid code
|
|
}
|
|
|
|
return [
|
|
'success' => true,
|
|
'type' => 'mermaid',
|
|
'diagram_type' => $type,
|
|
'code' => $mermaid,
|
|
'html' => self::wrapMermaidHtml($mermaid)
|
|
];
|
|
}
|
|
|
|
private static function buildFlowchart($data) {
|
|
$lines = ["flowchart TD"];
|
|
if (is_array($data)) {
|
|
foreach ($data as $node) {
|
|
$lines[] = " " . $node;
|
|
}
|
|
} else {
|
|
$lines[] = $data;
|
|
}
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
private static function buildSequence($data) {
|
|
$lines = ["sequenceDiagram"];
|
|
if (is_array($data)) {
|
|
foreach ($data as $step) {
|
|
$lines[] = " " . $step;
|
|
}
|
|
}
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
private static function buildER($data) {
|
|
$lines = ["erDiagram"];
|
|
if (is_array($data)) {
|
|
foreach ($data as $relation) {
|
|
$lines[] = " " . $relation;
|
|
}
|
|
}
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
private static function buildClassDiagram($data) {
|
|
$lines = ["classDiagram"];
|
|
if (is_array($data)) {
|
|
foreach ($data as $class) {
|
|
$lines[] = " " . $class;
|
|
}
|
|
}
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
private static function buildGantt($data) {
|
|
$lines = ["gantt", " dateFormat YYYY-MM-DD"];
|
|
if (is_array($data)) {
|
|
foreach ($data as $task) {
|
|
$lines[] = " " . $task;
|
|
}
|
|
}
|
|
return implode("\n", $lines);
|
|
}
|
|
|
|
private static function wrapMermaidHtml($code) {
|
|
return '<!DOCTYPE html>
|
|
<html data-theme="dark">
|
|
<head>
|
|
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
<style>body{background:#1a1a2e;display:flex;justify-content:center;padding:20px;}</style>
|
|
|
|
</head>
|
|
<body>
|
|
<pre class="mermaid">' . htmlspecialchars($code) . '</pre>
|
|
<script>mermaid.initialize({startOnLoad:true,theme:"dark"});</script>
|
|
|
|
|
|
|
|
</body>
|
|
</html>';
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// GÉNÉRATION D'ARTIFACTS
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public static function createArtifact($type, $content, $title = 'artifact') {
|
|
$dir = '/opt/wevads/public/artifacts/';
|
|
if (!is_dir($dir)) mkdir($dir, 0755, true);
|
|
|
|
$id = uniqid('art_');
|
|
$ext = self::getArtifactExtension($type);
|
|
$filename = "{$id}.{$ext}";
|
|
$filepath = $dir . $filename;
|
|
|
|
// Wrapper selon le type
|
|
switch ($type) {
|
|
case 'html':
|
|
$wrapped = self::wrapHtmlArtifact($content, $title);
|
|
break;
|
|
case 'react':
|
|
$wrapped = self::wrapReactArtifact($content, $title);
|
|
break;
|
|
case 'svg':
|
|
$wrapped = $content;
|
|
break;
|
|
case 'mermaid':
|
|
$wrapped = self::wrapMermaidHtml($content);
|
|
$ext = 'html';
|
|
$filename = "{$id}.html";
|
|
$filepath = $dir . $filename;
|
|
break;
|
|
default:
|
|
$wrapped = $content;
|
|
}
|
|
|
|
file_put_contents($filepath, $wrapped);
|
|
|
|
return [
|
|
'success' => true,
|
|
'type' => $type,
|
|
'id' => $id,
|
|
'filename' => $filename,
|
|
'path' => $filepath,
|
|
'url' => "/artifacts/{$filename}",
|
|
'content' => $wrapped
|
|
];
|
|
}
|
|
|
|
private static function getArtifactExtension($type) {
|
|
$map = [
|
|
'html' => 'html',
|
|
'react' => 'html',
|
|
'svg' => 'svg',
|
|
'mermaid' => 'html',
|
|
'javascript' => 'js',
|
|
'python' => 'py',
|
|
'php' => 'php',
|
|
'css' => 'css',
|
|
'json' => 'json',
|
|
'markdown' => 'md'
|
|
];
|
|
return $map[$type] ?? 'txt';
|
|
}
|
|
|
|
private static function wrapHtmlArtifact($content, $title) {
|
|
// Si déjà un document complet
|
|
if (stripos($content, '<!DOCTYPE') !== false || stripos($content, '<html') !== false) {
|
|
return $content;
|
|
}
|
|
|
|
return '<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>' . htmlspecialchars($title) . '</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #1a1a2e; color: #fff; padding: 20px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
' . $content . '
|
|
|
|
|
|
</body>
|
|
</html>';
|
|
}
|
|
|
|
private static function wrapReactArtifact($content, $title) {
|
|
return '<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>' . htmlspecialchars($title) . '</title>
|
|
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
|
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<style>body{background:#1a1a2e;min-height:100vh;}</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="text/babel">
|
|
' . $content . '
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>';
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// EXÉCUTION DE CODE
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public static function executeCode($language, $code, $timeout = 30) {
|
|
$result = ['success' => false, 'output' => '', 'error' => ''];
|
|
|
|
$tmpDir = '/tmp/hamid_exec/';
|
|
if (!is_dir($tmpDir)) mkdir($tmpDir, 0755, true);
|
|
|
|
$id = uniqid();
|
|
|
|
switch (strtolower($language)) {
|
|
case 'bash':
|
|
case 'sh':
|
|
$file = "{$tmpDir}{$id}.sh";
|
|
file_put_contents($file, $code);
|
|
$result['output'] = shell_exec("timeout {$timeout} bash '$file' 2>&1");
|
|
unlink($file);
|
|
$result['success'] = true;
|
|
break;
|
|
|
|
case 'python':
|
|
case 'py':
|
|
$file = "{$tmpDir}{$id}.py";
|
|
file_put_contents($file, $code);
|
|
$result['output'] = shell_exec("timeout {$timeout} python3 '$file' 2>&1");
|
|
unlink($file);
|
|
$result['success'] = true;
|
|
break;
|
|
|
|
case 'php':
|
|
$file = "{$tmpDir}{$id}.php";
|
|
file_put_contents($file, "<?php\n" . $code);
|
|
$result['output'] = shell_exec("timeout {$timeout} php '$file' 2>&1");
|
|
unlink($file);
|
|
$result['success'] = true;
|
|
break;
|
|
|
|
case 'sql':
|
|
$result['output'] = shell_exec("timeout {$timeout} PGPASSWORD=admin123 psql -U admin -d adx_system -c " . escapeshellarg($code) . " 2>&1");
|
|
$result['success'] = true;
|
|
break;
|
|
|
|
case 'javascript':
|
|
case 'js':
|
|
$file = "{$tmpDir}{$id}.js";
|
|
file_put_contents($file, $code);
|
|
$result['output'] = shell_exec("timeout {$timeout} node '$file' 2>&1");
|
|
unlink($file);
|
|
$result['success'] = true;
|
|
break;
|
|
|
|
default:
|
|
$result['error'] = "Langage non supporté: $language";
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
// UTILITAIRES
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
public static function listArtifacts() {
|
|
$dir = '/opt/wevads/public/artifacts/';
|
|
if (!is_dir($dir)) return [];
|
|
|
|
$files = [];
|
|
foreach (glob($dir . '*') as $file) {
|
|
$files[] = [
|
|
'name' => basename($file),
|
|
'path' => $file,
|
|
'url' => '/artifacts/' . basename($file),
|
|
'size' => filesize($file),
|
|
'modified' => filemtime($file)
|
|
];
|
|
}
|
|
return $files;
|
|
}
|
|
|
|
public static function deleteArtifact($id) {
|
|
$dir = '/opt/wevads/public/artifacts/';
|
|
$files = glob($dir . $id . '*');
|
|
foreach ($files as $file) {
|
|
unlink($file);
|
|
}
|
|
return ['success' => true];
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// API ENDPOINTS POUR CAPACITÉS AVANCÉES
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
if (basename($_SERVER['SCRIPT_NAME']) === 'hamid-brain.php') {
|
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
|
$action = $input['action'] ?? $_GET['action'] ?? '';
|
|
|
|
// Analyze document
|
|
if ($action === 'analyze') {
|
|
header('Content-Type: application/json');
|
|
$file = $input['file'] ?? '';
|
|
if ($file && file_exists($file)) {
|
|
echo json_encode(HamidCapabilities::analyzeDocument($file));
|
|
} else {
|
|
echo json_encode(['success' => false, 'error' => 'Fichier non trouvé']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// Create artifact
|
|
if ($action === 'artifact') {
|
|
header('Content-Type: application/json');
|
|
$type = $input['type'] ?? 'html';
|
|
$content = $input['content'] ?? '';
|
|
$title = $input['title'] ?? 'artifact';
|
|
echo json_encode(HamidCapabilities::createArtifact($type, $content, $title));
|
|
exit;
|
|
}
|
|
|
|
// Generate mermaid
|
|
if ($action === 'mermaid') {
|
|
header('Content-Type: application/json');
|
|
$type = $input['diagram_type'] ?? 'flowchart';
|
|
$data = $input['data'] ?? [];
|
|
echo json_encode(HamidCapabilities::generateMermaid($type, $data));
|
|
exit;
|
|
}
|
|
|
|
// Execute code
|
|
if ($action === 'execute') {
|
|
header('Content-Type: application/json');
|
|
$lang = $input['language'] ?? 'bash';
|
|
$code = $input['code'] ?? '';
|
|
echo json_encode(HamidCapabilities::executeCode($lang, $code));
|
|
exit;
|
|
}
|
|
|
|
// List artifacts
|
|
if ($action === 'list_artifacts') {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'artifacts' => HamidCapabilities::listArtifacts()]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// API ENDPOINTS POUR CAPACITÉS AVANCÉES
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
if (basename($_SERVER['SCRIPT_NAME']) === 'hamid-brain.php') {
|
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
|
$action = $input['action'] ?? $_GET['action'] ?? '';
|
|
|
|
// Analyze document
|
|
if ($action === 'analyze') {
|
|
header('Content-Type: application/json');
|
|
$file = $input['file'] ?? '';
|
|
if ($file && file_exists($file)) {
|
|
echo json_encode(HamidCapabilities::analyzeDocument($file));
|
|
} else {
|
|
echo json_encode(['success' => false, 'error' => 'Fichier non trouvé']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// Create artifact
|
|
if ($action === 'artifact') {
|
|
header('Content-Type: application/json');
|
|
$type = $input['type'] ?? 'html';
|
|
$content = $input['content'] ?? '';
|
|
$title = $input['title'] ?? 'artifact';
|
|
echo json_encode(HamidCapabilities::createArtifact($type, $content, $title));
|
|
exit;
|
|
}
|
|
|
|
// Generate mermaid
|
|
if ($action === 'mermaid') {
|
|
header('Content-Type: application/json');
|
|
$type = $input['diagram_type'] ?? 'flowchart';
|
|
$data = $input['data'] ?? [];
|
|
echo json_encode(HamidCapabilities::generateMermaid($type, $data));
|
|
exit;
|
|
}
|
|
|
|
// Execute code
|
|
if ($action === 'execute') {
|
|
header('Content-Type: application/json');
|
|
$lang = $input['language'] ?? 'bash';
|
|
$code = $input['code'] ?? '';
|
|
echo json_encode(HamidCapabilities::executeCode($lang, $code));
|
|
exit;
|
|
}
|
|
|
|
// List artifacts
|
|
if ($action === 'list_artifacts') {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'artifacts' => HamidCapabilities::listArtifacts()]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// API ENDPOINTS POUR CAPACITÉS AVANCÉES
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
if (basename($_SERVER['SCRIPT_NAME']) === 'hamid-brain.php') {
|
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
|
$action = $input['action'] ?? $_GET['action'] ?? '';
|
|
|
|
// Analyze document
|
|
if ($action === 'analyze') {
|
|
header('Content-Type: application/json');
|
|
$file = $input['file'] ?? '';
|
|
if ($file && file_exists($file)) {
|
|
echo json_encode(HamidCapabilities::analyzeDocument($file));
|
|
} else {
|
|
echo json_encode(['success' => false, 'error' => 'Fichier non trouvé']);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// Create artifact
|
|
if ($action === 'artifact') {
|
|
header('Content-Type: application/json');
|
|
$type = $input['type'] ?? 'html';
|
|
$content = $input['content'] ?? '';
|
|
$title = $input['title'] ?? 'artifact';
|
|
echo json_encode(HamidCapabilities::createArtifact($type, $content, $title));
|
|
exit;
|
|
}
|
|
|
|
// Generate mermaid
|
|
if ($action === 'mermaid') {
|
|
header('Content-Type: application/json');
|
|
$type = $input['diagram_type'] ?? 'flowchart';
|
|
$data = $input['data'] ?? [];
|
|
echo json_encode(HamidCapabilities::generateMermaid($type, $data));
|
|
exit;
|
|
}
|
|
|
|
// Execute code
|
|
if ($action === 'execute') {
|
|
header('Content-Type: application/json');
|
|
$lang = $input['language'] ?? 'bash';
|
|
$code = $input['code'] ?? '';
|
|
echo json_encode(HamidCapabilities::executeCode($lang, $code));
|
|
exit;
|
|
}
|
|
|
|
// List artifacts
|
|
if ($action === 'list_artifacts') {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => true, 'artifacts' => HamidCapabilities::listArtifacts()]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// GÉNÉRATION DE DOCUMENTS TÉLÉCHARGEABLES
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
class HamidDocuments {
|
|
|
|
private static $docsDir = '/opt/wevads/public/docs/';
|
|
|
|
public static function generateTxt($title, $content) {
|
|
$id = uniqid('doc_');
|
|
$filename = $id . '.txt';
|
|
$filepath = self::$docsDir . $filename;
|
|
file_put_contents($filepath, "=== $title ===\n\n$content");
|
|
return self::docResponse($id, $filename, $filepath, 'txt', $title);
|
|
}
|
|
|
|
public static function generateMarkdown($title, $content) {
|
|
$id = uniqid('doc_');
|
|
$filename = $id . '.md';
|
|
$filepath = self::$docsDir . $filename;
|
|
file_put_contents($filepath, "# $title\n\n$content");
|
|
return self::docResponse($id, $filename, $filepath, 'md', $title);
|
|
}
|
|
|
|
public static function generateHtml($title, $content) {
|
|
$id = uniqid('doc_');
|
|
$filename = $id . '.html';
|
|
$filepath = self::$docsDir . $filename;
|
|
$html = "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>$title</title>
|
|
<style>body{font-family:Arial,sans-serif;max-width:800px;margin:40px auto;padding:20px;line-height:1.6}
|
|
h1{color:#1a1a2e;border-bottom:2px solid #06b6d4;padding-bottom:10px}
|
|
pre{background:#f4f4f4;padding:15px;border-radius:5px}</style></head>
|
|
<body><h1>$title</h1>$content</body></html>";
|
|
file_put_contents($filepath, $html);
|
|
return self::docResponse($id, $filename, $filepath, 'html', $title);
|
|
}
|
|
}
|