All files / web/src/lib/session-song admin-validation-summary.ts

100% Statements 86/86
85% Branches 34/40
100% Functions 3/3
100% Lines 86/86

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 871x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 2x 2x 2x 2x 1x 1x 2x 2x 2x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 1x 1x 1x 2x 2x 2x 2x 2x 2x 1x 11x 11x 10x 1x 11x  
export interface AdminSongValidationIssue {
  code: string
  message: string
  evidenceType: string | null
}
 
export interface AdminSongValidationSummary {
  validationMode: string | null
  validationOutcome: string | null
  validationIssueCount: number
  validationIssues: AdminSongValidationIssue[]
  repairAttempts: number | null
  fallbackUsed: boolean
}
 
export interface AdminSongSectionSummary {
  name: string
  durationMs: number
  lineCount: number
}
 
export interface AdminSongPlanSummary {
  title: string | null
  styles: string[]
  totalDurationMs: number
  sectionSummary: AdminSongSectionSummary[]
}
 
export function getAdminSongPlanSummary(llmOutput: unknown): AdminSongPlanSummary {
  const output = asRecord(llmOutput)
  const plan = asRecord(output?.plan)
  const rawSections = Array.isArray(plan?.sections) ? plan.sections : []
  const sections = rawSections.filter(
    (section): section is Record<string, unknown> => asRecord(section) !== null
  )
 
  const sectionSummary = sections.map((section) => {
    const lines = section.lines
    const durationMs = typeof section.duration_ms === 'number' ? section.duration_ms : 0
 
    return {
      name: typeof section.section_name === 'string' ? section.section_name : 'Untitled section',
      durationMs,
      lineCount: Array.isArray(lines) ? lines.length : 0,
    }
  })
 
  return {
    title: typeof output?.title === 'string' ? output.title : null,
    styles: Array.isArray(plan?.positive_global_styles)
      ? plan.positive_global_styles.filter((style): style is string => typeof style === 'string')
      : [],
    totalDurationMs: sectionSummary.reduce((sum, section) => sum + section.durationMs, 0),
    sectionSummary,
  }
}
 
export function getSongPlanValidationSummary(llmOutput: unknown): AdminSongValidationSummary {
  const output = asRecord(llmOutput)
  const validation = asRecord(output?.validation)
  const issues = Array.isArray(validation?.issues)
    ? validation.issues.filter(
        (issue): issue is Record<string, unknown> => asRecord(issue) !== null
      )
    : []
 
  return {
    validationMode: typeof validation?.mode === 'string' ? validation.mode : null,
    validationOutcome: typeof validation?.outcome === 'string' ? validation.outcome : null,
    validationIssueCount: issues.length,
    validationIssues: issues.map((issue) => ({
      code: typeof issue.code === 'string' ? issue.code : 'unknown',
      message: typeof issue.message === 'string' ? issue.message : '',
      evidenceType: typeof issue.evidenceType === 'string' ? issue.evidenceType : null,
    })),
    repairAttempts:
      typeof validation?.repairAttempts === 'number' ? validation.repairAttempts : null,
    fallbackUsed: typeof validation?.fallbackUsed === 'boolean' ? validation.fallbackUsed : false,
  }
}
 
function asRecord(value: unknown): Record<string, unknown> | null {
  return value && typeof value === 'object' && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : null
}