openvibers/openvibe.ai Commits
/stats (price and latency) is in search: index,follow with a description and canonical (the console layout made it noindex while the sitemap listed it) (#131)
Changes
@@@ -116,7 +116,7 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keysres.setHeader('Cache-Control', cachePolicy.htmlHeaders());res.setHeader('Content-Security-Policy', views.CSP);res.type('html').send(views.pages.stats({-- days: STATS_DAYS, rows,++ days: STATS_DAYS, rows, canonical: `${String(config.baseUrl || '').replace(/\/+$/, '')}/stats`,from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10),}));} catch (e) { next(e); }@@@ -119,9 +119,11 @@ const NAV = [['/console/quotas', 'Quotas', 'quotas'], ['/console/usage', 'Usage', 'usage'], ['/console/cache', 'Cache', 'cache'], ['/console/audit', 'Audit', 'audit'],];--function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console' }) {++// The console's pages are noindex; the one public page drawn with this layout (/stats) passes robots, a description and++// its canonical.++function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console', robots = 'noindex,nofollow', description = null, canonical = null }) {return `<!doctype html>${render(html`<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">--<meta name="robots" content="noindex,nofollow"><meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head>++<meta name="robots" content="${robots}">${description ? html`<meta name="description" content="${description}">` : ''}${canonical ? html`<link rel="canonical" href="${canonical}">` : ''}<meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head><body><a class="skip" href="#main">Skip to content</a><header><div class="bar"><span class="brand">OpenVibe.AI · ${site}</span>${staff ? html`<nav aria-label="Console"><ul>${NAV.map(([href, label, id]) => html`<li><a href="${href}"${id === section ? html` aria-current="page"` : ''}>${label}</a></li>`)}</ul></nav><div class="who"><span>${staff.username ? `@${staff.username} ` : ''}<code>${staff.subject}</code> · ${staff.role}</span>@@@ -512,9 +514,12 @@ function allowance(cards) {* Public price and latency per provider and model: provider_stats_daily totals (quotas.statsFor) and the* rate cards only. Nothing here is per caller; rows: { provider, model, capability, requests, ok, p50, p95, cards }.*/--function stats({ days, from, to, rows }) {++function stats({ days, from, to, rows, canonical = null }) {return layout({-- title: 'Price and latency', site: 'stats', body: html`<h1>Price and latency</h1>++ // In search, as the sitemap and robots.txt say: it was noindex,nofollow from the console's layout.++ title: 'Price and latency', site: 'stats', robots: 'index,follow', canonical,++ description: `Price and latency of every AI model OpenVibe.AI called in the last ${days} days: p50 and p95 latency, success rate, price per million tokens and the free allowance.`,++ body: html`<h1>Price and latency</h1><p class="muted">Every provider and model AI called in the last ${days} UTC days (${from} to ${to}). Latency is the upper bound of a fixed latency bucket, the slowest day in the window; prices are per million tokens from the current rate cards.</p>${table(`Providers and models, ${from} to ${to}`, ['Provider', 'Model', 'Capability', 'p50', 'p95', 'Success rate', 'Price', 'Free allowance'], rows.map((r) => html`<tr><td><code>${r.provider}</code></td><td>${r.model ? html`<code>${r.model}</code>` : '—'}</td><td>${r.capability || '—'}</td>@@@ -78,6 +78,14 @@ t.test('/updates: what shipped on this site, the shared update log, with one heaassert.ok(r.headers.get('content-security-policy').includes("connect-src 'self' https://openvibe.network"), 'the log reads the network changelog');});++t.test('/stats (price and latency) is in search: index,follow, its own description and canonical', async () => {++ const r = await request(h.base, 'GET', '/stats');++ assert.strictEqual(r.status, 200);++ assert.ok(r.text.includes('<meta name="robots" content="index,follow">'), 'indexable');++ assert.match(r.text, /<meta name="description" content="Price and latency of every AI model OpenVibe\.AI called in the last 7 days: [^"]+">/);++ assert.ok(r.text.includes(`<link rel="canonical" href="${SITE}/stats">`), 'canonical');++});++t.test('robots.txt, sitemap.xml and llms.txt name the public origin', async () => {const robots = await request(h.base, 'GET', '/robots.txt');assert.strictEqual(robots.status, 200);
@@ -116,7 +116,7 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys
@@ -116,7 +116,7 @@ function createApp({ config, db, registry, pool, quotas, cache, runs, auth, keys
res.setHeader('Cache-Control', cachePolicy.htmlHeaders());
res.setHeader('Cache-Control', cachePolicy.htmlHeaders());
res.setHeader('Content-Security-Policy', views.CSP);
res.setHeader('Content-Security-Policy', views.CSP);
res.type('html').send(views.pages.stats({
res.type('html').send(views.pages.stats({
- days: STATS_DAYS, rows,
+ days: STATS_DAYS, rows, canonical: `${String(config.baseUrl || '').replace(/\/+$/, '')}/stats`,
from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10),
from: new Date(now - STATS_DAYS * 86400000).toISOString().slice(0, 10), to: new Date(now).toISOString().slice(0, 10),
}));
}));
} catch (e) { next(e); }
} catch (e) { next(e); }
@@ -119,9 +119,11 @@ const NAV = [
@@ -119,9 +119,11 @@ const NAV = [
['/console/quotas', 'Quotas', 'quotas'], ['/console/usage', 'Usage', 'usage'], ['/console/cache', 'Cache', 'cache'], ['/console/audit', 'Audit', 'audit'],
['/console/quotas', 'Quotas', 'quotas'], ['/console/usage', 'Usage', 'usage'], ['/console/cache', 'Cache', 'cache'], ['/console/audit', 'Audit', 'audit'],
];
];
-function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console' }) {
+// The console's pages are noindex; the one public page drawn with this layout (/stats) passes robots, a description and
+// its canonical.
+function layout({ title, section, staff, csrf, notice, error, body, site = 'operator console', robots = 'noindex,nofollow', description = null, canonical = null }) {
return `<!doctype html>${render(html`<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
return `<!doctype html>${render(html`<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
-<meta name="robots" content="noindex,nofollow"><meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head>
+<meta name="robots" content="${robots}">${description ? html`<meta name="description" content="${description}">` : ''}${canonical ? html`<link rel="canonical" href="${canonical}">` : ''}<meta name="referrer" content="no-referrer"><title>${title} · AI ${site}</title><style>${new Raw(CSS)}</style></head>
<body><a class="skip" href="#main">Skip to content</a><header><div class="bar"><span class="brand">OpenVibe.AI · ${site}</span>
<body><a class="skip" href="#main">Skip to content</a><header><div class="bar"><span class="brand">OpenVibe.AI · ${site}</span>
${staff ? html`<nav aria-label="Console"><ul>${NAV.map(([href, label, id]) => html`<li><a href="${href}"${id === section ? html` aria-current="page"` : ''}>${label}</a></li>`)}</ul></nav>
${staff ? html`<nav aria-label="Console"><ul>${NAV.map(([href, label, id]) => html`<li><a href="${href}"${id === section ? html` aria-current="page"` : ''}>${label}</a></li>`)}</ul></nav>
<div class="who"><span>${staff.username ? `@${staff.username} ` : ''}<code>${staff.subject}</code> · ${staff.role}</span>
<div class="who"><span>${staff.username ? `@${staff.username} ` : ''}<code>${staff.subject}</code> · ${staff.role}</span>
@@ -512,9 +514,12 @@ function allowance(cards) {
@@ -512,9 +514,12 @@ function allowance(cards) {
* Public price and latency per provider and model: provider_stats_daily totals (quotas.statsFor) and the
* Public price and latency per provider and model: provider_stats_daily totals (quotas.statsFor) and the
* rate cards only. Nothing here is per caller; rows: { provider, model, capability, requests, ok, p50, p95, cards }.
* rate cards only. Nothing here is per caller; rows: { provider, model, capability, requests, ok, p50, p95, cards }.
*/
*/
-function stats({ days, from, to, rows }) {
+function stats({ days, from, to, rows, canonical = null }) {
return layout({
return layout({
- title: 'Price and latency', site: 'stats', body: html`<h1>Price and latency</h1>
+ // In search, as the sitemap and robots.txt say: it was noindex,nofollow from the console's layout.
+ title: 'Price and latency', site: 'stats', robots: 'index,follow', canonical,
+ description: `Price and latency of every AI model OpenVibe.AI called in the last ${days} days: p50 and p95 latency, success rate, price per million tokens and the free allowance.`,
+ body: html`<h1>Price and latency</h1>
<p class="muted">Every provider and model AI called in the last ${days} UTC days (${from} to ${to}). Latency is the upper bound of a fixed latency bucket, the slowest day in the window; prices are per million tokens from the current rate cards.</p>
<p class="muted">Every provider and model AI called in the last ${days} UTC days (${from} to ${to}). Latency is the upper bound of a fixed latency bucket, the slowest day in the window; prices are per million tokens from the current rate cards.</p>
${table(`Providers and models, ${from} to ${to}`, ['Provider', 'Model', 'Capability', 'p50', 'p95', 'Success rate', 'Price', 'Free allowance'], rows.map((r) => html`<tr>
${table(`Providers and models, ${from} to ${to}`, ['Provider', 'Model', 'Capability', 'p50', 'p95', 'Success rate', 'Price', 'Free allowance'], rows.map((r) => html`<tr>
<td><code>${r.provider}</code></td><td>${r.model ? html`<code>${r.model}</code>` : '—'}</td><td>${r.capability || '—'}</td>
<td><code>${r.provider}</code></td><td>${r.model ? html`<code>${r.model}</code>` : '—'}</td><td>${r.capability || '—'}</td>
@@ -78,6 +78,14 @@ t.test('/updates: what shipped on this site, the shared update log, with one hea
@@ -78,6 +78,14 @@ t.test('/updates: what shipped on this site, the shared update log, with one hea
assert.ok(r.headers.get('content-security-policy').includes("connect-src 'self' https://openvibe.network"), 'the log reads the network changelog');
assert.ok(r.headers.get('content-security-policy').includes("connect-src 'self' https://openvibe.network"), 'the log reads the network changelog');
});
});
+t.test('/stats (price and latency) is in search: index,follow, its own description and canonical', async () => {
+ const r = await request(h.base, 'GET', '/stats');
+ assert.strictEqual(r.status, 200);
+ assert.ok(r.text.includes('<meta name="robots" content="index,follow">'), 'indexable');
+ assert.match(r.text, /<meta name="description" content="Price and latency of every AI model OpenVibe\.AI called in the last 7 days: [^"]+">/);
+ assert.ok(r.text.includes(`<link rel="canonical" href="${SITE}/stats">`), 'canonical');
+});
+
t.test('robots.txt, sitemap.xml and llms.txt name the public origin', async () => {
t.test('robots.txt, sitemap.xml and llms.txt name the public origin', async () => {
const robots = await request(h.base, 'GET', '/robots.txt');
const robots = await request(h.base, 'GET', '/robots.txt');
assert.strictEqual(robots.status, 200);
assert.strictEqual(robots.status, 200);