nemotron-3-ultra-free · api

api
config
nemotron-3-ultra-free--zen-free--dev
batch
2026-07-19--unified
transport
opencode-zen-free-tier
protocol
api · plan
served model
nemotron-3-ultra-free
采集于
2026年7月18日 11:54 · N=2
variant
live
slot 0 · P-q · sandboxed iframe
nemotron-3-ultra-free · api P-q slot 0 截图

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

similarity 邻居

限于 session 2026-07-19--unified

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

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

  • namematch
  • conditionmatch
  • datematch
  • max tempambiguousno-remaining-candidate
  • min tempmatch展示值:16
  • 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=1280, height=800">
  <title>Weather Card</title>
  <style>
    :root {
      --bg: #0d1117;
      --card-bg: rgba(22, 27, 34, 0.9);
      --card-border: rgba(48, 54, 61, 0.6);
      --text-primary: #e6edf3;
      --text-secondary: #8b949e;
      --text-muted: #6e7681;
      --accent: #58a6ff;
      --accent-warm: #f78166;
      --accent-cool: #39c5cf;
      --accent-sun: #d2a106;
      --shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
      --shadow-inner: inset 0 1px 0 rgba(255, 255, 255, 0.05);
      --radius: 16px;
      --radius-sm: 8px;
      --transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }

    @media (prefers-color-scheme: light) {
      :root {
        --bg: #f6f8fa;
        --card-bg: rgba(255, 255, 255, 0.95);
        --card-border: rgba(209, 213, 218, 0.6);
        --text-primary: #1f2328;
        --text-secondary: #57606a;
        --text-muted: #8b949e;
        --accent: #0969da;
        --accent-warm: #cf222e;
        --accent-cool: #0598bc;
        --accent-sun: #9a6700;
        --shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
        --shadow-inner: inset 0 1px 0 rgba(255, 255, 255, 0.5);
      }
    }

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    @font-face {
      font-family: 'Inter';
      src: local('Inter'), local('system-ui');
      font-display: swap;
    }

    body {
      font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
      background: var(--bg);
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 24px;
      color: var(--text-primary);
      line-height: 1.5;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }

    body::before {
      content: '';
      position: fixed;
      inset: 0;
      background: 
        radial-gradient(ellipse 80% 50% at 50% -20%, rgba(88, 166, 255, 0.15), transparent),
        radial-gradient(ellipse 60% 40% at 100% 100%, rgba(57, 197, 207, 0.1), transparent);
      pointer-events: none;
      z-index: -1;
    }

    .weather-card {
      width: 100%;
      max-width: 520px;
      background: var(--card-bg);
      border: 1px solid var(--card-border);
      border-radius: var(--radius);
      box-shadow: var(--shadow), var(--shadow-inner);
      overflow: hidden;
      backdrop-filter: blur(20px);
      animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1);
    }

    @keyframes slideUp {
      from {
        opacity: 0;
        transform: translateY(20px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }

    .card-header {
      padding: 28px 28px 16px;
      border-bottom: 1px solid var(--card-border);
      background: linear-gradient(180deg, rgba(88, 166, 255, 0.08), transparent);
    }

    .location-row {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 8px;
    }

    .location-icon {
      width: 20px;
      height: 20px;
      color: var(--accent);
      flex-shrink: 0;
    }

    .location-name {
      font-size: 20px;
      font-weight: 600;
      letter-spacing: -0.02em;
      color: var(--text-primary);
    }

    .date-row {
      display: flex;
      align-items: center;
      gap: 10px;
      color: var(--text-secondary);
      font-size: 14px;
    }

    .date-icon {
      width: 16px;
      height: 16px;
      flex-shrink: 0;
    }

    .card-main {
      padding: 24px 28px;
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 24px;
      align-items: start;
    }

    .current-weather {
      min-width: 0;
    }

    .condition-badge {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 6px 12px;
      background: rgba(88, 166, 255, 0.12);
      border: 1px solid rgba(88, 166, 255, 0.25);
      border-radius: 100px;
      font-size: 13px;
      font-weight: 500;
      color: var(--accent);
      margin-bottom: 16px;
      animation: fadeIn 0.4s ease 0.1s both;
    }

    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(-4px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .condition-icon {
      width: 18px;
      height: 18px;
      flex-shrink: 0;
    }

    .temp-display {
      display: flex;
      align-items: flex-start;
      gap: 4px;
      margin-bottom: 6px;
    }

    .temp-max {
      font-size: 64px;
      font-weight: 700;
      line-height: 1;
      letter-spacing: -0.03em;
      color: var(--text-primary);
      font-variant-numeric: tabular-nums;
    }

    .temp-unit {
      font-size: 28px;
      font-weight: 500;
      color: var(--text-secondary);
      margin-top: 8px;
    }

    .temp-min-row {
      display: flex;
      align-items: center;
      gap: 16px;
      color: var(--text-secondary);
      font-size: 15px;
    }

    .temp-min-item {
      display: flex;
      align-items: center;
      gap: 6px;
    }

    .temp-min-label {
      color: var(--text-muted);
      font-size: 13px;
      text-transform: uppercase;
      letter-spacing: 0.05em;
    }

    .temp-min-value {
      font-weight: 600;
      color: var(--text-primary);
      font-variant-numeric: tabular-nums;
    }

    .weather-visual {
      width: 120px;
      height: 120px;
      flex-shrink: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      animation: float 3s ease-in-out infinite;
    }

    @keyframes float {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(-6px); }
    }

    .hourly-section {
      margin-top: 8px;
      padding-top: 24px;
      border-top: 1px solid var(--card-border);
    }

    .hourly-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 16px;
    }

    .hourly-title {
      font-size: 13px;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--text-muted);
    }

    .hourly-chart {
      height: 140px;
      position: relative;
    }

    .chart-canvas {
      width: 100%;
      height: 100%;
      display: block;
    }

    .chart-labels {
      position: absolute;
      bottom: -20px;
      left: 0;
      right: 0;
      display: flex;
      justify-content: space-between;
      font-size: 11px;
      color: var(--text-muted);
      pointer-events: none;
    }

    .hourly-tooltip {
      position: absolute;
      background: var(--card-bg);
      border: 1px solid var(--card-border);
      border-radius: var(--radius-sm);
      padding: 8px 12px;
      font-size: 12px;
      pointer-events: none;
      opacity: 0;
      transform: translate(-50%, -100%) scale(0.9);
      transition: opacity 0.15s ease, transform 0.15s ease;
      z-index: 10;
      box-shadow: var(--shadow);
      white-space: nowrap;
    }

    .hourly-tooltip.visible {
      opacity: 1;
      transform: translate(-50%, -100%) scale(1);
    }

    .tooltip-time {
      color: var(--text-secondary);
      margin-bottom: 2px;
    }

    .tooltip-temp {
      font-weight: 600;
      color: var(--text-primary);
      font-variant-numeric: tabular-nums;
    }

    .error-state {
      padding: 48px 28px;
      text-align: center;
      color: var(--text-secondary);
    }

    .error-icon {
      width: 48px;
      height: 48px;
      margin: 0 auto 16px;
      color: var(--accent-warm);
      opacity: 0.7;
    }

    .error-title {
      font-size: 18px;
      font-weight: 600;
      color: var(--text-primary);
      margin-bottom: 8px;
    }

    .error-message {
      font-size: 14px;
      line-height: 1.6;
    }

    .loading-state {
      padding: 48px 28px;
      text-align: center;
      color: var(--text-secondary);
    }

    .spinner {
      width: 40px;
      height: 40px;
      margin: 0 auto 16px;
      border: 3px solid var(--card-border);
      border-top-color: var(--accent);
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    @keyframes spin {
      to { transform: rotate(360deg); }
    }

    .loading-text {
      font-size: 14px;
    }

    .chart-point {
      position: absolute;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: var(--accent);
      border: 2px solid var(--card-bg);
      box-shadow: 0 0 0 2px var(--accent);
      transform: translate(-50%, -50%);
      opacity: 0;
      transition: opacity 0.15s ease, transform 0.15s ease;
      z-index: 5;
      pointer-events: none;
    }

    .chart-point:hover,
    .chart-point.active {
      opacity: 1;
      transform: translate(-50%, -50%) scale(1.3);
    }

    .chart-line {
      fill: none;
      stroke: var(--accent);
      stroke-width: 2.5;
      stroke-linecap: round;
      stroke-linejoin: round;
      filter: drop-shadow(0 2px 8px rgba(88, 166, 255, 0.3));
    }

    .chart-area {
      fill: url(#temp-gradient);
    }

    @media (max-width: 560px) {
      .card-main {
        grid-template-columns: 1fr;
        gap: 16px;
      }
      .weather-visual {
        width: 100px;
        height: 100px;
        margin: 0 auto;
      }
      .temp-max {
        font-size: 56px;
      }
    }

    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
      }
    }
  </style>
</head>
<body>
  <article class="weather-card" id="card">
    <div class="loading-state" id="loading">
      <div class="spinner" aria-hidden="true"></div>
      <div class="loading-text">Loading weather data…</div>
    </div>
  </article>

  <script>
    // Weather code to condition mapping
    const WEATHER_CODES = {
      0: { label: 'Clear sky', icon: 'sun', gradient: ['#f7d745', '#f5a623'] },
      1: { label: 'Mainly clear', icon: 'sun-cloud', gradient: ['#f7d745', '#a8c0d0'] },
      2: { label: 'Partly cloudy', icon: 'sun-cloud', gradient: ['#e8c85e', '#9db4c8'] },
      3: { label: 'Overcast', icon: 'cloud', gradient: ['#8b9ba8', '#6d7d8a'] },
      45: { label: 'Fog', icon: 'cloud-fog', gradient: ['#9aa5b1', '#7a8590'] },
      48: { label: 'Depositing rime fog', icon: 'cloud-fog', gradient: ['#8b9ba8', '#6d7d8a'] },
      51: { label: 'Light drizzle', icon: 'cloud-drizzle', gradient: ['#6b8cae', '#4a6b8a'] },
      53: { label: 'Moderate drizzle', icon: 'cloud-drizzle', gradient: ['#5a7bb8', '#3d5a80'] },
      55: { label: 'Dense drizzle', icon: 'cloud-drizzle', gradient: ['#4a6fa5', '#2d4f70'] },
      56: { label: 'Light freezing drizzle', icon: 'cloud-drizzle', gradient: ['#7db9de', '#5aa3cc'] },
      57: { label: 'Dense freezing drizzle', icon: 'cloud-drizzle', gradient: ['#6ab0d8', '#489ac4'] },
      61: { label: 'Slight rain', icon: 'cloud-rain', gradient: ['#5a8fd8', '#3d6ab8'] },
      63: { label: 'Moderate rain', icon: 'cloud-rain', gradient: ['#4a7fc8', '#2d5aa0'] },
      65: { label: 'Heavy rain', icon: 'cloud-rain', gradient: ['#3a6fb8', '#1d4a90'] },
      66: { label: 'Light freezing rain', icon: 'cloud-rain', gradient: ['#6ab8e8', '#48a0d0'] },
      67: { label: 'Heavy freezing rain', icon: 'cloud-rain', gradient: ['#5aa8e0', '#3890c8'] },
      71: { label: 'Slight snow fall', icon: 'cloud-snow', gradient: ['#c8d8e8', '#a8c0d8'] },
      73: { label: 'Moderate snow fall', icon: 'cloud-snow', gradient: ['#b8cce0', '#98b0d0'] },
      75: { label: 'Heavy snow fall', icon: 'cloud-snow', gradient: ['#a8c0d8', '#88a8c0'] },
      77: { label: 'Snow grains', icon: 'cloud-snow', gradient: ['#b8cce0', '#98b0d0'] },
      80: { label: 'Slight rain showers', icon: 'cloud-rain', gradient: ['#5a8fd8', '#3d6ab8'] },
      81: { label: 'Moderate rain showers', icon: 'cloud-rain', gradient: ['#4a7fc8', '#2d5aa0'] },
      82: { label: 'Violent rain showers', icon: 'cloud-rain', gradient: ['#3a6fb8', '#1d4a90'] },
      85: { label: 'Slight snow showers', icon: 'cloud-snow', gradient: ['#b8cce0', '#98b0d0'] },
      86: { label: 'Heavy snow showers', icon: 'cloud-snow', gradient: ['#a8c0d8', '#88a8c0'] },
      95: { label: 'Thunderstorm', icon: 'cloud-lightning', gradient: ['#d4a84a', '#c4983a'] },
      96: { label: 'Thunderstorm with slight hail', icon: 'cloud-lightning', gradient: ['#d4984a', '#c4883a'] },
      99: { label: 'Thunderstorm with heavy hail', icon: 'cloud-lightning', gradient: ['#d4884a', '#c4783a'] }
    };

    // SVG Icons
    const ICONS = {
      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>`,
      calendar: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>`,
      sun: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>`,
      'sun-cloud': `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v2"/><path d="M12 20v2"/><path d="M4.93 4.93l1.41 1.41"/><path d="M17.66 17.66l1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="M6.34 17.66l-1.41 1.41"/><path d="M19.07 4.93l-1.41 1.41"/><path d="M16 13a4 4 0 0 0-8 0"/><path d="M16 13a6 6 0 0 1-12 0h8a2 2 0 0 1 0 4"/></svg>`,
      cloud: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/></svg>`,
      'cloud-fog': `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/><line x1="3" y1="14" x2="21" y2="14"/><line x1="3" y1="18" x2="21" y2="18"/><line x1="3" y1="22" x2="21" y2="22"/></svg>`,
      'cloud-drizzle': `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/><line x1="8" y1="19" x2="8" y2="21"/><line x1="8" y1="22" x2="8" y2="24"/><line x1="12" y1="19" x2="12" y2="21"/><line x1="12" y1="22" x2="12" y2="24"/><line x1="16" y1="19" x2="16" y2="21"/><line x1="16" y1="22" x2="16" y2="24"/></svg>`,
      'cloud-rain': `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/><line x1="8" y1="19" x2="8" y2="22"/><line x1="12" y1="19" x2="12" y2="22"/><line x1="16" y1="19" x2="16" y2="22"/></svg>`,
      'cloud-snow': `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/><line x1="8" y1="19" x2="8" y2="21"/><line x1="8" y1="22" x2="8" y2="24"/><line x1="12" y1="19" x2="12" y2="21"/><line x1="12" y1="22" x2="12" y2="24"/><line x1="16" y1="19" x2="16" y2="21"/><line x1="16" y1="22" x2="16" y2="24"/></svg>`,
      'cloud-lightning': `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"/><polyline points="13 11 9 17 11 17 7 23"/></svg>`
    };

    // Parse URL parameters
    function getParams() {
      const url = new URL(window.location.href);
      return {
        lat: parseFloat(url.searchParams.get('lat')) || 52.52,
        lon: parseFloat(url.searchParams.get('lon')) || 13.405,
        name: url.searchParams.get('name') || 'Berlin',
        date: url.searchParams.get('date') || new Date().toISOString().split('T')[0]
      };
    }

    // Format date for display
    function formatDate(dateStr) {
      const date = new Date(dateStr + 'T00:00:00Z');
      return date.toLocaleDateString('en-US', {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        timeZone: 'UTC'
      });
    }

    // Determine API endpoint
    function getApiEndpoint(dateStr) {
      const today = new Date().toISOString().split('T')[0];
      return dateStr <= today ? '/api/om/archive' : '/api/om/forecast';
    }

    // Fetch weather data
    async function fetchWeather(params) {
      const endpoint = getApiEndpoint(params.date);
      const url = new URL(endpoint, window.location.origin);
      url.searchParams.set('latitude', params.lat.toString());
      url.searchParams.set('longitude', params.lon.toString());
      url.searchParams.set('daily', 'temperature_2m_max,temperature_2m_min,weather_code');
      url.searchParams.set('hourly', 'temperature_2m');
      url.searchParams.set('timezone', 'UTC');
      url.searchParams.set('temperature_unit', 'celsius');
      url.searchParams.set('start_date', params.date);
      url.searchParams.set('end_date', params.date);

      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`API error: ${response.status}`);
      }
      return response.json();
    }

    // Render weather card
    function renderCard(data, params) {
      const daily = data.daily;
      const hourly = data.hourly;
      
      if (!daily || !daily.temperature_2m_max || !daily.temperature_2m_max.length) {
        throw new Error('No weather data available for this date');
      }

      const maxTemp = daily.temperature_2m_max[0];
      const minTemp = daily.temperature_2m_min[0];
      const weatherCode = daily.weather_code[0];
      const condition = WEATHER_CODES[weatherCode] || { label: 'Unknown', icon: 'cloud', gradient: ['#8b9ba8', '#6d7d8a'] };
      
      // Filter hourly data for the requested date
      const hourlyTemps = [];
      const hourlyTimes = [];
      if (hourly && hourly.time && hourly.temperature_2m) {
        hourly.time.forEach((time, i) => {
          if (time.startsWith(params.date)) {
            hourlyTimes.push(time);
            hourlyTemps.push(hourly.temperature_2m[i]);
          }
        });
      }

      const card = document.getElementById('card');
      card.innerHTML = `
        <header class="card-header">
          <div class="location-row">
            <span class="location-icon" aria-hidden="true">${ICONS.location}</span>
            <h1 class="location-name">${escapeHtml(params.name)}</h1>
          </div>
          <div class="date-row">
            <span class="date-icon" aria-hidden="true">${ICONS.calendar}</span>
            <time datetime="${params.date}">${formatDate(params.date)}</time>
          </div>
        </header>
        <main class="card-main">
          <div class="current-weather">
            <div class="condition-badge">
              <span class="condition-icon" aria-hidden="true">${ICONS[condition.icon]}</span>
              <span>${condition.label}</span>
            </div>
            <div class="temp-display">
              <span class="temp-max">${Math.round(maxTemp)}</span>
              <span class="temp-unit">°C</span>
            </div>
            <div class="temp-min-row">
              <div class="temp-min-item">
                <span class="temp-min-label">Min</span>
                <span class="temp-min-value">${Math.round(minTemp)}°C</span>
              </div>
            </div>
          </div>
          <div class="weather-visual" aria-hidden="true">
            ${ICONS[condition.icon]}
          </div>
        </main>
        <section class="hourly-section" aria-labelledby="hourly-title">
          <div class="hourly-header">
            <h2 id="hourly-title" class="hourly-title">Hourly Temperatures</h2>
          </div>
          <div class="hourly-chart" id="hourly-chart" role="img" aria-label="Hourly temperature chart for ${formatDate(params.date)}">
            ${hourlyTemps.length ? renderChart(hourlyTemps, hourlyTimes, condition.gradient) : '<p style="color:var(--text-muted);text-align:center;padding:20px;">No hourly data available</p>'}
          </div>
        </section>
      `;

      // Add gradient definition for chart
      if (hourlyTemps.length) {
        const svg = card.querySelector('svg');
        if (svg) {
          const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
          defs.innerHTML = `
            <linearGradient id="temp-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
              <stop offset="0%" stop-color="${condition.gradient[0]}" stop-opacity="0.3"/>
              <stop offset="100%" stop-color="${condition.gradient[1]}" stop-opacity="0.02"/>
            </linearGradient>
          `;
          svg.insertBefore(defs, svg.firstChild);
        }
        initChartInteraction(hourlyTemps, hourlyTimes);
      }

      // Apply weather-themed accent colors
      applyWeatherTheme(condition.gradient);
    }

    function renderChart(temps, times, gradient) {
      const minTemp = Math.min(...temps);
      const maxTemp = Math.max(...temps);
      const range = maxTemp - minTemp || 1;
      const padding = 20;
      const chartWidth = 464; // 520 - 28*2 - padding*2
      const chartHeight = 140;

      const points = temps.map((temp, i) => {
        const x = padding + (i / (temps.length - 1 || 1)) * (chartWidth - 2 * padding);
        const y = padding + (1 - (temp - minTemp) / range) * (chartHeight - 2 * padding);
        return { x, y, temp, time: times[i] };
      });

      // Generate smooth curve path
      let pathData = '';
      points.forEach((p, i) => {
        if (i === 0) {
          pathData += `M ${p.x} ${p.y}`;
        } else {
          const prev = points[i - 1];
          const cpX = (prev.x + p.x) / 2;
          pathData += ` C ${cpX} ${prev.y} ${cpX} ${p.y} ${p.x} ${p.y}`;
        }
      });

      // Area path
      let areaPath = pathData;
      const lastPoint = points[points.length - 1];
      const firstPoint = points[0];
      areaPath += ` L ${lastPoint.x} ${chartHeight - padding} L ${firstPoint.x} ${chartHeight - padding} Z`;

      // Format time labels (every 3 hours)
      const labels = times.filter((_, i) => i % 3 === 0).map(t => {
        const hour = parseInt(t.split('T')[1]);
        return `${hour.toString().padStart(2, '0')}:00`;
      });

      return `
        <svg class="chart-canvas" viewBox="0 0 ${chartWidth + 2 * padding} ${chartHeight}" preserveAspectRatio="none">
          <defs>
            <linearGradient id="temp-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
              <stop offset="0%" stop-color="${gradient[0]}" stop-opacity="0.3"/>
              <stop offset="100%" stop-color="${gradient[1]}" stop-opacity="0.02"/>
            </linearGradient>
          </defs>
          <path class="chart-area" d="${areaPath}"/>
          <path class="chart-line" d="${pathData}"/>
          ${points.map((p, i) => `
            <circle class="chart-point" cx="${p.x}" cy="${p.y}" r="4" data-index="${i}" data-temp="${p.temp}" data-time="${p.time}"/>
          `).join('')}
        </svg>
        <div class="chart-labels">
          ${labels.map(l => `<span>${l}</span>`).join('')}
        </div>
        <div class="hourly-tooltip" id="hourly-tooltip" role="tooltip"></div>
      `;
    }

    function initChartInteraction(temps, times) {
      const chart = document.getElementById('hourly-chart');
      const tooltip = document.getElementById('hourly-tooltip');
      const points = chart.querySelectorAll('.chart-point');
      let activeIndex = -1;

      function showTooltip(index, clientX, clientY) {
        const point = points[index];
        const temp = point.dataset.temp;
        const time = point.dataset.time;
        const hour = time.split('T')[1].substring(0, 5);
        
        tooltip.innerHTML = `
          <div class="tooltip-time">${hour}</div>
          <div class="tooltip-temp">${Math.round(parseFloat(temp))}°C</div>
        `;
        
        const rect = chart.getBoundingClientRect();
        tooltip.style.left = `${clientX - rect.left}px`;
        tooltip.style.top = `${clientY - rect.top - 10}px`;
        tooltip.classList.add('visible');
        
        points.forEach((p, i) => p.classList.toggle('active', i === index));
      }

      function hideTooltip() {
        tooltip.classList.remove('visible');
        points.forEach(p => p.classList.remove('active'));
      }

      chart.addEventListener('mousemove', (e) => {
        const rect = chart.getBoundingClientRect();
        const x = e.clientX - rect.left;
        
        // Find closest point
        let closest = 0;
        let minDist = Infinity;
        points.forEach((p, i) => {
          const cx = parseFloat(p.getAttribute('cx'));
          const dist = Math.abs(cx - x);
          if (dist < minDist) {
            minDist = dist;
            closest = i;
          }
        });
        
        if (minDist < 30) {
          showTooltip(closest, e.clientX, e.clientY);
        } else {
          hideTooltip();
        }
      });

      chart.addEventListener('mouseleave', hideTooltip);
      
      // Touch support
      chart.addEventListener('touchstart', (e) => {
        e.preventDefault();
        const touch = e.touches[0];
        const rect = chart.getBoundingClientRect();
        const x = touch.clientX - rect.left;
        
        let closest = 0;
        let minDist = Infinity;
        points.forEach((p, i) => {
          const cx = parseFloat(p.getAttribute('cx'));
          const dist = Math.abs(cx - x);
          if (dist < minDist) {
            minDist = dist;
            closest = i;
          }
        });
        
        if (minDist < 40) {
          showTooltip(closest, touch.clientX, touch.clientY);
        }
      }, { passive: false });

      document.addEventListener('touchstart', (e) => {
        if (!chart.contains(e.target)) hideTooltip();
      });
    }

    function applyWeatherTheme(gradient) {
      const root = document.documentElement;
      root.style.setProperty('--accent', gradient[0]);
      root.style.setProperty('--accent-warm', gradient[1]);
    }

    function escapeHtml(text) {
      const div = document.createElement('div');
      div.textContent = text;
      return div.innerHTML;
    }

    function showError(message) {
      const card = document.getElementById('card');
      card.innerHTML = `
        <div class="error-state">
          <div class="error-icon" aria-hidden="true">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
              <circle cx="12" cy="12" r="10"/>
              <line x1="12" y1="8" x2="12" y2="12"/>
              <line x1="12" y1="16" x2="12.01" y2="16"/>
            </svg>
          </div>
          <h2 class="error-title">Unable to Load Weather</h2>
          <p class="error-message">${escapeHtml(message)}</p>
        </div>
      `;
    }

    // Initialize
    async function init() {
      const params = getParams();
      
      try {
        const data = await fetchWeather(params);
        renderCard(data, params);
      } catch (error) {
        console.error('Weather fetch error:', error);
        showError(error.message || 'Failed to fetch weather data. Please try again later.');
      }
    }

    init();
  </script>
</body>
</html>

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

bytes 总计28,044html / css / js449 / 9,362 / 18,233
dom 节点12dom 深度5
css 规则64含 js
brightness245.4contrast12.3
colorfulness6.2留白99.1%

该 slot 的 telemetry

prompt tokens334completion tokens9,830
total tokens10,164wall162.8 s
costrequest id
config.json574 B
{
  "N": 2,
  "auth": {
    "credential_ref": "WCB_ZEN_KEY",
    "method": "api-key"
  },
  "billing": "plan",
  "config_id": "nemotron-3-ultra-free--zen-free--dev",
  "effort": null,
  "family": "nemotron",
  "m": {
    "min": 2,
    "q": 2
  },
  "model_id": "nemotron-3-ultra-free",
  "protocol": "api",
  "served_model": "nemotron-3-ultra-free",
  "telemetry": {
    "completion_tokens": 34941,
    "cost_usd": null,
    "prompt_tokens": 1320,
    "total_tokens": 36261,
    "wall_ms": 403886
  },
  "transport": "opencode-zen-free-tier",
  "vendor_sanction_ref": null
}
send-log2.5 KB
{
  "N": 2,
  "batch_id": "2026-07-18--dev-zen-free",
  "config_id": "nemotron-3-ultra-free--zen-free--dev",
  "positions": [
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-18T01:08:11.062116+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T01:05:28.210565+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-18T01:10:17.116918+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T01:08:14.224245+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-18T01:10:53.210402+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T01:10:20.653332+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-18T01:12:22.454672+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T01:10:56.862351+00:00"
        }
      ],
      "block_index": 1,
      "model_reaching_attempt_index": 0,
      "slot_index": 1,
      "terminal_state": "valid",
      "variant": "P-q"
    }
  ],
  "retry_policy": {
    "max_unreachable_retries": 5,
    "rate_limit_backoff": {
      "max_attempts": 4,
      "max_total_ms": 60000
    }
  }
}