SPB Git

spb/ai-atlas

Public
4.0 KB · 87 lines tsx
1import { ExternalLink } from 'lucide-react';2import { EntityLink } from '@/components/ui/entity';3import { DataTable, EmptyRow, Td, Th } from '@/components/ui/data-table';4import { Note } from '@/components/ui/section';5import { fmtDateTime, fmtAgo, fmtUsdPerM, num } from '@/lib/format';6import type { ChangeEvent } from '@/lib/types';78type PriceValue = { input_per_mtok?: unknown; output_per_mtok?: unknown } | null | undefined;910function pv(v: unknown): { input: number | null; output: number | null } {11  const o = (v && typeof v === 'object' ? v : {}) as PriceValue;12  return { input: num(o?.input_per_mtok), output: num(o?.output_per_mtok) };13}1415function host(url: string): string {16  try {17    return new URL(url).hostname.replace(/^www\./, '');18  } catch {19    return 'source';20  }21}2223/** Old → new price cell: strike-through old, bold new, direction sign. Amber = money. */24function Move({ from, to }: { from: number | null; to: number | null }) {25  if (from === null && to === null) return <span className="text-ink-3">—</span>;26  const dir = from !== null && to !== null ? (to < from ? '↓' : to > from ? '↑' : '=') : '';27  return (28    <span className="tnum inline-flex items-center gap-1.5">29      <span className="text-ink-3 line-through decoration-ink-3/60">{fmtUsdPerM(from)}</span>30      <span aria-hidden className="text-ink-3">→</span>31      <span className={dir === '=' ? 'text-ink-2' : 'font-semibold text-accent-2'}>{fmtUsdPerM(to)}</span>32      {dir && dir !== '=' && <span className={dir === '↓' ? 'text-positive' : 'text-danger'} aria-label={dir === '↓' ? 'decrease' : 'increase'}>{dir}</span>}33    </span>34  );35}3637/** PRICE_CHANGED events as a dense table: model · provider · input move · output move · when · source. */38export function PriceMovers({ movers }: { movers: ChangeEvent[] }) {39  const rows = movers.filter((e) => e.event_type === 'PRICE_CHANGED' || e.category === 'price');40  return (41    <>42      <DataTable caption="Recent price changes">43        <thead>44          <tr>45            <Th>Model</Th>46            <Th>Provider</Th>47            <Th>Input / 1M</Th>48            <Th>Output / 1M</Th>49            <Th>Observed</Th>50            <Th>Source</Th>51          </tr>52        </thead>53        <tbody>54          {rows.length === 0 && <EmptyRow cols={6}>No price change recorded in this window. The first observation of a price is not a change.</EmptyRow>}55          {rows.map((e) => {56            const o = pv(e.old_value);57            const n = pv(e.new_value);58            const provider = typeof e.meta?.provider === 'string' ? e.meta.provider : null;59            return (60              <tr key={e.id}>61                <Td primary>62                  {e.entity ? <EntityLink e={e.entity} /> : <span className="text-ink-3">—</span>}63                  {e.entity?.organization && <span className="ml-2 text-xs text-ink-3">{e.entity.organization.name}</span>}64                </Td>65                <Td label="Provider" className="text-ink-2">{provider ?? <span className="text-ink-3">—</span>}</Td>66                <Td label="Input / 1M"><Move from={o.input} to={n.input} /></Td>67                <Td label="Output / 1M"><Move from={o.output} to={n.output} /></Td>68                <Td label="Observed" className="text-ink-2" title={fmtDateTime(e.observed_at)}>{fmtAgo(e.observed_at)}</Td>69                <Td label="Source">70                  {e.source_url ? (71                    <a href={e.source_url} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-xs text-ink-2 hover:text-accent">72                      {host(e.source_url)} <ExternalLink className="size-3" aria-hidden />73                    </a>74                  ) : (75                    <span className="text-ink-3">—</span>76                  )}77                </Td>78              </tr>79            );80          })}81        </tbody>82      </DataTable>83      {rows.length > 0 && <Note className="mt-3">Movers are PRICE_CHANGED events emitted when a provider's published price for a model differs from the previous observation. Green arrow = cheaper, red = dearer.</Note>}84    </>85  );86}87
Files · 9f1db171c786cf4a3b99436329f29b145c02aeec
Menu

Theme