UNPKG

@kaifronsdal/transcript-viewer

Version:

A web-based viewer for AI conversation transcripts with rollback support

410 lines (349 loc) 11.7 kB
// Base types that match the Python Pydantic models import type { Operation } from 'fast-json-patch'; // Enums for better type safety and intellisense export const MessageType = { SYSTEM: 'system', ASSISTANT: 'assistant', USER: 'user', TOOL: 'tool', API_FAILURE: 'api_failure', INFO: 'info' } as const; export const ErrorCategory = { RATE_LIMIT: 'rate_limit', TIMEOUT: 'timeout', QUOTA_BILLING: 'quota_billing', AUTHENTICATION: 'authentication', CONTENT_FILTER: 'content_filter', EMPTY_CONTENT: 'empty_content', GENERIC_FAILURE: 'generic_failure', MODEL_NOT_FOUND: 'model_not_found', INVALID_REQUEST: 'invalid_request' } as const; export const EditOperation = { ADD: 'add', ROLLBACK: 'rollback', RESET: 'reset', JSON_PATCH: 'json_patch' } as const; export const EventType = { TRANSCRIPT_EVENT: 'transcript_event', TOOL_CREATION_EVENT: 'tool_creation_event', INFO_EVENT: 'info_event', DECISION_EVENT: 'decision_event' } as const; export type MessageTypeValue = typeof MessageType[keyof typeof MessageType]; export type ErrorCategoryValue = typeof ErrorCategory[keyof typeof ErrorCategory]; export type EditOperationValue = typeof EditOperation[keyof typeof EditOperation]; export type EventTypeValue = typeof EventType[keyof typeof EventType]; // Schema-accurate ToolCall shape (with backward-compat optional fields) export interface ToolCallContent { title?: string | null; format: 'text' | 'markdown'; content: string; } export type ToolCallErrorType = | 'parsing' | 'timeout' | 'unicode_decode' | 'permission' | 'file_not_found' | 'is_a_directory' | 'limit' | 'approval' | 'unknown' | 'output_limit'; export interface ToolCallError { type: ToolCallErrorType; message: string; } export interface ToolCall { id: string; function: string; arguments: Record<string, any>; internal?: any; parse_error?: ToolCallError | null; view?: ToolCallContent | null; type?: string | null; } // Base message interface with common fields interface BaseMessage { content: string | Array<string | Record<string, any>>; name?: string; id?: string; metadata?: Record<string, any>; // UI-specific properties isShared?: boolean; viewSource?: string | string[]; eventId?: string; // ID of the event that created this message } // Discriminated union for different message types (normalized) export interface SystemMessage extends BaseMessage { type: typeof MessageType.SYSTEM; } export interface UserMessage extends BaseMessage { type: typeof MessageType.USER; } export interface AssistantMessage extends BaseMessage { type: typeof MessageType.ASSISTANT; tool_calls?: ToolCall[]; reasoning?: string; } export interface ToolMessage extends BaseMessage { type: typeof MessageType.TOOL; tool_call_id?: string | null; function?: string | null; error?: ToolCallError | null; } export interface APIFailureMessage { type: typeof MessageType.API_FAILURE; id: string; error_message: string; error_category: ErrorCategoryValue; recoverable?: boolean; // UI-specific properties isShared?: boolean; viewSource?: string | string[]; eventId?: string; } export interface InfoMessage { type: 'info'; id?: string; info: string | Record<string, any>; timestamp?: string; // UI-specific properties isShared?: boolean; viewSource?: string | string[]; eventId?: string; } export type Message = SystemMessage | UserMessage | AssistantMessage | ToolMessage | APIFailureMessage | InfoMessage; // Raw role-based messages from schema (v3) export type RawChatMessage = | { role: 'system'; content: string | Array<string | Record<string, any>>; id?: string | null; source?: 'input' | 'generate' | null; metadata?: Record<string, any> | null; internal?: any | null; } | { role: 'user'; content: string | Array<string | Record<string, any>>; id?: string | null; source?: 'input' | 'generate' | null; metadata?: Record<string, any> | null; internal?: any | null; tool_call_id?: string[] | null; } | { role: 'assistant'; content: string | Array<string | Record<string, any>>; id?: string | null; source?: 'input' | 'generate' | null; metadata?: Record<string, any> | null; internal?: any | null; tool_calls?: ToolCall[] | null; model?: string | null; } | { role: 'tool'; content: string | Array<string | Record<string, any>>; id?: string | null; source?: 'input' | 'generate' | null; metadata?: Record<string, any> | null; internal?: any | null; tool_call_id?: string | null; function?: string | null; error?: ToolCallError | null; }; // Keep the old APIFailure interface for backward compatibility export type APIFailure = APIFailureMessage; // Discriminated union for edit operations (schema-driven) export interface AddMessage { operation: typeof EditOperation.ADD; message: RawChatMessage; } export interface Rollback { operation: typeof EditOperation.ROLLBACK; count: number; to_id?: string; } export interface Reset { operation: typeof EditOperation.RESET; new_messages?: RawChatMessage[]; } export interface JSONPatchEdit { operation: typeof EditOperation.JSON_PATCH; patch: Operation[]; name?: string | null; } export type Edit = AddMessage | Rollback | Reset | JSONPatchEdit; // Base event interface interface BaseEvent { id?: string; metadata?: Record<string, any>; timestamp?: string; } export interface TranscriptEvent extends BaseEvent { type: typeof EventType.TRANSCRIPT_EVENT; view: string | string[]; edit: Edit; } export interface InfoEvent extends BaseEvent { type: typeof EventType.INFO_EVENT; info: string | Record<string, any>; } export interface ToolDefinition { name: string; description: string; parameters: ToolParams; parallel?: boolean; options?: Record<string, any> | null; viewer?: any | null; model_input?: any | null; } // JSON Schema subset for tool params export interface JSONSchemaDef { type?: 'string' | 'integer' | 'number' | 'boolean' | 'array' | 'object' | 'null' | null; format?: string | null; description?: string | null; default?: any; enum?: any[] | null; items?: JSONSchemaDef | null; properties?: Record<string, JSONSchemaDef> | null; additionalProperties?: JSONSchemaDef | boolean | null; anyOf?: JSONSchemaDef[] | null; required?: string[] | null; } export interface ToolParams { type: 'object'; properties: Record<string, JSONSchemaDef>; required?: string[]; additionalProperties?: boolean; } export interface ToolCreationEvent extends BaseEvent { type: typeof EventType.TOOL_CREATION_EVENT; model: string; tool: ToolDefinition; } export interface DecisionEvent extends BaseEvent { type: typeof EventType.DECISION_EVENT; content: any; } export type Events = TranscriptEvent | ToolCreationEvent | InfoEvent | DecisionEvent; export interface JudgeOutput { response: string; summary: string; justification?: string; scores: Record<string, number>; score_descriptions?: Record<string, string>; } export interface TranscriptMetadata { transcript_id: string; auditor_model?: string; target_model?: string; created_at: string; updated_at: string; version?: 'v3.0'; description?: string; short_name?: string; tags?: string[]; judge_output?: JudgeOutput; } export interface Transcript { metadata: TranscriptMetadata; events?: Events[]; messages?: any[]; target_messages?: any[]; } // Split display types for clarity and correctness export interface TranscriptDisplayMeta { id: string; model: string; split: string; concerningScore: number; summary: string; scores: Record<string, number>; scoreDescriptions?: Record<string, string>; judgeSummary: string; justification: string; tags?: string[]; systemPrompt?: string; _filePath?: string; } export interface TranscriptDisplayFull extends TranscriptDisplayMeta { transcript: Transcript; } // Backward-compatible alias used across the app export type TranscriptDisplay = TranscriptDisplayMeta | TranscriptDisplayFull; // Filter and view state types export interface FilterState { filterExpression: string; searchQuery: string; } export interface ViewSettings { viewMode: 'tree' | 'list'; } export interface TranscriptViewSettings { selectedView: string; showApiFailures: boolean; showSharedHistory: boolean; showSystemPrompt: boolean; availableViews: string[]; } // Type guards for better runtime type checking export const isSystemMessage = (message: Message): message is SystemMessage => message.type === MessageType.SYSTEM; export const isUserMessage = (message: Message): message is UserMessage => message.type === MessageType.USER; export const isAssistantMessage = (message: Message): message is AssistantMessage => message.type === MessageType.ASSISTANT; export const isToolMessage = (message: Message): message is ToolMessage => message.type === MessageType.TOOL; export const isAPIFailureMessage = (message: Message): message is APIFailureMessage => message.type === MessageType.API_FAILURE; export const isInfoMessage = (message: Message): message is InfoMessage => message.type === MessageType.INFO; export const isTranscriptEvent = (event: Events): event is TranscriptEvent => event.type === EventType.TRANSCRIPT_EVENT; export const isToolCreationEvent = (event: Events): event is ToolCreationEvent => event.type === EventType.TOOL_CREATION_EVENT; export const isInfoEvent = (event: Events): event is InfoEvent => event.type === EventType.INFO_EVENT; export const isDecisionEvent = (event: Events): event is DecisionEvent => event.type === EventType.DECISION_EVENT; export const isAddOperation = (edit: Edit): edit is AddMessage => edit.operation === EditOperation.ADD; export const isRollbackOperation = (edit: Edit): edit is Rollback => edit.operation === EditOperation.ROLLBACK; export const isResetOperation = (edit: Edit): edit is Reset => edit.operation === EditOperation.RESET; export const isJsonPatchOperation = (edit: Edit): edit is JSONPatchEdit => edit.operation === EditOperation.JSON_PATCH; // Unified TanStack Table-compatible row type following the official nested sub-row pattern // https://tanstack.com/table/v8/docs/guide/data#nested-sub-row-data export interface TableRow { // Required fields for all rows id: string; name: string; path: string; type: 'folder' | 'transcript'; // TanStack Table recursive sub-rows (following official pattern) subRows?: TableRow[]; // Folder-specific fields isEmpty?: boolean; transcriptCount?: number; // Transcript data fields (only present when type === 'transcript') model?: string; split?: string; summary?: string; scores?: Record<string, number>; scoreDescriptions?: Record<string, string>; concerningScore?: number; judgeSummary?: string; justification?: string; tags?: string[]; originalTranscript?: TranscriptDisplay; } // Legacy aliases for backward compatibility during migration export type TreeNode = TableRow; export type FolderNode = TableRow & { type: 'folder' }; export type TranscriptNode = TableRow & { type: 'transcript' }; // Legacy alias for backward compatibility during migration export type FolderNodeWithStats = FolderNode; // Directory and loading types (shared between client and server) export interface DirectoryInfo { path: string; relativePath: string; isEmpty: boolean; hasTranscripts: boolean; transcriptCount: number; subdirectoryCount: number; }