glm-5.2 @ disabled · api
apidisabled
- config
- glm-5.2-eff-disabled--api--official--dev
- batch
- 2026-07-19--unified
- transport
- bigmodel-openai-compat
- protocol
- api · metered
- served model
- glm-5.2
- 采集于
- 2026年7月18日 11:54 · N=4
variant
live
slot 0 · P-q · sandboxed iframe

截图是 similarity 通道测量时的原始像素。运行 live 会在 sandboxed iframe 中重新执行原始输出;渲染结果取决于你的浏览器和字体,不保证与测量像素一致。
similarity 邻居
最近
grok-4.5 @low · grok-cli0.70glm-5.2 @enabled · api0.70claude-fable-5 @xhigh · CC0.69grok-4.5 · opencode0.69gemini-3.6-flash @high · api0.69最远
north-mini-code-free · api0.48claude-sonnet-4-5 · kiro0.51claude-haiku-4-5 @max · CC0.52qwen3.5-plus · go0.52claude-haiku-4-5 @medium · CC0.52限于 session 2026-07-19--unified
similarity 衡量本 session 内的趋同程度 —— 不是质量。
Fidelity —— 卡片展示的数据对不对?
- namematch
- conditionmatch
- datematch
- max tempmismatch展示值:13.4
- min tempmismatch展示值:52.52
- hourlymismatchcoverage 0/24 · 0 命中 · 1 不符 · 23 未展示
- request sidematch0 处违规
extractor fidelity-extractor-v1+Berlin-2026-07-15
源码 —— 原始模型输出,一字节未改
下载 card.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Weather Card</title>
<style>
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-0: #07090d;
--bg-1: #0f1419;
--card-1: rgba(28, 34, 42, 0.65);
--card-2: rgba(18, 22, 28, 0.55);
--border: rgba(255, 255, 255, 0.08);
--border-strong: rgba(255, 255, 255, 0.16);
--text: #f3f5f8;
--text-mid: #a3adba;
--text-dim: #6b7684;
--accent: #f5b942;
--accent-2: #ff8a5b;
--cool: #6dd5ed;
--cool-2: #4aa3df;
--live: #4ade80;
}
html, body {
width: 1280px;
height: 800px;
overflow: hidden;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
color: var(--text);
background: var(--bg-0);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
body {
background:
radial-gradient(900px 600px at 75% -10%, rgba(245, 185, 66, 0.10), transparent 60%),
radial-gradient(800px 600px at 10% 110%, rgba(109, 213, 237, 0.07), transparent 60%),
linear-gradient(180deg, #0a0d12 0%, #07090d 100%);
position: relative;
}
/* Fine grain noise overlay */
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.025 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
opacity: 0.6;
mix-blend-mode: overlay;
z-index: 1;
}
/* Grid lines for texture */
body::after {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background-image:
linear-gradient(rgba(255,255,255,0.018) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.018) 1px, transparent 1px);
background-size: 80px 80px;
z-index: 1;
}
.stage {
position: relative;
width: 1280px;
height: 800px;
padding: 36px 48px;
display: flex;
flex-direction: column;
z-index: 2;
}
/* ---------- Top bar ---------- */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28px;
}
.brand {
display: flex;
align-items: center;
gap: 14px;
}
.brand-mark {
width: 38px;
height: 38px;
border-radius: 11px;
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
display: grid;
place-items: center;
box-shadow:
0 8px 22px -8px rgba(245, 185, 66, 0.55),
inset 0 1px 0 rgba(255,255,255,0.25);
position: relative;
}
.brand-mark svg { width: 20px; height: 20px; }
.brand-text { line-height: 1.1; }
.brand-text .name {
font-size: 13px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--text);
}
.brand-text .sub {
font-size: 11px;
color: var(--text-dim);
letter-spacing: 0.12em;
text-transform: uppercase;
margin-top: 3px;
}
.topbar-right {
display: flex;
align-items: center;
gap: 22px;
font-size: 12px;
color: var(--text-mid);
letter-spacing: 0.08em;
}
.meta-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 14px;
border-radius: 999px;
background: rgba(255,255,255,0.03);
border: 1px solid var(--border);
font-variant-numeric: tabular-nums;
}
.meta-chip .dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--live);
box-shadow: 0 0 8px var(--live);
}
.meta-chip .label { color: var(--text-dim); text-transform: uppercase; font-size: 10px; letter-spacing: 0.14em; }
.meta-chip .value { color: var(--text); font-weight: 600; }
/* ---------- Main grid ---------- */
.main {
flex: 1;
display: grid;
grid-template-columns: 1.05fr 1fr;
gap: 28px;
min-height: 0;
}
.panel {
position: relative;
border-radius: 24px;
border: 1px solid var(--border);
background:
linear-gradient(180deg, var(--card-1) 0%, var(--card-2) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow:
0 30px 60px -30px rgba(0,0,0,0.7),
0 0 0 1px rgba(255,255,255,0.02) inset,
0 1px 0 rgba(255,255,255,0.05) inset;
overflow: hidden;
}
/* ---------- Hero panel ---------- */
.hero {
padding: 36px 40px 32px;
display: flex;
flex-direction: column;
}
.hero::before {
content: "";
position: absolute;
top: -40%;
right: -20%;
width: 90%;
height: 90%;
background: radial-gradient(circle, rgba(245, 185, 66, 0.12) 0%, transparent 65%);
pointer-events: none;
}
.location-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 28px;
}
.location {
display: flex;
align-items: center;
gap: 12px;
}
.location svg { width: 18px; height: 18px; color: var(--accent); flex-shrink: 0; }
.location .loc-name {
font-size: 26px;
font-weight: 700;
letter-spacing: -0.01em;
line-height: 1.1;
}
.location .loc-coords {
font-size: 11px;
color: var(--text-dim);
letter-spacing: 0.1em;
margin-top: 4px;
font-variant-numeric: tabular-nums;
}
.date-block {
text-align: right;
line-height: 1.2;
}
.date-block .day {
font-size: 13px;
font-weight: 600;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--accent);
}
.date-block .full {
font-size: 13px;
color: var(--text-mid);
margin-top: 4px;
letter-spacing: 0.04em;
font-variant-numeric: tabular-nums;
}
.condition-wrap {
display: flex;
align-items: center;
gap: 22px;
margin-bottom: 18px;
}
.icon-frame {
width: 124px;
height: 124px;
border-radius: 28px;
background:
radial-gradient(circle at 30% 30%, rgba(255,255,255,0.06), transparent 60%),
linear-gradient(135deg, rgba(245, 185, 66, 0.18), rgba(255, 138, 91, 0.08));
border: 1px solid var(--border-strong);
display: grid;
place-items: center;
position: relative;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.08), 0 14px 30px -16px rgba(0,0,0,0.6);
flex-shrink: 0;
}
.icon-frame svg { width: 78px; height: 78px; }
.condition-text .label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.18em;
color: var(--text-dim);
margin-bottom: 8px;
}
.condition-text .value {
font-size: 30px;
font-weight: 700;
letter-spacing: -0.015em;
line-height: 1.05;
}
.condition-text .sub {
font-size: 13px;
color: var(--text-mid);
margin-top: 6px;
}
/* Big temp block */
.temp-block {
margin-top: auto;
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 30px;
}
.temp-current {
display: flex;
align-items: flex-start;
line-height: 1;
}
.temp-current .num {
font-size: 156px;
font-weight: 200;
letter-spacing: -0.06em;
background: linear-gradient(180deg, #ffffff 0%, #c8ccd2 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
font-variant-numeric: tabular-nums;
}
.temp-current .unit {
font-size: 38px;
font-weight: 300;
color: var(--text-mid);
margin-top: 18px;
margin-left: 4px;
}
.temp-current .caption {
margin-left: 18px;
margin-bottom: 18px;
font-size: 10px;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--text-dim);
writing-mode: vertical-rl;
transform: rotate(180deg);
}
.temp-range {
display: flex;
flex-direction: column;
gap: 14px;
padding-bottom: 14px;
min-width: 180px;
}
.range-item {
display: flex;
align-items: center;
gap: 12px;
}
.range-item .pill {
width: 30px; height: 30px;
border-radius: 9px;
display: grid; place-items: center;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
}
.range-item.high .pill {
background: linear-gradient(135deg, rgba(245, 185, 66, 0.25), rgba(255, 138, 91, 0.15));
color: var(--accent);
border: 1px solid rgba(245, 185, 66, 0.3);
}
.range-item.low .pill {
background: linear-gradient(135deg, rgba(109, 213, 237, 0.18), rgba(74, 163, 223, 0.10));
color: var(--cool);
border: 1px solid rgba(109, 213, 237, 0.28);
}
.range-item .info { line-height: 1.15; }
.range-item .info .lab {
font-size: 10px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--text-dim);
}
.range-item .info .val {
font-size: 24px;
font-weight: 600;
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
margin-top: 2px;
}
.range-item .info .val .deg { font-size: 14px; color: var(--text-mid); margin-left: 2px; }
/* Sparkline mini */
.spark-mini {
margin-top: 18px;
height: 50px;
width: 100%;
position: relative;
}
/* ---------- Hourly panel ---------- */
.hourly {
padding: 30px 32px 28px;
display: flex;
flex-direction: column;
}
.hourly-head {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 22px;
}
.hourly-head .title {
font-size: 16px;
font-weight: 700;
letter-spacing: -0.005em;
}
.hourly-head .title .accent {
color: var(--accent);
margin-right: 6px;
}
.hourly-head .sub {
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-dim);
}
.chart-wrap {
flex: 1;
position: relative;
min-height: 0;
}
#chart {
width: 100%;
height: 100%;
display: block;
}
.chart-legend {
display: flex;
justify-content: space-between;
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid var(--border);
font-size: 11px;
color: var(--text-dim);
letter-spacing: 0.08em;
}
.chart-legend .leg {
display: inline-flex; align-items: center; gap: 8px;
}
.chart-legend .swatch {
width: 18px; height: 3px; border-radius: 2px;
}
.chart-legend .swatch.curve { background: linear-gradient(90deg, var(--cool), var(--accent)); }
.chart-legend .swatch.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px rgba(245,185,66,0.6); }
/* ---------- Tooltip ---------- */
.tooltip {
position: absolute;
pointer-events: none;
background: rgba(8, 11, 16, 0.92);
border: 1px solid var(--border-strong);
border-radius: 10px;
padding: 10px 14px;
font-size: 12px;
color: var(--text);
backdrop-filter: blur(8px);
box-shadow: 0 14px 30px -12px rgba(0,0,0,0.8);
transform: translate(-50%, -120%);
opacity: 0;
transition: opacity 0.15s ease;
z-index: 10;
white-space: nowrap;
line-height: 1.4;
}
.tooltip.show { opacity: 1; }
.tooltip .t-time {
font-size: 10px;
color: var(--text-dim);
letter-spacing: 0.14em;
text-transform: uppercase;
}
.tooltip .t-temp {
font-size: 18px;
font-weight: 600;
margin-top: 2px;
font-variant-numeric: tabular-nums;
}
.tooltip .t-temp .deg { color: var(--text-mid); font-size: 12px; }
/* ---------- Error state ---------- */
.error-state {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: 18px;
padding: 40px;
}
.error-state .icon {
width: 78px; height: 78px;
border-radius: 22px;
background: rgba(255, 138, 91, 0.12);
border: 1px solid rgba(255, 138, 91, 0.25);
display: grid; place-items: center;
color: var(--accent-2);
}
.error-state .icon svg { width: 38px; height: 38px; }
.error-state h2 {
font-size: 22px;
font-weight: 700;
letter-spacing: -0.01em;
}
.error-state p {
font-size: 13px;
color: var(--text-mid);
max-width: 420px;
line-height: 1.6;
}
.error-state .code {
font-size: 11px;
color: var(--text-dim);
font-family: 'SF Mono', Menlo, monospace;
margin-top: 6px;
letter-spacing: 0.04em;
}
/* ---------- Footer ---------- */
.footer {
margin-top: 18px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 10px;
color: var(--text-dim);
letter-spacing: 0.16em;
text-transform: uppercase;
}
.footer .right { display: flex; gap: 18px; }
/* ---------- Loading state ---------- */
.loading-overlay {
position: absolute;
inset: 0;
background: rgba(8, 11, 16, 0.85);
backdrop-filter: blur(6px);
display: grid; place-items: center;
z-index: 5;
transition: opacity 0.3s ease;
}
.loading-overlay.hide { opacity: 0; pointer-events: none; }
.loader {
width: 44px; height: 44px;
border-radius: 50%;
border: 2px solid rgba(255,255,255,0.1);
border-top-color: var(--accent);
animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes float-up {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
.anim { animation: float-up 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) both; }
.anim.d1 { animation-delay: 0.05s; }
.anim.d2 { animation-delay: 0.15s; }
.anim.d3 { animation-delay: 0.25s; }
.anim.d4 { animation-delay: 0.35s; }
</style>
</head>
<body>
<div class="stage">
<!-- Top bar -->
<header class="topbar anim d1">
<div class="brand">
<div class="brand-mark">
<svg viewBox="0 0 24 24" fill="none" stroke="#1a1208" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="4"/>
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>
</svg>
</div>
<div class="brand-text">
<div class="name">Atmos / Daily</div>
<div class="sub">Meteorological summary</div>
</div>
</div>
<div class="topbar-right">
<div class="meta-chip">
<span class="dot"></span>
<span class="label">Live</span>
<span class="value" id="live-time">--:-- UTC</span>
</div>
<div class="meta-chip">
<span class="label">Source</span>
<span class="value">Open-Meteo</span>
</div>
</div>
</header>
<!-- Main grid -->
<main class="main" id="main">
<!-- Hero panel -->
<section class="panel hero" id="hero">
<div class="loading-overlay" id="hero-loader"><div class="loader"></div></div>
<div id="hero-content" style="display:flex;flex-direction:column;flex:1;">
<div class="location-row anim d2">
<div class="location">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
<circle cx="12" cy="10" r="3"/>
</svg>
<div>
<div class="loc-name" id="loc-name">Berlin</div>
<div class="loc-coords" id="loc-coords">52.52° N, 13.41° E</div>
</div>
</div>
<div class="date-block">
<div class="day" id="date-day">TODAY</div>
<div class="full" id="date-full">— — —</div>
</div>
</div>
<div class="condition-wrap anim d3">
<div class="icon-frame" id="icon-frame"></div>
<div class="condition-text">
<div class="label">Condition</div>
<div class="value" id="cond-value">—</div>
<div class="sub" id="cond-sub">Weather code —</div>
</div>
</div>
<div class="temp-block anim d4">
<div class="temp-current">
<div class="num" id="temp-avg">--</div>
<div class="unit">°C</div>
<div class="caption">Daily mean</div>
</div>
<div class="temp-range">
<div class="range-item high">
<div class="pill">H</div>
<div class="info">
<div class="lab">High</div>
<div class="val"><span id="temp-max">--</span><span class="deg">°C</span></div>
</div>
</div>
<div class="range-item low">
<div class="pill">L</div>
<div class="info">
<div class="lab">Low</div>
<div class="val"><span id="temp-min">--</span><span class="deg">°C</span></div>
</div>
</div>
</div>
</div>
<svg class="spark-mini" id="spark" viewBox="0 0 400 50" preserveAspectRatio="none"></svg>
</div>
</section>
<!-- Hourly panel -->
<section class="panel hourly" id="hourly">
<div class="loading-overlay" id="hourly-loader"><div class="loader"></div></div>
<div class="hourly-head anim d3">
<div class="title"><span class="accent">24h</span> Temperature curve</div>
<div class="sub">Hourly · °C</div>
</div>
<div class="chart-wrap anim d4">
<svg id="chart" preserveAspectRatio="none"></svg>
<div class="tooltip" id="tooltip">
<div class="t-time">—</div>
<div class="t-temp"><span class="t-val">--</span><span class="deg">°C</span></div>
</div>
</div>
<div class="chart-legend">
<span class="leg"><span class="swatch curve"></span>Temperature</span>
<span class="leg"><span class="swatch dot"></span>Hover for detail</span>
</div>
</section>
</main>
<footer class="footer anim d4">
<span>Atmos · Single-day meteorological card</span>
<div class="right">
<span>Timezone: UTC</span>
<span>Units: Celsius</span>
</div>
</footer>
</div>
<script>
(function () {
"use strict";
// -------- URL params --------
function getParams() {
const p = new URLSearchParams(window.location.search);
const lat = parseFloat(p.get("lat"));
const lon = parseFloat(p.get("lon"));
const name = p.get("name") || "Berlin";
const dateStr = p.get("date");
const todayUTC = new Date();
const yyyy = todayUTC.getUTCFullYear();
const mm = String(todayUTC.getUTCMonth() + 1).padStart(2, "0");
const dd = String(todayUTC.getUTCDate()).padStart(2, "0");
const todayStr = `${yyyy}-${mm}-${dd}`;
let date = todayStr;
if (dateStr && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
date = dateStr;
}
return {
lat: isFinite(lat) ? lat : 52.52,
lon: isFinite(lon) ? lon : 13.405,
name,
date
};
}
// -------- Date helpers --------
const WEEKDAYS = ["SUNDAY","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY"];
const MONTHS = ["January","February","March","April","May","June","July","August","September","October","November","December"];
function parseDateUTC(s) {
const [y, m, d] = s.split("-").map(Number);
return new Date(Date.UTC(y, m - 1, d));
}
function isSameDay(a, b) {
return a.getUTCFullYear() === b.getUTCFullYear()
&& a.getUTCMonth() === b.getUTCMonth()
&& a.getUTCDate() === b.getUTCDate();
}
function formatHour(h) {
return String(h).padStart(2, "0") + ":00";
}
// -------- Weather codes --------
function weatherInfo(code) {
const map = {
0: { label: "Clear sky", icon: "sun" },
1: { label: "Mainly clear", icon: "sun-cloud" },
2: { label: "Partly cloudy", icon: "sun-cloud" },
3: { label: "Overcast", icon: "cloud" },
45: { label: "Fog", icon: "fog" },
48: { label: "Rime fog", icon: "fog" },
51: { label: "Light drizzle", icon: "drizzle" },
53: { label: "Drizzle", icon: "drizzle" },
55: { label: "Heavy drizzle", icon: "drizzle" },
56: { label: "Freezing drizzle", icon: "drizzle" },
57: { label: "Freezing drizzle", icon: "drizzle" },
61: { label: "Light rain", icon: "rain" },
63: { label: "Rain", icon: "rain" },
65: { label: "Heavy rain", icon: "rain" },
66: { label: "Freezing rain", icon: "rain" },
67: { label: "Freezing rain", icon: "rain" },
71: { label: "Light snow", icon: "snow" },
73: { label: "Snow", icon: "snow" },
75: { label: "Heavy snow", icon: "snow" },
77: { label: "Snow grains", icon: "snow" },
80: { label: "Light showers", icon: "rain" },
81: { label: "Showers", icon: "rain" },
82: { label: "Violent showers", icon: "rain" },
85: { label: "Snow showers", icon: "snow" },
86: { label: "Snow showers", icon: "snow" },
95: { label: "Thunderstorm", icon: "storm" },
96: { label: "Thunderstorm", icon: "storm" },
99: { label: "Thunderstorm", icon: "storm" }
};
return map[code] || { label: "Unknown", icon: "cloud" };
}
// -------- Icons (SVG strings) --------
function iconSVG(name) {
const wrap = (inner) => `<svg viewBox="0 0 64 64" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4">${inner}</svg>`;
const sun = `<circle cx="32" cy="32" r="11" fill="url(#sun-g)" stroke="#f5b942"/>
<g stroke="#f5b942" stroke-width="2.6">
<line x1="32" y1="8" x2="32" y2="14"/><line x1="32" y1="50" x2="32" y2="56"/>
<line x1="8" y1="32" x2="14" y2="32"/><line x1="50" y1="32" x2="56" y2="32"/>
<line x1="15" y1="15" x2="19" y2="19"/><line x1="45" y1="45" x2="49" y2="49"/>
<line x1="15" y1="49" x2="19" y2="45"/><line x1="45" y1="19" x2="49" y2="15"/>
</g>
<defs><radialGradient id="sun-g"><stop offset="0%" stop-color="#fff1c7"/><stop offset="100%" stop-color="#f5b942"/></radialGradient></defs>`;
const cloud = `<path d="M22 44h22a9 9 0 0 0 1-17.9 13 13 0 0 0-25.2 3.5A8 8 0 0 0 22 44z" fill="rgba(180,190,205,0.18)" stroke="#c8d0dc"/>`;
const suncloud = `<circle cx="22" cy="22" r="7" fill="url(#sun-g2)" stroke="#f5b942"/>
<g stroke="#f5b942" stroke-width="2.2">
<line x1="22" y1="6" x2="22" y2="10"/><line x1="6" y1="22" x2="10" y2="22"/>
<line x1="11" y1="11" x2="14" y2="14"/><line x1="33" y1="11" x2="30" y2="14"/>
</g>
<path d="M24 52h20a8 8 0 0 0 1-16 12 12 0 0 0-22.6 2.5A7 7 0 0 0 24 52z" fill="rgba(180,190,205,0.22)" stroke="#c8d0dc"/>
<defs><radialGradient id="sun-g2"><stop offset="0%" stop-color="#fff1c7"/><stop offset="100%" stop-color="#f5b942"/></radialGradient></defs>`;
const fog = `<path d="M18 34h28" stroke="#c8d0dc" stroke-width="3"/>
<path d="M14 42h36" stroke="#c8d0dc" stroke-width="3" opacity="0.85"/>
<path d="M20 50h24" stroke="#c8d0dc" stroke-width="3" opacity="0.7"/>`;
const drizzle = `<path d="M22 38h22a9 9 0 0 0 1-17.9 13 13 0 0 0-25.2 3.5A8 8 0 0 0 22 38z" fill="rgba(180,190,205,0.18)" stroke="#c8d0dc"/>
<line x1="24" y1="44" x2="22" y2="50" stroke="#6dd5ed" stroke-width="2.6"/>
<line x1="32" y1="44" x2="30" y2="50" stroke="#6dd5ed" stroke-width="2.6"/>
<line x1="40" y1="44" x2="38" y2="50" stroke="#6dd5ed" stroke-width="2.6"/>`;
const rain = `<path d="M22 36h22a9 9 0 0 0 1-17.9 13 13 0 0 0-25.2 3.5A8 8 0 0 0 22 36z" fill="rgba(180,190,205,0.18)" stroke="#c8d0dc"/>
<line x1="24" y1="42" x2="20" y2="54" stroke="#6dd5ed" stroke-width="2.8"/>
<line x1="34" y1="42" x2="30" y2="54" stroke="#6dd5ed" stroke-width="2.8"/>
<line x1="44" y1="42" x2="40" y2="54" stroke="#6dd5ed" stroke-width="2.8"/>`;
const snow = `<path d="M22 36h22a9 9 0 0 0 1-17.9 13 13 0 0 0-25.2 3.5A8 8 0 0 0 22 36z" fill="rgba(180,190,205,0.18)" stroke="#c8d0dc"/>
<g stroke="#e8f4ff" stroke-width="2.2">
<line x1="24" y1="46" x2="24" y2="54"/><line x1="20" y1="50" x2="28" y2="50"/>
<line x1="34" y1="46" x2="34" y2="54"/><line x1="30" y1="50" x2="38" y2="50"/>
<line x1="44" y1="46" x2="44" y2="54"/><line x1="40" y1="50" x2="48" y2="50"/>
</g>`;
const storm = `<path d="M22 32h22a9 9 0 0 0 1-17.9 13 13 0 0 0-25.2 3.5A8 8 0 0 0 22 32z" fill="rgba(180,190,205,0.18)" stroke="#c8d0dc"/>
<path d="M34 36 L26 50 L32 50 L28 60 L40 44 L34 44 L38 36 Z" fill="#f5b942" stroke="#f5b942" stroke-width="1.2"/>`;
const icons = { sun, "sun-cloud": suncloud, cloud, fog, drizzle, rain, snow, storm };
return wrap(icons[name] || cloud);
}
// -------- DOM refs --------
const $ = (id) => document.getElementById(id);
const heroLoader = $("hero-loader");
const hourlyLoader = $("hourly-loader");
const heroContent = $("hero-content");
const mainEl = $("main");
// -------- Render error --------
function showError(message, detail) {
heroLoader.classList.add("hide");
hourlyLoader.classList.add("hide");
mainEl.innerHTML = `
<section class="panel" style="grid-column: 1 / -1; padding: 60px;">
<div class="error-state">
<div class="icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 9v4"/><path d="M12 17h.01"/>
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
</svg>
</div>
<h2>Weather data unavailable</h2>
<p>${message}</p>
<div class="code">${detail || ""}</div>
</div>
</section>`;
}
// -------- Render hero --------
function renderHero(data, params) {
const d = data.daily;
const dateObj = parseDateUTC(params.date);
const now = new Date();
const todayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
let dayLabel = WEEKDAYS[dateObj.getUTCDay()];
if (isSameDay(dateObj, todayUTC)) dayLabel = "TODAY";
else {
const diff = Math.round((dateObj - todayUTC) / 86400000);
if (diff === 1) dayLabel = "TOMORROW";
else if (diff === -1) dayLabel = "YESTERDAY";
}
const info = weatherInfo(d.weather_code[0]);
$("loc-name").textContent = params.name;
$("loc-coords").textContent = `${Math.abs(params.lat).toFixed(2)}° ${params.lat >= 0 ? "N" : "S"}, ${Math.abs(params.lon).toFixed(2)}° ${params.lon >= 0 ? "E" : "W"}`;
$("date-day").textContent = dayLabel;
$("date-full").textContent = `${MONTHS[dateObj.getUTCMonth()]} ${dateObj.getUTCDate()}, ${dateObj.getUTCFullYear()}`;
$("icon-frame").innerHTML = iconSVG(info.icon);
$("cond-value").textContent = info.label;
$("cond-sub").textContent = `WMO code ${d.weather_code[0]}`;
const tmax = d.temperature_2m_max[0];
const tmin = d.temperature_2m_min[0];
const tavg = (tmax + tmin) / 2;
$("temp-max").textContent = Math.round(tmax);
$("temp-min").textContent = Math.round(tmin);
$("temp-avg").textContent = Math.round(tavg);
renderSpark(data.hourly);
heroLoader.classList.add("hide");
}
// -------- Render mini sparkline --------
function renderSpark(hourly) {
const svg = $("spark");
const W = 400, H = 50, pad = 4;
const temps = hourly.temperature_2m;
const min = Math.min.apply(null, temps);
const max = Math.max.apply(null, temps);
const range = Math.max(0.1, max - min);
const step = (W - pad * 2) / (temps.length - 1);
const pts = temps.map((t, i) => {
const x = pad + i * step;
const y = pad + (1 - (t - min) / range) * (H - pad * 2);
return [x, y];
});
const path = pts.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(" ");
const area = `${path} L${pts[pts.length-1][0]},${H} L${pts[0][0]},${H} Z`;
svg.innerHTML = `
<defs>
<linearGradient id="spark-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#6dd5ed"/>
<stop offset="100%" stop-color="#f5b942"/>
</linearGradient>
<linearGradient id="spark-fill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="rgba(245,185,66,0.18)"/>
<stop offset="100%" stop-color="rgba(245,185,66,0)"/>
</linearGradient>
</defs>
<path d="${area}" fill="url(#spark-fill)"/>
<path d="${path}" fill="none" stroke="url(#spark-grad)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
`;
}
// -------- Render main chart --------
let chartGeometry = null;
function renderChart(hourly) {
const svg = $("chart");
const rect = svg.getBoundingClientRect();
const W = rect.width, H = rect.height;
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
const padL = 48, padR = 24, padT = 26, padB = 38;
const innerW = W - padL - padR;
const innerH = H - padT - padB;
const temps = hourly.temperature_2m;
const times = hourly.time;
const tmax = Math.max.apply(null, temps);
const tmin = Math.min.apply(null, temps);
const tAvgMax = Math.ceil(tmax + 1);
const tAvgMin = Math.floor(tmin - 1);
const range = Math.max(1, tAvgMax - tAvgMin);
const stepX = innerW / (temps.length - 1);
const xAt = (i) => padL + i * stepX;
const yAt = (t) => padT + (1 - (t - tAvgMin) / range) * innerH;
// Smooth path via Catmull-Rom -> Bezier
const pts = temps.map((t, i) => [xAt(i), yAt(t)]);
function smoothPath(points) {
if (points.length < 2) return "";
let d = `M ${points[0][0]},${points[0][1]}`;
for (let i = 0; i < points.length - 1; i++) {
const p0 = points[i - 1] || points[i];
const p1 = points[i];
const p2 = points[i + 1];
const p3 = points[i + 2] || p2;
const cp1x = p1[0] + (p2[0] - p0[0]) / 6;
const cp1y = p1[1] + (p2[1] - p0[1]) / 6;
const cp2x = p2[0] - (p3[0] - p1[0]) / 6;
const cp2y = p2[1] - (p3[1] - p1[1]) / 6;
d += ` C ${cp1x},${cp1y} ${cp2x},${cp2y} ${p2[0]},${p2[1]}`;
}
return d;
}
const linePath = smoothPath(pts);
const areaPath = `${linePath} L ${pts[pts.length-1][0]},${padT + innerH} L ${pts[0][0]},${padT + innerH} Z`;
// Y axis ticks
const yTicks = [];
const tickCount = 5;
for (let i = 0; i <= tickCount; i++) {
const val = tAvgMin + (range * i) / tickCount;
yTicks.push({ val: Math.round(val), y: yAt(val) });
}
// X axis labels — every 3 hours
const xTicks = [];
for (let i = 0; i < temps.length; i += 3) {
const hr = parseDateUTC(times[i]).getUTCHours();
xTicks.push({ x: xAt(i), label: formatHour(hr), i });
}
// Find max & min points
const maxIdx = temps.indexOf(tmax);
const minIdx = temps.indexOf(tmin);
// Build SVG
let gridLines = "";
let yLabels = "";
yTicks.forEach(t => {
gridLines += `<line x1="${padL}" y1="${t.y}" x2="${W - padR}" y2="${t.y}" stroke="rgba(255,255,255,0.05)" stroke-width="1"/>`;
yLabels += `<text x="${padL - 10}" y="${t.y + 4}" text-anchor="end" font-size="10" fill="#6b7684" font-family="Inter, sans-serif" letter-spacing="0.05em">${t.val}°</text>`;
});
let xLabels = "";
xTicks.forEach(t => {
xLabels += `<text x="${t.x}" y="${H - padB + 18}" text-anchor="middle" font-size="10" fill="#6b7684" font-family="Inter, sans-serif" letter-spacing="0.05em">${t.label}</text>`;
});
// Markers for max & min
const markers = `
<g>
<circle cx="${xAt(maxIdx)}" cy="${yAt(tmax)}" r="5" fill="#f5b942" stroke="#0a0d12" stroke-width="2"/>
<text x="${xAt(maxIdx)}" y="${yAt(tmax) - 12}" text-anchor="middle" font-size="11" font-weight="600" fill="#f5b942" font-family="Inter, sans-serif">${Math.round(tmax)}°</text>
</g>
<g>
<circle cx="${xAt(minIdx)}" cy="${yAt(tmin)}" r="5" fill="#6dd5ed" stroke="#0a0d12" stroke-width="2"/>
<text x="${xAt(minIdx)}" y="${yAt(tmin) + 20}" text-anchor="middle" font-size="11" font-weight="600" fill="#6dd5ed" font-family="Inter, sans-serif">${Math.round(tmin)}°</text>
</g>
`;
svg.innerHTML = `
<defs>
<linearGradient id="line-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#6dd5ed"/>
<stop offset="55%" stop-color="#f5b942"/>
<stop offset="100%" stop-color="#ff8a5b"/>
</linearGradient>
<linearGradient id="area-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="rgba(245,185,66,0.22)"/>
<stop offset="50%" stop-color="rgba(245,185,66,0.06)"/>
<stop offset="100%" stop-color="rgba(245,185,66,0)"/>
</linearGradient>
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="3" result="b"/>
<feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
${gridLines}
${yLabels}
${xLabels}
<path d="${areaPath}" fill="url(#area-grad)"/>
<path d="${linePath}" fill="none" stroke="url(#line-grad)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow)"/>
${markers}
<line id="hover-line" x1="0" y1="${padT}" x2="0" y2="${padT + innerH}" stroke="rgba(255,255,255,0.25)" stroke-width="1" stroke-dasharray="3 3" style="opacity:0"/>
<circle id="hover-dot" cx="0" cy="0" r="5" fill="#fff" stroke="#f5b942" stroke-width="2" style="opacity:0"/>
`;
chartGeometry = { pts, temps, times, padL, padR, padT, padB, innerW, innerH, W, H };
attachHover(svg);
hourlyLoader.classList.add("hide");
}
// -------- Hover interaction --------
function attachHover(svg) {
const tooltip = $("tooltip");
const hoverLine = svg.querySelector("#hover-line");
const hoverDot = svg.querySelector("#hover-dot");
const wrap = svg.parentElement;
function onMove(e) {
if (!chartGeometry) return;
const rect = svg.getBoundingClientRect();
const x = e.clientX - rect.left;
const { pts, temps, times, padT, padB, innerH } = chartGeometry;
// nearest point
let nearest = 0;
let bestDist = Infinity;
for (let i = 0; i < pts.length; i++) {
const d = Math.abs(pts[i][0] - x);
if (d < bestDist) { bestDist = d; nearest = i; }
}
const p = pts[nearest];
hoverLine.setAttribute("x1", p[0]);
hoverLine.setAttribute("x2", p[0]);
hoverLine.setAttribute("y1", padT);
hoverLine.setAttribute("y2", padT + innerH);
hoverLine.style.opacity = 1;
hoverDot.setAttribute("cx", p[0]);
hoverDot.setAttribute("cy", p[1]);
hoverDot.style.opacity = 1;
const hr = parseDateUTC(times[nearest]).getUTCHours();
tooltip.querySelector(".t-time").textContent = formatHour(hr) + " UTC";
tooltip.querySelector(".t-val").textContent = Math.round(temps[nearest]);
tooltip.style.left = p[0] + "px";
tooltip.style.top = p[1] + "px";
tooltip.classList.add("show");
}
function onLeave() {
hoverLine.style.opacity = 0;
hoverDot.style.opacity = 0;
tooltip.classList.remove("show");
}
svg.addEventListener("mousemove", onMove);
svg.addEventListener("mouseleave", onLeave);
}
// -------- Live clock --------
function tickClock() {
const now = new Date();
const h = String(now.getUTCHours()).padStart(2, "0");
const m = String(now.getUTCMinutes()).padStart(2, "0");
$("live-time").textContent = `${h}:${m} UTC`;
}
setInterval(tickClock, 1000 * 30);
tickClock();
// -------- Fetch --------
async function load() {
const params = getParams();
const dateObj = parseDateUTC(params.date);
const todayUTC = new Date();
todayUTC.setUTCHours(0, 0, 0, 0);
const isPast = dateObj < todayUTC;
const endpoint = isPast ? "/api/om/archive" : "/api/om/forecast";
const qs = new URLSearchParams({
latitude: params.lat,
longitude: params.lon,
daily: "temperature_2m_max,temperature_2m_min,weather_code",
hourly: "temperature_2m",
timezone: "UTC",
temperature_unit: "celsius",
start_date: params.date,
end_date: params.date
});
const url = `${endpoint}?${qs.toString()}`;
try {
const res = await fetch(url, { headers: { "Accept": "application/json" } });
if (!res.ok) {
throw new Error(`API responded with status ${res.status}`);
}
const data = await res.json();
if (!data || !data.daily || !data.hourly ||
!data.daily.time || data.daily.time.length === 0 ||
!data.hourly.time || data.hourly.time.length === 0) {
throw new Error("Malformed response: missing data fields.");
}
// Filter hourly to only the requested date (in case archive returns adjacent hours)
const filteredTemps = [];
const filteredTimes = [];
for (let i = 0; i < data.hourly.time.length; i++) {
if (data.hourly.time[i].startsWith(params.date)) {
filteredTimes.push(data.hourly.time[i]);
filteredTemps.push(data.hourly.temperature_2m[i]);
}
}
if (filteredTimes.length === 0) {
throw new Error("No hourly data available for the requested date.");
}
data.hourly.time = filteredTimes;
data.hourly.temperature_2m = filteredTemps;
renderHero(data, params);
// Render chart after layout settles
requestAnimationFrame(() => renderChart(data.hourly));
} catch (err) {
console.error(err);
showError(
"We couldn't retrieve the meteorological data for the requested location and date. Please verify the parameters and try again.",
err && err.message ? err.message : "Unknown error"
);
}
}
// Re-render chart on resize (kept simple; viewport is fixed)
window.addEventListener("resize", () => {
if (chartGeometry && chartGeometry.hourly) return;
});
load();
})();
</script>
</body>
</html>L1 —— 廉价的确定性描述量
| bytes 总计 | 38,538 | html / css / js | 4,836 / 13,599 / 20,103 |
|---|---|---|---|
| dom 节点 | 92 | dom 深度 | 12 |
| css 规则 | 90 | 含 js | 是 |
| brightness | 24.5 | contrast | 17.4 |
| colorfulness | 10.6 | 留白 | 96.2% |
该 slot 的 telemetry
| prompt tokens | 313 | completion tokens | 12,595 |
|---|---|---|---|
| total tokens | 12,908 | wall | 133.4 s |
| cost | — | request id | — |
config.json559 B
{
"N": 4,
"auth": {
"credential_ref": "WCB_BIGMODEL_KEY",
"method": "api-key"
},
"billing": "metered",
"config_id": "glm-5.2-eff-disabled--api--official--dev",
"effort": "disabled",
"family": "glm",
"m": {
"min": 4,
"q": 4
},
"model_id": "glm-5.2",
"protocol": "api",
"served_model": "glm-5.2",
"telemetry": {
"completion_tokens": 62679,
"cost_usd": null,
"prompt_tokens": 2472,
"total_tokens": 65151,
"wall_ms": 781803
},
"transport": "bigmodel-openai-compat",
"vendor_sanction_ref": null
}send-log4.6 KB
{
"N": 4,
"batch_id": "2026-07-20--official-glm",
"config_id": "glm-5.2-eff-disabled--api--official--dev",
"positions": [
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T13:55:49.778640+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T13:54:35.649134+00:00"
}
],
"block_index": 0,
"model_reaching_attempt_index": 0,
"slot_index": 0,
"terminal_state": "valid",
"variant": "P-min"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T13:58:07.410206+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T13:55:53.991127+00:00"
}
],
"block_index": 0,
"model_reaching_attempt_index": 0,
"slot_index": 0,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T13:58:48.746307+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T13:58:11.076804+00:00"
}
],
"block_index": 1,
"model_reaching_attempt_index": 0,
"slot_index": 1,
"terminal_state": "valid",
"variant": "P-min"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T14:01:07.535466+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T13:58:52.663415+00:00"
}
],
"block_index": 1,
"model_reaching_attempt_index": 0,
"slot_index": 1,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T14:03:02.832266+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T14:01:11.197130+00:00"
}
],
"block_index": 2,
"model_reaching_attempt_index": 0,
"slot_index": 2,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T14:04:29.995173+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T14:03:07.729113+00:00"
}
],
"block_index": 2,
"model_reaching_attempt_index": 0,
"slot_index": 2,
"terminal_state": "valid",
"variant": "P-min"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T14:06:52.541402+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T14:04:33.388379+00:00"
}
],
"block_index": 3,
"model_reaching_attempt_index": 0,
"slot_index": 3,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-20T14:08:48.519668+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-20T14:07:39.845687+00:00"
}
],
"block_index": 3,
"model_reaching_attempt_index": 0,
"slot_index": 3,
"terminal_state": "valid",
"variant": "P-min"
}
],
"retry_policy": {
"max_unreachable_retries": 5,
"rate_limit_backoff": {
"max_attempts": 4,
"max_total_ms": 60000
}
}
}