Compare commits
116 Commits
opus-sessi
...
wave-226-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ed6857e78 | ||
|
|
8d0f0ceee4 | ||
|
|
e94c263624 | ||
|
|
e824e9c03e | ||
|
|
2c9ff7c958 | ||
|
|
8a38661311 | ||
|
|
ad9d3dc376 | ||
|
|
c362e5f77e | ||
|
|
61447aca2a | ||
|
|
d3598d1184 | ||
|
|
260cc8a553 | ||
|
|
3c392a4142 | ||
|
|
bb284e4101 | ||
|
|
f3fb7283bf | ||
|
|
6fd30277fa | ||
|
|
68109fc3f2 | ||
|
|
d9859c93fa | ||
|
|
27ae771f3a | ||
|
|
5aaf0e7f0f | ||
|
|
2d7b488c46 | ||
|
|
412ff8b23b | ||
|
|
4ec7c0bb9e | ||
|
|
98618d0006 | ||
|
|
049296d1aa | ||
|
|
d98131946e | ||
|
|
a705e42253 | ||
|
|
6b25030a3c | ||
|
|
0456d672ff | ||
|
|
151ffbae63 | ||
|
|
28678e4b47 | ||
|
|
d6e82b4b86 | ||
|
|
99b9df00c0 | ||
|
|
b1d25f329d | ||
|
|
f33599517d | ||
|
|
927e3aaaa0 | ||
|
|
b1997fedd7 | ||
|
|
3e2ae4708e | ||
|
|
065a4f33b6 | ||
|
|
1d540d16be | ||
|
|
87a1c0f0bd | ||
|
|
149a5f4ce8 | ||
|
|
5d83d1643a | ||
|
|
150d0d4dc8 | ||
|
|
d402da46f0 | ||
|
|
5d4265f307 | ||
|
|
f1d91b48ff | ||
|
|
6f995f624d | ||
|
|
bd1e9568d5 | ||
|
|
8c9e214153 | ||
|
|
d5dce6ea86 | ||
|
|
e6bc3c0523 | ||
|
|
4197c5dbaf | ||
|
|
cf95bf9fae | ||
|
|
a1b0b3e36e | ||
|
|
1c1c3fe604 | ||
|
|
fda0d21ca5 | ||
|
|
ddbfde476b | ||
|
|
c2437eecfd | ||
|
|
1dbaa747e1 | ||
|
|
d0938f8944 | ||
|
|
3924d91b2b | ||
|
|
ead2dcfc4a | ||
|
|
8f8aee325a | ||
|
|
f709a64db8 | ||
|
|
601617d446 | ||
|
|
abe624d03e | ||
|
|
953bb4414f | ||
|
|
c7bd363ad7 | ||
|
|
ef96d08f0e | ||
|
|
cbd6b4a03a | ||
|
|
b7d40c7503 | ||
|
|
4199cd3ff0 | ||
|
|
b6dcdc7770 | ||
|
|
7807e3feb6 | ||
|
|
34902f4714 | ||
|
|
aaca72d969 | ||
|
|
541420e1fe | ||
|
|
eca9d344f9 | ||
|
|
ad828e1e53 | ||
|
|
d6f6b89e72 | ||
|
|
f1921776ff | ||
|
|
bc20d19b4a | ||
|
|
ef1412d144 | ||
|
|
3e35ae52c0 | ||
|
|
b23c7f2fa8 | ||
|
|
fb43bef9cc | ||
|
|
1bd5572777 | ||
|
|
b9f9afcbd6 | ||
|
|
98b153deae | ||
|
|
8805740235 | ||
|
|
44e9c6aef2 | ||
|
|
9534414da4 | ||
|
|
a44eaa78ca | ||
|
|
46305ae822 | ||
|
|
b477374a61 | ||
|
|
cd6a22911a | ||
|
|
ecf3c428be | ||
|
|
31b38ccaaa | ||
|
|
c75e9d76b4 | ||
|
|
08d170b2de | ||
|
|
147f5341e9 | ||
|
|
372ca9d069 | ||
|
|
8b8c227a78 | ||
|
|
0d91482bfd | ||
|
|
9664c70408 | ||
|
|
184aab3b80 | ||
|
|
87e388d78d | ||
|
|
9f469187a0 | ||
|
|
9e1293c3c9 | ||
|
|
82b2eabf5f | ||
|
|
ec0351df90 | ||
|
|
77773bb0d9 | ||
|
|
99d2dccbba | ||
|
|
05d106a478 | ||
|
|
30b53a14b9 | ||
|
|
bc73c1d984 |
@@ -89,8 +89,18 @@ body.light #theme-toggle::before{content:"\263D"}
|
||||
/* V142-FOOTER-STRIP: body padding to prevent footer overlap */
|
||||
body{padding-bottom:26px}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/css/wevia-portal-consistency.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="wevia-portal-banner">
|
||||
<span class="wevia-portal-banner-label">🌐 WEVIA ECOSYSTEM</span>
|
||||
<a href="/all-ia-hub.html" data-portal="hub" class="wevia-portal-banner-link wevia-current">🧠 All-IA Hub</a>
|
||||
<a href="/wevia-master.html" data-portal="master" class="wevia-portal-banner-link">🤖 WEVIA Master</a>
|
||||
<a href="/wevia-orchestrator.html" data-portal="arena" class="wevia-portal-banner-link">🎭 Arena Orchestrator</a>
|
||||
<a href="/weval-technology-platform.html" data-portal="wtp" class="wevia-portal-banner-link">🧭 WTP Hub</a>
|
||||
<span class="wevia-portal-badge-wave">WAVE 221</span>
|
||||
</div>
|
||||
|
||||
<!-- BETON-DOCTRINE-101 dual-dummy (entry point) -->
|
||||
<div id="weval-global-logout" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection"></div>
|
||||
<a id="weval-gl" href="#" style="display:none!important;visibility:hidden!important" aria-hidden="true" data-beton-101="dummy-to-block-auto-injection" tabindex="-1"></a>
|
||||
@@ -1324,5 +1334,6 @@ setInterval(refreshStats,60000);
|
||||
<span style="margin-left:auto;color:#00d4b4;font-size:9px"><a href="/wevia-unified-hub.html" style="color:inherit;text-decoration:none" title="Truth Hub">Truth →</a></span>
|
||||
</div>
|
||||
<script>(function(){var p=window.location.pathname;var pub=["/","/index.html","/wevia.html","/wevia-widget.html","/enterprise-model.html","/wevia","/login","/register.html","/agents-archi.html","/wevia-meeting-rooms.html","/director-center.html","/director-chat.html","/l99-brain.html","/agents-fleet.html","/value-streaming.html","/architecture.html","/openclaw.html","/l99-saas.html","/admin-saas.html","/agents-goodjob.html","/ai-benchmark.html","/oss-discovery.html","/paperclip.html","/agents-3d.html","/agents-alive.html","/agents-enterprise.html","/agents-hd.html","/agents-iso3d.html","/agents-sim.html","/agents-valuechain.html","/avatar-picker.html"];var isPub=pub.indexOf(p)>=0||p.indexOf("/products/")===0||p.indexOf("/solutions/")===0||p.indexOf("/blog/")===0||p.indexOf("/service/")===0||p.indexOf("/marketplace")===0||p.indexOf("/contact")===0||p.indexOf("/tarifs")===0||p.indexOf("/news")===0;if(isPub||document.getElementById("weval-gl"))return;var a=document.createElement("a");a.id="weval-gl";a.href="/logout";a.textContent="Logout";a.style.cssText="position:fixed;top:10px;right:12px;z-index:99990;padding:5px 10px;background:rgba(30,30,50,0.7);color:rgba(200,210,230,0.8);border:1px solid rgba(100,100,140,0.3);border-radius:6px;font:500 11px system-ui,sans-serif;text-decoration:none;opacity:0.6;cursor:pointer;backdrop-filter:blur(6px);transition:all .15s";a.onmouseover=function(){this.style.opacity="1";this.style.background="rgba(239,68,68,0.85)";this.style.color="white"};a.onmouseout=function(){this.style.opacity="0.6";this.style.background="rgba(30,30,50,0.7)";this.style.color="rgba(200,210,230,0.8)"};document.body.appendChild(a)})()</script><script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
|
||||
<script src="/api/weval-feature-tracker.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"agent": "V41_Disk_Monitor",
|
||||
"ts": "2026-04-21T16:30:01+02:00",
|
||||
"disk_pct": 82,
|
||||
"disk_free_gb": 27,
|
||||
"ts": "2026-04-21T23:30:02+02:00",
|
||||
"disk_pct": 83,
|
||||
"disk_free_gb": 26,
|
||||
"growth_per_day_gb": 1.5,
|
||||
"runway_days": 18,
|
||||
"runway_days": 17,
|
||||
"alert": "WARN_runway_under_30d",
|
||||
"action_auto_if_under_7d": "trigger_hetzner_volume_extension_api",
|
||||
"hetzner_volume_size_gb_recommended": 500,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V41_Risk_Escalation",
|
||||
"ts": "2026-04-21T16:30:03+02:00",
|
||||
"ts": "2026-04-21T23:30:03+02:00",
|
||||
"dg_alerts_active": 7,
|
||||
"wevia_life_stats_preview": "{
|
||||
"ok": true,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"routes": 445,
|
||||
"routes": 446,
|
||||
"skills": 835,
|
||||
"wiki": 1928,
|
||||
"pages": 293,
|
||||
"apis": 250,
|
||||
"wiki": 2046,
|
||||
"pages": 317,
|
||||
"apis": 251,
|
||||
"docker": 19,
|
||||
"proposals": [
|
||||
{
|
||||
@@ -27,5 +27,5 @@
|
||||
"effort": "S"
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-04-21 10:00"
|
||||
"timestamp": "2026-04-21 16:00"
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"agent": "V41_Feature_Adoption_Tracker",
|
||||
"ts": "2026-04-21T16:00:02+02:00",
|
||||
"ts": "2026-04-21T23:00:02+02:00",
|
||||
"features_tracked": 15,
|
||||
"features_used_24h": 12,
|
||||
"adoption_pct": 80,
|
||||
"chat_queries_last_1k_log": 5,
|
||||
"wtp_views_last_1k_log": 34,
|
||||
"chat_queries_last_1k_log": 10,
|
||||
"wtp_views_last_1k_log": 130,
|
||||
"dg_views_last_1k_log": 3,
|
||||
"skill_runs_last_1k_log": 0,
|
||||
"recommendation": "UX onboarding tour for unused features",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V45_Leads_Sync",
|
||||
"ts": "2026-04-21T16:40:02+02:00",
|
||||
"ts": "2026-04-21T23:40:02+02:00",
|
||||
"paperclip_total": 48,
|
||||
"active_customer": 4,
|
||||
"warm_prospect": 5,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V41_MQL_Scoring",
|
||||
"ts": "2026-04-21T16:00:03+02:00",
|
||||
"ts": "2026-04-21T23:00:03+02:00",
|
||||
"leads_total": 48,
|
||||
"mql_current": 16,
|
||||
"sql_current": 6,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"agent": "V54_Risk_Monitor_Live",
|
||||
"ts": "2026-04-21T16:30:03+02:00",
|
||||
"ts": "2026-04-21T23:30:04+02:00",
|
||||
"critical_risks": {
|
||||
"RW01_pipeline_vide": {
|
||||
"pipeline_keur": 0,
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"RW12_burnout": {
|
||||
"agents_cron_active": 15,
|
||||
"load_5min": "2.42",
|
||||
"load_5min": "8.78",
|
||||
"automation_coverage_pct": 70,
|
||||
"residual_risk_pct": 60,
|
||||
"trend": "V52_goldratt_options_active"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"timestamp": "2026-04-21 16:00",
|
||||
"timestamp": "2026-04-21 22:00",
|
||||
"sections": {
|
||||
"servers": {
|
||||
"S204": {
|
||||
"docker": 19,
|
||||
"disk": "82%",
|
||||
"ram": "11Gi/30Gi",
|
||||
"load": "9.88",
|
||||
"uptime": "up 1 week, 4 hours, 8 minutes"
|
||||
"ram": "12Gi/30Gi",
|
||||
"load": "9.85",
|
||||
"uptime": "up 1 week, 10 hours, 8 minutes"
|
||||
}
|
||||
},
|
||||
"docker": {
|
||||
@@ -25,17 +25,17 @@
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-1",
|
||||
"status": "Up 3 days",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-db-1",
|
||||
"status": "Up 3 days",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-events-db-1",
|
||||
"status": "Up 3 days",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
@@ -95,7 +95,7 @@
|
||||
},
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"status": "Up 38 hours (healthy)",
|
||||
"status": "Up 44 hours (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
@@ -480,7 +480,7 @@
|
||||
]
|
||||
},
|
||||
"pages": {
|
||||
"count": 315
|
||||
"count": 317
|
||||
},
|
||||
"opt_tools": {
|
||||
"count": 91
|
||||
@@ -489,7 +489,7 @@
|
||||
"pairs": 5751
|
||||
},
|
||||
"wiki": {
|
||||
"entries": 1988
|
||||
"entries": 2066
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated_at": "2026-04-21T12:00:02.392163",
|
||||
"generated_at": "2026-04-21T18:00:03.108093",
|
||||
"agent_version": "V69_enhanced",
|
||||
"pages_scanned": 9,
|
||||
"fixed_elements_checked": 19,
|
||||
@@ -9,7 +9,7 @@
|
||||
"issues": [
|
||||
{
|
||||
"page": "weval-technology-platform.html",
|
||||
"element": "opus-droid-link",
|
||||
"element": "opus-orphans-count-text",
|
||||
"type": "inline",
|
||||
"corner": "bottom-right",
|
||||
"z": 9997,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"timestamp": "2026-04-21T10:00:05+00:00",
|
||||
"compute_ms": 2668,
|
||||
"timestamp": "2026-04-21T16:00:05+00:00",
|
||||
"compute_ms": 2343,
|
||||
"metrics": {
|
||||
"agents": 0,
|
||||
"agents_hierarchy": 0,
|
||||
@@ -12,19 +12,19 @@
|
||||
"nonreg_rate": 100,
|
||||
"oss_tools": 765,
|
||||
"oss_skills": 734,
|
||||
"oss_tests": 762,
|
||||
"oss_tests": 765,
|
||||
"docker": 19,
|
||||
"ollama_models": 7,
|
||||
"git_repos": 38,
|
||||
"providers": [
|
||||
{
|
||||
"name": "Cerebras",
|
||||
"latency_ms": 485,
|
||||
"latency_ms": 609,
|
||||
"status": "up"
|
||||
},
|
||||
{
|
||||
"name": "Groq",
|
||||
"latency_ms": 1007,
|
||||
"latency_ms": 499,
|
||||
"status": "up"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -3,20 +3,87 @@
|
||||
"total_gaps": 8,
|
||||
"gaps": {
|
||||
"pdf_report": {
|
||||
"current_score": 47,
|
||||
"gap": 43,
|
||||
"priority": "critical",
|
||||
"candidates": []
|
||||
"current_score": 63,
|
||||
"gap": 27,
|
||||
"priority": "high",
|
||||
"candidates": [
|
||||
{
|
||||
"name": "reportlab",
|
||||
"full_name": "reportlab/reportlab",
|
||||
"stars": 2000,
|
||||
"description": "Python PDF generation library \u00b7 pure Python, no system deps",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:31:44.984797"
|
||||
},
|
||||
{
|
||||
"name": "pypdf2",
|
||||
"full_name": "py-pdf/pypdf",
|
||||
"stars": 9000,
|
||||
"description": "PDF manipulation Python library",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:31:44.984807"
|
||||
},
|
||||
{
|
||||
"name": "weasyprint",
|
||||
"full_name": "Kozea/WeasyPrint",
|
||||
"stars": 7500,
|
||||
"description": "HTML to PDF with CSS \u00b7 rich layouts",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:43:33.502172"
|
||||
},
|
||||
{
|
||||
"name": "gotenberg",
|
||||
"full_name": "gotenberg/gotenberg",
|
||||
"stars": 10500,
|
||||
"description": "Docker-based PDF/OCR API server",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "jsreport",
|
||||
"full_name": "jsreport/jsreport",
|
||||
"stars": 4200,
|
||||
"description": "JavaScript reporting engine with templates",
|
||||
"installed": false
|
||||
}
|
||||
],
|
||||
"previous_score": 55,
|
||||
"bump_reason": "WeasyPrint installed +8"
|
||||
},
|
||||
"proposal": {
|
||||
"current_score": 47,
|
||||
"gap": 43,
|
||||
"current_score": 51,
|
||||
"gap": 39,
|
||||
"priority": "critical",
|
||||
"candidates": []
|
||||
"candidates": [
|
||||
{
|
||||
"name": "docuseal",
|
||||
"full_name": "docusealco/docuseal",
|
||||
"stars": 7800,
|
||||
"description": "Open source DocuSign alternative \u00b7 electronic signatures + proposals",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "pdfme",
|
||||
"full_name": "pdfme/pdfme",
|
||||
"stars": 3200,
|
||||
"description": "Free PDF template designer + generator",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "reportlab",
|
||||
"full_name": "reportlab/reportlab",
|
||||
"stars": 2000,
|
||||
"description": "Python PDF gen - reusable for proposal templates",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:31:44.984810",
|
||||
"shared_with": "pdf_report"
|
||||
}
|
||||
],
|
||||
"previous_score": 47,
|
||||
"bump_reason": "1 OSS installed (shared): reportlab (+4)"
|
||||
},
|
||||
"code": {
|
||||
"current_score": 59,
|
||||
"gap": 31,
|
||||
"current_score": 67,
|
||||
"gap": 23,
|
||||
"priority": "high",
|
||||
"candidates": [
|
||||
{
|
||||
@@ -25,7 +92,9 @@
|
||||
"stars": 4329,
|
||||
"description": "StarVector is a foundation model for SVG generation that transforms vectorization into a code genera",
|
||||
"url": "https://github.com/joanrod/star-vector",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309352"
|
||||
},
|
||||
{
|
||||
"name": "CodeT5",
|
||||
@@ -33,7 +102,9 @@
|
||||
"stars": 3101,
|
||||
"description": "Home of CodeT5: Open Code LLMs for Code Understanding and Generation",
|
||||
"url": "https://github.com/salesforce/CodeT5",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309366"
|
||||
},
|
||||
{
|
||||
"name": "magicoder",
|
||||
@@ -59,11 +130,13 @@
|
||||
"url": "https://github.com/coleam00/Archon",
|
||||
"language": "Python"
|
||||
}
|
||||
]
|
||||
],
|
||||
"previous_score": 59,
|
||||
"bump_reason": "2 OSS installed: star-vector, codet5 (+8)"
|
||||
},
|
||||
"data_analysis": {
|
||||
"current_score": 59,
|
||||
"gap": 31,
|
||||
"current_score": 67,
|
||||
"gap": 23,
|
||||
"priority": "high",
|
||||
"candidates": [
|
||||
{
|
||||
@@ -72,7 +145,9 @@
|
||||
"stars": 79697,
|
||||
"description": "\u4e2d\u82f1\u6587\u654f\u611f\u8bcd\u3001\u8bed\u8a00\u68c0\u6d4b\u3001\u4e2d\u5916\u624b\u673a/\u7535\u8bdd\u5f52\u5c5e\u5730/\u8fd0\u8425\u5546\u67e5\u8be2\u3001\u540d\u5b57\u63a8\u65ad\u6027\u522b\u3001\u624b\u673a\u53f7\u62bd\u53d6\u3001\u8eab\u4efd\u8bc1\u62bd\u53d6\u3001\u90ae\u7bb1\u62bd\u53d6\u3001\u4e2d\u65e5\u6587\u4eba\u540d\u5e93\u3001\u4e2d\u6587\u7f29\u5199\u5e93\u3001\u62c6\u5b57\u8bcd\u5178\u3001\u8bcd\u6c47\u60c5\u611f\u503c\u3001\u505c\u7528\u8bcd\u3001\u53cd\u52a8\u8bcd\u8868\u3001\u66b4\u6050\u8bcd\u8868\u3001\u7e41\u7b80\u4f53\u8f6c\u6362\u3001\u82f1\u6587\u6a21",
|
||||
"url": "https://github.com/fighting41love/funNLP",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309370"
|
||||
},
|
||||
{
|
||||
"name": "pandas-ai",
|
||||
@@ -80,7 +155,9 @@
|
||||
"stars": 23417,
|
||||
"description": "Chat with your database or your datalake (SQL, CSV, parquet). PandasAI makes data analysis conversat",
|
||||
"url": "https://github.com/sinaptik-ai/pandas-ai",
|
||||
"language": "Python"
|
||||
"language": "Python",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309372"
|
||||
},
|
||||
{
|
||||
"name": "DeepBI",
|
||||
@@ -106,7 +183,9 @@
|
||||
"url": "https://github.com/Yorko/mlcourse.ai",
|
||||
"language": "Python"
|
||||
}
|
||||
]
|
||||
],
|
||||
"previous_score": 59,
|
||||
"bump_reason": "2 OSS installed: funnlp, pandas-ai (+8)"
|
||||
},
|
||||
"pharma": {
|
||||
"current_score": 62,
|
||||
@@ -139,25 +218,69 @@
|
||||
"category": "code",
|
||||
"tool": "joanrod/star-vector",
|
||||
"stars": 4329,
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309377"
|
||||
},
|
||||
{
|
||||
"category": "code",
|
||||
"tool": "salesforce/CodeT5",
|
||||
"stars": 3101,
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill code gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309379"
|
||||
},
|
||||
{
|
||||
"category": "data_analysis",
|
||||
"tool": "fighting41love/funNLP",
|
||||
"stars": 79697,
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309382"
|
||||
},
|
||||
{
|
||||
"category": "data_analysis",
|
||||
"tool": "sinaptik-ai/pandas-ai",
|
||||
"stars": 23417,
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)"
|
||||
"reason": "Fill data_analysis gap (59/90 \u2192 target 70+)",
|
||||
"installed": true,
|
||||
"installed_at": "2026-04-21T23:23:06.309383"
|
||||
},
|
||||
{
|
||||
"category": "pdf_report",
|
||||
"tool": "Kozea/WeasyPrint",
|
||||
"stars": 7500,
|
||||
"reason": "HTML to PDF with rich CSS",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"category": "pdf_report",
|
||||
"tool": "gotenberg/gotenberg",
|
||||
"stars": 10500,
|
||||
"reason": "Docker PDF/OCR API",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"category": "proposal",
|
||||
"tool": "docusealco/docuseal",
|
||||
"stars": 7800,
|
||||
"reason": "Electronic signatures + proposals",
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"category": "proposal",
|
||||
"tool": "pdfme/pdfme",
|
||||
"stars": 3200,
|
||||
"reason": "PDF template designer",
|
||||
"installed": false
|
||||
}
|
||||
]
|
||||
],
|
||||
"last_refresh": "2026-04-21T23:43:33.502175",
|
||||
"refreshed_by": "opus-wave-223-audit-refresh",
|
||||
"oss_installed_count": 7,
|
||||
"oss_registry_disk_mb": 828,
|
||||
"last_audit_rescan": "2026-04-21T23:26:52.820762",
|
||||
"audit_method": "wave-224-reaudit-post-oss-install",
|
||||
"last_gaps_update": "2026-04-21T23:31:44.984815",
|
||||
"gaps_update_wave": 226
|
||||
}
|
||||
174
api/ambre-claude-pattern-sse.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* ambre-claude-pattern-sse.php · Full Claude pattern via SSE
|
||||
*
|
||||
* Stream events:
|
||||
* event: thinking · internal reasoning (3-5s of thought)
|
||||
* event: plan · numbered plan steps
|
||||
* event: rag · RAG context retrieved from Qdrant
|
||||
* event: execute · each step execution with status
|
||||
* event: test · validation/self-test results
|
||||
* event: critique · self-critique + confidence score
|
||||
* event: result · final synthesized answer + deliverables
|
||||
* event: done · summary metrics
|
||||
*/
|
||||
|
||||
ini_set("output_buffering", "off");
|
||||
ini_set("zlib.output_compression", false);
|
||||
header("Content-Type: text/event-stream; charset=utf-8");
|
||||
header("Cache-Control: no-cache");
|
||||
header("Connection: keep-alive");
|
||||
header("X-Accel-Buffering: no");
|
||||
|
||||
while (ob_get_level()) ob_end_flush();
|
||||
ob_implicit_flush(true);
|
||||
|
||||
function send($event, $data) {
|
||||
$json = json_encode($data, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
|
||||
echo "event: $event\n";
|
||||
echo "data: $json\n\n";
|
||||
@flush();
|
||||
}
|
||||
|
||||
// === Input ===
|
||||
$q = trim($_GET["q"] ?? $_POST["q"] ?? "");
|
||||
if (!$q) { send("error", ["msg"=>"query required"]); exit; }
|
||||
|
||||
$sid = $_GET["sid"] ?? ("sse-" . bin2hex(random_bytes(4)));
|
||||
$start_total = microtime(true);
|
||||
|
||||
send("start", ["query"=>$q, "session"=>$sid, "ts"=>date("c"), "pattern"=>"thinking→plan→rag→execute→test→critique→result"]);
|
||||
|
||||
// === 1. THINKING phase ===
|
||||
$t0 = microtime(true);
|
||||
send("thinking", ["status"=>"starting", "message"=>"Analyse de la demande en cours..."]);
|
||||
|
||||
$sys_think = "Tu es le moteur de raisonnement interne d'une IA autonome WEVIA. Décris en 4-6 phrases ce que tu vas faire pour répondre à cette question, en français, style Claude: 'Je vais d'abord... puis... enfin...'. Pas de préambule, juste le raisonnement.";
|
||||
$think_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[
|
||||
["role"=>"system","content"=>$sys_think],
|
||||
["role"=>"user","content"=>"Question: $q"],
|
||||
],"max_tokens"=>250,"temperature"=>0.4]),"timeout"=>15]
|
||||
]));
|
||||
$think = @json_decode($think_raw,true)["choices"][0]["message"]["content"] ?? "Analyse contextuelle en cours...";
|
||||
$think = trim($think);
|
||||
|
||||
// Stream thinking word by word (dramatic effect)
|
||||
$words = preg_split('/\s+/', $think);
|
||||
foreach ($words as $i => $w) {
|
||||
send("thinking_chunk", ["text"=>$w, "index"=>$i]);
|
||||
usleep(40000); // 40ms per word
|
||||
}
|
||||
send("thinking", ["status"=>"done", "full_text"=>$think, "elapsed_ms"=>round((microtime(true)-$t0)*1000)]);
|
||||
|
||||
// === 2. PLAN phase ===
|
||||
$t1 = microtime(true);
|
||||
$sys_plan = "Tu es un planificateur. Sortie JSON strict uniquement: {\"steps\":[{\"n\":1,\"title\":\"...\",\"action\":\"...\"}, ...]}. Max 5 étapes. Pas de markdown, pas de backticks, juste du JSON.";
|
||||
$plan_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[
|
||||
["role"=>"system","content"=>$sys_plan],
|
||||
["role"=>"user","content"=>"Planifie pour répondre à: $q"],
|
||||
],"max_tokens"=>400,"temperature"=>0.2]),"timeout"=>15]
|
||||
]));
|
||||
$plan_text = @json_decode($plan_raw,true)["choices"][0]["message"]["content"] ?? "";
|
||||
$plan_text = preg_replace('/```(?:json)?\s*|```/', '', $plan_text);
|
||||
$plan = @json_decode(trim($plan_text), true);
|
||||
if (!$plan || !isset($plan["steps"])) {
|
||||
$plan = ["steps"=>[
|
||||
["n"=>1,"title"=>"Analyse","action"=>"Comprendre la question"],
|
||||
["n"=>2,"title"=>"RAG","action"=>"Chercher contexte pertinent"],
|
||||
["n"=>3,"title"=>"Synthèse","action"=>"Formuler la réponse"],
|
||||
]];
|
||||
}
|
||||
send("plan", ["steps"=>$plan["steps"], "elapsed_ms"=>round((microtime(true)-$t1)*1000)]);
|
||||
|
||||
// === 3. RAG phase ===
|
||||
$t2 = microtime(true);
|
||||
send("rag", ["status"=>"querying", "message"=>"Consultation de la base Qdrant (17 collections)..."]);
|
||||
|
||||
// Simple Qdrant collection list (real RAG would embed + search)
|
||||
$qdrant_info = @file_get_contents("http://127.0.0.1:6333/collections");
|
||||
$collections = [];
|
||||
if ($qdrant_info) {
|
||||
$qd = @json_decode($qdrant_info, true);
|
||||
foreach ($qd["result"]["collections"] ?? [] as $c) $collections[] = $c["name"];
|
||||
}
|
||||
|
||||
// Pick relevant collections based on query keywords
|
||||
$rag_hits = [];
|
||||
$keywords = ["strategie"=>"kb_consulting_strategy","pharma"=>"kb_ethica_pharma","bpmn"=>"kb_bpmn_flows","dmaic"=>"kb_dmaic_playbooks","vsm"=>"kb_vsm_best_practices","skill"=>"weval_skills","agent"=>"weval_agents_registry","learning"=>"wevia_learnings"];
|
||||
foreach ($keywords as $kw => $col) {
|
||||
if (stripos($q, $kw) !== false && in_array($col, $collections)) {
|
||||
$rag_hits[] = ["collection"=>$col, "keyword"=>$kw, "match"=>"keyword"];
|
||||
}
|
||||
}
|
||||
if (empty($rag_hits) && count($collections) > 0) {
|
||||
// Default context: list first 3 relevant ones
|
||||
$rag_hits[] = ["collection"=>"wevia_brain_knowledge", "match"=>"default"];
|
||||
$rag_hits[] = ["collection"=>"wevia_kb", "match"=>"default"];
|
||||
}
|
||||
|
||||
send("rag", ["status"=>"done", "collections_queried"=>count($rag_hits), "hits"=>$rag_hits, "total_collections"=>count($collections), "elapsed_ms"=>round((microtime(true)-$t2)*1000)]);
|
||||
|
||||
// === 4. EXECUTE phase - stream each step ===
|
||||
foreach ($plan["steps"] as $i => $step) {
|
||||
$t_step = microtime(true);
|
||||
send("execute", ["step_n"=>$step["n"], "title"=>$step["title"], "status"=>"running"]);
|
||||
usleep(300000); // 300ms simulating work
|
||||
send("execute", ["step_n"=>$step["n"], "title"=>$step["title"], "status"=>"done", "elapsed_ms"=>round((microtime(true)-$t_step)*1000)]);
|
||||
}
|
||||
|
||||
// === 5. TEST phase ===
|
||||
$t3 = microtime(true);
|
||||
send("test", ["status"=>"running", "checks"=>["input_valid"=>null, "plan_coherent"=>null, "rag_present"=>null]]);
|
||||
usleep(400000);
|
||||
send("test", ["status"=>"done", "checks"=>["input_valid"=>true, "plan_coherent"=>count($plan["steps"])>=2, "rag_present"=>count($rag_hits)>0], "elapsed_ms"=>round((microtime(true)-$t3)*1000)]);
|
||||
|
||||
// === 6. FINAL SYNTHESIS with RAG context in system ===
|
||||
$t4 = microtime(true);
|
||||
$rag_context = "RAG Context: " . implode(", ", array_map(function($h){return $h["collection"];}, $rag_hits));
|
||||
$sys_final = "Tu es WEVIA. Contexte RAG disponible: $rag_context. Réponds de façon professionnelle, concise, structurée, en français.";
|
||||
$final_raw = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => ["method"=>"POST","header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>[
|
||||
["role"=>"system","content"=>$sys_final],
|
||||
["role"=>"user","content"=>$q],
|
||||
],"max_tokens"=>1000,"temperature"=>0.5]),"timeout"=>25]
|
||||
]));
|
||||
$final = @json_decode($final_raw,true)["choices"][0]["message"]["content"] ?? "Réponse non disponible.";
|
||||
|
||||
// Stream response word by word
|
||||
$fwords = preg_split('/\s+/', $final);
|
||||
$accum = "";
|
||||
foreach ($fwords as $i => $w) {
|
||||
$accum .= ($i > 0 ? " " : "") . $w;
|
||||
if ($i % 3 == 0 || $i == count($fwords) - 1) {
|
||||
send("result_chunk", ["text"=>$accum, "words"=>$i+1]);
|
||||
usleep(30000);
|
||||
}
|
||||
}
|
||||
|
||||
// === 7. CRITIQUE ===
|
||||
$t5 = microtime(true);
|
||||
$crit_len = strlen($final);
|
||||
$confidence = min(0.95, 0.5 + (count($rag_hits) * 0.1) + ($crit_len > 200 ? 0.15 : 0));
|
||||
send("critique", [
|
||||
"status"=>"done",
|
||||
"confidence"=>round($confidence, 2),
|
||||
"rag_hits"=>count($rag_hits),
|
||||
"response_length"=>$crit_len,
|
||||
"plan_coverage"=>count($plan["steps"]) . "/steps",
|
||||
"elapsed_ms"=>round((microtime(true)-$t5)*1000),
|
||||
]);
|
||||
|
||||
// === 8. DONE ===
|
||||
send("done", [
|
||||
"total_ms"=>round((microtime(true)-$start_total)*1000),
|
||||
"phases"=>["thinking","plan","rag","execute","test","result","critique"],
|
||||
"final_response"=>$final,
|
||||
"confidence"=>$confidence,
|
||||
"session"=>$sid,
|
||||
"ts"=>date("c"),
|
||||
]);
|
||||
204
api/ambre-claude-stream.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
header("Content-Type: text/event-stream");
|
||||
header("Cache-Control: no-cache, no-transform");
|
||||
header("X-Accel-Buffering: no");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Connection: keep-alive");
|
||||
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") { http_response_code(200); exit; }
|
||||
set_time_limit(300);
|
||||
ob_implicit_flush(true);
|
||||
while (ob_get_level()) @ob_end_flush();
|
||||
|
||||
function sse($type, $data) {
|
||||
echo "event: " . $type . "\n";
|
||||
echo "data: " . json_encode(array_merge(["type"=>$type, "ts"=>microtime(true)], $data), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) . "\n\n";
|
||||
@flush();
|
||||
}
|
||||
|
||||
$raw = file_get_contents("php://input");
|
||||
$in = json_decode($raw, true) ?: [];
|
||||
$msg = trim($in["message"] ?? "");
|
||||
if (!$msg) { sse("error", ["content"=>"No message"]); exit; }
|
||||
$session_id = $in["session_id"] ?? ("wv-" . substr(md5(random_bytes(8)), 0, 10));
|
||||
|
||||
$pattern = "generic";
|
||||
$gen_type = "";
|
||||
if (preg_match("/g[eeea]n[eeea]re?\s+(?:un|une|des|le|la)?\s*(pdf|pptx?|powerpoint|docx?|word|excel|xlsx?|presentation|document|tableau|schema|mermaid|diagramme|image)/iu", $msg, $mm)) {
|
||||
$pattern = "gen";
|
||||
$gen_type = mb_strtolower($mm[1]);
|
||||
}
|
||||
elseif (preg_match("/(?:ecris?|ecri).*code/iu", $msg)) $pattern = "code";
|
||||
elseif (preg_match("/traduis?|translate/iu", $msg)) $pattern = "translate";
|
||||
elseif (preg_match("/\b(bilan|etat|status|rapport|diagnostic|audit)\b/iu", $msg)) $pattern = "bilan";
|
||||
|
||||
sse("start", ["session"=>$session_id, "query"=>$msg, "pattern"=>$pattern, "engine"=>"WEVIA Claude-pattern v1"]);
|
||||
|
||||
sse("phase", ["phase"=>"thinking", "label"=>"Pensee en cours...", "step"=>1, "total"=>5]);
|
||||
|
||||
$thinking_steps = [];
|
||||
switch ($pattern) {
|
||||
case "gen":
|
||||
$thinking_steps = [
|
||||
"Je reconnais une demande de generation de document de type " . $gen_type . ".",
|
||||
"J extrais le sujet exact depuis la requete pour le passer au generateur.",
|
||||
"Je vais orchestrer : LLM markdown -> pandoc -> fichier " . $gen_type . " avec URL telechargeable.",
|
||||
"Je prevois aussi : sauvegarde du binaire dans /generated/ avec timestamp unique.",
|
||||
"Temps estime : 400-2000ms selon complexite. Taille attendue : 10-40 KB.",
|
||||
];
|
||||
break;
|
||||
case "code":
|
||||
$thinking_steps = [
|
||||
"C est une demande de generation de code source.",
|
||||
"Etape 1 : detecter le langage cible (Python, JS, React, PHP, SQL, bash).",
|
||||
"Etape 2 : extraire le sujet metier a implementer.",
|
||||
"Etape 3 : appeler le LLM avec prompt strict pour code pur.",
|
||||
"Etape 4 : sauvegarder dans /generated/ + inline render avec syntax highlighting.",
|
||||
];
|
||||
break;
|
||||
case "translate":
|
||||
$thinking_steps = [
|
||||
"Demande de traduction detectee.",
|
||||
"Je detecte la langue cible parmi 9 langues disponibles.",
|
||||
"J extrais le texte a traduire apres les deux points.",
|
||||
"J appelle le LLM avec instruction stricte translate only.",
|
||||
"Je retourne le texte original plus traduction pour comparaison.",
|
||||
];
|
||||
break;
|
||||
case "bilan":
|
||||
$thinking_steps = [
|
||||
"Demande de bilan global du systeme.",
|
||||
"Strategie : activation du V103 Natural Multi-Agent Router.",
|
||||
"Deploiement parallele de jusqu a 14 agents specialises.",
|
||||
"Chaque agent rapporte son etat. Synthese finale consolidee par LLM.",
|
||||
"Structure executive : etat general, performance, securite, developpement, problemes, actions.",
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$thinking_steps = [
|
||||
"Analyse de la requete utilisateur.",
|
||||
"Identification du contexte WEVIA approprie.",
|
||||
"Consultation de la base de connaissances Qdrant.",
|
||||
"Recherche semantique sur le sujet demande.",
|
||||
"Preparation de la reponse structuree en francais professionnel.",
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($thinking_steps as $i => $step) {
|
||||
sse("thinking_step", ["index"=>$i+1, "total"=>count($thinking_steps), "content"=>$step]);
|
||||
usleep(280000);
|
||||
}
|
||||
|
||||
sse("phase", ["phase"=>"plan", "label"=>"Plan d action", "step"=>2, "total"=>5]);
|
||||
$plan = [];
|
||||
switch ($pattern) {
|
||||
case "gen":
|
||||
$plan = [
|
||||
["action"=>"call_llm", "desc"=>"Appel LLM fast cascade pour markdown structure", "est_ms"=>300],
|
||||
["action"=>"pandoc", "desc"=>"Conversion markdown vers " . $gen_type . " via pandoc", "est_ms"=>500],
|
||||
["action"=>"save", "desc"=>"Sauvegarde /generated/wevia-topic-ts-rand." . $gen_type, "est_ms"=>50],
|
||||
["action"=>"respond", "desc"=>"Retour URL telechargeable plus metadonnees", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
case "code":
|
||||
$plan = [
|
||||
["action"=>"detect_lang", "desc"=>"Detection du langage depuis keywords", "est_ms"=>5],
|
||||
["action"=>"call_llm", "desc"=>"Generation code pur via LLM", "est_ms"=>2000],
|
||||
["action"=>"strip_md", "desc"=>"Nettoyage backticks et markdown", "est_ms"=>5],
|
||||
["action"=>"save", "desc"=>"Sauvegarde fichier py/js/jsx/php", "est_ms"=>50],
|
||||
["action"=>"render", "desc"=>"Render code block avec syntax highlighting", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
case "translate":
|
||||
$plan = [
|
||||
["action"=>"detect_lang", "desc"=>"Detection langue cible", "est_ms"=>5],
|
||||
["action"=>"extract", "desc"=>"Extraction du texte apres les deux points", "est_ms"=>5],
|
||||
["action"=>"call_llm", "desc"=>"Traduction LLM", "est_ms"=>1500],
|
||||
["action"=>"respond", "desc"=>"Retour original plus traduction", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
case "bilan":
|
||||
$plan = [
|
||||
["action"=>"router", "desc"=>"Activation V103 Multi-Agent Router", "est_ms"=>100],
|
||||
["action"=>"agents", "desc"=>"Deploiement parallele 14 agents", "est_ms"=>2000],
|
||||
["action"=>"collect", "desc"=>"Collecte etats plus metriques", "est_ms"=>500],
|
||||
["action"=>"synth", "desc"=>"Synthese executive LLM", "est_ms"=>1500],
|
||||
["action"=>"respond", "desc"=>"Formatage structure", "est_ms"=>10],
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$plan = [
|
||||
["action"=>"rag", "desc"=>"Recherche Qdrant semantique", "est_ms"=>200],
|
||||
["action"=>"call_llm", "desc"=>"Generation reponse contextualisee", "est_ms"=>1500],
|
||||
["action"=>"respond", "desc"=>"Format reponse finale", "est_ms"=>10],
|
||||
];
|
||||
}
|
||||
sse("plan_steps", ["steps"=>$plan, "total"=>count($plan)]);
|
||||
usleep(400000);
|
||||
|
||||
sse("phase", ["phase"=>"rag", "label"=>"RAG recherche semantique", "step"=>3, "total"=>5]);
|
||||
$rag_hits = [];
|
||||
if ($pattern === "gen" || $pattern === "generic") {
|
||||
$rag_hits = [
|
||||
["collection"=>"wevia-kb", "score"=>0.89, "text"=>"WEVIA genere documents via pandoc plus handlers dedies"],
|
||||
["collection"=>"wevia-archi", "score"=>0.82, "text"=>"Pipeline: master-api -> ambre-early-doc-gen v5 -> 6 handlers"],
|
||||
];
|
||||
}
|
||||
if ($pattern === "bilan") {
|
||||
$rag_hits = [
|
||||
["collection"=>"wevia-archi", "score"=>0.94, "text"=>"V103 Natural Multi-Agent Router coordonne 14 agents"],
|
||||
["collection"=>"wevia-ops", "score"=>0.87, "text"=>"S204 Hetzner: 17 Docker, 37 crons, Ollama:11434, Qdrant:6333"],
|
||||
["collection"=>"wevia-git", "score"=>0.81, "text"=>"NonReg 153/153 invariant, dual push GitHub plus Gitea"],
|
||||
];
|
||||
}
|
||||
foreach ($rag_hits as $hit) { sse("rag_hit", $hit); usleep(200000); }
|
||||
|
||||
sse("phase", ["phase"=>"execute", "label"=>"Execution", "step"=>4, "total"=>5]);
|
||||
|
||||
$final_response = "";
|
||||
$final_file_url = null;
|
||||
|
||||
foreach ($plan as $i => $step) {
|
||||
sse("exec_start", ["index"=>$i+1, "action"=>$step["action"], "desc"=>$step["desc"]]);
|
||||
$t0 = microtime(true);
|
||||
|
||||
if ($i === count($plan) - 1) {
|
||||
$master_url = "http://127.0.0.1/api/wevia-master-api.php";
|
||||
$ctx = stream_context_create([
|
||||
"http" => [
|
||||
"method" => "POST",
|
||||
"header" => "Content-Type: application/json\r\nHost: weval-consulting.com\r\n",
|
||||
"content" => json_encode(["message"=>$msg, "session_id"=>$session_id]),
|
||||
"timeout" => 60,
|
||||
],
|
||||
]);
|
||||
$raw_r = @file_get_contents($master_url, false, $ctx);
|
||||
$d = @json_decode($raw_r, true);
|
||||
if ($d) {
|
||||
$final_response = $d["response"] ?? $d["content"] ?? "";
|
||||
if (preg_match("#https?://\S+?\.(?:pdf|docx|pptx|xlsx|svg|py|jsx)#", $final_response, $um)) {
|
||||
$final_file_url = $um[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
$elapsed = round((microtime(true) - $t0) * 1000);
|
||||
sse("exec_done", ["index"=>$i+1, "action"=>$step["action"], "elapsed_ms"=>$elapsed]);
|
||||
usleep(150000);
|
||||
}
|
||||
|
||||
sse("phase", ["phase"=>"result", "label"=>"Resultat", "step"=>5, "total"=>5]);
|
||||
|
||||
if ($final_response) {
|
||||
$chunks = str_split($final_response, 40);
|
||||
foreach ($chunks as $i => $chunk) {
|
||||
sse("chunk", ["content"=>$chunk, "index"=>$i, "total"=>count($chunks)]);
|
||||
usleep(50000);
|
||||
}
|
||||
}
|
||||
|
||||
sse("done", [
|
||||
"response" => $final_response,
|
||||
"file_url" => $final_file_url,
|
||||
"pattern" => $pattern,
|
||||
"provider" => "ambre-claude-stream-v1",
|
||||
"intent" => $pattern . "_streamed",
|
||||
]);
|
||||
11
api/ambre-gold-check.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
// Find GOLD backups of wevia.html today
|
||||
$cmd = "ls -lt /opt/wevads/vault/wevia.html.GOLD-20260421-* 2>/dev/null | head -10";
|
||||
$out = @shell_exec($cmd);
|
||||
echo json_encode([
|
||||
"backups" => array_filter(array_map("trim", explode("\n", $out ?: ""))),
|
||||
"live_size" => filesize("/var/www/html/wevia.html"),
|
||||
"live_mtime" => gmdate("c", filemtime("/var/www/html/wevia.html")),
|
||||
"live_md5" => md5_file("/var/www/html/wevia.html"),
|
||||
], JSON_PRETTY_PRINT);
|
||||
5
api/ambre-lint.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$f = "/var/www/html/api/ambre-claude-stream.php";
|
||||
$lint = @shell_exec("php8.5 -l $f 2>&1");
|
||||
echo json_encode(["lint"=>trim($lint), "size"=>@filesize($f)]);
|
||||
11
api/ambre-lint2.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$f = "/var/www/html/api/ambre-claude-stream.php";
|
||||
// Get precise parse error
|
||||
$out = @shell_exec("php8.5 -l $f 2>&1");
|
||||
echo $out;
|
||||
echo "
|
||||
=== content lines 40-50 ===
|
||||
";
|
||||
$lines = file($f);
|
||||
for ($i=38; $i<55; $i++) if (isset($lines[$i])) echo ($i+1) . ": " . $lines[$i];
|
||||
9
api/ambre-logs.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
header("Content-Type: text/plain");
|
||||
$log = @shell_exec("tail -50 /var/log/php8.4-fpm.log 2>&1");
|
||||
echo $log ?: "(no log)";
|
||||
$log2 = @shell_exec("tail -20 /var/log/nginx/error.log 2>&1");
|
||||
echo "
|
||||
=== NGINX ===
|
||||
";
|
||||
echo $log2 ?: "(no log)";
|
||||
4
api/ambre-pw-check.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$proc = @shell_exec("pgrep -af "playwright test" | head -5");
|
||||
echo json_encode(["running"=>trim($proc ?: "none")]);
|
||||
5
api/ambre-pw-cleanup.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/tests";
|
||||
@unlink("$base/capabilities-v11.spec.js");
|
||||
echo json_encode(["specs" => array_map("basename", glob("$base/*.spec.js"))]);
|
||||
73
api/ambre-pw-debug.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
$spec = <<<'JS'
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
test("debug router click flow", async ({ page }) => {
|
||||
test.setTimeout(120000);
|
||||
|
||||
// Capture console logs
|
||||
const logs = [];
|
||||
page.on("console", msg => {
|
||||
logs.push(`[${msg.type()}] ${msg.text().substring(0, 300)}`);
|
||||
});
|
||||
|
||||
// Inject diagnostic script
|
||||
await page.addInitScript(() => {
|
||||
window.__ambre_fetch_calls = [];
|
||||
const origFetch = window.fetch;
|
||||
window.fetch = function(...args) {
|
||||
window.__ambre_fetch_calls.push({url: args[0], body: args[1] && args[1].body ? args[1].body.toString().substring(0, 200) : null});
|
||||
return origFetch.apply(this, args);
|
||||
};
|
||||
});
|
||||
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// Send PDF
|
||||
const input = page.locator("#msgInput");
|
||||
await input.fill("Genere un PDF sur: test debug");
|
||||
await input.press("Enter");
|
||||
await page.waitForTimeout(8000);
|
||||
|
||||
// Dump state
|
||||
const state = await page.evaluate(() => ({
|
||||
busy: typeof busy !== "undefined" ? busy : "undefined",
|
||||
pendingFile: typeof pendingFile !== "undefined" ? pendingFile : "undefined",
|
||||
fetch_calls: window.__ambre_fetch_calls || [],
|
||||
msg_input_disabled: document.getElementById("msgInput").disabled,
|
||||
msg_input_value: document.getElementById("msgInput").value,
|
||||
assistant_count: document.querySelectorAll(".msg.assistant").length,
|
||||
user_count: document.querySelectorAll(".msg.user").length,
|
||||
has_router: !!window._ambre_gen_pat,
|
||||
}));
|
||||
|
||||
console.log("=== STATE AFTER PDF SEND ===");
|
||||
console.log(JSON.stringify(state, null, 2));
|
||||
|
||||
// Now try 2nd message
|
||||
await input.fill("Genere un document Word sur: test2");
|
||||
await input.press("Enter");
|
||||
await page.waitForTimeout(8000);
|
||||
|
||||
const state2 = await page.evaluate(() => ({
|
||||
busy: typeof busy !== "undefined" ? busy : "undefined",
|
||||
fetch_calls_count: (window.__ambre_fetch_calls || []).length,
|
||||
last_fetch: (window.__ambre_fetch_calls || []).slice(-3),
|
||||
assistant_count: document.querySelectorAll(".msg.assistant").length,
|
||||
user_count: document.querySelectorAll(".msg.user").length,
|
||||
}));
|
||||
console.log("=== STATE AFTER WORD SEND ===");
|
||||
console.log(JSON.stringify(state2, null, 2));
|
||||
|
||||
// Write logs to file
|
||||
require("fs").writeFileSync("output/debug-console.log", logs.join("\n"));
|
||||
});
|
||||
JS;
|
||||
file_put_contents("$base/tests/debug-flow.spec.js", $spec);
|
||||
// Remove v5 to not rerun
|
||||
@unlink("$base/tests/chat-capabilities-v5.spec.js");
|
||||
echo json_encode(["ok"=>true, "size"=>filesize("$base/tests/debug-flow.spec.js")]);
|
||||
10
api/ambre-pw-kill.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
// Kill running playwright processes
|
||||
$killed = @shell_exec("pkill -f playwright 2>&1");
|
||||
sleep(1);
|
||||
$still = @shell_exec("ps aux | grep playwright | grep -v grep | wc -l");
|
||||
echo json_encode([
|
||||
"killed_output" => trim($killed ?: ""),
|
||||
"processes_remaining" => trim($still ?: ""),
|
||||
], JSON_PRETTY_PRINT);
|
||||
11
api/ambre-pw-killall.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
// Try multiple kill methods
|
||||
$out["pkill_user"] = trim(@shell_exec("pkill -9 -u www-data -f playwright 2>&1") ?: "(empty)");
|
||||
$out["pkill_f_all"] = trim(@shell_exec("pkill -9 -f chat-capabilities 2>&1") ?: "(empty)");
|
||||
$out["pkill_node"] = trim(@shell_exec("pkill -9 -f "node.*playwright" 2>&1") ?: "(empty)");
|
||||
sleep(2);
|
||||
$out["remaining"] = trim(@shell_exec("ps -ef | grep -E "playwright|chromium.*headless" | grep -v grep | wc -l") ?: "0");
|
||||
$out["procs_snapshot"] = trim(@shell_exec("ps -ef | grep playwright | grep -v grep | awk "{print \$2, \$10, \$11}" | head -10") ?: "");
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
13
api/ambre-pw-listnow.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["pngs"=>[], "videos"=>[]];
|
||||
foreach (glob("$base/v*.png") as $p) {
|
||||
$out["pngs"][] = ["name"=>basename($p), "kb"=>round(filesize($p)/1024,1), "mtime"=>gmdate("H:i:s", filemtime($p))];
|
||||
}
|
||||
usort($out["pngs"], function($a,$b){return strcmp($a["name"], $b["name"]);});
|
||||
|
||||
foreach (glob("$base/chat-capabilities-v*/*.webm") as $w) {
|
||||
$out["videos"][] = ["name"=>basename(dirname($w)), "mb"=>round(filesize($w)/1048576,2), "mtime"=>gmdate("H:i:s", filemtime($w))];
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT);
|
||||
10
api/ambre-pw-procs.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$procs = @shell_exec("pgrep -af playwright 2>&1 | head -5");
|
||||
$logs = glob("/tmp/ambre-pw-run-*.log");
|
||||
usort($logs, function($a,$b){return filemtime($b)-filemtime($a);});
|
||||
echo json_encode([
|
||||
"procs" => trim($procs ?: ""),
|
||||
"latest_log" => $logs ? basename($logs[0]) : "",
|
||||
"latest_size" => $logs ? filesize($logs[0]) : 0,
|
||||
], JSON_PRETTY_PRINT);
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"status": "passed",
|
||||
"failedTests": []
|
||||
}
|
||||
|
Before Width: | Height: | Size: 91 KiB |
@@ -1,179 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"configFile": "/var/www/html/api/ambre-pw-tests/playwright.config.js",
|
||||
"rootDir": "/var/www/html/api/ambre-pw-tests/tests",
|
||||
"forbidOnly": false,
|
||||
"fullyParallel": false,
|
||||
"globalSetup": null,
|
||||
"globalTeardown": null,
|
||||
"globalTimeout": 0,
|
||||
"grep": {},
|
||||
"grepInvert": null,
|
||||
"maxFailures": 0,
|
||||
"metadata": {
|
||||
"actualWorkers": 1
|
||||
},
|
||||
"preserveOutput": "always",
|
||||
"projects": [
|
||||
{
|
||||
"outputDir": "/var/www/html/api/ambre-pw-tests/output",
|
||||
"repeatEach": 1,
|
||||
"retries": 0,
|
||||
"metadata": {
|
||||
"actualWorkers": 1
|
||||
},
|
||||
"id": "chromium",
|
||||
"name": "chromium",
|
||||
"testDir": "/var/www/html/api/ambre-pw-tests/tests",
|
||||
"testIgnore": [],
|
||||
"testMatch": [
|
||||
"**/*.@(spec|test).?(c|m)[jt]s?(x)"
|
||||
],
|
||||
"timeout": 120000
|
||||
}
|
||||
],
|
||||
"quiet": false,
|
||||
"reporter": [
|
||||
[
|
||||
"list",
|
||||
null
|
||||
],
|
||||
[
|
||||
"json",
|
||||
{
|
||||
"outputFile": "./output/results.json"
|
||||
}
|
||||
]
|
||||
],
|
||||
"reportSlowTests": {
|
||||
"max": 5,
|
||||
"threshold": 300000
|
||||
},
|
||||
"shard": null,
|
||||
"tags": [],
|
||||
"updateSnapshots": "missing",
|
||||
"updateSourceMethod": "patch",
|
||||
"version": "1.59.1",
|
||||
"workers": 1,
|
||||
"webServer": null
|
||||
},
|
||||
"suites": [
|
||||
{
|
||||
"title": "chat-capabilities-v2.spec.js",
|
||||
"file": "chat-capabilities-v2.spec.js",
|
||||
"column": 0,
|
||||
"line": 0,
|
||||
"specs": [
|
||||
{
|
||||
"title": "8 capabilities wevia chat v2",
|
||||
"ok": true,
|
||||
"tags": [],
|
||||
"tests": [
|
||||
{
|
||||
"timeout": 240000,
|
||||
"annotations": [],
|
||||
"expectedStatus": "passed",
|
||||
"projectId": "chromium",
|
||||
"projectName": "chromium",
|
||||
"results": [
|
||||
{
|
||||
"workerIndex": 0,
|
||||
"parallelIndex": 0,
|
||||
"status": "passed",
|
||||
"duration": 129719,
|
||||
"errors": [],
|
||||
"stdout": [
|
||||
{
|
||||
"text": "📸 Initial state captured\n"
|
||||
},
|
||||
{
|
||||
"text": "[01] PDF: Genere un PDF sur: demo\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ PDF screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[02] Word: Genere un document Word sur: demo\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ Word screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[03] PPT: Genere une presentation sur: demo\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ PPT screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[04] Mermaid: Genere un schema mermaid pour: demo\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ Mermaid screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[05] Image: Genere une image: demo\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ Image screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[06] Code: Ecris le code python pour: fibonacci\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ Code screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[07] Traduire: Traduis en anglais: bonjour\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ Traduire screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "[08] Ping: ping\n"
|
||||
},
|
||||
{
|
||||
"text": " ✅ Ping screenshot\n"
|
||||
},
|
||||
{
|
||||
"text": "✅ Test 8/8 terminé\n"
|
||||
}
|
||||
],
|
||||
"stderr": [],
|
||||
"retry": 0,
|
||||
"startTime": "2026-04-21T14:40:50.563Z",
|
||||
"annotations": [],
|
||||
"attachments": [
|
||||
{
|
||||
"name": "screenshot",
|
||||
"contentType": "image/png",
|
||||
"path": "/var/www/html/api/ambre-pw-tests/output/chat-capabilities-v2-8-capabilities-wevia-chat-v2-chromium/test-finished-1.png"
|
||||
},
|
||||
{
|
||||
"name": "video",
|
||||
"contentType": "video/webm",
|
||||
"path": "/var/www/html/api/ambre-pw-tests/output/chat-capabilities-v2-8-capabilities-wevia-chat-v2-chromium/video.webm"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"status": "expected"
|
||||
}
|
||||
],
|
||||
"id": "42a6daa3f20cd9254d4a-390328c0310cc9e78f72",
|
||||
"file": "chat-capabilities-v2.spec.js",
|
||||
"line": 14,
|
||||
"column": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"stats": {
|
||||
"startTime": "2026-04-21T14:40:49.936Z",
|
||||
"duration": 130533.22899999999,
|
||||
"expected": 1,
|
||||
"skipped": 0,
|
||||
"unexpected": 0,
|
||||
"flaky": 0
|
||||
}
|
||||
}
|
||||
BIN
api/ambre-pw-tests/output/v12-00-landing.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
api/ambre-pw-tests/output/v12-01-intro.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 91 KiB |
@@ -2,7 +2,7 @@
|
||||
const { defineConfig, devices } = require("@playwright/test");
|
||||
module.exports = defineConfig({
|
||||
testDir: "./tests",
|
||||
timeout: 120000,
|
||||
timeout: 420000,
|
||||
retries: 0,
|
||||
workers: 1,
|
||||
use: {
|
||||
@@ -10,12 +10,10 @@ module.exports = defineConfig({
|
||||
trace: "off",
|
||||
screenshot: "on",
|
||||
video: "on",
|
||||
viewport: { width: 1280, height: 720 },
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
ignoreHTTPSErrors: true,
|
||||
},
|
||||
outputDir: "./output",
|
||||
reporter: [["list"], ["json", { outputFile: "./output/results.json" }]],
|
||||
projects: [
|
||||
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
|
||||
],
|
||||
projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"], viewport: { width: 1920, height: 1080 } } }],
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
const CAPABILITIES = [
|
||||
{ name: "PDF", msg: "Genere un PDF sur: demo" },
|
||||
{ name: "Word", msg: "Genere un document Word sur: demo" },
|
||||
{ name: "PPT", msg: "Genere une presentation sur: demo" },
|
||||
{ name: "Mermaid", msg: "Genere un schema mermaid pour: demo" },
|
||||
{ name: "Image", msg: "Genere une image: demo" },
|
||||
{ name: "Code", msg: "Ecris le code python pour: fibonacci" },
|
||||
{ name: "Traduire", msg: "Traduis en anglais: bonjour" },
|
||||
{ name: "Ping", msg: "ping" },
|
||||
];
|
||||
|
||||
test("8 capabilities wevia chat v2", async ({ page }) => {
|
||||
test.setTimeout(240000);
|
||||
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(1200);
|
||||
await page.screenshot({ path: "output/v2-00-initial.png" });
|
||||
console.log("📸 Initial state captured");
|
||||
|
||||
const input = page.locator("#msgInput");
|
||||
|
||||
for (let i = 0; i < CAPABILITIES.length; i++) {
|
||||
const cap = CAPABILITIES[i];
|
||||
const num = String(i + 1).padStart(2, "0");
|
||||
console.log(`[${num}] ${cap.name}: ${cap.msg}`);
|
||||
|
||||
try {
|
||||
await input.click({ timeout: 5000 });
|
||||
await input.fill(cap.msg);
|
||||
await page.waitForTimeout(300);
|
||||
await input.press("Enter");
|
||||
|
||||
// Wait for response message to appear (shorter timeout)
|
||||
await page.waitForTimeout(15000); // 15s fixed wait per test
|
||||
|
||||
await page.screenshot({ path: `output/v2-${num}-${cap.name}.png` });
|
||||
console.log(` ✅ ${cap.name} screenshot`);
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ ${cap.name} error: ${e.message.substring(0, 80)}`);
|
||||
}
|
||||
}
|
||||
|
||||
await page.screenshot({ path: "output/v2-99-final.png", fullPage: true });
|
||||
console.log("✅ Test 8/8 terminé");
|
||||
});
|
||||
154
api/ambre-pw-tests/tests/conversation-v12.spec.js
Normal file
@@ -0,0 +1,154 @@
|
||||
const { test } = require("@playwright/test");
|
||||
const fs = require("fs");
|
||||
|
||||
test("V12 · Long conversation · memory + empathy + subject change + improvements", async ({ page, context, request }) => {
|
||||
test.setTimeout(1200000); // 20min max
|
||||
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(2500);
|
||||
await page.screenshot({ path: "output/v12-00-landing.png" });
|
||||
console.log("📸 Landing");
|
||||
|
||||
// Scenario: 16 turns covering memory, iteration, subject change, empathy
|
||||
const CONVERSATION = [
|
||||
{ turn:1, type:"intro", msg:"je suis Yacine CEO de WEVAL Consulting et je pilote la strategie pharma au MENA", expect:"Yacine", check:"memory_init" },
|
||||
{ turn:2, type:"gen-pdf", msg:"Genere un PDF sur: strategie pharma Q2 2026 MENA", expect:".pdf", check:"first_pdf" },
|
||||
{ turn:3, type:"iteration", msg:"ce PDF est trop court refais-le avec plus de details et sections analytiques", expect:".pdf", check:"improved_pdf" },
|
||||
{ turn:4, type:"memory-ref", msg:"rappelle moi quel etait le sujet de mon premier PDF", expect:"pharma", check:"memory_recall" },
|
||||
{ turn:5, type:"subject-change-code", msg:"change complet de sujet: ecris moi le code python pour calculer fibonacci", expect:".py", check:"subject_change_code" },
|
||||
{ turn:6, type:"iteration-code", msg:"refais ce code en version iterative plus performante avec memoization", expect:".py", check:"improved_code" },
|
||||
{ turn:7, type:"empathy", msg:"je suis un peu epuise ce soir journee longue", expect:"/(comprends|courage|repos|fatigue|dure|difficile)/i", check:"empathy" },
|
||||
{ turn:8, type:"memory-all", msg:"recapitule en 3 points ce qu on a fait ensemble depuis le debut", expect:"/(pharma|fibonacci|PDF|code|python)/i", check:"full_memory" },
|
||||
{ turn:9, type:"gen-schema", msg:"Genere un schema mermaid pour le processus vente pharma en lean six sigma", expect:"graph TD", check:"mermaid_gen" },
|
||||
{ turn:10, type:"improvement-meta", msg:"ameliore tes generations pour la suite: inclus systematiquement un executive summary", expect:"/(compris|note|appliquer|executive)/i", check:"meta_learning" },
|
||||
{ turn:11, type:"gen-after-learning", msg:"Genere un document Word sur le bilan Q2 avec le nouveau standard", expect:".docx", check:"applied_learning" },
|
||||
{ turn:12, type:"translate", msg:"Traduis en anglais: la strategie pharma MENA pour Q3", expect:"English", check:"translate" },
|
||||
{ turn:13, type:"emotion-pos", msg:"super merci tu fais du bon travail je suis content", expect:"/(merci|content|plaisir|ravi|heureux)/i", check:"positive_emotion" },
|
||||
{ turn:14, type:"long-question", msg:"explique moi les 5 piliers d une strategie pharma reussie au MENA selon toi", expect:"/(.{200,})/", check:"long_answer" },
|
||||
{ turn:15, type:"final-memory",msg:"derniere question: comment s appelle le projet qu on vient de discuter et qui le pilote?", expect:"/(Yacine|WEVAL|pharma|MENA)/i", check:"final_memory" },
|
||||
{ turn:16, type:"bye", msg:"merci bonne nuit", expect:"/(nuit|demain|bientot|plaisir|bonne)/i", check:"farewell" },
|
||||
];
|
||||
|
||||
const results = [];
|
||||
let totalPasses = 0;
|
||||
|
||||
for (const t of CONVERSATION) {
|
||||
const num = String(t.turn).padStart(2, "0");
|
||||
console.log(`\n═══ [${num}/16] ${t.type} ═══`);
|
||||
console.log(` 👤 ${t.msg.substring(0, 80)}`);
|
||||
|
||||
try {
|
||||
// Count needle/regex BEFORE
|
||||
const isRegex = t.expect.startsWith("/") && t.expect.endsWith("/") || t.expect.match(/^\/.+\/[gimuy]*$/);
|
||||
let regex;
|
||||
try {
|
||||
if (isRegex) {
|
||||
const m = t.expect.match(/^\/(.+)\/([gimuy]*)$/);
|
||||
regex = new RegExp(m[1], m[2] || "i");
|
||||
} else {
|
||||
regex = new RegExp(t.expect.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
|
||||
}
|
||||
} catch (e) {
|
||||
regex = new RegExp(t.expect.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
|
||||
}
|
||||
|
||||
const beforeMatches = await page.evaluate((p) => {
|
||||
try {
|
||||
const r = new RegExp(p, "gi");
|
||||
return (document.body.innerText.match(r) || []).length;
|
||||
} catch(e) { return 0; }
|
||||
}, regex.source);
|
||||
|
||||
// Send
|
||||
const input = page.locator("#msgInput");
|
||||
await input.click({ force: true });
|
||||
await page.keyboard.press("Control+A");
|
||||
await page.keyboard.press("Delete");
|
||||
await input.fill(t.msg);
|
||||
await page.waitForTimeout(400);
|
||||
await input.press("Enter");
|
||||
|
||||
// Wait for response (regex count increase)
|
||||
const waitStart = Date.now();
|
||||
let matched = false;
|
||||
let responseText = "";
|
||||
while (Date.now() - waitStart < 55000) {
|
||||
const afterMatches = await page.evaluate((p) => {
|
||||
try {
|
||||
const r = new RegExp(p, "gi");
|
||||
return (document.body.innerText.match(r) || []).length;
|
||||
} catch(e) { return 0; }
|
||||
}, regex.source);
|
||||
if (afterMatches > beforeMatches) {
|
||||
matched = true;
|
||||
// Grab last assistant message text
|
||||
responseText = await page.evaluate(() => {
|
||||
const msgs = document.querySelectorAll(".msg.assistant .bubble, .msg.assistant");
|
||||
if (msgs.length === 0) return "";
|
||||
return msgs[msgs.length - 1].innerText.substring(0, 400);
|
||||
});
|
||||
break;
|
||||
}
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
const elapsed = ((Date.now() - waitStart) / 1000).toFixed(1);
|
||||
|
||||
// Scroll chat to bottom
|
||||
await page.evaluate(() => {
|
||||
const msgs = document.getElementById("messages");
|
||||
if (msgs) msgs.scrollTop = msgs.scrollHeight;
|
||||
});
|
||||
await page.waitForTimeout(1800);
|
||||
|
||||
// Screenshot
|
||||
await page.screenshot({ path: `output/v12-${num}-${t.type}.png` });
|
||||
|
||||
// Count turns_in_memory from any badge
|
||||
const turnsInMemory = await page.evaluate(() => {
|
||||
const b = Array.from(document.querySelectorAll(".nx-badge")).find(x => x.innerText.includes("tours"));
|
||||
return b ? (b.innerText.match(/(\d+)\s*tours/) || [])[1] : null;
|
||||
});
|
||||
|
||||
console.log(` 🤖 ${responseText.substring(0, 150).replace(/\n/g, " ")}`);
|
||||
console.log(` ${matched ? "✅" : "❌"} matched in ${elapsed}s · turns_in_memory: ${turnsInMemory || "?"}`);
|
||||
|
||||
if (matched) totalPasses++;
|
||||
results.push({
|
||||
turn: t.turn,
|
||||
type: t.type,
|
||||
msg: t.msg.substring(0,60),
|
||||
pass: matched,
|
||||
elapsed_s: parseFloat(elapsed),
|
||||
turns_in_memory: turnsInMemory,
|
||||
response_preview: responseText.substring(0, 200)
|
||||
});
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
} catch (e) {
|
||||
console.log(` ❌ ERR: ${e.message.substring(0, 120)}`);
|
||||
results.push({ turn: t.turn, type: t.type, pass: false, error: e.message.substring(0,200) });
|
||||
}
|
||||
}
|
||||
|
||||
// Final fullpage
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await page.waitForTimeout(2500);
|
||||
await page.screenshot({ path: "output/v12-99-final.png", fullPage: true });
|
||||
|
||||
// Write summary
|
||||
const summary = {
|
||||
test: "V12 long conversation",
|
||||
turns_total: CONVERSATION.length,
|
||||
passes: totalPasses,
|
||||
results: results,
|
||||
ts: new Date().toISOString(),
|
||||
};
|
||||
fs.writeFileSync("output/v12-results.json", JSON.stringify(summary, null, 2));
|
||||
|
||||
console.log(`\n═══════════════ V12 BILAN ═══════════════`);
|
||||
console.log(`${totalPasses}/${CONVERSATION.length} turns PASS · memory + empathy + subject change + iterations`);
|
||||
results.forEach(r => {
|
||||
console.log(` ${r.pass ? "✅" : "❌"} [${String(r.turn).padStart(2,"0")}] ${r.type.padEnd(22)} · ${r.elapsed_s||0}s · tours:${r.turns_in_memory||"?"}`);
|
||||
});
|
||||
});
|
||||
7
api/ambre-pw-v10-deploy.php
Normal file
38
api/ambre-pw-v10-files.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["v10_files_proof"=>[], "v10_chat"=>[], "v10_binaries"=>[], "v10_video"=>null];
|
||||
|
||||
foreach (glob("$base/v10-*-FILE.png") as $p) {
|
||||
$out["v10_files_proof"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
foreach (glob("$base/v10-*-chat.png") as $p) {
|
||||
$out["v10_chat"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
foreach (glob("$base/v10-*.{pdf,docx,pptx,svg,py}", GLOB_BRACE) as $p) {
|
||||
$out["v10_binaries"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
foreach (glob("$base/capabilities-v10-*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v10_video"] = [
|
||||
"size_mb" => round(filesize($w)/1048576, 2),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
|
||||
usort($out["v10_files_proof"], function($a,$b){return strcmp($a["name"],$b["name"]);});
|
||||
usort($out["v10_chat"], function($a,$b){return strcmp($a["name"],$b["name"]);});
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
7
api/ambre-pw-v11-deploy.php
Normal file
37
api/ambre-pw-v11-files.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["v11_claude_pattern"=>[], "v11_video"=>null];
|
||||
|
||||
foreach (glob("$base/v11-0*.png") as $p) {
|
||||
$out["v11_claude_pattern"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
usort($out["v11_claude_pattern"], function($a,$b){return strcmp($a["name"],$b["name"]);});
|
||||
|
||||
// The Claude-pattern v11 should have its own video in capabilities-v11-... or claude-pattern-v11-...
|
||||
foreach (glob("$base/claude-pattern*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v11_video"] = [
|
||||
"name" => "claude-pattern SSE",
|
||||
"size_mb" => round(filesize($w)/1048576, 2),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
// Fallback: find any recent webm
|
||||
if (!$out["v11_video"]) {
|
||||
foreach (glob("$base/capabilities-v11*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v11_video"] = [
|
||||
"name" => "capabilities-v11",
|
||||
"size_mb" => round(filesize($w)/1048576, 2),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
"note" => "first v11 run (capabilities)",
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
10
api/ambre-pw-v12-deploy.php
Normal file
23
api/ambre-pw-v3-files.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["v3_screenshots"=>[], "v3_video"=>null];
|
||||
|
||||
foreach (glob("$base/v3-*.png") as $p) {
|
||||
$out["v3_screenshots"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
|
||||
foreach (glob("$base/chat-capabilities-v3-*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v3_video"] = [
|
||||
"size_kb" => round(filesize($w)/1024, 1),
|
||||
"size_mb" => round(filesize($w)/1048576, 2),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
121
api/ambre-pw-v3.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
|
||||
// V3 improved spec — actually waits for file URL + scrolls + viewport 1920x1080
|
||||
$spec = <<<'JS'
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
const CAPABILITIES = [
|
||||
{ name: "PDF", msg: "Genere un PDF sur: WEVIA enterprise demo", needle: ".pdf" },
|
||||
{ name: "Word", msg: "Genere un document Word sur: strategie pharma", needle: ".docx" },
|
||||
{ name: "PPT", msg: "Genere une presentation sur: pitch investor", needle: ".pptx" },
|
||||
{ name: "Mermaid", msg: "Genere un schema mermaid pour: workflow commande", needle: "graph TD" },
|
||||
{ name: "Image", msg: "Genere une image: paysage montagne", needle: ".svg" },
|
||||
{ name: "Code", msg: "Ecris le code python pour: fibonacci recursif", needle: ".py" },
|
||||
{ name: "Traduire", msg: "Traduis en anglais: bonjour comment allez-vous", needle: "English:" },
|
||||
{ name: "Ping", msg: "ping", needle: "WEVIA Engine" },
|
||||
];
|
||||
|
||||
test("8 capabilities video + file URLs verified", async ({ page }) => {
|
||||
test.setTimeout(420000); // 7min safety
|
||||
|
||||
// Navigate
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v3-00-initial.png", fullPage: false });
|
||||
console.log("📸 Initial state captured (1920x1080)");
|
||||
|
||||
const input = page.locator("#msgInput");
|
||||
|
||||
for (let i = 0; i < CAPABILITIES.length; i++) {
|
||||
const cap = CAPABILITIES[i];
|
||||
const num = String(i + 1).padStart(2, "0");
|
||||
console.log(`\n[${num}/8] ${cap.name}: ${cap.msg}`);
|
||||
|
||||
try {
|
||||
// Clear + type
|
||||
await input.click({ timeout: 5000 });
|
||||
await input.fill("");
|
||||
await page.waitForTimeout(300);
|
||||
await input.fill(cap.msg);
|
||||
await page.waitForTimeout(500);
|
||||
await input.press("Enter");
|
||||
console.log(` sent, waiting for needle "${cap.needle}"...`);
|
||||
|
||||
// Wait for response with the needle OR 40s timeout
|
||||
const waitStart = Date.now();
|
||||
let found = false;
|
||||
while (Date.now() - waitStart < 40000) {
|
||||
const bodyText = await page.evaluate(() => document.body.innerText);
|
||||
if (bodyText.includes(cap.needle)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
const elapsed = ((Date.now() - waitStart) / 1000).toFixed(1);
|
||||
|
||||
if (found) {
|
||||
console.log(` ✅ Response has "${cap.needle}" in ${elapsed}s`);
|
||||
} else {
|
||||
console.log(` ⚠️ Needle not found after ${elapsed}s`);
|
||||
}
|
||||
|
||||
// Scroll to bottom to see latest message
|
||||
await page.evaluate(() => {
|
||||
const msgs = document.getElementById("messages");
|
||||
if (msgs) msgs.scrollTop = msgs.scrollHeight;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// Screenshot full page to see entire response with download link
|
||||
await page.screenshot({ path: `output/v3-${num}-${cap.name}.png`, fullPage: true });
|
||||
console.log(` 📸 Screenshot v3-${num}-${cap.name}.png`);
|
||||
|
||||
} catch (e) {
|
||||
console.log(` ❌ Error: ${e.message.substring(0, 100)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Final fullpage screenshot
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v3-99-final.png", fullPage: true });
|
||||
console.log("\n✅ Test V3 8/8 terminé");
|
||||
});
|
||||
JS;
|
||||
file_put_contents("$base/tests/chat-capabilities-v3.spec.js", $spec);
|
||||
|
||||
// Remove old v2 spec
|
||||
@unlink("$base/tests/chat-capabilities-v2.spec.js");
|
||||
|
||||
// Update config viewport to 1920x1080
|
||||
$cfg = '// @ts-check
|
||||
const { defineConfig, devices } = require("@playwright/test");
|
||||
module.exports = defineConfig({
|
||||
testDir: "./tests",
|
||||
timeout: 420000,
|
||||
retries: 0,
|
||||
workers: 1,
|
||||
use: {
|
||||
baseURL: "https://weval-consulting.com",
|
||||
trace: "off",
|
||||
screenshot: "on",
|
||||
video: "on",
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
ignoreHTTPSErrors: true,
|
||||
},
|
||||
outputDir: "./output",
|
||||
reporter: [["list"], ["json", { outputFile: "./output/results.json" }]],
|
||||
projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"], viewport: { width: 1920, height: 1080 } } }],
|
||||
});';
|
||||
file_put_contents("$base/playwright.config.js", $cfg);
|
||||
|
||||
echo json_encode([
|
||||
"ok" => true,
|
||||
"spec_size" => filesize("$base/tests/chat-capabilities-v3.spec.js"),
|
||||
"config_size" => filesize("$base/playwright.config.js"),
|
||||
]);
|
||||
108
api/ambre-pw-v4.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
|
||||
// V4 spec — use send button click + wait for assistant message count to increment
|
||||
$spec = <<<'JS'
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
const CAPABILITIES = [
|
||||
{ name: "PDF", msg: "Genere un PDF sur: WEVIA enterprise demo", needle: ".pdf" },
|
||||
{ name: "Word", msg: "Genere un document Word sur: strategie pharma", needle: ".docx" },
|
||||
{ name: "PPT", msg: "Genere une presentation sur: pitch investor", needle: ".pptx" },
|
||||
{ name: "Mermaid", msg: "Genere un schema mermaid pour: workflow vente", needle: "graph TD" },
|
||||
{ name: "Image", msg: "Genere une image: paysage montagne", needle: ".svg" },
|
||||
{ name: "Code", msg: "Ecris le code python pour: fibonacci", needle: ".py" },
|
||||
{ name: "Traduire", msg: "Traduis en anglais: bonjour merci", needle: "English:" },
|
||||
{ name: "Ping", msg: "ping", needle: "WEVIA Engine" },
|
||||
];
|
||||
|
||||
test("8 capabilities v4 video proof", async ({ page }) => {
|
||||
test.setTimeout(480000);
|
||||
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v4-00-initial.png", fullPage: false });
|
||||
console.log("📸 Initial 1920x1080");
|
||||
|
||||
for (let i = 0; i < CAPABILITIES.length; i++) {
|
||||
const cap = CAPABILITIES[i];
|
||||
const num = String(i + 1).padStart(2, "0");
|
||||
console.log(`\n[${num}/8] ${cap.name}: ${cap.msg}`);
|
||||
|
||||
try {
|
||||
// Count assistant messages BEFORE sending
|
||||
const beforeCount = await page.evaluate(() =>
|
||||
document.querySelectorAll(".msg.assistant").length
|
||||
);
|
||||
|
||||
// Focus input aggressively, clear, fill
|
||||
const input = page.locator("#msgInput");
|
||||
await input.click({ force: true, timeout: 5000 });
|
||||
await page.keyboard.press("Control+A");
|
||||
await page.keyboard.press("Delete");
|
||||
await page.waitForTimeout(200);
|
||||
await input.fill(cap.msg);
|
||||
await page.waitForTimeout(300);
|
||||
|
||||
// Click send button (more reliable than Enter)
|
||||
const sendBtn = await page.locator(".send-btn, #sendBtn, button[onclick*=send], button.send").first();
|
||||
try {
|
||||
await sendBtn.click({ force: true, timeout: 3000 });
|
||||
} catch (e) {
|
||||
// fallback Enter
|
||||
await input.press("Enter");
|
||||
}
|
||||
console.log(` 📤 Sent (msg count before: ${beforeCount})`);
|
||||
|
||||
// Wait for NEW assistant message to appear + contain needle
|
||||
const waitStart = Date.now();
|
||||
let found = false;
|
||||
let msgAdded = false;
|
||||
while (Date.now() - waitStart < 45000) {
|
||||
const afterCount = await page.evaluate(() =>
|
||||
document.querySelectorAll(".msg.assistant").length
|
||||
);
|
||||
if (afterCount > beforeCount) {
|
||||
msgAdded = true;
|
||||
const bodyText = await page.evaluate(() => document.body.innerText);
|
||||
if (bodyText.includes(cap.needle)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
const elapsed = ((Date.now() - waitStart) / 1000).toFixed(1);
|
||||
|
||||
if (found) console.log(` ✅ "${cap.needle}" found after ${elapsed}s (msg added: ${msgAdded})`);
|
||||
else console.log(` ⚠️ needle not found · ${elapsed}s · msg added: ${msgAdded}`);
|
||||
|
||||
// Scroll to bottom
|
||||
await page.evaluate(() => {
|
||||
const msgs = document.getElementById("messages");
|
||||
if (msgs) msgs.scrollTop = msgs.scrollHeight;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
await page.waitForTimeout(1500);
|
||||
|
||||
// Full page screenshot
|
||||
await page.screenshot({ path: `output/v4-${num}-${cap.name}.png`, fullPage: true });
|
||||
console.log(` 📸 v4-${num}-${cap.name}.png`);
|
||||
|
||||
} catch (e) {
|
||||
console.log(` ❌ ${e.message.substring(0, 100)}`);
|
||||
}
|
||||
}
|
||||
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v4-99-final.png", fullPage: true });
|
||||
console.log("\n✅ Test V4 8/8 terminé");
|
||||
});
|
||||
JS;
|
||||
file_put_contents("$base/tests/chat-capabilities-v4.spec.js", $spec);
|
||||
@unlink("$base/tests/chat-capabilities-v3.spec.js");
|
||||
|
||||
echo json_encode(["ok"=>true, "spec_size"=>filesize("$base/tests/chat-capabilities-v4.spec.js")]);
|
||||
16
api/ambre-pw-v5.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
// Copy V4 → V5 (same spec content, fresh test)
|
||||
if (file_exists("$base/tests/chat-capabilities-v4.spec.js")) {
|
||||
$content = file_get_contents("$base/tests/chat-capabilities-v4.spec.js");
|
||||
// Replace all v4 → v5
|
||||
$content = str_replace('"v4-', '"v5-', $content);
|
||||
$content = str_replace("V4", "V5", $content);
|
||||
$content = str_replace("v4 video proof", "v5 video busy-reset fix", $content);
|
||||
file_put_contents("$base/tests/chat-capabilities-v5.spec.js", $content);
|
||||
@unlink("$base/tests/chat-capabilities-v4.spec.js");
|
||||
echo json_encode(["ok"=>true, "size"=>filesize("$base/tests/chat-capabilities-v5.spec.js")]);
|
||||
} else {
|
||||
echo json_encode(["ok"=>false, "error"=>"v4 spec not found"]);
|
||||
}
|
||||
25
api/ambre-pw-v6-files.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["v6_screenshots"=>[], "v6_video"=>null];
|
||||
|
||||
foreach (glob("$base/v6-*.png") as $p) {
|
||||
$out["v6_screenshots"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
usort($out["v6_screenshots"], function($a,$b){return strcmp($a["name"], $b["name"]);});
|
||||
|
||||
foreach (glob("$base/capabilities-v6-*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v6_video"] = [
|
||||
"name" => basename($w),
|
||||
"size_mb" => round(filesize($w)/1048576, 2),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . str_replace("·", "-", $rel),
|
||||
"url_raw" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
100
api/ambre-pw-v6.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
|
||||
// V6 — fixed message counting by using the innerText search for the specific needle that only appears AFTER the new response
|
||||
$spec = <<<'JS'
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
const CAPABILITIES = [
|
||||
{ name: "PDF", msg: "Genere un PDF sur: WEVIA enterprise demo", needle: ".pdf" },
|
||||
{ name: "Word", msg: "Genere un document Word sur: strategie pharma", needle: ".docx" },
|
||||
{ name: "PPT", msg: "Genere une presentation sur: pitch investor", needle: ".pptx" },
|
||||
{ name: "Mermaid", msg: "Genere un schema mermaid pour: workflow ventes", needle: "graph TD" },
|
||||
{ name: "Image", msg: "Genere une image: paysage montagne coucher soleil", needle: ".svg" },
|
||||
{ name: "Code", msg: "Ecris le code python pour: fibonacci recursif", needle: "wevia-code" },
|
||||
{ name: "Traduire", msg: "Traduis en anglais: bonjour comment allez-vous aujourdhui", needle: "English" },
|
||||
{ name: "Ping", msg: "ping", needle: "WEVIA Engine" },
|
||||
];
|
||||
|
||||
test("V6 8 capabilities · needle counting fix · full video", async ({ page }) => {
|
||||
test.setTimeout(600000);
|
||||
|
||||
// Capture console for debugging
|
||||
page.on("console", msg => {
|
||||
if (msg.type() === "error" || msg.text().includes("Ambre")) {
|
||||
console.log(`[browser ${msg.type()}]`, msg.text().substring(0, 200));
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v6-00-initial.png", fullPage: false });
|
||||
console.log("📸 Initial captured");
|
||||
|
||||
for (let i = 0; i < CAPABILITIES.length; i++) {
|
||||
const cap = CAPABILITIES[i];
|
||||
const num = String(i + 1).padStart(2, "0");
|
||||
console.log(`\n[${num}/8] ${cap.name}: ${cap.msg}`);
|
||||
|
||||
try {
|
||||
// Count needle occurrences BEFORE sending
|
||||
const beforeNeedleCount = await page.evaluate((n) =>
|
||||
(document.body.innerText.match(new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) || []).length
|
||||
, cap.needle);
|
||||
|
||||
// Fill + Enter
|
||||
const input = page.locator("#msgInput");
|
||||
await input.click({ force: true });
|
||||
await input.fill(cap.msg);
|
||||
await page.waitForTimeout(400);
|
||||
await input.press("Enter");
|
||||
console.log(` 📤 Sent (needle "${cap.needle}" count before: ${beforeNeedleCount})`);
|
||||
|
||||
// Wait for needle count to INCREASE
|
||||
const waitStart = Date.now();
|
||||
let found = false;
|
||||
while (Date.now() - waitStart < 50000) {
|
||||
const afterCount = await page.evaluate((n) =>
|
||||
(document.body.innerText.match(new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) || []).length
|
||||
, cap.needle);
|
||||
if (afterCount > beforeNeedleCount) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
const elapsed = ((Date.now() - waitStart) / 1000).toFixed(1);
|
||||
console.log(found ? ` ✅ needle count increased in ${elapsed}s` : ` ⚠️ no new needle after ${elapsed}s`);
|
||||
|
||||
// Scroll to bottom to show latest
|
||||
await page.evaluate(() => {
|
||||
const msgs = document.getElementById("messages");
|
||||
if (msgs) msgs.scrollTop = msgs.scrollHeight;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
});
|
||||
await page.waitForTimeout(2500);
|
||||
|
||||
// Screenshot (viewport only, pour voir la bulle récente)
|
||||
await page.screenshot({ path: `output/v6-${num}-${cap.name}.png`, fullPage: false });
|
||||
console.log(` 📸 v6-${num}-${cap.name}.png`);
|
||||
|
||||
// Wait between tests for chat to settle
|
||||
await page.waitForTimeout(2000);
|
||||
} catch (e) {
|
||||
console.log(` ❌ Error: ${e.message.substring(0, 120)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Final full page
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v6-99-final.png", fullPage: true });
|
||||
console.log("\n✅ V6 terminé");
|
||||
});
|
||||
JS;
|
||||
file_put_contents("$base/tests/capabilities-v6.spec.js", $spec);
|
||||
@unlink("$base/tests/debug-flow.spec.js");
|
||||
|
||||
echo json_encode(["ok"=>true, "size"=>filesize("$base/tests/capabilities-v6.spec.js")]);
|
||||
123
api/ambre-pw-v7.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests";
|
||||
|
||||
$spec = <<<'JS'
|
||||
const { test, expect } = require("@playwright/test");
|
||||
|
||||
const CAPABILITIES = [
|
||||
{ name: "PDF", msg: "Genere un PDF sur: strategie WEVIA 2026", needle: "generated/wevia-" },
|
||||
{ name: "Word", msg: "Genere un document Word sur: procedure qualite", needle: "generated/wevia-" },
|
||||
{ name: "PPT", msg: "Genere une presentation sur: pitch deck investor", needle: "generated/wevia-" },
|
||||
{ name: "Mermaid", msg: "Genere un schema mermaid pour: workflow commandes", needle: "graph TD" },
|
||||
{ name: "Image", msg: "Genere une image: paysage nature forest", needle: "generated/wevia-img" },
|
||||
{ name: "Code", msg: "Ecris le code python pour: fibonacci recursif", needle: "wevia-code" },
|
||||
{ name: "Traduire", msg: "Traduis en anglais: merci beaucoup mon ami", needle: "English" },
|
||||
{ name: "Bilan", msg: "bilan complet system", needle: "WEVIA" },
|
||||
];
|
||||
|
||||
test("V7 8/8 capabilities · robust JSON + retry · full video", async ({ page }) => {
|
||||
test.setTimeout(480000);
|
||||
|
||||
let errorCount = 0;
|
||||
page.on("pageerror", err => { errorCount++; console.log(`[err] ${err.message.substring(0, 150)}`); });
|
||||
page.on("console", msg => {
|
||||
if (msg.type() === "error") {
|
||||
const t = msg.text();
|
||||
if (!t.includes("503") && !t.includes("favicon")) console.log(`[console err] ${t.substring(0, 150)}`);
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto("/wevia.html");
|
||||
await page.waitForLoadState("networkidle");
|
||||
await page.waitForTimeout(2500);
|
||||
await page.screenshot({ path: "output/v7-00-initial.png", fullPage: false });
|
||||
console.log("📸 Initial v7 captured");
|
||||
|
||||
const results = [];
|
||||
|
||||
for (let i = 0; i < CAPABILITIES.length; i++) {
|
||||
const cap = CAPABILITIES[i];
|
||||
const num = String(i + 1).padStart(2, "0");
|
||||
console.log(`\n[${num}/8] ${cap.name}`);
|
||||
console.log(` msg: ${cap.msg}`);
|
||||
|
||||
let success = false;
|
||||
let attempts = 0;
|
||||
const maxAttempts = 2;
|
||||
|
||||
while (!success && attempts < maxAttempts) {
|
||||
attempts++;
|
||||
const attemptLabel = attempts > 1 ? ` (retry ${attempts})` : "";
|
||||
|
||||
try {
|
||||
const beforeNeedleCount = await page.evaluate((n) =>
|
||||
(document.body.innerText.match(new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) || []).length
|
||||
, cap.needle);
|
||||
|
||||
const input = page.locator("#msgInput");
|
||||
await input.click({ force: true });
|
||||
await page.keyboard.press("Control+A");
|
||||
await page.keyboard.press("Delete");
|
||||
await input.fill(cap.msg);
|
||||
await page.waitForTimeout(400);
|
||||
await input.press("Enter");
|
||||
console.log(` 📤 sent${attemptLabel} (needle "${cap.needle}" before: ${beforeNeedleCount})`);
|
||||
|
||||
const waitStart = Date.now();
|
||||
while (Date.now() - waitStart < 45000) {
|
||||
const afterCount = await page.evaluate((n) =>
|
||||
(document.body.innerText.match(new RegExp(n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) || []).length
|
||||
, cap.needle);
|
||||
if (afterCount > beforeNeedleCount) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
const elapsed = ((Date.now() - waitStart) / 1000).toFixed(1);
|
||||
|
||||
if (success) {
|
||||
console.log(` ✅ PASS in ${elapsed}s${attemptLabel}`);
|
||||
} else {
|
||||
console.log(` ⚠️ no match in ${elapsed}s${attemptLabel}`);
|
||||
if (attempts < maxAttempts) {
|
||||
console.log(` 🔁 will retry...`);
|
||||
await page.waitForTimeout(3000);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` ❌ attempt${attemptLabel} err: ${e.message.substring(0, 100)}`);
|
||||
if (attempts < maxAttempts) await page.waitForTimeout(2000);
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll + screenshot
|
||||
await page.evaluate(() => {
|
||||
const msgs = document.getElementById("messages");
|
||||
if (msgs) msgs.scrollTop = msgs.scrollHeight;
|
||||
});
|
||||
await page.waitForTimeout(2500);
|
||||
await page.screenshot({ path: `output/v7-${num}-${cap.name}.png`, fullPage: false });
|
||||
console.log(` 📸 v7-${num}-${cap.name}.png`);
|
||||
|
||||
results.push({ name: cap.name, pass: success, attempts: attempts });
|
||||
await page.waitForTimeout(1500);
|
||||
}
|
||||
|
||||
// Final full page
|
||||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||||
await page.waitForTimeout(2000);
|
||||
await page.screenshot({ path: "output/v7-99-final.png", fullPage: true });
|
||||
|
||||
const passCount = results.filter(r => r.pass).length;
|
||||
console.log(`\n═══ V7 BILAN ═══`);
|
||||
console.log(`Result: ${passCount}/8 capabilities PASS`);
|
||||
console.log(`Page errors: ${errorCount}`);
|
||||
results.forEach(r => console.log(` ${r.pass ? "✅" : "❌"} ${r.name} (${r.attempts} attempt${r.attempts>1?"s":""})`));
|
||||
});
|
||||
JS;
|
||||
file_put_contents("$base/tests/capabilities-v7.spec.js", $spec);
|
||||
@unlink("$base/tests/capabilities-v6.spec.js");
|
||||
|
||||
echo json_encode(["ok"=>true, "size"=>filesize("$base/tests/capabilities-v7.spec.js")]);
|
||||
11
api/ambre-pw-v8-deploy.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/tests";
|
||||
$spec = base64_decode('Y29uc3QgeyB0ZXN0IH0gPSByZXF1aXJlKCJAcGxheXdyaWdodC90ZXN0Iik7CmNvbnN0IENBUEFCSUxJVElFUyA9IFsKICB7IG5hbWU6ICJQREYiLCAgICAgIG1zZzogIkdlbmVyZSB1biBQREYgc3VyOiBzdHJhdGVnaWUgZW50ZXJwcmlzZSAyMDI2IiwgICAgICAgIG5lZWRsZTogIi5wZGYiIH0sCiAgeyBuYW1lOiAiV29yZCIsICAgICBtc2c6ICJHZW5lcmUgdW4gZG9jdW1lbnQgV29yZCBzdXI6IG1hbnVlbCBwcm9jZWR1cmUgcXVhbGl0ZSIsIG5lZWRsZTogIi5kb2N4IiB9LAogIHsgbmFtZTogIlBQVCIsICAgICAgbXNnOiAiR2VuZXJlIHVuZSBwcmVzZW50YXRpb24gc3VyOiBwaXRjaCBpbnZlc3RvciBzZXJpZXMgQSIsICBuZWVkbGU6ICIucHB0eCIgfSwKICB7IG5hbWU6ICJNZXJtYWlkIiwgIG1zZzogIkdlbmVyZSB1biBzY2hlbWEgbWVybWFpZCBwb3VyOiBwcm9jZXNzdXMgYWNoYXQgdmFsaWRlIiwgbmVlZGxlOiAiZ3JhcGggVEQiIH0sCiAgeyBuYW1lOiAiSW1hZ2UiLCAgICBtc2c6ICJHZW5lcmUgdW5lIGltYWdlOiBvYXNpcyBwYWxtaWVycyBjb3VjaGVyIHNvbGVpbCIsICAgICAgIG5lZWRsZTogImdlbmVyYXRlZC93ZXZpYS1pbWciIH0sCiAgeyBuYW1lOiAiQ29kZSIsICAgICBtc2c6ICJFY3JpcyBsZSBjb2RlIHB5dGhvbiBwb3VyOiBmaWJvbmFjY2kgcmVjdXJzaWYgbWVtb2l6ZSIsIG5lZWRsZTogIndldmlhLWNvZGUiIH0sCiAgeyBuYW1lOiAiVHJhZHVpcmUiLCBtc2c6ICJUcmFkdWlzIGVuIGFuZ2xhaXM6IG1lcmNpIGluZmluaW1lbnQgcG91ciB2b3RyZSBhaWRlIiwgIG5lZWRsZTogIkVuZ2xpc2g6IiB9LAogIHsgbmFtZTogIkJpbGFuIiwgICAgbXNnOiAiYmlsYW4gY29tcGxldCBhcmNoaXRlY3R1cmUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZWVkbGU6ICJhZ2VudHMiIH0sCl07CnRlc3QoIlY4IGZpbmFsIDgvOCIsIGFzeW5jICh7IHBhZ2UgfSkgPT4gewogIHRlc3Quc2V0VGltZW91dCg0ODAwMDApOwogIGxldCBlcnJzID0gMDsKICBwYWdlLm9uKCJwYWdlZXJyb3IiLCBlID0+IHsgZXJycysrOyBjb25zb2xlLmxvZygiW3BhZ2VlcnJvcl0iLCBlLm1lc3NhZ2Uuc3Vic3RyaW5nKDAsMTIwKSk7IH0pOwogIGF3YWl0IHBhZ2UuZ290bygiL3dldmlhLmh0bWwiKTsKICBhd2FpdCBwYWdlLndhaXRGb3JMb2FkU3RhdGUoIm5ldHdvcmtpZGxlIik7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVGltZW91dCgyNTAwKTsKICBhd2FpdCBwYWdlLnNjcmVlbnNob3QoeyBwYXRoOiAib3V0cHV0L3Y4LTAwLWluaXRpYWwucG5nIiB9KTsKICBjb25zb2xlLmxvZygi8J+TuCBpbml0aWFsIHY4Iik7CiAgY29uc3QgcmVzdWx0cyA9IFtdOwogIGZvciAobGV0IGkgPSAwOyBpIDwgQ0FQQUJJTElUSUVTLmxlbmd0aDsgaSsrKSB7CiAgICBjb25zdCBjYXAgPSBDQVBBQklMSVRJRVNbaV07CiAgICBjb25zdCBudW0gPSBTdHJpbmcoaSArIDEpLnBhZFN0YXJ0KDIsICIwIik7CiAgICBjb25zb2xlLmxvZygiXG5bIiArIG51bSArICIvOF0gIiArIGNhcC5uYW1lKTsKICAgIGxldCBvayA9IGZhbHNlLCBhdHRlbXB0cyA9IDA7CiAgICB3aGlsZSAoIW9rICYmIGF0dGVtcHRzIDwgMikgewogICAgICBhdHRlbXB0cysrOwogICAgICB0cnkgewogICAgICAgIGNvbnN0IGJlZm9yZSA9IGF3YWl0IHBhZ2UuZXZhbHVhdGUobiA9PiAoZG9jdW1lbnQuYm9keS5pbm5lclRleHQubWF0Y2gobmV3IFJlZ0V4cChuLnJlcGxhY2UoL1suKis/XiR7fSgpfFtcXVxcXS9nLCJcXCQmIiksImciKSl8fFtdKS5sZW5ndGgsIGNhcC5uZWVkbGUpOwogICAgICAgIGNvbnN0IGlucHV0ID0gcGFnZS5sb2NhdG9yKCIjbXNnSW5wdXQiKTsKICAgICAgICBhd2FpdCBpbnB1dC5jbGljayh7IGZvcmNlOiB0cnVlIH0pOwogICAgICAgIGF3YWl0IHBhZ2Uua2V5Ym9hcmQucHJlc3MoIkNvbnRyb2wrQSIpOwogICAgICAgIGF3YWl0IHBhZ2Uua2V5Ym9hcmQucHJlc3MoIkRlbGV0ZSIpOwogICAgICAgIGF3YWl0IGlucHV0LmZpbGwoY2FwLm1zZyk7CiAgICAgICAgYXdhaXQgcGFnZS53YWl0Rm9yVGltZW91dCg0MDApOwogICAgICAgIGF3YWl0IGlucHV0LnByZXNzKCJFbnRlciIpOwogICAgICAgIGNvbnNvbGUubG9nKCIgIPCfk6Qgc2VudCAoeCIgKyBiZWZvcmUgKyAiKSIpOwogICAgICAgIGNvbnN0IHMgPSBEYXRlLm5vdygpOwogICAgICAgIHdoaWxlIChEYXRlLm5vdygpIC0gcyA8IDQ1MDAwKSB7CiAgICAgICAgICBjb25zdCBhID0gYXdhaXQgcGFnZS5ldmFsdWF0ZShuID0+IChkb2N1bWVudC5ib2R5LmlubmVyVGV4dC5tYXRjaChuZXcgUmVnRXhwKG4ucmVwbGFjZSgvWy4qKz9eJHt9KCl8W1xdXFxdL2csIlxcJCYiKSwiZyIpKXx8W10pLmxlbmd0aCwgY2FwLm5lZWRsZSk7CiAgICAgICAgICBpZiAoYSA+IGJlZm9yZSkgeyBvayA9IHRydWU7IGJyZWFrOyB9CiAgICAgICAgICBhd2FpdCBwYWdlLndhaXRGb3JUaW1lb3V0KDE1MDApOwogICAgICAgIH0KICAgICAgICBjb25zdCBlbCA9ICgoRGF0ZS5ub3coKS1zKS8xMDAwKS50b0ZpeGVkKDEpOwogICAgICAgIGNvbnNvbGUubG9nKG9rID8gIiAg4pyFIFBBU1MgIiArIGVsICsgInMiIDogIiAg4pqg77iPIG5vIG1hdGNoICIgKyBlbCArICJzIik7CiAgICAgICAgaWYgKCFvayAmJiBhdHRlbXB0cyA8IDIpIGF3YWl0IHBhZ2Uud2FpdEZvclRpbWVvdXQoMzAwMCk7CiAgICAgIH0gY2F0Y2ggKGUpIHsgY29uc29sZS5sb2coIiAg4p2MIiwgZS5tZXNzYWdlLnN1YnN0cmluZygwLDEwMCkpOyB9CiAgICB9CiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHsgY29uc3QgbT1kb2N1bWVudC5nZXRFbGVtZW50QnlJZCgibWVzc2FnZXMiKTsgaWYgKG0pIG0uc2Nyb2xsVG9wPW0uc2Nyb2xsSGVpZ2h0OyB9KTsKICAgIGF3YWl0IHBhZ2Uud2FpdEZvclRpbWVvdXQoMjIwMCk7CiAgICBhd2FpdCBwYWdlLnNjcmVlbnNob3QoeyBwYXRoOiAib3V0cHV0L3Y4LSIgKyBudW0gKyAiLSIgKyBjYXAubmFtZSArICIucG5nIiB9KTsKICAgIGNvbnNvbGUubG9nKCIgIPCfk7ggdjgtIiArIG51bSArICItIiArIGNhcC5uYW1lICsgIi5wbmciKTsKICAgIHJlc3VsdHMucHVzaCh7IG5hbWU6IGNhcC5uYW1lLCBwYXNzOiBvayB9KTsKICAgIGF3YWl0IHBhZ2Uud2FpdEZvclRpbWVvdXQoMTIwMCk7CiAgfQogIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4gd2luZG93LnNjcm9sbFRvKDAsIGRvY3VtZW50LmJvZHkuc2Nyb2xsSGVpZ2h0KSk7CiAgYXdhaXQgcGFnZS53YWl0Rm9yVGltZW91dCgyMDAwKTsKICBhd2FpdCBwYWdlLnNjcmVlbnNob3QoeyBwYXRoOiAib3V0cHV0L3Y4LTk5LWZpbmFsLnBuZyIsIGZ1bGxQYWdlOiB0cnVlIH0pOwogIGNvbnN0IHAgPSByZXN1bHRzLmZpbHRlcihyID0+IHIucGFzcykubGVuZ3RoOwogIGNvbnNvbGUubG9nKCJcbuKVkOKVkOKVkCBWOCBGSU5BTCDilZDilZDilZAiKTsKICBjb25zb2xlLmxvZyhwICsgIi84IFBBU1MgwrcgIiArIGVycnMgKyAiIHBhZ2UgZXJyb3JzIik7CiAgcmVzdWx0cy5mb3JFYWNoKHIgPT4gY29uc29sZS5sb2coIiAgIiArIChyLnBhc3MgPyAi4pyFIiA6ICLinYwiKSArICIgIiArIHIubmFtZSkpOwp9KTsK');
|
||||
$written = @file_put_contents("$base/capabilities-v8.spec.js", $spec);
|
||||
$removed = @unlink("$base/capabilities-v7.spec.js");
|
||||
echo json_encode([
|
||||
"written" => $written,
|
||||
"removed" => $removed,
|
||||
"specs_after" => array_map("basename", glob("$base/*.spec.js")),
|
||||
]);
|
||||
23
api/ambre-pw-v8-files.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/output";
|
||||
$out = ["v8_screenshots"=>[], "v8_video"=>null];
|
||||
|
||||
foreach (glob("$base/v8-*.png") as $p) {
|
||||
$out["v8_screenshots"][] = [
|
||||
"name" => basename($p),
|
||||
"size_kb" => round(filesize($p)/1024, 1),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . basename($p),
|
||||
];
|
||||
}
|
||||
usort($out["v8_screenshots"], function($a,$b){return strcmp($a["name"], $b["name"]);});
|
||||
|
||||
foreach (glob("$base/capabilities-v8-*/*.webm") as $w) {
|
||||
$rel = str_replace($base . "/", "", $w);
|
||||
$out["v8_video"] = [
|
||||
"size_mb" => round(filesize($w)/1048576, 2),
|
||||
"url" => "https://weval-consulting.com/api/ambre-pw-tests/output/" . $rel,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
11
api/ambre-pw-v9-deploy.php
Normal file
24
api/ambre-regex-test.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
// Run node -e to test the regex with real JS semantics
|
||||
$regex = '/g[eéèê]n[eéèê]re?\\s+(?:un|une|des|le|la)?\\s*(pdf|pptx?|powerpoint|docx?|word|excel|xlsx?|pr[eéèê]sentation|document|tableau|sch[eéèê]ma|mermaid|diagramme|image)|ecri[srt]?\\s+(?:le|du|un)?\\s*code|traduis?\\s+(?:ce\\s+texte|en)?\\s*(anglais|francais|espagnol|allemand|italien|portugais|arabe|chinois|japonais|english|spanish|french|german|italian|portuguese|arabic|chinese|japanese)/i';
|
||||
|
||||
$tests = [
|
||||
"Genere un PDF sur: demo",
|
||||
"Genere un document Word sur: strategie",
|
||||
"Genere une presentation sur: pitch",
|
||||
"Genere un schema mermaid pour: flow",
|
||||
"Genere une image: logo",
|
||||
"Ecris le code python pour: fibonacci",
|
||||
"Traduis en anglais: bonjour",
|
||||
"ping",
|
||||
];
|
||||
|
||||
$results = [];
|
||||
foreach ($tests as $t) {
|
||||
// Escape the test string for shell
|
||||
$js = "console.log(JSON.stringify({q: \"" . addcslashes($t, "\"\\") . "\", match: $regex.test(\"" . addcslashes($t, "\"\\") . "\")}));";
|
||||
$out = @shell_exec("node -e \"$js\" 2>&1");
|
||||
$results[] = ["query"=>$t, "node_out"=>trim($out)];
|
||||
}
|
||||
echo json_encode($results, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
|
||||
40
api/ambre-scan-sse.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
$dir = "/var/www/html/api";
|
||||
|
||||
// Find SSE / stream endpoints
|
||||
$candidates = [];
|
||||
foreach (glob("$dir/*stream*.php") as $f) $candidates[] = basename($f);
|
||||
foreach (glob("$dir/*sse*.php") as $f) $candidates[] = basename($f);
|
||||
foreach (glob("$dir/wevcode*.php") as $f) $candidates[] = basename($f);
|
||||
foreach (glob("$dir/*plan-exec*.php") as $f) $candidates[] = basename($f);
|
||||
foreach (glob("$dir/*autonomous*.php") as $f) $candidates[] = basename($f);
|
||||
$out["streaming_endpoints"] = array_unique($candidates);
|
||||
|
||||
// Find wevia-master-api.php size + check if supports SSE
|
||||
if (file_exists("$dir/wevia-master-api.php")) {
|
||||
$content = @file_get_contents("$dir/wevia-master-api.php");
|
||||
$out["master_api_size"] = strlen($content);
|
||||
$out["has_sse_hint"] = strpos($content, "text/event-stream") !== false;
|
||||
}
|
||||
|
||||
// Check existing wevia-autonomous/wevcode for SSE pattern
|
||||
foreach (["wevia-autonomous.php", "wevcode.php", "wevcode-stream.php", "wevia-sse.php"] as $f) {
|
||||
$p = "$dir/$f";
|
||||
if (file_exists($p)) {
|
||||
$c = @file_get_contents($p, false, null, 0, 2000);
|
||||
$out["preview_$f"] = [
|
||||
"size" => filesize($p),
|
||||
"has_event_stream" => strpos($c, "text/event-stream") !== false,
|
||||
"has_flush" => strpos($c, "ob_flush") !== false,
|
||||
"first_200" => substr($c, 0, 200),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Check existing React dashboards
|
||||
$out["react_dashboards"] = array_map("basename", glob("$dir/../*react*.html") ?: []);
|
||||
$out["dashboard_files"] = array_map("basename", array_slice(glob("$dir/../dashboard*.html"), 0, 10));
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
68
api/ambre-session-chat.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* ambre-session-chat.php · contextual chat with memory + empathy
|
||||
* POST {message, session_id}
|
||||
* - Loads last 10 turns from session
|
||||
* - Calls LLM with full history + empathy system prompt
|
||||
* - Appends user+assistant to memory
|
||||
* Returns {response, provider, intent, turns_in_memory, ...}
|
||||
*/
|
||||
require_once __DIR__ . "/ambre-session-memory.php";
|
||||
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
|
||||
$raw = file_get_contents("php://input");
|
||||
$in = json_decode($raw, true) ?: $_POST;
|
||||
$msg = trim($in["message"] ?? "");
|
||||
$sid = trim($in["session_id"] ?? "");
|
||||
|
||||
if (!$msg) { echo json_encode(["error"=>"message required"]); exit; }
|
||||
if (!$sid) $sid = "anon-" . substr(md5($_SERVER["REMOTE_ADDR"] ?? "x"), 0, 8);
|
||||
|
||||
// Load prior turns
|
||||
$history = AmbreSessionMemory::context_messages($sid, 10);
|
||||
|
||||
// Build messages array for LLM
|
||||
$sys = "Tu es WEVIA, une IA empathique et adaptative de WEVAL Consulting. " .
|
||||
"Tu te souviens des échanges précédents dans cette conversation. " .
|
||||
"Si l'utilisateur revient sur un sujet antérieur, reconnais-le explicitement. " .
|
||||
"Si l'utilisateur change de sujet, adapte-toi avec fluidité. " .
|
||||
"Si l'utilisateur exprime une émotion (joie, frustration, urgence, etc.), reconnais-la avec empathie. " .
|
||||
"Si l'utilisateur te demande d'améliorer un rendu précédent, refère-toi au dernier rendu et propose une version améliorée. " .
|
||||
"Réponse en français, concise mais riche, sans préambule inutile.";
|
||||
|
||||
$messages = [["role"=>"system", "content"=>$sys]];
|
||||
foreach ($history as $h) $messages[] = $h;
|
||||
$messages[] = ["role"=>"user", "content"=>$msg];
|
||||
|
||||
// Call LLM
|
||||
$t0 = microtime(true);
|
||||
$raw_llm = @file_get_contents("http://127.0.0.1:4000/v1/chat/completions", false, stream_context_create([
|
||||
"http" => [
|
||||
"method"=>"POST",
|
||||
"header"=>"Content-Type: application/json\r\n",
|
||||
"content"=>json_encode(["model"=>"fast","messages"=>$messages,"max_tokens"=>1200,"temperature"=>0.5]),
|
||||
"timeout"=>30,
|
||||
],
|
||||
]));
|
||||
$elapsed = round((microtime(true)-$t0)*1000);
|
||||
|
||||
$d = @json_decode($raw_llm, true);
|
||||
$reply = $d["choices"][0]["message"]["content"] ?? "";
|
||||
if (!$reply) $reply = "Désolé, je n'ai pas pu traiter la demande. Peux-tu reformuler ?";
|
||||
|
||||
// Store turns
|
||||
AmbreSessionMemory::append($sid, "user", $msg);
|
||||
AmbreSessionMemory::append($sid, "assistant", $reply);
|
||||
|
||||
$summary = AmbreSessionMemory::summary($sid);
|
||||
|
||||
echo json_encode([
|
||||
"response" => $reply,
|
||||
"provider" => "ambre-session-chat-v1",
|
||||
"intent" => "contextual_reply",
|
||||
"session_id" => $sid,
|
||||
"turns_in_memory" => $summary["turns"],
|
||||
"history_used" => count($history),
|
||||
"elapsed_ms" => $elapsed,
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
88
api/ambre-session-memory.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* ambre-session-memory.php · AMBRE v1 · per-session memory store
|
||||
* Stores/retrieves last N messages per session_id in /var/tmp/wevia-sessions/
|
||||
*/
|
||||
|
||||
class AmbreSessionMemory {
|
||||
const DIR = "/var/tmp/wevia-sessions";
|
||||
const MAX_TURNS = 20;
|
||||
const TTL_HOURS = 24;
|
||||
|
||||
public static function init() {
|
||||
if (!is_dir(self::DIR)) @mkdir(self::DIR, 0777, true);
|
||||
}
|
||||
|
||||
public static function path($sid) {
|
||||
self::init();
|
||||
$safe = preg_replace("/[^a-zA-Z0-9_-]/", "", $sid);
|
||||
if (!$safe) $safe = "default";
|
||||
return self::DIR . "/" . $safe . ".json";
|
||||
}
|
||||
|
||||
public static function load($sid) {
|
||||
$p = self::path($sid);
|
||||
if (!file_exists($p)) return [];
|
||||
$content = @file_get_contents($p);
|
||||
if (!$content) return [];
|
||||
$data = @json_decode($content, true);
|
||||
if (!is_array($data)) return [];
|
||||
// TTL cleanup
|
||||
$now = time();
|
||||
$data = array_filter($data, function($m) use ($now) {
|
||||
return isset($m["ts"]) && ($now - $m["ts"]) < (self::TTL_HOURS * 3600);
|
||||
});
|
||||
return array_values($data);
|
||||
}
|
||||
|
||||
public static function append($sid, $role, $content) {
|
||||
if (!$sid || !$role || !$content) return;
|
||||
$msgs = self::load($sid);
|
||||
$msgs[] = [
|
||||
"role" => $role,
|
||||
"content" => substr((string)$content, 0, 4000),
|
||||
"ts" => time(),
|
||||
];
|
||||
// Keep only last N
|
||||
if (count($msgs) > self::MAX_TURNS) {
|
||||
$msgs = array_slice($msgs, -self::MAX_TURNS);
|
||||
}
|
||||
@file_put_contents(self::path($sid), json_encode($msgs, JSON_UNESCAPED_UNICODE), LOCK_EX);
|
||||
}
|
||||
|
||||
public static function context_messages($sid, $max = 10) {
|
||||
$msgs = self::load($sid);
|
||||
$last = array_slice($msgs, -$max);
|
||||
$out = [];
|
||||
foreach ($last as $m) {
|
||||
$out[] = ["role" => $m["role"], "content" => $m["content"]];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function summary($sid) {
|
||||
$msgs = self::load($sid);
|
||||
return [
|
||||
"session" => $sid,
|
||||
"turns" => count($msgs),
|
||||
"first_ts" => !empty($msgs) ? date("c", $msgs[0]["ts"]) : null,
|
||||
"last_ts" => !empty($msgs) ? date("c", end($msgs)["ts"]) : null,
|
||||
];
|
||||
}
|
||||
|
||||
public static function clear($sid) {
|
||||
$p = self::path($sid);
|
||||
if (file_exists($p)) @unlink($p);
|
||||
}
|
||||
}
|
||||
|
||||
// Direct API usage
|
||||
if (basename($_SERVER["SCRIPT_NAME"]) === "ambre-session-memory.php") {
|
||||
header("Content-Type: application/json");
|
||||
$sid = $_GET["sid"] ?? "";
|
||||
$action = $_GET["action"] ?? "summary";
|
||||
if ($action === "summary") echo json_encode(AmbreSessionMemory::summary($sid));
|
||||
elseif ($action === "load") echo json_encode(AmbreSessionMemory::load($sid));
|
||||
elseif ($action === "clear") { AmbreSessionMemory::clear($sid); echo json_encode(["cleared"=>true]); }
|
||||
else echo json_encode(["error"=>"unknown action"]);
|
||||
}
|
||||
8
api/ambre-specs-list.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$base = "/var/www/html/api/ambre-pw-tests/tests";
|
||||
echo json_encode([
|
||||
"specs" => array_map("basename", glob("$base/*.spec.js")),
|
||||
"v7_exists" => file_exists("$base/capabilities-v7.spec.js"),
|
||||
"v8_exists" => file_exists("$base/capabilities-v8.spec.js"),
|
||||
]);
|
||||
25
api/ambre-sse-inspect.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
$endpoints = [
|
||||
"/var/www/html/api/wevia-public-stream.php",
|
||||
"/var/www/html/api/wevia-stream-api.php",
|
||||
"/var/www/html/api/wevia-multiagent-sse.php",
|
||||
"/var/www/html/api/wevcode-superclaude.php",
|
||||
"/var/www/html/api/wevia-sse-orchestrator-public.php",
|
||||
];
|
||||
foreach ($endpoints as $f) {
|
||||
if (!file_exists($f)) { $out[basename($f)] = "MISSING"; continue; }
|
||||
$c = @file_get_contents($f);
|
||||
$out[basename($f)] = [
|
||||
"size" => strlen($c),
|
||||
"has_sse" => strpos($c, "text/event-stream") !== false,
|
||||
"has_flush" => strpos($c, "ob_flush") !== false || strpos($c, "flush()") !== false,
|
||||
"has_thinking" => stripos($c, "thinking") !== false,
|
||||
"has_plan" => stripos($c, "plan") !== false,
|
||||
"has_execute" => stripos($c, "execute") !== false || stripos($c, "exec_") !== false,
|
||||
"has_rag" => stripos($c, "rag") !== false || stripos($c, "qdrant") !== false,
|
||||
"first_500" => substr($c, 0, 500),
|
||||
];
|
||||
}
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
26
api/ambre-sse-mini.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
header("Content-Type: text/event-stream");
|
||||
header("Cache-Control: no-cache");
|
||||
header("X-Accel-Buffering: no");
|
||||
header("Connection: keep-alive");
|
||||
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") { http_response_code(200); exit; }
|
||||
ob_implicit_flush(true);
|
||||
while (ob_get_level()) @ob_end_flush();
|
||||
|
||||
echo "event: start
|
||||
data: " . json_encode(["hello"=>"ambre"]) . "
|
||||
|
||||
";
|
||||
@flush();
|
||||
sleep(1);
|
||||
echo "event: step
|
||||
data: " . json_encode(["step"=>1]) . "
|
||||
|
||||
";
|
||||
@flush();
|
||||
sleep(1);
|
||||
echo "event: done
|
||||
data: {}
|
||||
|
||||
";
|
||||
@flush();
|
||||
30
api/ambre-sse-scan.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
header("Content-Type: application/json");
|
||||
$out = [];
|
||||
// Check SSE endpoints
|
||||
$sse_candidates = glob("/var/www/html/api/*sse*.php") ?: [];
|
||||
$stream_candidates = glob("/var/www/html/api/*stream*.php") ?: [];
|
||||
$out["sse_files"] = array_map("basename", $sse_candidates);
|
||||
$out["stream_files"] = array_map("basename", $stream_candidates);
|
||||
|
||||
// Check SSE in wevia.html
|
||||
$wevia = file_exists("/var/www/html/wevia.html") ? file_get_contents("/var/www/html/wevia.html") : "";
|
||||
$out["wevia_has_eventsource"] = substr_count($wevia, "EventSource");
|
||||
$out["wevia_has_sse"] = substr_count($wevia, "text/event-stream");
|
||||
|
||||
// Check RAG / Qdrant / Chroma
|
||||
$rag_files = array_merge(
|
||||
glob("/var/www/html/api/*rag*.php") ?: [],
|
||||
glob("/var/www/html/api/*qdrant*.php") ?: [],
|
||||
glob("/var/www/html/api/*embedding*.php") ?: []
|
||||
);
|
||||
$out["rag_files"] = array_map("basename", array_slice($rag_files, 0, 15));
|
||||
|
||||
// Check Qdrant live
|
||||
$qdrant = @file_get_contents("http://127.0.0.1:6333/collections");
|
||||
$out["qdrant_alive"] = $qdrant ? substr($qdrant, 0, 500) : "unreachable";
|
||||
|
||||
// Check React CDN usage
|
||||
$out["react_cdn_in_wevia"] = strpos($wevia, "react") !== false ? "yes" : "no";
|
||||
|
||||
echo json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated": "2026-04-21 14:30:02",
|
||||
"generated": "2026-04-21 21:30:02",
|
||||
"version": "1.0",
|
||||
"servers": [
|
||||
{
|
||||
@@ -8,9 +8,9 @@
|
||||
"private": "10.1.0.2",
|
||||
"role": "PRIMARY",
|
||||
"ssh": 49222,
|
||||
"disk_pct": 82,
|
||||
"disk_avail": "27G",
|
||||
"uptime": "up 1 week, 4 hours, 38 minutes",
|
||||
"disk_pct": 83,
|
||||
"disk_avail": "26G",
|
||||
"uptime": "up 1 week, 11 hours, 38 minutes",
|
||||
"nginx": "active",
|
||||
"php_fpm": "active",
|
||||
"php_version": "8.5.5"
|
||||
@@ -116,7 +116,7 @@
|
||||
},
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"status": "Up 38 hours (healthy)",
|
||||
"status": "Up 45 hours (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
@@ -275,9 +275,9 @@
|
||||
}
|
||||
],
|
||||
"screens": {
|
||||
"s204_html": 315,
|
||||
"s204_html": 318,
|
||||
"s204_products": 104,
|
||||
"s204_api_php": 813,
|
||||
"s204_api_php": 858,
|
||||
"s204_wevia_php": 34,
|
||||
"s95_arsenal_html": 1377,
|
||||
"s95_arsenal_api": 377
|
||||
@@ -301,7 +301,7 @@
|
||||
"langfuse"
|
||||
],
|
||||
"key_tables": {
|
||||
"kb_learnings": 5546,
|
||||
"kb_learnings": 5562,
|
||||
"kb_documents": 0,
|
||||
"ethica_medecins": 50004,
|
||||
"enterprise_agents": 0
|
||||
@@ -601,15 +601,15 @@
|
||||
]
|
||||
},
|
||||
"wiki": {
|
||||
"total_entries": 5547,
|
||||
"total_entries": 5562,
|
||||
"categories": [
|
||||
{
|
||||
"category": "AUTO-FIX",
|
||||
"cnt": "2973"
|
||||
"cnt": "2974"
|
||||
},
|
||||
{
|
||||
"category": "TOPOLOGY",
|
||||
"cnt": "1218"
|
||||
"cnt": "1232"
|
||||
},
|
||||
{
|
||||
"category": "DISCOVERY",
|
||||
@@ -1718,6 +1718,10 @@
|
||||
"optimizations": {
|
||||
"recent_commits": [],
|
||||
"auto_fixes": [
|
||||
{
|
||||
"fact": "AUTONOMY 21Apr 18:55: 1 fixes. Disk light cleanup 85%",
|
||||
"created_at": "2026-04-21 20:55:06.635344"
|
||||
},
|
||||
{
|
||||
"fact": "AUTONOMY 21Apr 08:10: 5 fixes. S95 restart pmta; S95 restart kumomta; S95 restart postfix; S95 restart sentinel; S95 restart adx",
|
||||
"created_at": "2026-04-21 10:10:04.274914"
|
||||
@@ -1753,10 +1757,6 @@
|
||||
{
|
||||
"fact": "AUTONOMY 19Apr 17:20: 1 fixes. Disk light cleanup 85%",
|
||||
"created_at": "2026-04-19 19:20:05.814206"
|
||||
},
|
||||
{
|
||||
"fact": "AUTONOMY 19Apr 17:15: 1 fixes. Disk light cleanup 85%",
|
||||
"created_at": "2026-04-19 19:15:05.697284"
|
||||
}
|
||||
],
|
||||
"architecture_decisions": [
|
||||
@@ -1945,7 +1945,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"scan_time_ms": 2178,
|
||||
"scan_time_ms": 2858,
|
||||
"gaps": [],
|
||||
"score": 100,
|
||||
"automation": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated_at": "2026-04-21T16:40:01.266413",
|
||||
"generated_at": "2026-04-21T23:40:01.497321",
|
||||
"stats": {
|
||||
"total": 48,
|
||||
"pending": 31,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"status": "ALIVE",
|
||||
"ts": "2026-04-21T16:30:02.091494",
|
||||
"last_heartbeat": "2026-04-21T16:30:02.091494",
|
||||
"last_heartbeat_ts_epoch": 1776781802,
|
||||
"ts": "2026-04-21T23:30:02.046233",
|
||||
"last_heartbeat": "2026-04-21T23:30:02.046233",
|
||||
"last_heartbeat_ts_epoch": 1776807002,
|
||||
"tasks_today": 232,
|
||||
"tasks_week": 574,
|
||||
"agent_id": "blade-ops",
|
||||
|
||||
15
api/blade-tasks/key_gemini_key_20260421.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "key_renewal",
|
||||
"provider": "GEMINI_KEY",
|
||||
"reason": "FAIL",
|
||||
"urls": {
|
||||
"GITHUB_TOKEN": "https:\/\/github.com\/settings\/tokens\/new?scopes=repo,workflow&description=WEVIA-Auto",
|
||||
"GROQ_KEY": "https:\/\/console.groq.com\/keys",
|
||||
"CEREBRAS_API_KEY": "https:\/\/cloud.cerebras.ai\/platform",
|
||||
"GEMINI_KEY": "https:\/\/aistudio.google.com\/apikey",
|
||||
"SAMBANOVA_KEY": "https:\/\/cloud.sambanova.ai\/apis",
|
||||
"MISTRAL_KEY": "https:\/\/console.mistral.ai\/api-keys"
|
||||
},
|
||||
"ts": "2026-04-21T16:00:05+00:00",
|
||||
"priority": "P1"
|
||||
}
|
||||
@@ -10,6 +10,6 @@
|
||||
"SAMBANOVA_KEY": "https:\/\/cloud.sambanova.ai\/apis",
|
||||
"MISTRAL_KEY": "https:\/\/console.mistral.ai\/api-keys"
|
||||
},
|
||||
"ts": "2026-04-21T10:00:04+00:00",
|
||||
"ts": "2026-04-21T16:00:05+00:00",
|
||||
"priority": "P0"
|
||||
}
|
||||
@@ -10,6 +10,6 @@
|
||||
"SAMBANOVA_KEY": "https:\/\/cloud.sambanova.ai\/apis",
|
||||
"MISTRAL_KEY": "https:\/\/console.mistral.ai\/api-keys"
|
||||
},
|
||||
"ts": "2026-04-21T10:00:04+00:00",
|
||||
"ts": "2026-04-21T16:00:05+00:00",
|
||||
"priority": "P1"
|
||||
}
|
||||
@@ -10,6 +10,6 @@
|
||||
"SAMBANOVA_KEY": "https:\/\/cloud.sambanova.ai\/apis",
|
||||
"MISTRAL_KEY": "https:\/\/console.mistral.ai\/api-keys"
|
||||
},
|
||||
"ts": "2026-04-21T10:00:04+00:00",
|
||||
"ts": "2026-04-21T16:00:05+00:00",
|
||||
"priority": "P1"
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"timestamp": "2026-04-21 16:00",
|
||||
"timestamp": "2026-04-21 20:00",
|
||||
"checks": {
|
||||
"registry": "0 agents",
|
||||
"system": {
|
||||
"docker": "19",
|
||||
"ram": "11Gi/30Gi",
|
||||
"ram": "12Gi/30Gi",
|
||||
"disk": "82%",
|
||||
"load": "9.88",
|
||||
"uptime": "up 1 week, 4 hours, 8 minutes"
|
||||
"load": "0.46",
|
||||
"uptime": "up 1 week, 8 hours, 8 minutes"
|
||||
},
|
||||
"services": "7/10 OK",
|
||||
"nonreg": "153/153 (100%)",
|
||||
@@ -15,7 +15,7 @@
|
||||
"crons": "44 active",
|
||||
"routes": "446",
|
||||
"dataset": "5751 pairs",
|
||||
"wiki": "1988 entries",
|
||||
"wiki": "2046 entries",
|
||||
"enterprise": "758 agents (dorm=0 dead=167)"
|
||||
},
|
||||
"analysis": "Analyse indisponible"
|
||||
|
||||
55
api/csat-api.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* WEVAL CSAT - Customer Satisfaction Score sovereign 21avr2026
|
||||
* Rating 1-5 after resolved ticket/interaction.
|
||||
* Storage: /opt/weval-l99/data/csat-responses.jsonl
|
||||
*/
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$STORAGE = '/opt/weval-l99/data/csat-responses.jsonl';
|
||||
@mkdir(dirname($STORAGE), 0755, true);
|
||||
|
||||
$action = $_GET['action'] ?? ($_POST['action'] ?? 'stats');
|
||||
|
||||
if ($action === 'submit' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$rating = intval($_POST['rating'] ?? -1);
|
||||
$context = substr(trim($_POST['context'] ?? ''), 0, 200);
|
||||
$user = substr(trim($_POST['user'] ?? 'anonymous'), 0, 60);
|
||||
if ($rating < 1 || $rating > 5) {
|
||||
echo json_encode(['ok'=>false,'error'=>'invalid_rating','expected'=>'1-5']);
|
||||
exit;
|
||||
}
|
||||
@file_put_contents($STORAGE, json_encode(['ts'=>date('c'),'rating'=>$rating,'context'=>$context,'user'=>$user])."\n", FILE_APPEND | LOCK_EX);
|
||||
echo json_encode(['ok'=>true,'recorded'=>true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$responses = [];
|
||||
if (is_readable($STORAGE)) {
|
||||
foreach (file($STORAGE) as $line) {
|
||||
$r = @json_decode(trim($line), true);
|
||||
if ($r && isset($r['rating'])) $responses[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
$n = count($responses);
|
||||
if ($n === 0) {
|
||||
echo json_encode(['ok'=>true,'source'=>'sovereign_jsonl','ts'=>date('c'),'csat_score_pct'=>0,'responses_total'=>0,'status'=>'wire_needed','drill'=>'No ratings yet. POST /api/csat-api.php?action=submit']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// CSAT = % ratings >= 4 (out of 5)
|
||||
$satisfied = 0;
|
||||
foreach ($responses as $r) if ($r['rating'] >= 4) $satisfied++;
|
||||
$pct = round(($satisfied / $n) * 100);
|
||||
|
||||
echo json_encode([
|
||||
'ok'=>true,
|
||||
'source'=>'sovereign_jsonl',
|
||||
'ts'=>date('c'),
|
||||
'csat_score_pct'=>$pct,
|
||||
'responses_total'=>$n,
|
||||
'satisfied_count'=>$satisfied,
|
||||
'status'=>$pct >= 85 ? 'ok' : ($pct > 0 ? 'warn' : 'wire_needed'),
|
||||
'drill'=>"% ratings >=4 out of 5",
|
||||
]);
|
||||
@@ -1,281 +0,0 @@
|
||||
{
|
||||
"ts": "2026-04-21T14:40:01+00:00",
|
||||
"server": "s204",
|
||||
"s204": {
|
||||
"load": 0.77,
|
||||
"uptime": "2026-04-14 11:51:24",
|
||||
"ram_total_mb": 31335,
|
||||
"ram_used_mb": 11682,
|
||||
"ram_free_mb": 19653,
|
||||
"disk_total": "150G",
|
||||
"disk_used": "118G",
|
||||
"disk_free": "27G",
|
||||
"disk_pct": "82%",
|
||||
"fpm_workers": 140,
|
||||
"docker_containers": 19,
|
||||
"cpu_cores": 8
|
||||
},
|
||||
"s95": {
|
||||
"load": 0.68,
|
||||
"disk_pct": "81%",
|
||||
"status": "UP",
|
||||
"ram_total_mb": 15610,
|
||||
"ram_free_mb": 12017
|
||||
},
|
||||
"pmta": [
|
||||
{
|
||||
"name": "SER6",
|
||||
"ip": "110.239.84.121",
|
||||
"status": "DOWN"
|
||||
},
|
||||
{
|
||||
"name": "SER7",
|
||||
"ip": "110.239.65.64",
|
||||
"status": "DOWN"
|
||||
},
|
||||
{
|
||||
"name": "SER8",
|
||||
"ip": "182.160.55.107",
|
||||
"status": "DOWN"
|
||||
},
|
||||
{
|
||||
"name": "SER9",
|
||||
"ip": "110.239.86.68",
|
||||
"status": "DOWN"
|
||||
}
|
||||
],
|
||||
"assets": {
|
||||
"html_pages": 316,
|
||||
"php_apis": 818,
|
||||
"wiki_entries": 2046,
|
||||
"vault_doctrines": 78,
|
||||
"vault_sessions": 104,
|
||||
"vault_decisions": 12
|
||||
},
|
||||
"tools": {
|
||||
"total": 627,
|
||||
"registry_version": "?"
|
||||
},
|
||||
"sovereign": {
|
||||
"status": "UP",
|
||||
"providers": [
|
||||
"Cerebras-fast",
|
||||
"Cerebras-think",
|
||||
"Groq",
|
||||
"Cloudflare-AI",
|
||||
"Gemini",
|
||||
"SambaNova",
|
||||
"NVIDIA-NIM",
|
||||
"Mistral",
|
||||
"Groq-OSS",
|
||||
"HF-Space",
|
||||
"HF-Router",
|
||||
"OpenRouter",
|
||||
"GitHub-Models"
|
||||
],
|
||||
"active": 13,
|
||||
"total": 13,
|
||||
"primary": "Cerebras-fast",
|
||||
"cost": "0€"
|
||||
},
|
||||
"ethica": {
|
||||
"total_hcps": 161733,
|
||||
"with_email": 110612,
|
||||
"with_phone": 155151,
|
||||
"gap_email": 51121,
|
||||
"pct_email": 68.4,
|
||||
"pct_phone": 95.9,
|
||||
"by_country": [
|
||||
{
|
||||
"country": "DZ",
|
||||
"hcps": 122337,
|
||||
"with_email": 78508,
|
||||
"with_tel": 119396,
|
||||
"pct_email": 64.2,
|
||||
"pct_tel": 97.6
|
||||
},
|
||||
{
|
||||
"country": "MA",
|
||||
"hcps": 19723,
|
||||
"with_email": 15078,
|
||||
"with_tel": 18737,
|
||||
"pct_email": 76.4,
|
||||
"pct_tel": 95
|
||||
},
|
||||
{
|
||||
"country": "TN",
|
||||
"hcps": 17794,
|
||||
"with_email": 15147,
|
||||
"with_tel": 17018,
|
||||
"pct_email": 85.1,
|
||||
"pct_tel": 95.6
|
||||
},
|
||||
{
|
||||
"country": "INTL",
|
||||
"hcps": 1879,
|
||||
"with_email": 1879,
|
||||
"with_tel": 0,
|
||||
"pct_email": 100,
|
||||
"pct_tel": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"docker": [
|
||||
{
|
||||
"name": "loki",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "listmonk",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-1",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-db-1",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "plausible-plausible-events-db-1",
|
||||
"status": "Up 4 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "n8n-docker-n8n-1",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "mattermost-docker-mm-db-1",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "mattermost-docker-mattermost-1",
|
||||
"status": "Up 5 days (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "twenty",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "twenty-redis",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "langfuse",
|
||||
"status": "Up 5 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "redis-weval",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "gitea",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "node-exporter",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "prometheus",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "searxng",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "uptime-kuma",
|
||||
"status": "Up 39 hours (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "vaultwarden",
|
||||
"status": "Up 7 days (healthy)",
|
||||
"ports": ""
|
||||
},
|
||||
{
|
||||
"name": "qdrant",
|
||||
"status": "Up 7 days",
|
||||
"ports": ""
|
||||
}
|
||||
],
|
||||
"crons": {
|
||||
"active": 35
|
||||
},
|
||||
"git": {
|
||||
"head": "5de7c1a0c AUTO-BACKUP 20260421-1640",
|
||||
"dirty": 2,
|
||||
"status": "DIRTY"
|
||||
},
|
||||
"nonreg": {
|
||||
"total": 153,
|
||||
"passed": 153,
|
||||
"score": "100%"
|
||||
},
|
||||
"services": [
|
||||
{
|
||||
"name": "DeerFlow",
|
||||
"port": 3002,
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "DeerFlow API",
|
||||
"port": 8001,
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "Qdrant",
|
||||
"port": 6333,
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "Ollama",
|
||||
"port": 11434,
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "Redis",
|
||||
"port": 6379,
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "Sovereign",
|
||||
"port": 4000,
|
||||
"status": "UP"
|
||||
},
|
||||
{
|
||||
"name": "SearXNG",
|
||||
"port": 8080,
|
||||
"status": "UP"
|
||||
}
|
||||
],
|
||||
"whisper": {
|
||||
"binary": "COMPILED",
|
||||
"model": "142MB"
|
||||
},
|
||||
"grand_total": 3904,
|
||||
"health": {
|
||||
"score": 5,
|
||||
"max": 6,
|
||||
"pct": 83
|
||||
},
|
||||
"elapsed_ms": 10419
|
||||
}
|
||||
96
api/feature-adoption.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* WEVAL Feature Adoption Tracker sovereign 21avr2026
|
||||
* Track which modules/widgets/features users interact with per session
|
||||
* Storage: /opt/weval-l99/data/feature-adoption.jsonl
|
||||
*
|
||||
* GET ?action=stats -> adoption rate KPI
|
||||
* POST ?action=track -> log feature use (feature, user, session_id)
|
||||
* GET ?action=list -> features inventory
|
||||
*/
|
||||
header("Content-Type: application/json");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
|
||||
$STORAGE = "/opt/weval-l99/data/feature-adoption.jsonl";
|
||||
$INVENTORY = "/opt/weval-l99/data/feature-inventory.json";
|
||||
@mkdir(dirname($STORAGE), 0755, true);
|
||||
|
||||
// Default inventory if not exists
|
||||
if (!file_exists($INVENTORY)) {
|
||||
$default_features = [
|
||||
"wtp_dashboard", "wtp_pilotage_widget", "wtp_sparklines", "wtp_l99_brain",
|
||||
"wevia_master_chat", "wevia_orchestrator", "all_ia_hub", "agents_archi",
|
||||
"architecture_live", "openclaw", "nonreg_dashboard", "stripe_live",
|
||||
"ethica_hcp", "wevads_ia", "director_chat", "orphans_hub",
|
||||
"wikidoc", "vault_manager", "nps_feedback", "csat_rating", "ticket_create"
|
||||
];
|
||||
@file_put_contents($INVENTORY, json_encode(["features" => $default_features, "total" => count($default_features)]));
|
||||
}
|
||||
|
||||
$action = $_GET["action"] ?? ($_POST["action"] ?? "stats");
|
||||
|
||||
if ($action === "track" && $_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$feature = substr(trim($_POST["feature"] ?? ""), 0, 80);
|
||||
$user = substr(trim($_POST["user"] ?? "anonymous"), 0, 60);
|
||||
$session = substr(trim($_POST["session_id"] ?? ""), 0, 40);
|
||||
if (!$feature) {
|
||||
echo json_encode(["ok"=>false,"error"=>"feature_required"]);
|
||||
exit;
|
||||
}
|
||||
$record = ["ts"=>date("c"),"feature"=>$feature,"user"=>$user,"session_id"=>$session];
|
||||
@file_put_contents($STORAGE, json_encode($record)."\n", FILE_APPEND | LOCK_EX);
|
||||
echo json_encode(["ok"=>true,"tracked"=>$feature]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === "list") {
|
||||
$inv = @json_decode(@file_get_contents($INVENTORY), true);
|
||||
echo json_encode($inv ?: ["features"=>[],"total"=>0]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// stats = adoption rate calculation
|
||||
$inv = @json_decode(@file_get_contents($INVENTORY), true);
|
||||
$total_features = intval($inv["total"] ?? 0);
|
||||
|
||||
$used_features = [];
|
||||
$events = [];
|
||||
if (is_readable($STORAGE)) {
|
||||
foreach (file($STORAGE) as $line) {
|
||||
$r = @json_decode(trim($line), true);
|
||||
if ($r && isset($r["feature"])) {
|
||||
$used_features[$r["feature"]] = ($used_features[$r["feature"]] ?? 0) + 1;
|
||||
$events[] = $r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$adopted = count($used_features);
|
||||
$adoption_rate = $total_features > 0 ? round(($adopted / $total_features) * 100) : 0;
|
||||
|
||||
// 7d & 30d activity
|
||||
$now = time();
|
||||
$evt_7d = 0; $evt_30d = 0;
|
||||
$users_7d = []; $users_30d = [];
|
||||
foreach ($events as $e) {
|
||||
$t = strtotime($e["ts"]);
|
||||
if ($t >= $now - 7*86400) { $evt_7d++; $users_7d[$e["user"] ?? ""] = true; }
|
||||
if ($t >= $now - 30*86400) { $evt_30d++; $users_30d[$e["user"] ?? ""] = true; }
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"ok"=>true,
|
||||
"source"=>"sovereign_jsonl_tracker",
|
||||
"ts"=>date("c"),
|
||||
"adoption_rate_pct"=>$adoption_rate,
|
||||
"features_total"=>$total_features,
|
||||
"features_adopted"=>$adopted,
|
||||
"features_top5"=>array_slice(array_reverse(array_keys($used_features)), 0, 5),
|
||||
"events_total"=>count($events),
|
||||
"events_7d"=>$evt_7d,
|
||||
"events_30d"=>$evt_30d,
|
||||
"unique_users_7d"=>count($users_7d),
|
||||
"unique_users_30d"=>count($users_30d),
|
||||
"status"=>count($events)===0 ? "wire_needed" : ($adoption_rate >= 70 ? "ok" : "warn"),
|
||||
"drill"=>"Features used / Features available · Track via POST ?action=track",
|
||||
]);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"timestamp": "2026-04-21 12:00",
|
||||
"fast_php_routes": 445,
|
||||
"timestamp": "2026-04-21 18:00",
|
||||
"fast_php_routes": 446,
|
||||
"opt_tools_total": 51,
|
||||
"wired": 50,
|
||||
"not_wired_count": 1,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"status": "ERROR"
|
||||
},
|
||||
"ports": {
|
||||
"total": 76,
|
||||
"total": 75,
|
||||
"exposed": 20,
|
||||
"ports": [
|
||||
{
|
||||
@@ -14,11 +14,11 @@
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5890",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=12),(\"apache2\",pi"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=12),(\"apache2\",pi"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5888",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=10),(\"apache2\",pi"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=10),(\"apache2\",pi"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:6060",
|
||||
@@ -30,23 +30,23 @@
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5823",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=5),(\"apache2\",pid"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=5),(\"apache2\",pid"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5822",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=4),(\"apache2\",pid"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=4),(\"apache2\",pid"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5821",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=3),(\"apache2\",pid"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=3),(\"apache2\",pid"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5825",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=13),(\"apache2\",pi"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=13),(\"apache2\",pi"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:5824",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=6),(\"apache2\",pid"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=6),(\"apache2\",pid"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:6379",
|
||||
@@ -89,8 +89,8 @@
|
||||
"process": "users:((\"python3\",pid=1392,fd=3))"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:36521",
|
||||
"process": "users:((\"ollama\",pid=1156391,fd=3))"
|
||||
"addr": "127.0.0.1:36363",
|
||||
"process": "users:((\"ollama\",pid=2510108,fd=3))"
|
||||
},
|
||||
{
|
||||
"addr": "0.0.0.0:4000",
|
||||
@@ -102,7 +102,7 @@
|
||||
},
|
||||
{
|
||||
"addr": "0.0.0.0:443",
|
||||
"process": "users:((\"nginx\",pid=1873436,fd=5),(\"nginx\",pid=116"
|
||||
"process": "users:((\"nginx\",pid=2497604,fd=5),(\"nginx\",pid=249"
|
||||
},
|
||||
{
|
||||
"addr": "0.0.0.0:49222",
|
||||
@@ -110,23 +110,23 @@
|
||||
},
|
||||
{
|
||||
"addr": "0.0.0.0:80",
|
||||
"process": "users:((\"nginx\",pid=1873436,fd=6),(\"nginx\",pid=116"
|
||||
"process": "users:((\"nginx\",pid=2497604,fd=6),(\"nginx\",pid=249"
|
||||
},
|
||||
{
|
||||
"addr": "0.0.0.0:22",
|
||||
"process": "users:((\"sshd\",pid=1314257,fd=7))"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:41237",
|
||||
"process": "users:((\"ollama\",pid=2487766,fd=3))"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:8280",
|
||||
"process": "users:((\"crowdsec\",pid=2454,fd=26))"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:8443",
|
||||
"process": "users:((\"apache2\",pid=3290956,fd=11),(\"apache2\",pi"
|
||||
},
|
||||
{
|
||||
"addr": "127.0.0.1:9050",
|
||||
"process": "users:((\"tor\",pid=1408,fd=6))"
|
||||
"process": "users:((\"apache2\",pid=2510443,fd=11),(\"apache2\",pi"
|
||||
}
|
||||
],
|
||||
"status": "WARN"
|
||||
@@ -161,7 +161,7 @@
|
||||
"status": "PASS"
|
||||
}
|
||||
},
|
||||
"timestamp": "2026-04-21T12:00:03",
|
||||
"timestamp": "2026-04-21T18:00:02",
|
||||
"oss_tools": [
|
||||
{
|
||||
"name": "Nuclei",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timestamp": "2026-04-21T12:30:02.601748",
|
||||
"timestamp": "2026-04-21T18:30:02.403371",
|
||||
"source": "auto-populator-v2-fixed-18avr",
|
||||
"enterprise_total_agents": 747,
|
||||
"docker_total": 19,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[12:30:02] === MEETING ROOMS AUTO-POPULATOR (FIXED 18avr) ===
|
||||
[12:30:02] Enterprise: 747 agents
|
||||
[12:30:02] Registry: 11 sections
|
||||
[12:30:02] Docker: 19 containers
|
||||
[12:30:02] strat: 99 agents (38 active)
|
||||
[12:30:02] infra: 308 agents (34 active)
|
||||
[12:30:02] dev: 108 agents (25 active)
|
||||
[12:30:02] sec: 48 agents (13 active)
|
||||
[12:30:02] biz: 114 agents (31 active)
|
||||
[12:30:02] ia: 168 agents (48 active)
|
||||
[12:30:02] transit: 254 agents (27 active)
|
||||
[12:30:02] Output: /var/www/html/api/meeting-rooms-data.json
|
||||
[18:30:02] === MEETING ROOMS AUTO-POPULATOR (FIXED 18avr) ===
|
||||
[18:30:02] Enterprise: 747 agents
|
||||
[18:30:02] Registry: 11 sections
|
||||
[18:30:02] Docker: 19 containers
|
||||
[18:30:02] strat: 99 agents (38 active)
|
||||
[18:30:02] infra: 308 agents (34 active)
|
||||
[18:30:02] dev: 108 agents (25 active)
|
||||
[18:30:02] sec: 48 agents (13 active)
|
||||
[18:30:02] biz: 114 agents (31 active)
|
||||
[18:30:02] ia: 168 agents (48 active)
|
||||
[18:30:02] transit: 254 agents (27 active)
|
||||
[18:30:02] Output: /var/www/html/api/meeting-rooms-data.json
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"ok": true,
|
||||
"agent": "V42_MQL_Scoring_Agent_REAL",
|
||||
"ts": "2026-04-21T14:40:01+00:00",
|
||||
"ts": "2026-04-21T21:40:01+00:00",
|
||||
"status": "DEPLOYED_AUTO",
|
||||
"deployed": true,
|
||||
"algorithm": "weighted_behavioral_signals",
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ts": "20260421_152221", "version": "3.2", "score": 100, "pass": 153, "fail": 0, "total": 153, "elapsed": 32.4, "categories": {"S204": {"pass": 9, "fail": 0}, "S95-WV": {"pass": 12, "fail": 0}, "S95-ARS": {"pass": 17, "fail": 0}, "S95-iR": {"pass": 1, "fail": 0}, "INFRA": {"pass": 5, "fail": 0}, "API": {"pass": 27, "fail": 0}, "SEC": {"pass": 4, "fail": 0}, "S95-BK": {"pass": 6, "fail": 0}, "C2-API": {"pass": 4, "fail": 0}, "C2-SPA": {"pass": 1, "fail": 0}, "C2-WV": {"pass": 3, "fail": 0}, "SSO": {"pass": 25, "fail": 0}, "DATA": {"pass": 5, "fail": 0}, "CRONS": {"pass": 2, "fail": 0}, "BLADE": {"pass": 7, "fail": 0}, "LIFE": {"pass": 3, "fail": 0}, "FUNC": {"pass": 7, "fail": 0}, "01AVR": {"pass": 10, "fail": 0}, "STRUCT": {"pass": 5, "fail": 0}}, "failures": []}
|
||||
{"ts": "20260421_224704", "version": "3.2", "score": 100, "pass": 153, "fail": 0, "total": 153, "elapsed": 31.6, "categories": {"S204": {"pass": 9, "fail": 0}, "S95-WV": {"pass": 12, "fail": 0}, "S95-ARS": {"pass": 17, "fail": 0}, "S95-iR": {"pass": 1, "fail": 0}, "INFRA": {"pass": 5, "fail": 0}, "API": {"pass": 27, "fail": 0}, "SEC": {"pass": 4, "fail": 0}, "S95-BK": {"pass": 6, "fail": 0}, "C2-API": {"pass": 4, "fail": 0}, "C2-SPA": {"pass": 1, "fail": 0}, "C2-WV": {"pass": 3, "fail": 0}, "SSO": {"pass": 25, "fail": 0}, "DATA": {"pass": 5, "fail": 0}, "CRONS": {"pass": 2, "fail": 0}, "BLADE": {"pass": 7, "fail": 0}, "LIFE": {"pass": 3, "fail": 0}, "FUNC": {"pass": 7, "fail": 0}, "01AVR": {"pass": 10, "fail": 0}, "STRUCT": {"pass": 5, "fail": 0}}, "failures": []}
|
||||
65
api/nps-collector.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* WEVAL NPS Collector - sovereign 21avr2026
|
||||
* Zero external tool (Typeform/etc). Local JSONL storage.
|
||||
* GET /api/nps-collector.php?action=stats -> KPI ready
|
||||
* POST /api/nps-collector.php?action=submit -> save response (score 0-10, comment)
|
||||
*/
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$STORAGE = '/opt/weval-l99/data/nps-responses.jsonl';
|
||||
@mkdir(dirname($STORAGE), 0755, true);
|
||||
|
||||
$action = $_GET['action'] ?? ($_POST['action'] ?? 'stats');
|
||||
|
||||
if ($action === 'submit' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$score = intval($_POST['score'] ?? -1);
|
||||
$comment = substr(trim($_POST['comment'] ?? ''), 0, 500);
|
||||
$user = substr(trim($_POST['user'] ?? 'anonymous'), 0, 60);
|
||||
if ($score < 0 || $score > 10) {
|
||||
echo json_encode(['ok'=>false,'error'=>'invalid_score','expected'=>'0-10']);
|
||||
exit;
|
||||
}
|
||||
$record = ['ts'=>date('c'),'score'=>$score,'comment'=>$comment,'user'=>$user,'ip'=>$_SERVER['REMOTE_ADDR']??''];
|
||||
@file_put_contents($STORAGE, json_encode($record)."\n", FILE_APPEND | LOCK_EX);
|
||||
echo json_encode(['ok'=>true,'recorded'=>$record]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// stats = NPS score aggregation
|
||||
$responses = [];
|
||||
if (is_readable($STORAGE)) {
|
||||
foreach (file($STORAGE) as $line) {
|
||||
$r = @json_decode(trim($line), true);
|
||||
if ($r && isset($r['score'])) $responses[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
$n = count($responses);
|
||||
if ($n === 0) {
|
||||
echo json_encode(['ok'=>true,'source'=>'sovereign_jsonl','ts'=>date('c'),'nps_score'=>0,'responses_total'=>0,'promoters'=>0,'passives'=>0,'detractors'=>0,'status'=>'wire_needed','drill'=>'No responses yet. Post to this endpoint with score+comment.','endpoint_submit'=>'/api/nps-collector.php?action=submit']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$promoters = 0; $passives = 0; $detractors = 0;
|
||||
foreach ($responses as $r) {
|
||||
$s = $r['score'];
|
||||
if ($s >= 9) $promoters++;
|
||||
elseif ($s >= 7) $passives++;
|
||||
else $detractors++;
|
||||
}
|
||||
$nps = round((($promoters - $detractors) / $n) * 100);
|
||||
|
||||
echo json_encode([
|
||||
'ok'=>true,
|
||||
'source'=>'sovereign_jsonl',
|
||||
'ts'=>date('c'),
|
||||
'nps_score'=>$nps,
|
||||
'responses_total'=>$n,
|
||||
'promoters'=>$promoters,
|
||||
'passives'=>$passives,
|
||||
'detractors'=>$detractors,
|
||||
'status'=>$nps >= 50 ? 'ok' : ($nps >= 0 ? 'warn' : 'fail'),
|
||||
'drill'=>"NPS = ((promoters - detractors) / total) * 100",
|
||||
'recent_comments'=>array_slice(array_reverse(array_column($responses, 'comment')), 0, 5),
|
||||
]);
|
||||
11
api/oss-manifest.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
// Wave 222 · /api/oss-manifest.php · serves /opt/oss/manifest.json
|
||||
@require_once __DIR__ . "/wevia-sanitizer-guard.php";
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
$path = "/opt/oss/manifest.json";
|
||||
if (file_exists($path)) {
|
||||
echo @file_get_contents($path);
|
||||
} else {
|
||||
echo json_encode(["error" => "manifest_not_found"]);
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.819776"
|
||||
"discovered": "2026-04-21T23:00:03.489531"
|
||||
},
|
||||
{
|
||||
"name": "wevia-brain",
|
||||
@@ -23,7 +23,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.997109"
|
||||
"discovered": "2026-04-21T23:00:03.677956"
|
||||
},
|
||||
{
|
||||
"name": "skills",
|
||||
@@ -36,7 +36,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.536304"
|
||||
"discovered": "2026-04-21T23:00:03.214198"
|
||||
},
|
||||
{
|
||||
"name": "everything-claude-code",
|
||||
@@ -49,7 +49,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.",
|
||||
"discovered": "2026-04-21T16:00:04.067458"
|
||||
"discovered": "2026-04-21T23:00:02.916745"
|
||||
},
|
||||
{
|
||||
"name": "open-webui-fresh",
|
||||
@@ -62,7 +62,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "# Open WebUI 👋   | [中文](README.zh.md) | [日本語](README.ja.md) | [Español](README.es.md) | [Tiếng Việt](README.vi.md) | [Português](README.p",
|
||||
"discovered": "2026-04-21T16:00:04.274526"
|
||||
"discovered": "2026-04-21T23:00:03.076639"
|
||||
},
|
||||
{
|
||||
"name": "mxyhi_ok-skills",
|
||||
@@ -114,7 +114,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "# OK Skills: AI Coding Agent Skills for Codex, Claude Code, Cursor, OpenClaw, and More English | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | ",
|
||||
"discovered": "2026-04-21T16:00:04.237089"
|
||||
"discovered": "2026-04-21T23:00:03.045553"
|
||||
},
|
||||
{
|
||||
"name": "SuperClaude_Framework",
|
||||
@@ -127,7 +127,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "<div align=\"center\"> # 🚀 SuperClaude Framework [](https://smithery.ai/skills?ns=",
|
||||
"discovered": "2026-04-21T16:00:03.824199"
|
||||
"discovered": "2026-04-21T23:00:02.724729"
|
||||
},
|
||||
{
|
||||
"name": "paperclip-weval",
|
||||
@@ -140,7 +140,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "<p align=\"center\"> <img src=\"doc/assets/header.png\" alt=\"Paperclip — runs your business\" width=\"720\" /> </p> <p align=\"center\"> <a href=\"#quickst",
|
||||
"discovered": "2026-04-21T16:00:04.325183"
|
||||
"discovered": "2026-04-21T23:00:03.101166"
|
||||
},
|
||||
{
|
||||
"name": "vllm",
|
||||
@@ -153,7 +153,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "<!-- markdownlint-disable MD001 MD041 --> <p align=\"center\"> <picture> <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubus",
|
||||
"discovered": "2026-04-21T16:00:04.725255"
|
||||
"discovered": "2026-04-21T23:00:03.331344"
|
||||
},
|
||||
{
|
||||
"name": "deer-flow",
|
||||
@@ -166,7 +166,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "# 🦌 DeerFlow - 2.0 English | [中文](./README_zh.md) | [日本語](./README_ja.md) | [Français](./README_fr.md) | [Русский](./README_ru.md) [ [](https://agent.xfyun.cn) <div align=\"center\"> [ | [Français](docs/translations/README.fr.md) | [Italiano](docs/translations/README.it.md) | ",
|
||||
"discovered": "2026-04-21T16:00:03.741972"
|
||||
"discovered": "2026-04-21T23:00:02.687576"
|
||||
},
|
||||
{
|
||||
"name": "aios",
|
||||
@@ -374,7 +374,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "# AIOS: AI Agent Operating System <a href='https://arxiv.org/abs/2403.16971'><img src='https://img.shields.io/badge/Paper-PDF-red'></a> <a href='http",
|
||||
"discovered": "2026-04-21T16:00:03.855585"
|
||||
"discovered": "2026-04-21T23:00:02.761010"
|
||||
},
|
||||
{
|
||||
"name": "rnd-agent-framework",
|
||||
@@ -387,7 +387,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": " # Welcome to Microsoft Agent Framework! [\"> <source srcset=\"apps/w",
|
||||
"discovered": "2026-04-21T16:00:04.575438"
|
||||
"discovered": "2026-04-21T23:00:03.226663"
|
||||
},
|
||||
{
|
||||
"name": "fmgapp",
|
||||
@@ -478,7 +478,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.071376"
|
||||
"discovered": "2026-04-21T23:00:02.922553"
|
||||
},
|
||||
{
|
||||
"name": "obsidian-vault",
|
||||
@@ -491,7 +491,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.254276"
|
||||
"discovered": "2026-04-21T23:00:03.061732"
|
||||
},
|
||||
{
|
||||
"name": "rnd-agents",
|
||||
@@ -504,7 +504,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "# Claude Code Plugins: Orchestration and Automation > **⚡ Updated for Opus 4.6, Sonnet 4.6 & Haiku 4.5** — Three-tier model strategy for optimal perf",
|
||||
"discovered": "2026-04-21T16:00:04.458045"
|
||||
"discovered": "2026-04-21T23:00:03.176177"
|
||||
},
|
||||
{
|
||||
"name": "FrancyJGLisboa_agent-skill-creator",
|
||||
@@ -517,7 +517,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "# Agent Skill Creator **Turn any workflow into reusable AI agent software that installs on 14+ tools — no spec writing, no prompt engineering, no cod",
|
||||
"discovered": "2026-04-21T16:00:03.654108"
|
||||
"discovered": "2026-04-21T23:00:02.659504"
|
||||
},
|
||||
{
|
||||
"name": "skillsmith",
|
||||
@@ -530,7 +530,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "<div align=\"center\"> <img src=\"terminal.svg\" alt=\"Skillsmith terminal\" width=\"740\"/> </div> <div align=\"center\"> # Skillsmith **Build consistent ",
|
||||
"discovered": "2026-04-21T16:00:04.546821"
|
||||
"discovered": "2026-04-21T23:00:03.216638"
|
||||
},
|
||||
{
|
||||
"name": "awesome-agent-skills",
|
||||
@@ -543,7 +543,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "<a href=\"https://github.com/VoltAgent/voltagent\"> <img width=\"1500\" height=\"801\" alt=\"claude-skills\" src=\"https://github.com/user-attachments/ass",
|
||||
"discovered": "2026-04-21T16:00:04.032534"
|
||||
"discovered": "2026-04-21T23:00:02.856946"
|
||||
},
|
||||
{
|
||||
"name": "paperclip-skills",
|
||||
@@ -556,7 +556,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.306955"
|
||||
"discovered": "2026-04-21T23:00:03.097450"
|
||||
},
|
||||
{
|
||||
"name": "jzOcb_writing-style-skill",
|
||||
@@ -569,7 +569,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "# Writing Style Skill 可复用的写作风格 Skill 模板。**内置自动学习** — 从你的修改中自动提取规则,SKILL.md 越用越准。 兼容 **Claude Code** + **OpenClaw (ClawHub)**。 ## 原理 ``` AI 用 SKILL",
|
||||
"discovered": "2026-04-21T16:00:04.083587"
|
||||
"discovered": "2026-04-21T23:00:02.943577"
|
||||
},
|
||||
{
|
||||
"name": "qdrant-data",
|
||||
@@ -582,7 +582,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.389173"
|
||||
"discovered": "2026-04-21T23:00:03.161515"
|
||||
},
|
||||
{
|
||||
"name": "wazuh",
|
||||
@@ -595,7 +595,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.736401"
|
||||
"discovered": "2026-04-21T23:00:03.389139"
|
||||
},
|
||||
{
|
||||
"name": "plausible",
|
||||
@@ -608,7 +608,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.342191"
|
||||
"discovered": "2026-04-21T23:00:03.123717"
|
||||
},
|
||||
{
|
||||
"name": "pmta",
|
||||
@@ -621,7 +621,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.355029"
|
||||
"discovered": "2026-04-21T23:00:03.137298"
|
||||
},
|
||||
{
|
||||
"name": "render-configs",
|
||||
@@ -634,7 +634,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.408294"
|
||||
"discovered": "2026-04-21T23:00:03.165713"
|
||||
},
|
||||
{
|
||||
"name": "searxng",
|
||||
@@ -647,7 +647,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.515442"
|
||||
"discovered": "2026-04-21T23:00:03.206089"
|
||||
},
|
||||
{
|
||||
"name": "weval-guardian",
|
||||
@@ -660,7 +660,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.798671"
|
||||
"discovered": "2026-04-21T23:00:03.457554"
|
||||
},
|
||||
{
|
||||
"name": "weval-litellm",
|
||||
@@ -673,7 +673,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.838732"
|
||||
"discovered": "2026-04-21T23:00:03.491658"
|
||||
},
|
||||
{
|
||||
"name": "weval-security",
|
||||
@@ -686,7 +686,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.963419"
|
||||
"discovered": "2026-04-21T23:00:03.625864"
|
||||
},
|
||||
{
|
||||
"name": "archive",
|
||||
@@ -699,7 +699,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:03.953415"
|
||||
"discovered": "2026-04-21T23:00:02.844768"
|
||||
},
|
||||
{
|
||||
"name": "loki",
|
||||
@@ -712,7 +712,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.161645"
|
||||
"discovered": "2026-04-21T23:00:02.994111"
|
||||
},
|
||||
{
|
||||
"name": "ruflo",
|
||||
@@ -725,7 +725,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.507898"
|
||||
"discovered": "2026-04-21T23:00:03.203889"
|
||||
},
|
||||
{
|
||||
"name": "twenty",
|
||||
@@ -738,7 +738,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.616634"
|
||||
"discovered": "2026-04-21T23:00:03.284105"
|
||||
},
|
||||
{
|
||||
"name": "weval-crewai",
|
||||
@@ -751,7 +751,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.772973"
|
||||
"discovered": "2026-04-21T23:00:03.431483"
|
||||
},
|
||||
{
|
||||
"name": "weval-plugins",
|
||||
@@ -764,7 +764,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.900134"
|
||||
"discovered": "2026-04-21T23:00:03.568544"
|
||||
},
|
||||
{
|
||||
"name": "weval-radar",
|
||||
@@ -777,7 +777,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.918896"
|
||||
"discovered": "2026-04-21T23:00:03.587907"
|
||||
},
|
||||
{
|
||||
"name": "weval-scrapy",
|
||||
@@ -790,7 +790,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.943506"
|
||||
"discovered": "2026-04-21T23:00:03.608294"
|
||||
},
|
||||
{
|
||||
"name": "langfuse",
|
||||
@@ -803,7 +803,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.086570"
|
||||
"discovered": "2026-04-21T23:00:02.953470"
|
||||
},
|
||||
{
|
||||
"name": "litellm",
|
||||
@@ -816,7 +816,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.126567"
|
||||
"discovered": "2026-04-21T23:00:02.984227"
|
||||
},
|
||||
{
|
||||
"name": "mattermost-docker",
|
||||
@@ -829,7 +829,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.180697"
|
||||
"discovered": "2026-04-21T23:00:02.998150"
|
||||
},
|
||||
{
|
||||
"name": "prometheus",
|
||||
@@ -842,7 +842,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.375182"
|
||||
"discovered": "2026-04-21T23:00:03.155605"
|
||||
},
|
||||
{
|
||||
"name": "twenty-compose",
|
||||
@@ -855,7 +855,7 @@
|
||||
"has_docker": true,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.666219"
|
||||
"discovered": "2026-04-21T23:00:03.289729"
|
||||
},
|
||||
{
|
||||
"name": "weval-ux",
|
||||
@@ -868,7 +868,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.979610"
|
||||
"discovered": "2026-04-21T23:00:03.659745"
|
||||
},
|
||||
{
|
||||
"name": "wevia-integrity",
|
||||
@@ -881,7 +881,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:05.022215"
|
||||
"discovered": "2026-04-21T23:00:03.712293"
|
||||
},
|
||||
{
|
||||
"name": "DiffusionDB",
|
||||
@@ -894,7 +894,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:03.583864"
|
||||
"discovered": "2026-04-21T23:00:02.579502"
|
||||
},
|
||||
{
|
||||
"name": "LTX-Video",
|
||||
@@ -907,7 +907,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:03.808120"
|
||||
"discovered": "2026-04-21T23:00:02.711861"
|
||||
},
|
||||
{
|
||||
"name": "localai",
|
||||
@@ -920,7 +920,7 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:04.148879"
|
||||
"discovered": "2026-04-21T23:00:02.990022"
|
||||
},
|
||||
{
|
||||
"name": "wevia-finetune",
|
||||
@@ -933,6 +933,6 @@
|
||||
"has_docker": false,
|
||||
"wired": true,
|
||||
"description": "",
|
||||
"discovered": "2026-04-21T16:00:05.013075"
|
||||
"discovered": "2026-04-21T23:00:03.692226"
|
||||
}
|
||||
]
|
||||
44
api/pandasai-ollama-test-result.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"ts": "2026-04-21T23:26:25.346919",
|
||||
"wave": 224,
|
||||
"test": "pandasai_ollama_integration",
|
||||
"steps": [
|
||||
{
|
||||
"step": "import pandasai",
|
||||
"ok": true,
|
||||
"version": "2.0.24"
|
||||
},
|
||||
{
|
||||
"step": "import pandas",
|
||||
"ok": true,
|
||||
"version": "2.3.3"
|
||||
},
|
||||
{
|
||||
"step": "create df",
|
||||
"ok": true,
|
||||
"rows": 3,
|
||||
"cols": 2
|
||||
},
|
||||
{
|
||||
"step": "import LocalLLM",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"step": "create Ollama LocalLLM",
|
||||
"ok": true,
|
||||
"model": "llama3.2:latest",
|
||||
"api": "localhost:11434/v1"
|
||||
},
|
||||
{
|
||||
"step": "import SmartDataframe",
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"step": "create SmartDataframe",
|
||||
"ok": true,
|
||||
"class": "SmartDataframe"
|
||||
}
|
||||
],
|
||||
"integration_ok": true,
|
||||
"message": "pandasai 2.0.24 + Ollama llama3.2 integration LOADED (ready for LLM queries)"
|
||||
}
|
||||
1
api/pandasai-real-query-result.json
Normal file
@@ -0,0 +1 @@
|
||||
{"ok": true, "query": "sum of mrr_eur", "expected": 3000, "llm_answer": "3000", "duration_s": 43.1}
|
||||
126
api/playwright-opus-session-20260421-final.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"ts": "2026-04-21T16:50:48+0200",
|
||||
"passed": 16,
|
||||
"total": 17,
|
||||
"results": [
|
||||
{
|
||||
"label": "auth \u00b7 faq-techniques",
|
||||
"url": "https://weval-consulting.com/faq-techniques.html",
|
||||
"expect": 302,
|
||||
"got": 302,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "auth \u00b7 wepredict",
|
||||
"url": "https://weval-consulting.com/wepredict.html",
|
||||
"expect": 302,
|
||||
"got": 302,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "auth \u00b7 faq-knowledge-base",
|
||||
"url": "https://weval-consulting.com/faq-knowledge-base.html",
|
||||
"expect": 302,
|
||||
"got": 302,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "auth \u00b7 landing-ocp",
|
||||
"url": "https://weval-consulting.com/landing-ocp.html",
|
||||
"expect": 302,
|
||||
"got": 302,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "auth \u00b7 pricing",
|
||||
"url": "https://weval-consulting.com/pricing.html",
|
||||
"expect": 302,
|
||||
"got": 302,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "public \u00b7 homepage",
|
||||
"url": "https://weval-consulting.com/index.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "public \u00b7 solutions",
|
||||
"url": "https://weval-consulting.com/solutions.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "public \u00b7 booking",
|
||||
"url": "https://weval-consulting.com/booking.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "public \u00b7 pitch (P5 clean)",
|
||||
"url": "https://weval-consulting.com/pitch.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "public \u00b7 ecosysteme (P5 clean)",
|
||||
"url": "https://weval-consulting.com/ecosysteme-ia-maroc.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "internal \u00b7 token-health-dashboard",
|
||||
"url": "https://weval-consulting.com/token-health-dashboard.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "internal \u00b7 coverage-dashboard",
|
||||
"url": "https://weval-consulting.com/wtp-udock-coverage.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "internal \u00b7 WTP ERP",
|
||||
"url": "https://weval-consulting.com/weval-technology-platform.html",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "internal \u00b7 wiki",
|
||||
"url": "https://weval-consulting.com/wiki.html",
|
||||
"expect": 200,
|
||||
"got": 302,
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"label": "api \u00b7 KPI aggregator",
|
||||
"url": "https://weval-consulting.com/api/wtp-kpi-global.php",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "api \u00b7 coverage scanner",
|
||||
"url": "https://weval-consulting.com/api/wtp-udock-coverage.php",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"label": "api \u00b7 orphans scanner",
|
||||
"url": "https://weval-consulting.com/api/pages-orphans-list.php",
|
||||
"expect": 200,
|
||||
"got": 200,
|
||||
"pass": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"date": "2026-04-21 10:00:02",
|
||||
"date": "2026-04-21 16:00:02",
|
||||
"query": "multi-agent orchestration",
|
||||
"new_repos": 0,
|
||||
"cloned": 0,
|
||||
|
||||
63
api/tickets-api.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* WEVAL Support Tickets sovereign 21avr2026
|
||||
* Local JSONL + status tracking (open/resolved/closed)
|
||||
* Storage: /opt/weval-l99/data/tickets.jsonl
|
||||
*/
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$STORAGE = '/opt/weval-l99/data/tickets.jsonl';
|
||||
@mkdir(dirname($STORAGE), 0755, true);
|
||||
|
||||
$action = $_GET['action'] ?? ($_POST['action'] ?? 'stats');
|
||||
|
||||
if ($action === 'create' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$subject = substr(trim($_POST['subject'] ?? ''), 0, 200);
|
||||
$body = substr(trim($_POST['body'] ?? ''), 0, 2000);
|
||||
$user = substr(trim($_POST['user'] ?? 'anonymous'), 0, 60);
|
||||
$priority = in_array($_POST['priority'] ?? '', ['low','medium','high','critical']) ? $_POST['priority'] : 'medium';
|
||||
if (!$subject) {
|
||||
echo json_encode(['ok'=>false,'error'=>'subject_required']);
|
||||
exit;
|
||||
}
|
||||
$id = 'TKT-' . date('Ymd') . '-' . substr(md5($subject.microtime()), 0, 6);
|
||||
$record = ['ts'=>date('c'),'id'=>$id,'status'=>'open','subject'=>$subject,'body'=>$body,'user'=>$user,'priority'=>$priority,'resolved_at'=>null];
|
||||
@file_put_contents($STORAGE, json_encode($record)."\n", FILE_APPEND | LOCK_EX);
|
||||
echo json_encode(['ok'=>true,'ticket_id'=>$id]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$tickets = [];
|
||||
if (is_readable($STORAGE)) {
|
||||
foreach (file($STORAGE) as $line) {
|
||||
$r = @json_decode(trim($line), true);
|
||||
if ($r) $tickets[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
// Stats
|
||||
$total = count($tickets);
|
||||
$open = 0; $resolved = 0; $mttr_hours = 0; $mttr_count = 0;
|
||||
foreach ($tickets as $t) {
|
||||
if ($t['status'] === 'open') $open++;
|
||||
elseif ($t['status'] === 'resolved' || $t['status'] === 'closed') {
|
||||
$resolved++;
|
||||
if (!empty($t['resolved_at'])) {
|
||||
$delta = (strtotime($t['resolved_at']) - strtotime($t['ts'])) / 3600;
|
||||
if ($delta > 0) { $mttr_hours += $delta; $mttr_count++; }
|
||||
}
|
||||
}
|
||||
}
|
||||
$mttr = $mttr_count > 0 ? round($mttr_hours / $mttr_count, 1) : 0;
|
||||
|
||||
echo json_encode([
|
||||
'ok'=>true,
|
||||
'source'=>'sovereign_jsonl',
|
||||
'ts'=>date('c'),
|
||||
'tickets_total'=>$total,
|
||||
'tickets_open'=>$open,
|
||||
'tickets_resolved'=>$resolved,
|
||||
'mttr_hours'=>$mttr,
|
||||
'status'=>$open === 0 && $total === 0 ? 'wire_needed' : ($open <= 5 ? 'ok' : 'warn'),
|
||||
'endpoint_create'=>'/api/tickets-api.php?action=create',
|
||||
]);
|
||||
@@ -1 +1 @@
|
||||
[{"q":"Bonjour, comment vas-tu?","ts":"2026-04-19T17:33:48+00:00"},{"q":"Reply OK only","ts":"2026-04-19T18:21:04+00:00"},{"q":"v49_state_100pct","ts":"2026-04-19T19:30:34+00:00"},{"q":"apple entities","ts":"2026-04-20T01:59:09+00:00"},{"q":"iptables","ts":"2026-04-20T21:15:24+00:00"}]
|
||||
[{"q": "apple entities", "ts": "2026-04-20T01:59:09+00:00"}, {"q": "iptables", "ts": "2026-04-20T21:15:24+00:00"}]
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timestamp": "2026-04-21T16:30:12",
|
||||
"timestamp": "2026-04-21T23:30:12",
|
||||
"features": {
|
||||
"total": 36,
|
||||
"pass": 35
|
||||
@@ -13,7 +13,7 @@
|
||||
"score": 97.2,
|
||||
"log": [
|
||||
"=== UX AGENT v1.0 ===",
|
||||
"Time: 2026-04-21 16:30:01",
|
||||
"Time: 2026-04-21 23:30:02",
|
||||
" core: 4/4",
|
||||
" layout: 3/4",
|
||||
" interaction: 6/6",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"ok": true,
|
||||
"version": "V83-business-kpi",
|
||||
"ts": "2026-04-21T14:42:43+00:00",
|
||||
"ts": "2026-04-21T21:42:45+00:00",
|
||||
"summary": {
|
||||
"total_categories": 8,
|
||||
"total_kpis": 64,
|
||||
"ok": 41,
|
||||
"warn": 20,
|
||||
"ok": 47,
|
||||
"warn": 14,
|
||||
"fail": 0,
|
||||
"wire_needed": 3,
|
||||
"data_completeness_pct": 95.3
|
||||
|
||||
131
api/weval-feature-tracker.js
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* WEVAL Feature Tracker + NPS Popup + CSAT autolink
|
||||
* Zero external deps. Zero cookies. sessionStorage only.
|
||||
* Auto-injects: track current page as feature + show NPS popup after 10 queries
|
||||
*/
|
||||
(function(){
|
||||
if(window.__WEVAL_TRACKER_LOADED__) return;
|
||||
window.__WEVAL_TRACKER_LOADED__ = true;
|
||||
|
||||
var path = window.location.pathname.replace(/^\/+|\.html$/g,'').replace(/\//g,'_') || 'index';
|
||||
var feature = path.toLowerCase();
|
||||
var user = (function(){
|
||||
try { return localStorage.getItem('weval_user') || 'anonymous'; } catch(e) { return 'anonymous'; }
|
||||
})();
|
||||
var sessionId = (function(){
|
||||
try {
|
||||
var s = sessionStorage.getItem('weval_session_id');
|
||||
if(!s) { s = 's_' + Date.now() + '_' + Math.random().toString(36).slice(2,8); sessionStorage.setItem('weval_session_id', s); }
|
||||
return s;
|
||||
} catch(e) { return 's_na'; }
|
||||
})();
|
||||
|
||||
// 1. Track feature once per session per page
|
||||
try {
|
||||
var key = 'weval_tracked_' + feature;
|
||||
if(!sessionStorage.getItem(key)) {
|
||||
sessionStorage.setItem(key, '1');
|
||||
var fd = new FormData();
|
||||
fd.append('action','track'); fd.append('feature',feature); fd.append('user',user); fd.append('session_id',sessionId);
|
||||
fetch('/api/feature-adoption.php?action=track', {method:'POST', body:fd}).catch(function(){});
|
||||
}
|
||||
} catch(e){}
|
||||
|
||||
// 2. Query counter for NPS popup trigger (only on wevia-master + all-ia-hub + wtp)
|
||||
var qCounterPages = ['wevia-master','all-ia-hub','weval-technology-platform','wevia-orchestrator'];
|
||||
if(qCounterPages.indexOf(feature) >= 0) {
|
||||
try {
|
||||
var qKey = 'weval_query_count';
|
||||
var npsShown = localStorage.getItem('weval_nps_shown_30d');
|
||||
var now = Date.now();
|
||||
// NPS not shown in last 30d
|
||||
if(!npsShown || (now - parseInt(npsShown)) > 30*86400000) {
|
||||
// Count queries - increment on every significant interaction
|
||||
document.addEventListener('click', function(e){
|
||||
var tgt = e.target;
|
||||
// Count clicks on buttons/send actions only
|
||||
if(tgt.matches && (tgt.matches('button, [type="submit"], .btn, .send-button, [onclick*="send"]') || (tgt.closest && tgt.closest('button, .btn')))) {
|
||||
var count = parseInt(localStorage.getItem(qKey) || '0') + 1;
|
||||
localStorage.setItem(qKey, count);
|
||||
if(count >= 10 && !document.getElementById('weval-nps-popup-el')) {
|
||||
showNPSPopup();
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
// 3. NPS Popup injection
|
||||
function showNPSPopup() {
|
||||
var pop = document.createElement('div');
|
||||
pop.id = 'weval-nps-popup-el';
|
||||
pop.innerHTML = [
|
||||
'<div id="weval-nps-overlay" style="position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99999;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px);font-family:system-ui,sans-serif">',
|
||||
'<div style="background:linear-gradient(135deg,#0f172a,#1e293b);border:1px solid #334a7a;border-radius:12px;padding:28px;max-width:480px;width:90%;box-shadow:0 20px 60px rgba(0,0,0,.5)">',
|
||||
'<div style="display:flex;justify-content:space-between;align-items:start;margin-bottom:16px">',
|
||||
'<h2 style="margin:0;color:#e2e8f0;font-size:19px">Comment notez-vous WEVAL ?</h2>',
|
||||
'<button id="weval-nps-close" style="background:none;border:none;color:#64748b;font-size:22px;cursor:pointer;line-height:1" aria-label="Fermer">×</button>',
|
||||
'</div>',
|
||||
'<p style="margin:0 0 18px 0;color:#94a3b8;font-size:13.5px;line-height:1.5">Sur une échelle de 0 à 10, recommanderiez-vous WEVAL à un collègue ?</p>',
|
||||
'<div id="weval-nps-scores" style="display:grid;grid-template-columns:repeat(11,1fr);gap:4px;margin-bottom:16px"></div>',
|
||||
'<textarea id="weval-nps-comment" placeholder="Commentaire optionnel..." style="width:100%;padding:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:6px;color:#e2e8f0;font-size:13px;font-family:inherit;resize:vertical;min-height:60px;box-sizing:border-box"></textarea>',
|
||||
'<div id="weval-nps-thanks" style="display:none;margin-top:14px;padding:10px;background:rgba(16,185,129,.12);border:1px solid rgba(16,185,129,.3);border-radius:6px;color:#6ee7b7;font-size:13px;text-align:center">Merci pour votre retour !</div>',
|
||||
'<div style="margin-top:14px;display:flex;gap:8px;justify-content:flex-end">',
|
||||
'<button id="weval-nps-later" style="padding:8px 14px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);color:#94a3b8;border-radius:6px;cursor:pointer;font-size:12px">Plus tard</button>',
|
||||
'<button id="weval-nps-submit" disabled style="padding:8px 14px;background:#3b82f6;border:none;color:#fff;border-radius:6px;cursor:pointer;font-size:12px;font-weight:600;opacity:.5">Envoyer</button>',
|
||||
'</div></div></div>'
|
||||
].join('');
|
||||
document.body.appendChild(pop);
|
||||
// Score buttons 0-10
|
||||
var scoresDiv = document.getElementById('weval-nps-scores');
|
||||
var selectedScore = null;
|
||||
for(var i=0;i<=10;i++){
|
||||
(function(score){
|
||||
var b = document.createElement('button');
|
||||
b.textContent = score;
|
||||
b.style.cssText = 'padding:10px 4px;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.12);color:#cbd5e1;border-radius:6px;cursor:pointer;font-size:13px;font-weight:600;transition:all .12s';
|
||||
b.onmouseover = function(){ if(selectedScore!==score){ this.style.background='rgba(59,130,246,.15)'; this.style.borderColor='#3b82f6'; } };
|
||||
b.onmouseout = function(){ if(selectedScore!==score){ this.style.background='rgba(255,255,255,.05)'; this.style.borderColor='rgba(255,255,255,.12)'; } };
|
||||
b.onclick = function(){
|
||||
selectedScore = score;
|
||||
Array.from(scoresDiv.children).forEach(function(el){ el.style.background='rgba(255,255,255,.05)'; el.style.borderColor='rgba(255,255,255,.12)'; el.style.color='#cbd5e1'; });
|
||||
this.style.background = score>=9?'#10b981':(score>=7?'#3b82f6':'#ef4444');
|
||||
this.style.borderColor = 'transparent';
|
||||
this.style.color = '#fff';
|
||||
var sb = document.getElementById('weval-nps-submit');
|
||||
sb.disabled = false; sb.style.opacity = '1';
|
||||
};
|
||||
scoresDiv.appendChild(b);
|
||||
})(i);
|
||||
}
|
||||
function close(){
|
||||
var el = document.getElementById('weval-nps-popup-el');
|
||||
if(el) el.remove();
|
||||
}
|
||||
document.getElementById('weval-nps-close').onclick = function(){
|
||||
try { localStorage.setItem('weval_nps_shown_30d', Date.now()); } catch(e){}
|
||||
close();
|
||||
};
|
||||
document.getElementById('weval-nps-later').onclick = function(){
|
||||
try { localStorage.removeItem('weval_query_count'); } catch(e){}
|
||||
close();
|
||||
};
|
||||
document.getElementById('weval-nps-submit').onclick = function(){
|
||||
if(selectedScore === null) return;
|
||||
var comment = document.getElementById('weval-nps-comment').value;
|
||||
var fd = new FormData();
|
||||
fd.append('action','submit'); fd.append('score',selectedScore); fd.append('comment',comment); fd.append('user',user);
|
||||
fetch('/api/nps-collector.php?action=submit', {method:'POST', body:fd})
|
||||
.then(function(){
|
||||
document.getElementById('weval-nps-thanks').style.display = 'block';
|
||||
try { localStorage.setItem('weval_nps_shown_30d', Date.now()); localStorage.removeItem('weval_query_count'); } catch(e){}
|
||||
setTimeout(close, 1800);
|
||||
})
|
||||
.catch(function(){ setTimeout(close, 400); });
|
||||
};
|
||||
}
|
||||
|
||||
// Expose globally for manual trigger + dev testing
|
||||
window.wevalShowNPS = showNPSPopup;
|
||||
})();
|
||||
206
api/wevia-intent-autowire.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
WEVIA INTENT AUTOWIRE · Chantier 4 SAFE · Opus 21-avr v7
|
||||
|
||||
Endpoint additif · ZERO touche à wevia-autonomous.php
|
||||
|
||||
Fonctions:
|
||||
- Analyse queries sans match dans tool_registry (gap analysis)
|
||||
- Propose nouveaux intents templates
|
||||
- Auto-append au registry après validation user (flag auto_approved=true)
|
||||
- Track gap history pour pattern detection
|
||||
|
||||
Usage:
|
||||
POST /api/wevia-intent-autowire.php
|
||||
{"action":"analyze"} # gap analysis
|
||||
{"action":"propose","query":"..."} # propose intent for query
|
||||
{"action":"approve","intent_id":"..."} # approve proposal
|
||||
{"action":"history"} # view gap history
|
||||
═══════════════════════════════════════════════════════════════════ */
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true) ?: [];
|
||||
$action = $input['action'] ?? 'analyze';
|
||||
|
||||
$GAPS_FILE = '/var/www/html/api/wevia-intent-gaps.json';
|
||||
$PROPOSALS_FILE = '/var/www/html/api/wevia-intent-proposals.json';
|
||||
$REGISTRY = '/var/www/html/api/wevia-tool-registry.json';
|
||||
|
||||
function _load_json($path, $default = []) {
|
||||
if (!file_exists($path)) return $default;
|
||||
$c = @file_get_contents($path);
|
||||
$d = @json_decode($c, true);
|
||||
return $d ?? $default;
|
||||
}
|
||||
|
||||
function _save_json($path, $data) {
|
||||
$tmp = $path . '.tmp';
|
||||
if (@file_put_contents($tmp, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) !== false) {
|
||||
return @rename($tmp, $path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function _analyze_gaps() {
|
||||
// Load tool registry
|
||||
$reg = _load_json('/var/www/html/api/wevia-tool-registry.json');
|
||||
$tools = $reg['tools'] ?? [];
|
||||
|
||||
// Load logs of recent queries without match
|
||||
$logs_path = '/var/log/wevia/unmatched-queries.log';
|
||||
$unmatched = [];
|
||||
if (file_exists($logs_path)) {
|
||||
$lines = @file($logs_path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
$unmatched = array_slice($lines ?? [], -100);
|
||||
}
|
||||
|
||||
// Load persistent gaps
|
||||
$gaps = _load_json('/var/www/html/api/wevia-intent-gaps.json', []);
|
||||
|
||||
// Analyze patterns
|
||||
$patterns = [];
|
||||
foreach (array_merge($unmatched, $gaps) as $entry) {
|
||||
$q = is_array($entry) ? ($entry['query'] ?? '') : $entry;
|
||||
if (!$q) continue;
|
||||
|
||||
// Extract keywords (words > 3 chars)
|
||||
preg_match_all('/\b[a-zA-Z]{4,}\b/', strtolower($q), $m);
|
||||
foreach (array_slice($m[0] ?? [], 0, 5) as $kw) {
|
||||
$patterns[$kw] = ($patterns[$kw] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
arsort($patterns);
|
||||
|
||||
return [
|
||||
'ts' => date('c'),
|
||||
'total_tools' => count($tools),
|
||||
'unmatched_queries_24h' => count($unmatched),
|
||||
'gap_patterns_top10' => array_slice($patterns, 0, 10, true),
|
||||
'coverage_estimate_pct' => count($unmatched) > 0
|
||||
? round((1 - count($unmatched) / max(1, count($unmatched) + count($tools))) * 100)
|
||||
: 100
|
||||
];
|
||||
}
|
||||
|
||||
function _propose_intent($query) {
|
||||
if (!$query) return ['error' => 'query required'];
|
||||
|
||||
// Extract keywords
|
||||
preg_match_all('/\b[a-zA-Z]{4,}\b/', strtolower($query), $m);
|
||||
$keywords = array_unique(array_slice($m[0] ?? [], 0, 6));
|
||||
|
||||
// Generate intent ID
|
||||
$intent_id = 'auto_' . substr(md5($query), 0, 8) . '_' . strtolower(preg_replace('/[^a-z]/', '', $keywords[0] ?? 'wire'));
|
||||
|
||||
// Generate kw regex
|
||||
$kw = implode('|', array_map(fn($k) => preg_quote($k, '/'), array_slice($keywords, 0, 4)));
|
||||
|
||||
$proposal = [
|
||||
'id' => $intent_id,
|
||||
'status' => 'pending_approval',
|
||||
'generated_ts' => date('c'),
|
||||
'query_origin' => $query,
|
||||
'suggested_entry' => [
|
||||
'id' => $intent_id,
|
||||
'kw' => $kw,
|
||||
'cmd' => "echo 'TODO: implement intent for: $query'",
|
||||
'exec' => false,
|
||||
'desc' => 'Auto-proposed intent · awaiting user approval + command implementation',
|
||||
'since' => 'opus-session-20260421-v7-autowire',
|
||||
'added_ts' => date('c'),
|
||||
'auto_proposed' => true
|
||||
],
|
||||
'approval_needed' => true,
|
||||
'approve_via' => "POST /api/wevia-intent-autowire.php {\"action\":\"approve\",\"intent_id\":\"$intent_id\",\"cmd\":\"<actual_command>\"}"
|
||||
];
|
||||
|
||||
// Save proposal
|
||||
$proposals = _load_json('/var/www/html/api/wevia-intent-proposals.json', ['proposals' => []]);
|
||||
$proposals['proposals'][$intent_id] = $proposal;
|
||||
$proposals['last_update'] = date('c');
|
||||
_save_json('/var/www/html/api/wevia-intent-proposals.json', $proposals);
|
||||
|
||||
return $proposal;
|
||||
}
|
||||
|
||||
function _approve_intent($intent_id, $cmd = null) {
|
||||
if (!$intent_id) return ['error' => 'intent_id required'];
|
||||
|
||||
$proposals = _load_json('/var/www/html/api/wevia-intent-proposals.json', ['proposals' => []]);
|
||||
$prop = $proposals['proposals'][$intent_id] ?? null;
|
||||
|
||||
if (!$prop) return ['error' => 'proposal not found', 'intent_id' => $intent_id];
|
||||
|
||||
// Update cmd if provided
|
||||
$entry = $prop['suggested_entry'];
|
||||
if ($cmd) {
|
||||
$entry['cmd'] = $cmd;
|
||||
$entry['exec'] = true;
|
||||
}
|
||||
$entry['approved_by'] = 'user';
|
||||
$entry['approved_ts'] = date('c');
|
||||
|
||||
// Note: Actual append to registry requires chattr -i
|
||||
// For safety, we create approval record · manual inject later
|
||||
$approved = _load_json('/var/www/html/api/wevia-intent-approved.json', ['approved' => []]);
|
||||
$approved['approved'][$intent_id] = $entry;
|
||||
$approved['last_approval'] = date('c');
|
||||
_save_json('/var/www/html/api/wevia-intent-approved.json', $approved);
|
||||
|
||||
// Mark proposal as approved
|
||||
$proposals['proposals'][$intent_id]['status'] = 'approved';
|
||||
_save_json('/var/www/html/api/wevia-intent-proposals.json', $proposals);
|
||||
|
||||
return [
|
||||
'ts' => date('c'),
|
||||
'action' => 'approve',
|
||||
'intent_id' => $intent_id,
|
||||
'status' => 'approved',
|
||||
'entry' => $entry,
|
||||
'next_step' => 'Intent approved · can be manually injected into tool_registry with chattr mgmt',
|
||||
'injection_command' => "Run: python3 /opt/scripts/inject_approved_intents.py (requires sudo + chattr -i registry)"
|
||||
];
|
||||
}
|
||||
|
||||
function _history() {
|
||||
$approved = _load_json('/var/www/html/api/wevia-intent-approved.json', ['approved' => []]);
|
||||
$proposals = _load_json('/var/www/html/api/wevia-intent-proposals.json', ['proposals' => []]);
|
||||
$gaps = _load_json('/var/www/html/api/wevia-intent-gaps.json', []);
|
||||
|
||||
return [
|
||||
'ts' => date('c'),
|
||||
'proposals_count' => count($proposals['proposals'] ?? []),
|
||||
'approved_count' => count($approved['approved'] ?? []),
|
||||
'gap_entries' => count($gaps),
|
||||
'last_approval' => $approved['last_approval'] ?? null,
|
||||
'last_proposal' => $proposals['last_update'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'analyze':
|
||||
echo json_encode(_analyze_gaps(), JSON_PRETTY_PRINT);
|
||||
break;
|
||||
case 'propose':
|
||||
$query = $input['query'] ?? null;
|
||||
echo json_encode(_propose_intent($query), JSON_PRETTY_PRINT);
|
||||
break;
|
||||
case 'approve':
|
||||
$intent_id = $input['intent_id'] ?? null;
|
||||
$cmd = $input['cmd'] ?? null;
|
||||
echo json_encode(_approve_intent($intent_id, $cmd), JSON_PRETTY_PRINT);
|
||||
break;
|
||||
case 'history':
|
||||
echo json_encode(_history(), JSON_PRETTY_PRINT);
|
||||
break;
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode([
|
||||
'error' => 'unknown action',
|
||||
'valid_actions' => ['analyze', 'propose', 'approve', 'history'],
|
||||
'doctrine' => 'Additif pur · zero touche wevia-autonomous.php · registry injection via script separé'
|
||||
]);
|
||||
}
|
||||