Files
insights/products/skills/frontend/LLMSkillScene.stories.tsx
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
Every first-party Python caller and test now names the mount the API actually
answers on. Mechanical: URL literals only, and only where the /api/ was ours --
a preceding word character means it was a module path like insights/api/ or a
third-party host like slack.com/api/, and those are untouched.

Left saying /api/, each for a reason rather than for safety:
- products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints
- services/llm-gateway deploys separately and is under a code freeze, so moving
  its two calls would break it during the skew
- the mcp_store and Slack/Teams redirect_uris are registered with third parties
  and must byte-match what was registered
- the Wizard and Raycast client-metadata paths are OAuth client_ids
- /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the
  ingress routes to the Rust fleets, held by released SDK versions

Test URLconfs moved with the requests that call them, and the Datastore
user-selection fixtures now carry the leading slash that request.path has.
2026-08-08 18:38:44 -07:00

163 lines
4.4 KiB
TypeScript

import { Meta, StoryObj } from '@storybook/react'
import { App } from 'scenes/App'
import { urls } from 'scenes/urls'
import { mswDecorator } from '~/mocks/browser'
import { toPaginatedResponse } from '~/mocks/handlers'
import type {
LLMSkillApi,
LLMSkillListApi,
LLMSkillResolveResponseApi,
LLMSkillVersionSummaryApi,
UserBasicApi,
} from 'products/skills/frontend/generated/api.schemas'
const MOCK_AUTHOR: UserBasicApi = {
id: 178,
uuid: '01853eba-3d18-0000-9d9b-000000000001',
distinct_id: 'mock-user-178-distinct-id',
first_name: 'John',
email: '[email protected]',
mascot_config: null,
}
const SKILL_NAME = 'pdf-extractor'
const SKILL_BODY = `# PDF extractor
## When to use
Use this skill when you need to pull text out of a PDF, fill a form, or merge several PDFs.
## Steps
1. Detect the PDF type (text-based vs scanned).
2. Extract text using the matching strategy.
3. Optionally summarize the result.
## Notes
- Scanned PDFs require OCR.
- Encrypted PDFs need the password supplied as input.
`
function makeVersion(version: number, isLatest: boolean): LLMSkillVersionSummaryApi {
return {
id: `skill-version-${version}`,
version,
is_latest: isLatest,
created_at: `2025-01-${String(10 + version).padStart(2, '0')}T10:00:00Z`,
created_by: MOCK_AUTHOR,
}
}
const VERSIONS: LLMSkillVersionSummaryApi[] = [
makeVersion(4, true),
makeVersion(3, false),
makeVersion(2, false),
makeVersion(1, false),
]
const SKILL: LLMSkillApi = {
id: 'skill-version-4',
name: SKILL_NAME,
description: 'Extract text from PDFs, fill forms, and merge files. Use when handling PDFs.',
body: SKILL_BODY,
body_total_length: SKILL_BODY.length,
body_next_offset: null,
license: 'Apache-2.0',
compatibility: 'Requires poppler-utils on the host',
allowed_tools: ['read', 'shell'],
metadata: {},
category: '',
files: [
{ path: 'scripts/extract.sh', content_type: 'text/x-shellscript' },
{ path: 'references/pdf-spec.md', content_type: 'text/markdown' },
],
outline: [
{ level: 1, text: 'PDF extractor' },
{ level: 2, text: 'When to use' },
{ level: 2, text: 'Steps' },
{ level: 2, text: 'Notes' },
],
version: 4,
created_by: MOCK_AUTHOR,
owners: [MOCK_AUTHOR],
created_at: '2025-01-14T10:00:00Z',
updated_at: '2025-01-14T10:00:00Z',
deleted: false,
is_latest: true,
latest_version: 4,
version_count: 4,
first_version_created_at: '2025-01-11T10:00:00Z',
}
const RESOLVE_RESPONSE: LLMSkillResolveResponseApi = {
skill: SKILL,
versions: VERSIONS,
has_more: false,
}
const SKILL_LIST_ENTRY: LLMSkillListApi = {
id: SKILL.id,
name: SKILL.name,
description: SKILL.description,
license: SKILL.license,
compatibility: SKILL.compatibility,
allowed_tools: SKILL.allowed_tools,
metadata: {},
category: SKILL.category,
outline: SKILL.outline,
version: SKILL.version,
created_by: SKILL.created_by,
owners: SKILL.owners,
created_at: SKILL.created_at,
updated_at: SKILL.updated_at,
deleted: SKILL.deleted,
is_latest: SKILL.is_latest,
latest_version: SKILL.latest_version,
version_count: SKILL.version_count,
first_version_created_at: SKILL.first_version_created_at,
}
const meta: Meta = {
component: App,
title: 'Scenes-App/AI observability/Skills',
parameters: {
layout: 'fullscreen',
viewMode: 'story',
mockDate: '2025-01-28',
pageUrl: urls.skill(SKILL_NAME),
testOptions: {
waitForLoadersToDisappear: true,
},
},
decorators: [
mswDecorator({
get: {
'/v1/projects/:team_id/llm_skills/': toPaginatedResponse([SKILL_LIST_ENTRY]),
'/v1/projects/:team_id/llm_skills/resolve/name/:name/': RESOLVE_RESPONSE,
},
}),
],
}
export default meta
type Story = StoryObj<{}>
export const SkillDetailStackedBelow2xlBreakpoint: Story = {
parameters: {
testOptions: {
waitForLoadersToDisappear: true,
viewportWidths: ['medium', 'wide'],
},
},
}
export const SkillDetailSideBySideAbove2xlBreakpoint: Story = {
parameters: {
testOptions: {
waitForLoadersToDisappear: true,
viewportWidths: ['superwide'],
},
},
}