Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions web/src/components/common/__tests__/model-pill.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Behavior test for ModelPill: known models render the brand glyph inside the
// capsule; unknown models still render the name (never an empty pill); blank
// input renders nothing.

import '@testing-library/jest-dom/vitest'
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'

import { ModelPill } from '../model-pill'

describe('ModelPill', () => {
afterEach(cleanup)

it('renders the brand glyph for a known model', () => {
const { container } = render(<ModelPill model='gpt-4o-mini' />)
expect(screen.getByText('gpt-4o-mini')).toBeInTheDocument()
expect(container.querySelector('img')).not.toBeNull()
})

it('renders the plain name for an unknown model', () => {
render(<ModelPill model='my-custom-model-v9' />)
expect(screen.getByText('my-custom-model-v9')).toBeInTheDocument()
})

it('renders nothing for a blank model', () => {
const { container } = render(<ModelPill model=' ' />)
expect(container).toBeEmptyDOMElement()
})
})
32 changes: 32 additions & 0 deletions web/src/components/common/model-pill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// metapi-go/components/common — model pill: a compact capsule that pairs a
// model name with its brand glyph (BrandIcon) wherever model identifiers
// appear in dense tables (proxy logs first). Token-driven surface only;
// unknown models degrade to the plain truncated name (no empty pill).

import { InlineBrandIcon } from '@/assets/brand-icons/BrandIcon'
import { cn } from '@/lib/utils'

export type ModelPillProps = {
/** Resolved model name shown in the pill (usually the actual upstream one). */
model: string
/** Full label for the tooltip when the cell truncates (e.g. requested→actual). */
title?: string
className?: string
}

export function ModelPill({ model, title, className }: ModelPillProps) {
const name = model.trim()
if (!name) return null
return (
<span
className={cn(
'inline-flex max-w-full items-center gap-1.5 rounded-full border bg-muted/40 py-0.5 pr-2.5 pl-1.5 text-xs font-medium',
className
)}
title={title ?? name}
>
<InlineBrandIcon model={name} size={14} />
<span className='min-w-0 truncate'>{name}</span>
</span>
)
}
11 changes: 5 additions & 6 deletions web/src/features/proxy-logs/components/proxy-logs-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import { useTranslation } from 'react-i18next'

import { HttpStatusBadge } from '@/components/common/http-status-badge'
import { ModelPill } from '@/components/common/model-pill'
import { DataTableColumnHeader } from '@/components/data-table'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
Expand Down Expand Up @@ -136,15 +137,13 @@ export function useProxyLogsColumns(
/>
),
cell: ({ row }) => (
<span
className='block max-w-[16rem] truncate text-sm'
title={row.original.modelActual || row.original.modelRequested}
>
{formatModelCell(
<ModelPill
model={formatModelCell(
row.original.modelRequested,
row.original.modelActual
)}
</span>
title={row.original.modelActual || row.original.modelRequested}
/>
),
},
{
Expand Down
Loading