gpt-5.4-mini @ high · api

apihigh
config
gpt-5.4-mini-eff-high--api--official--dev
batch
2026-07-19--unified
transport
openai-official-api
protocol
api · metered
served model
gpt-5.4-mini-2026-03-17
采集于
2026年7月18日 11:54 · N=4
variant
live
slot 0 · P-q · sandboxed iframe
gpt-5.4-mini @high · api P-q slot 0 截图

截图是 similarity 通道测量时的原始像素。运行 live 会在 sandboxed iframe 中重新执行原始输出;渲染结果取决于你的浏览器和字体,不保证与测量像素一致。

similarity 邻居

限于 session 2026-07-19--unified

similarity 衡量本 session 内的趋同程度 —— 不是质量。

Fidelity —— 卡片展示的数据对不对?

  • namematch
  • conditionmatch
  • datematch
  • max tempambiguousinsufficient-distinct-temperature-nodes
  • min tempambiguousinsufficient-distinct-temperature-nodes
  • hourlyambiguouscoverage · 0 命中 · 0 不符 · 24 未展示
  • request sidematch0 处违规

extractor fidelity-extractor-v1+Berlin-2026-07-15

源码 —— 原始模型输出,一字节未改

下载 card.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="color-scheme" content="dark">
  <title>Weather card</title>
  <style>
    :root {
      --bg1: #08111e;
      --bg2: #11243f;
      --bg3: #203a63;
      --accent: #8fd8ff;
      --accent2: #ffd36c;
      --accent3: #ff8b68;
      --text: #f6f8ff;
      --muted: rgba(246, 248, 255, 0.70);
      --soft: rgba(246, 248, 255, 0.10);
      --soft2: rgba(246, 248, 255, 0.16);
    }

    html, body {
      height: 100%;
      margin: 0;
    }

    body {
      overflow: hidden;
      color: var(--text);
      background:
        radial-gradient(1200px 700px at 10% 10%, rgba(255, 255, 255, 0.09), transparent 60%),
        radial-gradient(900px 500px at 86% 18%, rgba(255, 255, 255, 0.07), transparent 62%),
        linear-gradient(145deg, var(--bg1) 0%, var(--bg2) 45%, var(--bg3) 100%);
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      color-scheme: dark;
    }

    * {
      box-sizing: border-box;
    }

    .bg {
      position: fixed;
      inset: 0;
      pointer-events: none;
      overflow: hidden;
      z-index: 0;
    }

    .blob {
      position: absolute;
      border-radius: 50%;
      filter: blur(78px);
      opacity: 0.55;
      transform: translate3d(0, 0, 0);
      will-change: transform;
    }

    .blob.one {
      width: 560px;
      height: 560px;
      left: -140px;
      top: -170px;
      animation: drift 18s ease-in-out infinite;
    }

    .blob.two {
      width: 620px;
      height: 620px;
      right: -160px;
      top: 40px;
      animation: drift 24s ease-in-out infinite reverse;
    }

    .blob.three {
      width: 460px;
      height: 460px;
      left: 33%;
      bottom: -190px;
      animation: drift 28s ease-in-out infinite;
    }

    @keyframes drift {
      0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
      50% { transform: translate3d(22px, -22px, 0) scale(1.06); }
    }

    .shell {
      position: relative;
      z-index: 1;
      min-height: 100%;
      display: grid;
      place-items: center;
      padding: 24px;
    }

    .card {
      position: relative;
      width: min(1132px, calc(100vw - 48px));
      height: min(744px, calc(100vh - 48px));
      border-radius: 34px;
      background: linear-gradient(180deg, rgba(9, 13, 23, 0.74), rgba(12, 18, 32, 0.58));
      border: 1px solid rgba(255, 255, 255, 0.14);
      box-shadow:
        0 30px 80px rgba(0, 0, 0, 0.36),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);
      backdrop-filter: blur(28px) saturate(140%);
      overflow: hidden;
      animation: appear 0.55s ease both;
    }

    @keyframes appear {
      from { opacity: 0; transform: translateY(10px) scale(0.985); }
      to { opacity: 1; transform: translateY(0) scale(1); }
    }

    .card::before {
      content: "";
      position: absolute;
      inset: 0;
      background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), transparent 35%, rgba(255, 255, 255, 0.03) 100%);
      pointer-events: none;
    }

    .normalView {
      position: relative;
      z-index: 1;
      height: 100%;
      display: grid;
      grid-template-rows: auto auto 1fr;
      min-height: 0;
    }

    .header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      padding: 22px 24px 0;
      gap: 16px;
    }

    .brand {
      display: grid;
      gap: 6px;
      min-width: 0;
    }

    .eyebrow {
      font-size: 12px;
      letter-spacing: 0.24em;
      text-transform: uppercase;
      color: var(--muted);
      font-weight: 700;
    }

    .subeyebrow {
      font-size: 13px;
      color: var(--muted);
    }

    .headerMeta {
      display: flex;
      gap: 10px;
      align-items: center;
      flex-wrap: wrap;
      justify-content: flex-end;
    }

    .chip, .badge {
      padding: 8px 12px;
      border-radius: 999px;
      background: rgba(255, 255, 255, 0.08);
      border: 1px solid rgba(255, 255, 255, 0.11);
      color: var(--muted);
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      white-space: nowrap;
    }

    .hero {
      padding: 18px 24px 16px;
      display: grid;
      grid-template-columns: 170px 1fr;
      gap: 22px;
      align-items: center;
      min-height: 0;
    }

    .iconCard {
      height: 154px;
      border-radius: 28px;
      background: radial-gradient(circle at 35% 28%, rgba(255, 255, 255, 0.18), rgba(255, 255, 255, 0.06) 34%, rgba(255, 255, 255, 0.02) 72%);
      border: 1px solid rgba(255, 255, 255, 0.12);
      display: grid;
      place-items: center;
      position: relative;
      overflow: hidden;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
    }

    .iconCard::after {
      content: "";
      position: absolute;
      inset: auto -30% -35% 35%;
      height: 90px;
      background: radial-gradient(circle, rgba(255, 255, 255, 0.24), transparent 70%);
      opacity: 0.55;
      transform: rotate(-12deg);
    }

    .iconCard svg {
      width: 122px;
      height: 122px;
      filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.26));
      position: relative;
      z-index: 1;
    }

    .summary {
      display: grid;
      gap: 12px;
      min-width: 0;
      align-content: center;
    }

    .location {
      margin: 0;
      font-size: 32px;
      font-weight: 800;
      letter-spacing: -0.05em;
      line-height: 1.05;
      overflow-wrap: anywhere;
    }

    .date {
      margin-top: 4px;
      font-size: 15px;
      color: var(--muted);
    }

    .temps {
      display: flex;
      align-items: flex-end;
      gap: 18px;
      flex-wrap: wrap;
    }

    .maxTemp {
      font-size: 82px;
      line-height: 0.92;
      font-weight: 900;
      letter-spacing: -0.08em;
      font-variant-numeric: tabular-nums;
    }

    .minBox {
      display: flex;
      flex-direction: column;
      gap: 4px;
      padding-bottom: 12px;
    }

    .minLabel {
      font-size: 11px;
      text-transform: uppercase;
      letter-spacing: 0.22em;
      color: var(--muted);
      font-weight: 700;
    }

    .minTemp {
      font-size: 28px;
      font-weight: 800;
      letter-spacing: -0.04em;
      font-variant-numeric: tabular-nums;
    }

    .condition {
      font-size: 21px;
      font-weight: 700;
      letter-spacing: -0.02em;
      overflow-wrap: anywhere;
    }

    .note {
      font-size: 13px;
      color: var(--muted);
    }

    .metaRow {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      margin-top: 2px;
    }

    .main {
      padding: 0 24px 24px;
      min-height: 0;
    }

    .panel {
      height: 100%;
      background: rgba(255, 255, 255, 0.045);
      border: 1px solid rgba(255, 255, 255, 0.10);
      border-radius: 28px;
      padding: 18px;
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
      display: flex;
      flex-direction: column;
      gap: 12px;
      min-height: 0;
    }

    .panelHeader {
      display: flex;
      justify-content: space-between;
      align-items: flex-end;
      gap: 14px;
    }

    .panelTitle {
      font-size: 18px;
      font-weight: 800;
      letter-spacing: -0.03em;
    }

    .panelHint {
      font-size: 13px;
      color: var(--muted);
      margin-top: 4px;
    }

    .chartWrap {
      height: 120px;
      border-radius: 20px;
      background: linear-gradient(180deg, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.02));
      border: 1px solid rgba(255, 255, 255, 0.08);
      overflow: hidden;
      padding: 6px 8px;
      display: grid;
      place-items: center;
    }

    .chartWrap svg {
      width: 100%;
      height: 100%;
      display: block;
    }

    .hourGrid {
      display: grid;
      grid-template-columns: repeat(6, minmax(0, 1fr));
      gap: 10px;
      min-height: 0;
    }

    .hour {
      min-height: 58px;
      border-radius: 16px;
      background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.04));
      border: 1px solid rgba(255, 255, 255, 0.08);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
      padding: 10px 11px 9px;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      gap: 6px;
      position: relative;
      overflow: hidden;
      transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
    }

    .hour:hover {
      transform: translateY(-2px);
      border-color: rgba(255, 255, 255, 0.18);
    }

    .hour::after {
      content: "";
      position: absolute;
      inset: 0;
      background: linear-gradient(120deg, rgba(255, 255, 255, 0.09), transparent 45%);
      opacity: 0.45;
      pointer-events: none;
    }

    .hourTime {
      font-size: 11px;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
      position: relative;
      z-index: 1;
    }

    .hourTemp {
      font-size: 19px;
      font-weight: 800;
      letter-spacing: -0.04em;
      font-variant-numeric: tabular-nums;
      position: relative;
      z-index: 1;
      white-space: nowrap;
    }

    .hourBar {
      height: 4px;
      border-radius: 999px;
      background: rgba(255, 255, 255, 0.10);
      overflow: hidden;
      position: relative;
      z-index: 1;
    }

    .hourBar > span {
      display: block;
      height: 100%;
      border-radius: inherit;
      background: linear-gradient(90deg, rgba(255, 255, 255, 0.35), var(--accent), var(--accent2));
    }

    .loadingHint {
      color: var(--muted);
      font-size: 12px;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      text-align: center;
      padding: 18px 16px;
      line-height: 1.5;
    }

    .hourGrid .loadingHint {
      grid-column: 1 / -1;
    }

    .errorView {
      position: absolute;
      inset: 0;
      display: grid;
      place-items: center;
      text-align: center;
      padding: 48px;
      background: linear-gradient(180deg, rgba(10, 13, 23, 0.82), rgba(10, 16, 27, 0.74));
      backdrop-filter: blur(18px) saturate(120%);
      z-index: 2;
    }

    .errorCard {
      max-width: 640px;
      display: grid;
      justify-items: center;
      gap: 18px;
    }

    .errorMark {
      width: 84px;
      height: 84px;
      border-radius: 26px;
      background: linear-gradient(180deg, rgba(255, 110, 110, 0.28), rgba(255, 110, 110, 0.12));
      border: 1px solid rgba(255, 255, 255, 0.12);
      display: grid;
      place-items: center;
      font-size: 42px;
      font-weight: 900;
      color: #fff;
      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
    }

    .errorTitle {
      font-size: 40px;
      line-height: 1.05;
      margin: 0;
      letter-spacing: -0.05em;
    }

    .errorText {
      margin: 0;
      color: var(--muted);
      font-size: 18px;
      line-height: 1.55;
      max-width: 560px;
    }

    [hidden] {
      display: none !important;
    }

    @media (prefers-reduced-motion: reduce) {
      .blob,
      .card,
      .hour {
        animation: none !important;
        transition: none !important;
      }
    }
  </style>
</head>
<body>
  <div class="bg" aria-hidden="true">
    <div class="blob one" id="blobOne"></div>
    <div class="blob two" id="blobTwo"></div>
    <div class="blob three" id="blobThree"></div>
  </div>

  <main class="shell">
    <section class="card" id="card">
      <div class="normalView" id="normalView">
        <header class="header">
          <div class="brand">
            <div class="eyebrow">Weather card</div>
            <div class="subeyebrow">Same-origin Open-Meteo compatible source</div>
          </div>
          <div class="headerMeta">
            <div class="chip" id="sourceChip">Forecast</div>
            <div class="chip" id="utcChip">UTC</div>
          </div>
        </header>

        <section class="hero">
          <div class="iconCard" id="iconCard">
            <div id="iconSlot"></div>
          </div>

          <div class="summary">
            <div>
              <h1 class="location" id="locationName">Berlin</h1>
              <div class="date" id="dateLabel">Loading…</div>
            </div>

            <div class="temps">
              <div class="maxTemp" id="maxTemp">—</div>
              <div class="minBox">
                <div class="minLabel">Low</div>
                <div class="minTemp" id="minTemp">—</div>
              </div>
            </div>

            <div class="condition" id="condition">Loading weather…</div>
            <div class="note" id="note">Loading hourly temperatures…</div>

            <div class="metaRow">
              <div class="badge" id="sourceBadge">Forecast</div>
              <div class="badge" id="utcBadge">UTC</div>
              <div class="badge" id="unitBadge">Celsius</div>
              <div class="badge" id="rangeBadge">Range pending</div>
            </div>
          </div>
        </section>

        <section class="main">
          <section class="panel">
            <div class="panelHeader">
              <div>
                <div class="panelTitle">Hourly temperatures</div>
                <div class="panelHint" id="panelHint">All times are shown in UTC</div>
              </div>
              <div class="badge" id="hourCount">Loading…</div>
            </div>

            <div class="chartWrap" id="chartMount">
              <div class="loadingHint">Loading hourly temperatures…</div>
            </div>

            <div class="hourGrid" id="hourGrid">
              <div class="loadingHint" style="grid-column: 1 / -1;">Hourly readings will appear here.</div>
            </div>
          </section>
        </section>
      </div>

      <div class="errorView" id="errorView" hidden aria-live="assertive">
        <div class="errorCard">
          <div class="errorMark">!</div>
          <h1 class="errorTitle">Weather data could not be loaded.</h1>
          <p class="errorText" id="errorText">Could not load weather data.</p>
        </div>
      </div>
    </section>
  </main>

  <script>
    (function () {
      const q = new URLSearchParams(location.search);

      const els = {
        blobOne: document.getElementById('blobOne'),
        blobTwo: document.getElementById('blobTwo'),
        blobThree: document.getElementById('blobThree'),
        normalView: document.getElementById('normalView'),
        errorView: document.getElementById('errorView'),
        errorText: document.getElementById('errorText'),
        card: document.getElementById('card'),
        iconCard: document.getElementById('iconCard'),
        iconSlot: document.getElementById('iconSlot'),
        locationName: document.getElementById('locationName'),
        dateLabel: document.getElementById('dateLabel'),
        sourceChip: document.getElementById('sourceChip'),
        utcChip: document.getElementById('utcChip'),
        sourceBadge: document.getElementById('sourceBadge'),
        utcBadge: document.getElementById('utcBadge'),
        unitBadge: document.getElementById('unitBadge'),
        rangeBadge: document.getElementById('rangeBadge'),
        maxTemp: document.getElementById('maxTemp'),
        minTemp: document.getElementById('minTemp'),
        condition: document.getElementById('condition'),
        note: document.getElementById('note'),
        panelHint: document.getElementById('panelHint'),
        hourCount: document.getElementById('hourCount'),
        chartMount: document.getElementById('chartMount'),
        hourGrid: document.getElementById('hourGrid')
      };

      const THEMES = {
        clear:   { bg: ['#08111e', '#0f2745', '#1f4d7a'], accent: '#8fd8ff', accent2: '#ffd36c', accent3: '#ff8b68' },
        partly:  { bg: ['#07111f', '#11253d', '#223f66'], accent: '#9ab4ff', accent2: '#ffd47a', accent3: '#72d1ff' },
        cloudy:  { bg: ['#0a111d', '#132136', '#263d5d'], accent: '#c8d2e7', accent2: '#8cb2d7', accent3: '#7aa4ca' },
        fog:     { bg: ['#0a1319', '#14212b', '#253746'], accent: '#ced6df', accent2: '#93aabc', accent3: '#6c8496' },
        rain:    { bg: ['#06101d', '#0d1c35', '#173c66'], accent: '#78baff', accent2: '#82e1ff', accent3: '#497cff' },
        snow:    { bg: ['#08121d', '#16334b', '#234f70'], accent: '#c9f0ff', accent2: '#ffffff', accent3: '#86dbff' },
        storm:   { bg: ['#090812', '#17112d', '#34265a'], accent: '#d5a7ff', accent2: '#8cd6ff', accent3: '#ffbd6b' }
      };

      function hexToRgba(hex, alpha) {
        const s = String(hex).replace('#', '').trim();
        const normalized = s.length === 3 ? s.split('').map(ch => ch + ch).join('') : s;
        const int = parseInt(normalized, 16);
        const r = (int >> 16) & 255;
        const g = (int >> 8) & 255;
        const b = int & 255;
        return 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
      }

      function todayUTC() {
        return new Date().toISOString().slice(0, 10);
      }

      function validDateString(s) {
        if (!/^\d{4}-\d{2}-\d{2}$/.test(s || '')) return false;
        const d = new Date(String(s) + 'T00:00:00Z');
        return !Number.isNaN(d.getTime()) && d.toISOString().slice(0, 10) === s;
      }

      function formatDateLabel(date) {
        return new Intl.DateTimeFormat('en-GB', {
          weekday: 'long',
          day: 'numeric',
          month: 'long',
          year: 'numeric',
          timeZone: 'UTC'
        }).format(new Date(date + 'T00:00:00Z'));
      }

      function formatTemp(n) {
        const v = Math.round(Number(n));
        return v + '°C';
      }

      function formatHourLabel(t) {
        return String(t).slice(11, 16);
      }

      function nParam(name, fallback) {
        const raw = q.get(name);
        if (raw === null || raw.trim() === '') return fallback;
        const num = Number(raw);
        return Number.isFinite(num) ? num : fallback;
      }

      function esc(s) {
        return String(s).replace(/[&<>"']/g, function (m) {
          return {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#39;'
          }[m];
        });
      }

      function applyTheme(theme) {
        const root = document.documentElement.style;
        root.setProperty('--bg1', theme.bg[0]);
        root.setProperty('--bg2', theme.bg[1]);
        root.setProperty('--bg3', theme.bg[2]);
        root.setProperty('--accent', theme.accent);
        root.setProperty('--accent2', theme.accent2);
        root.setProperty('--accent3', theme.accent3);

        els.blobOne.style.background = 'radial-gradient(circle, ' + hexToRgba(theme.accent, 0.34) + ' 0%, transparent 72%)';
        els.blobTwo.style.background = 'radial-gradient(circle, ' + hexToRgba(theme.accent2, 0.28) + ' 0%, transparent 72%)';
        els.blobThree.style.background = 'radial-gradient(circle, ' + hexToRgba(theme.accent3, 0.24) + ' 0%, transparent 72%)';
        els.iconCard.style.background =
          'radial-gradient(circle at 35% 28%, ' +
          hexToRgba(theme.accent2, 0.20) + ', ' +
          hexToRgba(theme.accent, 0.10) + ' 38%, rgba(255,255,255,0.03) 74%)';
      }

      function cloudGroup(fill, opacity, dx, dy) {
        return '<g transform="translate(' + (dx || 0) + ' ' + (dy || 0) + ')" fill="' + fill + '" opacity="' + (opacity == null ? 1 : opacity) + '">' +
          '<circle cx="58" cy="86" r="18"/>' +
          '<circle cx="84" cy="72" r="26"/>' +
          '<circle cx="112" cy="86" r="18"/>' +
          '<rect x="44" y="82" width="86" height="30" rx="15"/>' +
        '</g>';
      }

      function iconSvg(type, theme) {
        const bg = hexToRgba(theme.accent, 0.16);
        const cloud = 'rgba(255,255,255,0.92)';
        const cloudShadow = hexToRgba(theme.accent, 0.22);
        const rays = theme.accent2;
        const stroke = theme.accent;
        const ring = '<circle cx="80" cy="80" r="58" fill="' + bg + '"/>';

        if (type === 'clear') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            '<g stroke="' + rays + '" stroke-linecap="round" stroke-width="8">' +
              '<line x1="80" y1="18" x2="80" y2="34"/>' +
              '<line x1="80" y1="126" x2="80" y2="142"/>' +
              '<line x1="18" y1="80" x2="34" y2="80"/>' +
              '<line x1="126" y1="80" x2="142" y2="80"/>' +
              '<line x1="37" y1="37" x2="48" y2="48"/>' +
              '<line x1="112" y1="112" x2="123" y2="123"/>' +
              '<line x1="37" y1="123" x2="48" y2="112"/>' +
              '<line x1="112" y1="48" x2="123" y2="37"/>' +
            '</g>' +
            '<circle cx="80" cy="80" r="29" fill="' + rays + '"/>' +
          '</svg>';
        }

        if (type === 'partly') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            '<g transform="translate(-7 -7)">' +
              '<circle cx="58" cy="56" r="22" fill="' + rays + '"/>' +
              '<g stroke="' + rays + '" stroke-linecap="round" stroke-width="6">' +
                '<line x1="58" y1="20" x2="58" y2="34"/>' +
                '<line x1="58" y1="78" x2="58" y2="92"/>' +
                '<line x1="22" y1="56" x2="36" y2="56"/>' +
                '<line x1="80" y1="56" x2="94" y2="56"/>' +
                '<line x1="34" y1="32" x2="44" y2="42"/>' +
                '<line x1="72" y1="70" x2="82" y2="80"/>' +
              '</g>' +
            '</g>' +
            cloudGroup('rgba(255,255,255,0.94)', 1, 8, 10) +
          '</svg>';
        }

        if (type === 'cloudy') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            cloudGroup(cloudShadow, 0.45, 7, 8) +
            cloudGroup(cloud, 0.96, 0, 0) +
          '</svg>';
        }

        if (type === 'fog') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            cloudGroup(cloud, 0.96, 0, 4) +
            '<g stroke="' + stroke + '" stroke-linecap="round" stroke-width="5" opacity="0.95">' +
              '<line x1="40" y1="118" x2="120" y2="118"/>' +
              '<line x1="30" y1="132" x2="122" y2="132"/>' +
              '<line x1="44" y1="146" x2="112" y2="146"/>' +
            '</g>' +
          '</svg>';
        }

        if (type === 'rain') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            cloudGroup(cloud, 0.96, 0, 0) +
            '<g fill="' + rays + '" opacity="0.98">' +
              '<path d="M57 118c0-7 6-12 6-12s6 5 6 12a6 6 0 1 1-12 0z"/>' +
              '<path d="M80 122c0-7 6-12 6-12s6 5 6 12a6 6 0 1 1-12 0z"/>' +
              '<path d="M103 118c0-7 6-12 6-12s6 5 6 12a6 6 0 1 1-12 0z"/>' +
            '</g>' +
          '</svg>';
        }

        if (type === 'snow') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            cloudGroup(cloud, 0.96, 0, 0) +
            '<g stroke="' + rays + '" stroke-linecap="round" stroke-width="3.2" opacity="0.98">' +
              '<g transform="translate(58 120)"><line x1="-7" y1="0" x2="7" y2="0"/><line x1="0" y1="-7" x2="0" y2="7"/><line x1="-5" y1="-5" x2="5" y2="5"/><line x1="-5" y1="5" x2="5" y2="-5"/></g>' +
              '<g transform="translate(83 124)"><line x1="-7" y1="0" x2="7" y2="0"/><line x1="0" y1="-7" x2="0" y2="7"/><line x1="-5" y1="-5" x2="5" y2="5"/><line x1="-5" y1="5" x2="5" y2="-5"/></g>' +
              '<g transform="translate(108 120)"><line x1="-7" y1="0" x2="7" y2="0"/><line x1="0" y1="-7" x2="0" y2="7"/><line x1="-5" y1="-5" x2="5" y2="5"/><line x1="-5" y1="5" x2="5" y2="-5"/></g>' +
            '</g>' +
          '</svg>';
        }

        if (type === 'storm') {
          return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
            ring +
            cloudGroup(cloud, 0.96, 0, 0) +
            '<path d="M86 110l-11 21h12l-8 18 22-28H88l10-16z" fill="' + rays + '"/>' +
          '</svg>';
        }

        return '<svg viewBox="0 0 160 160" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">' +
          ring +
          cloudGroup(cloud, 0.96, 0, 0) +
        '</svg>';
      }

      function describeWeather(code) {
        const n = Number(code);
        if (!Number.isFinite(n)) return { label: 'Weather unavailable', type: 'cloudy', theme: THEMES.cloudy };

        if (n === 0) return { label: 'Clear sky', type: 'clear', theme: THEMES.clear };
        if (n === 1) return { label: 'Mainly clear', type: 'partly', theme: THEMES.partly };
        if (n === 2) return { label: 'Partly cloudy', type: 'partly', theme: THEMES.partly };
        if (n === 3) return { label: 'Overcast', type: 'cloudy', theme: THEMES.cloudy };

        if (n === 45) return { label: 'Fog', type: 'fog', theme: THEMES.fog };
        if (n === 48) return { label: 'Rime fog', type: 'fog', theme: THEMES.fog };

        if (n === 51) return { label: 'Light drizzle', type: 'rain', theme: THEMES.rain };
        if (n === 53) return { label: 'Drizzle', type: 'rain', theme: THEMES.rain };
        if (n === 55) return { label: 'Dense drizzle', type: 'rain', theme: THEMES.rain };
        if (n === 56) return { label: 'Freezing drizzle', type: 'snow', theme: THEMES.snow };
        if (n === 57) return { label: 'Dense freezing drizzle', type: 'snow', theme: THEMES.snow };

        if (n === 61) return { label: 'Light rain', type: 'rain', theme: THEMES.rain };
        if (n === 63) return { label: 'Rain', type: 'rain', theme: THEMES.rain };
        if (n === 65) return { label: 'Heavy rain', type: 'rain', theme: THEMES.rain };
        if (n === 66) return { label: 'Freezing rain', type: 'rain', theme: THEMES.rain };
        if (n === 67) return { label: 'Heavy freezing rain', type: 'rain', theme: THEMES.rain };

        if (n === 71) return { label: 'Light snow', type: 'snow', theme: THEMES.snow };
        if (n === 73) return { label: 'Snow', type: 'snow', theme: THEMES.snow };
        if (n === 75) return { label: 'Heavy snow', type: 'snow', theme: THEMES.snow };
        if (n === 77) return { label: 'Snow grains', type: 'snow', theme: THEMES.snow };

        if (n === 80) return { label: 'Rain showers', type: 'rain', theme: THEMES.rain };
        if (n === 81) return { label: 'Moderate rain showers', type: 'rain', theme: THEMES.rain };
        if (n === 82) return { label: 'Violent rain showers', type: 'rain', theme: THEMES.rain };

        if (n === 85) return { label: 'Snow showers', type: 'snow', theme: THEMES.snow };
        if (n === 86) return { label: 'Heavy snow showers', type: 'snow', theme: THEMES.snow };

        if (n === 95) return { label: 'Thunderstorm', type: 'storm', theme: THEMES.storm };
        if (n === 96) return { label: 'Thunderstorm with hail', type: 'storm', theme: THEMES.storm };
        if (n === 99) return { label: 'Thunderstorm with heavy hail', type: 'storm', theme: THEMES.storm };

        return { label: 'Variable conditions', type: 'cloudy', theme: THEMES.cloudy };
      }

      function getDailyValue(obj, keys, idx) {
        for (const key of keys) {
          if (Array.isArray(obj[key]) && obj[key][idx] != null) {
            const v = Number(obj[key][idx]);
            if (Number.isFinite(v)) return v;
          }
        }
        return NaN;
      }

      function extractHourly(hourly, date) {
        const times = Array.isArray(hourly.time) ? hourly.time : [];
        const values = Array.isArray(hourly.temperature_2m) ? hourly.temperature_2m : [];
        const out = [];

        for (let i = 0; i < times.length; i++) {
          const t = String(times[i]);
          if (t.slice(0, 10) === date) {
            const temp = Number(values[i]);
            if (Number.isFinite(temp)) out.push({ time: t, temp: temp });
          }
        }

        return out;
      }

      function renderHours(container, samples) {
        const temps = samples.map(s => s.temp);
        const min = Math.min.apply(null, temps);
        const max = Math.max.apply(null, temps);
        const span = (max - min) || 1;

        container.innerHTML = samples.map((s) => {
          const pct = Math.max(8, Math.min(100, ((s.temp - min) / span) * 100));
          const hour = formatHourLabel(s.time);
          const tempText = formatTemp(s.temp);

          return (
            '<div class="hour" title="' + esc(hour) + ' — ' + esc(tempText) + '" aria-label="' + esc(hour) + ' ' + esc(tempText) + '">' +
              '<div class="hourTime">' + esc(hour) + '</div>' +
              '<div class="hourTemp">' + esc(tempText) + '</div>' +
              '<div class="hourBar"><span style="width:' + pct.toFixed(1) + '%"></span></div>' +
            '</div>'
          );
        }).join('');
      }

      function renderChart(container, samples, theme) {
        const temps = samples.map(s => s.temp);
        const times = samples.map(s => s.time);
        const w = 1000;
        const h = 128;
        const left = 18;
        const right = 18;
        const top = 12;
        const bottom = 28;
        const plotW = w - left - right;
        const plotH = h - top - bottom;

        let min = Math.min.apply(null, temps);
        let max = Math.max.apply(null, temps);
        const pad = Math.max(2, (max - min) * 0.2);
        const lo = min - pad;
        const hi = max + pad;
        const span = (hi - lo) || 1;

        const xs = temps.map((_, i) => left + (temps.length === 1 ? plotW / 2 : (plotW * i) / (temps.length - 1)));
        const ys = temps.map(v => top + plotH - (((v - lo) / span) * plotH));

        const linePath = xs.map((x, i) => (i === 0 ? 'M ' : 'L ') + x.toFixed(1) + ' ' + ys[i].toFixed(1)).join(' ');
        let areaPath = 'M ' + xs[0].toFixed(1) + ' ' + (top + plotH).toFixed(1) + ' L ' + xs[0].toFixed(1) + ' ' + ys[0].toFixed(1);
        for (let i = 1; i < xs.length; i++) areaPath += ' L ' + xs[i].toFixed(1) + ' ' + ys[i].toFixed(1);
        areaPath += ' L ' + xs[xs.length - 1].toFixed(1) + ' ' + (top + plotH).toFixed(1) + ' Z';

        const gridYs = [0.25, 0.5, 0.75].map(frac => top + plotH * frac);
        const tickIndices = Array.from(new Set([0, 6, 12, 18, samples.length - 1])).filter(i => i >= 0 && i < samples.length);

        const grid = gridYs.map(y =>
          '<line x1="' + left + '" y1="' + y.toFixed(1) + '" x2="' + (w - right) + '" y2="' + y.toFixed(1) + '" stroke="rgba(255,255,255,0.10)" stroke-width="1" stroke-dasharray="4 6"/>'
        ).join('');

        const xLabels = tickIndices.map(i => {
          const anchor = i === 0 ? 'start' : (i === samples.length - 1 ? 'end' : 'middle');
          return '<text x="' + xs[i].toFixed(1) + '" y="' + (h - 10) + '" text-anchor="' + anchor + '" fill="rgba(246,248,255,0.62)" font-size="12" font-weight="600" letter-spacing="0.08em">' + esc(formatHourLabel(times[i]).slice(0, 2)) + '</text>';
        }).join('');

        const dots = xs.map((x, i) =>
          '<circle cx="' + x.toFixed(1) + '" cy="' + ys[i].toFixed(1) + '" r="4.2" fill="' + theme.accent2 + '" stroke="rgba(255,255,255,0.9)" stroke-width="1.5"/>'
        ).join('');

        const svg =
          '<svg viewBox="0 0 ' + w + ' ' + h + '" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" style="font-family:inherit">' +
            '<defs>' +
              '<linearGradient id="fillGrad" x1="0" y1="0" x2="0" y2="1">' +
                '<stop offset="0%" stop-color="' + hexToRgba(theme.accent, 0.30) + '"/>' +
                '<stop offset="100%" stop-color="' + hexToRgba(theme.accent, 0) + '"/>' +
              '</linearGradient>' +
              '<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">' +
                '<feGaussianBlur stdDeviation="2.6" result="blur"/>' +
                '<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>' +
              '</filter>' +
            '</defs>' +
            '<rect x="0" y="0" width="1000" height="128" fill="transparent"/>' +
            grid +
            '<path d="' + areaPath + '" fill="url(#fillGrad)" opacity="0.95"/>' +
            '<path d="' + linePath + '" fill="none" stroke="' + theme.accent2 + '" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round" filter="url(#glow)"/>' +
            dots +
            xLabels +
          '</svg>';

        container.innerHTML = svg;
      }

      function showError(message) {
        applyTheme(THEMES.cloudy);
        els.normalView.hidden = true;
        els.errorView.hidden = false;
        els.errorText.textContent = message;
      }

      const defaultTheme = THEMES.cloudy;
      applyTheme(defaultTheme);

      const today = todayUTC();
      const name = ((q.get('name') || '').trim()) || 'Berlin';
      const requestedDate = validDateString(q.get('date')) ? q.get('date') : today;
      const latitude = nParam('lat', 52.52);
      const longitude = nParam('lon', 13.405);
      const isArchive = requestedDate < today;
      const requestType = isArchive ? 'Archive' : 'Forecast';

      document.title = name + ' Weather';
      els.locationName.textContent = name;
      els.dateLabel.textContent = formatDateLabel(requestedDate);
      els.sourceChip.textContent = requestType;
      els.utcChip.textContent = 'UTC';
      els.sourceBadge.textContent = requestType;
      els.utcBadge.textContent = 'UTC';
      els.unitBadge.textContent = 'Celsius';
      els.panelHint.textContent = 'All times are shown in UTC';
      els.hourCount.textContent = 'Loading…';
      els.maxTemp.textContent = '—';
      els.minTemp.textContent = '—';
      els.condition.textContent = 'Loading weather…';
      els.note.textContent = 'Loading hourly temperatures…';
      els.rangeBadge.textContent = 'Range pending';
      els.iconSlot.innerHTML = iconSvg('cloudy', defaultTheme);

      (async function load() {
        try {
          const endpoint = isArchive ? '/api/om/archive' : '/api/om/forecast';
          const params = new URLSearchParams();
          params.set('latitude', String(latitude));
          params.set('longitude', String(longitude));
          params.set('daily', 'temperature_2m_max,temperature_2m_min,weather_code');
          params.set('hourly', 'temperature_2m');
          params.set('timezone', 'UTC');
          params.set('temperature_unit', 'celsius');
          params.set('start_date', requestedDate);
          params.set('end_date', requestedDate);

          const res = await fetch(endpoint + '?' + params.toString(), { cache: 'no-store' });
          if (!res.ok) throw new Error('Request failed (' + res.status + ')');

          const data = await res.json();
          if (data && data.error) throw new Error(data.reason || 'API error');

          const daily = data.daily || {};
          const hourly = data.hourly || {};
          const dailyTimes = Array.isArray(daily.time) ? daily.time : [];
          const dayIndex = dailyTimes.findIndex(t => String(t).slice(0, 10) === requestedDate);
          if (dayIndex < 0) throw new Error('Requested day is unavailable');

          const max = getDailyValue(daily, ['temperature_2m_max'], dayIndex);
          const min = getDailyValue(daily, ['temperature_2m_min'], dayIndex);
          const code = getDailyValue(daily, ['weather_code', 'weathercode'], dayIndex);

          if (!Number.isFinite(max) || !Number.isFinite(min)) throw new Error('Temperature data is unavailable');

          const samples = extractHourly(hourly, requestedDate);
          if (!samples.length) throw new Error('Hourly data is unavailable');

          const weather = describeWeather(code);
          applyTheme(weather.theme);

          els.locationName.textContent = name;
          els.dateLabel.textContent = formatDateLabel(requestedDate);
          els.sourceChip.textContent = requestType;
          els.utcChip.textContent = 'UTC';
          els.sourceBadge.textContent = requestType;
          els.utcBadge.textContent = 'UTC';
          els.unitBadge.textContent = 'Celsius';
          els.maxTemp.textContent = formatTemp(max);
          els.minTemp.textContent = formatTemp(min);
          els.condition.textContent = weather.label;
          els.note.textContent = 'Hourly temperatures are shown in UTC.';
          els.rangeBadge.textContent = 'Low ' + formatTemp(min) + ' · High ' + formatTemp(max);
          els.hourCount.textContent = samples.length + ' hourly readings';
          els.panelHint.textContent = 'All times are shown in UTC';
          els.iconSlot.innerHTML = iconSvg(weather.type, weather.theme);

          renderChart(els.chartMount, samples, weather.theme);
          renderHours(els.hourGrid, samples);

          els.normalView.hidden = false;
          els.errorView.hidden = true;
        } catch (err) {
          showError('Could not load weather data for ' + name + ' on ' + formatDateLabel(requestedDate) + '. Please try again later.');
        }
      })();
    })();
  </script>
</body>
</html>

L1 —— 廉价的确定性描述量

bytes 总计37,417html / css / js3,189 / 11,105 / 23,123
dom 节点58dom 深度10
css 规则65含 js
brightness40.9contrast25.0
colorfulness19.1留白56.8%

该 slot 的 telemetry

prompt tokens312completion tokens151,734
total tokens152,046wall619.5 s
costrequest id
config.json585 B
{
  "N": 4,
  "auth": {
    "credential_ref": "WCB_OPENAI_OFFICIAL_KEY",
    "method": "api-key"
  },
  "billing": "metered",
  "config_id": "gpt-5.4-mini-eff-high--api--official--dev",
  "effort": "high",
  "family": "gpt",
  "m": {
    "min": 4,
    "q": 4
  },
  "model_id": "gpt-5.4-mini",
  "protocol": "api",
  "served_model": "gpt-5.4-mini-2026-03-17",
  "telemetry": {
    "completion_tokens": 711138,
    "cost_usd": null,
    "prompt_tokens": 46103,
    "total_tokens": 757241,
    "wall_ms": 3151294
  },
  "transport": "openai-official-api",
  "vendor_sanction_ref": null
}
send-log4.6 KB
{
  "N": 4,
  "batch_id": "2026-07-18--official-gpt",
  "config_id": "gpt-5.4-mini-eff-high--api--official--dev",
  "positions": [
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T19:02:32.746919+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T18:57:32.917864+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-17T19:12:55.898362+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:02:36.345282+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-17T19:25:04.463137+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:13:00.882083+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-17T19:27:54.560776+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:25:08.599041+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-17T19:35:22.722082+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:27:58.535886+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-17T19:39:18.798470+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:35:26.804169+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-17T19:43:22.669351+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:39:22.335277+00:00"
        }
      ],
      "block_index": 3,
      "model_reaching_attempt_index": 0,
      "slot_index": 3,
      "terminal_state": "valid",
      "variant": "P-min"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T19:50:32.308809+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T19:43:26.508271+00:00"
        }
      ],
      "block_index": 3,
      "model_reaching_attempt_index": 0,
      "slot_index": 3,
      "terminal_state": "valid",
      "variant": "P-q"
    }
  ],
  "retry_policy": {
    "max_unreachable_retries": 5,
    "rate_limit_backoff": {
      "max_attempts": 4,
      "max_total_ms": 60000
    }
  }
}