""" A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the 'date-time' format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard. """ scalar DateTime """ A JSON scalar type that can represent any valid JSON value including objects, arrays, strings, numbers, booleans, and null. """ scalar JSON type Query { _empty: String """ Fetches a paginated list of courses, optionally filtered by source id or name. Example: Get the first 10 courses ``` query { courses(first: 10) { edges { node { id title description } } pageInfo { hasNextPage endCursor } } } ``` Example: Filter by source name and subject ``` query { courses(sourceName: "Khan", subject: "math", first: 10) { edges { node { id title subject grade } } } } ``` """ courses(sourceId: ID, sourceName: String, grade: String, educationLevel: EducationLevelType, subject: SubjectType, subSubject: String, first: Int, after: String, last: Int, before: String): CourseConnection! """ Fetches a single course by its unique ID. Example: ``` query { course(id: "course-id-here") { id title description units { id title orderIndex } } } ``` """ course(id: ID): Course courseByUrl(url: String!): Course sources: [Source!]! source(id: ID!): Source """ Compares two courses to identify missing units, lessons, content, and incomplete quizzes. """ compareCourses(originalCourseId: ID!, replicaCourseId: ID!): CourseComparisonReport! """ Fetches a paginated list of lessons, optionally filtered by course or unit. Example: Get lessons for a course ``` query { lessons(courseId: "course-id-here", first: 20) { edges { node { id title unitId } } } } ``` Example: Get lessons for a specific unit ``` query { lessons(unitId: "unit-id-here", first: 20) { edges { node { id title isQuiz } } } } ``` """ lessons(courseId: ID, unitId: ID, first: Int, after: String, last: Int, before: String): LessonConnection! """ Fetches a single lesson by its unique ID. Example: ``` query { lesson(id: "lesson-id-here") { id title description courseId unitId } } ``` """ lesson(id: ID!): Lesson lessonByUrl(url: String!): Lesson """ Fetches a paginated list of content items for a specific lesson, optionally filtered by type. Example: Get all content for a lesson ``` query { contents(lessonId: "lesson-id-here", first: 20) { edges { node { id title type htmlBody youtubeUrl pdfUrl } } } } ``` Example: Get only video content for a lesson ``` query { contents(lessonId: "lesson-id-here", type: video, first: 10) { edges { node { id title youtubeUrl transcript } } } } ``` """ contents(lessonId: ID!, type: ContentType, category: String, first: Int, after: String, last: Int, before: String): ContentConnection! """ Fetches a single content item by its unique ID. Example: ``` query { content(id: "content-id-here") { id title type htmlBody youtubeUrl pdfUrl transcript } } ``` """ content(id: ID!): Content """ Fetches a paginated list of questions for a specific content item. Example: Get all questions for a quiz content ``` query { questions(contentId: "content-id-here", first: 50) { edges { node { id questionHtml answerHtml orderIndex } } totalCount } } ``` """ questions(contentId: ID!, first: Int, after: String, last: Int, before: String): QuestionConnection! """ Fetches a single question by its unique ID. Example: ``` query { question(id: "question-id-here") { id questionHtml answerHtml contentId } } ``` """ question(id: ID!): Question """ Fetches a paginated list of standards, optionally filtered by set or standard ID. Example: Get all NGSS middle school standards ``` query { standards(setId: "NGSS.MS", first: 20) { edges { node { id setId standardId description } } } } ``` """ standards(setId: String, standardId: String, first: Int, after: String, last: Int, before: String): StandardConnection! """ Fetches a single standard by its unique ID. Example: ``` query { standard(id: "standard-id-here") { id setId standardId description } } ``` """ standard(id: ID!): Standard """ Query standardized tests with flexible filtering options. Returns tests with optional QTI coverage statistics via the summary field. Example - Get Texas 5th grade math tests: ```graphql tests(state: "TX", grade: G5, subject: math, first: 10) { edges { node { id title releaseYear testQuestionIds } } summary { testsWithQti testsFullyQti } } ``` Example - Find tests with QTI content from recent years: ```graphql tests( subject: science educationLevel: middle_school minReleaseYear: 2020 hasQuestionsWithQtiXml: true first: 20 ) { edges { node { id title state grade } } } ``` """ tests(subject: SubjectType, subSubject: String, educationLevel: EducationLevelType, grade: GradeType, state: String, language: LanguageType, releaseYear: Int, minReleaseYear: Int, maxReleaseYear: Int, source: String, hasQuestions: Boolean, hasQuestionsWithQtiXml: Boolean, first: Int, after: String, last: Int, before: String): TestConnection! """ Get a single test by ID. Use questionSummary field to check QTI coverage before fetching questions. Example: ```graphql test(id: "507f1f77bcf86cd799439011") { id title subject grade questionSummary { totalQuestions questionsWithQtiXml standards { standardCode questionCount } } } ``` """ test(id: ID!): Test """ Get all questions for a specific test. Questions are returned in orderIndex sequence. Example: ```graphql testQuestions(testId: "507f1f77bcf86cd799439011", first: 20) { edges { node { id title qtiXml questionDetails { standards { code, description } cognitiveRigor { level } } } } } ``` """ testQuestions(testId: ID!, first: Int, after: String, last: Int, before: String): TestQuestionConnection! """ Get a single test question by ID. Example: ```graphql testQuestion(id: "507f1f77bcf86cd799439011") { id title qtiXml questionHtml answerHtml } ``` """ testQuestion(id: ID!): TestQuestion """ Returns a list of all unique sources found in the test collection. Useful for building filters and showing available sources to users. """ testSources: [String!]! """ Search across all test questions with powerful curriculum and standards filters. This is the primary query for finding questions by standards, cognitive rigor, and QTI availability. FILTERING BEHAVIOR: - All filters combine with AND logic - isStudentFacing defaults to true (student-facing questions only) - hasQtiXml filters by QTI XML availability - Standards can be filtered by exact codes, prefixes, or standard sets COGNITIVE RIGOR FILTERING: - cognitiveRigorCell: Combined DOK × Bloom metric (higher = more rigorous) - dokLevels: Depth of Knowledge levels (1-4) - bloomLevels: Bloom's taxonomy levels (Remember, Understand, Apply, Analyze, Evaluate, Create) - highestRigorPerStandard: Returns only the highest-rigor question per standard (deduplication) Example - Find 8th grade NGSS life science questions with QTI: ```graphql searchTestQuestions( subject: science grade: G8 standardPrefixes: ["MS-LS1", "MS-LS2"] hasQtiXml: true minCognitiveRigor: 8 first: 20 ) { edges { node { question { id title qtiXml questionDetails { standards { code } cognitiveRigor { level, dokLevel, bloomLevel } } } test { title, state, releaseYear } } } summary { totalQuestions questionsWithQtiXml standardSummaries { standardCode questionCount highestRigorAvailable } } } ``` Example - Get one high-rigor question per CCSS math standard: ```graphql searchTestQuestions( subject: math grade: G8 standardSets: ["CCSS"] highestRigorPerStandard: true hasQtiXml: true ) { edges { node { question { id qtiXml questionDetails { standards { code, description } cognitiveRigor { level } } } } } } ``` Example - Search by exact standards with rigor filtering: ```graphql searchTestQuestions( standardCodes: ["8.EE.A.1", "8.EE.A.2", "8.EE.B.5"] minCognitiveRigor: 10 dokLevels: [3, 4] hasQtiXml: true first: 50 ) { edges { node { question { id, qtiXml } } } summary { questionsWithQtiXml missingStandards } } ``` """ searchTestQuestions( subject: SubjectType subSubject: String educationLevel: EducationLevelType grade: GradeType state: String language: LanguageType releaseYear: Int source: String """Exact match on standard codes (e.g. ["MS-LS1-1"]).""" standardCodes: [String!] """Prefix match on codes (e.g. ["MS-LS1"]).""" standardPrefixes: [String!] """Match on standard set, e.g. ["NGSS", "CCSS"].""" standardSets: [String!] """Inclusive lower bound on cognitiveRigorCell.""" minCognitiveRigor: Int """Inclusive upper bound on cognitiveRigorCell.""" maxCognitiveRigor: Int dokLevels: [Int!] bloomLevels: [String!] """If true, return only the highest-rigor question per standard.""" highestRigorPerStandard: Boolean """Filter by presence / absence of QTI XML.""" hasQtiXml: Boolean qtiInteractionTypes: [String!] """ Filter by student-facing questions. Default is true (only show student-facing). Set to false to include non-student-facing questions. """ isStudentFacing: Boolean first: Int after: String last: Int before: String ): TestQuestionWithContextConnection! """ Returns a list of all unique standards found in test questions with question counts. Useful for building filters and showing available standards to users. """ testQuestionStandardsList( subject: SubjectType subSubject: String educationLevel: EducationLevelType grade: GradeType state: String language: LanguageType releaseYear: Int source: String standardSet: String hasQtiXml: Boolean """ Filter by student-facing questions. Default is true (only show student-facing). Set to false to include non-student-facing questions. """ isStudentFacing: Boolean ): [StandardWithCount!]! """ Returns a list of all unique standard sets found in test questions with question counts. Useful for building filters and showing available standard sets to users. """ testQuestionStandardSetsList( subject: SubjectType subSubject: String educationLevel: EducationLevelType grade: GradeType state: String language: LanguageType releaseYear: Int source: String hasQtiXml: Boolean """ Filter by student-facing questions. Default is true (only show student-facing). Set to false to include non-student-facing questions. """ isStudentFacing: Boolean ): [StandardSetWithCount!]! """Get parsing status for a test""" testParsingStatus(testId: ID!): TestParsingStatus! """ Browse educational images from standardized tests with filtering. IMPORTANT DEFAULTS: - aiAnalyzed defaults to true (high-quality AI-analyzed images only) - isEducational defaults to true (excludes logos, branding, etc.) - isIncomplete defaults to false (excludes cut-off images) IMAGE PROCESSING PIPELINE: 1. Process 1 (Extend API): Extracts images from PDFs, provides basic metadata (figureType, caption, boundingBox) 2. Process 2 (GPT-5): AI analysis for educational value, concept extraction, detailed description Use aiAnalyzed=false to include images with only basic Extend metadata. Example - Find 5th grade math diagrams: ```graphql images(subject: math, grade: G5, type: diagram, first: 20) { edges { node { id imageUrl concept gptAnalysis { concept description tags } extendAnalysis { figureType caption } } } } ``` Example - Get all images from a specific test: ```graphql images(testId: "507f1f77bcf86cd799439011", first: 50) { edges { node { id imageUrl type concept testReferences { testId, testTitle } } } } ``` """ images( subject: SubjectType subjectBranch: String grade: GradeType concept: String type: ImageType testId: ID tags: [String!] """ Filter by AI analysis status (Process 2). Default is true (only show AI-analyzed images with high-quality metadata). Set to false to include images that only have basic Extend analysis. """ aiAnalyzed: Boolean """Filter by educational content flag""" isEducational: Boolean """Filter by incomplete/cut-off status""" isIncomplete: Boolean first: Int after: String last: Int before: String ): ImageConnection! """ Get a single image by ID with full metadata. Example: ```graphql image(id: "507f1f77bcf86cd799439011") { id imageUrl concept gptAnalysis { concept, description, tags, confidence } extendAnalysis { figureType, caption, boundingBox } testReferences { testId, testTitle } } ``` """ image(id: ID!): Image """ Search images using text across concept, description, and tags. Same defaults as images() query apply (aiAnalyzed=true, isEducational=true, isIncomplete=false). Example - Search for photosynthesis images: ```graphql searchImages( searchText: "photosynthesis chloroplast" subject: science grade: G7 first: 20 ) { edges { node { id imageUrl concept gptAnalysis { description tags } } } } ``` """ searchImages( searchText: String! subject: SubjectType subjectBranch: String grade: GradeType type: ImageType testId: ID """ Filter by AI analysis status (Process 2). Default is true (only show AI-analyzed images with high-quality metadata). Set to false to include images that only have basic Extend analysis. """ aiAnalyzed: Boolean """Filter by educational content flag""" isEducational: Boolean """Filter by incomplete/cut-off status""" isIncomplete: Boolean first: Int after: String last: Int before: String ): ImageConnection! """ Fetches a paginated list of atoms with optional filtering. Example - Find all Grade 5 science atoms: ```graphql query { atoms(grades: ["5"], subject: science, first: 20) { edges { node { atomId title difficulty { easy medium hard } supports } } } } ``` """ atoms( """Filter by atom ID pattern (supports partial match)""" atomId: String """Filter by standard code this atom supports""" supports: String """ Filter by grade levels (matches atoms that include ANY of these grades) """ grades: [String!] """Filter by subject area""" subject: SubjectType """Filter by publication status""" status: AtomStatusType """Search in title and description""" search: String first: Int after: String last: Int before: String ): AtomConnection! """Fetches a single atom by its unique MongoDB ID.""" atom(id: ID!): Atom """Fetches a single atom by its atomId (e.g., '3-PS2-4_atom_1').""" atomByAtomId(atomId: String!): Atom """ Fetches a paginated list of exemplar questions with filtering. Example - Find all Hard Grade 5 science questions: ```graphql query { exemplarQuestions(grade: G5, subject: science, difficulty: Hard, first: 20) { edges { node { id standard difficulty difficultyAtom qtiXmlWithFeedback } } } } ``` """ exemplarQuestions( """Filter by source question ID""" sourceQuestionId: ID """Filter by standard code""" standard: String """Filter by subject area""" subject: SubjectType """Filter by grade level""" grade: GradeType """Filter by state code""" state: String """Filter by difficulty level""" difficulty: ExemplarDifficultyType """Filter by atom used for tagging""" difficultyAtom: String """Filter by pipeline stage completion""" hasFeedback: Boolean """Filter by pipeline stage completion""" hasDifficultyTag: Boolean first: Int after: String last: Int before: String ): ExemplarQuestionConnection! """Fetches a single exemplar question by its MongoDB ID.""" exemplarQuestion(id: ID!): ExemplarQuestion """Fetches an exemplar question by its source question ID.""" exemplarQuestionBySource(sourceQuestionId: ID!): ExemplarQuestion } """ Base Mutation type - requires full API access. Mutations are only available with a full-access API key. """ type Mutation { _empty: String createCourse(input: CreateCourseInput!): Course! updateCourse(id: ID!, input: UpdateCourseInput!): Course! deleteCourse(id: ID!): Boolean! createSource(input: CreateSourceInput!): Source! updateSource(id: ID!, input: UpdateSourceInput!): Source! deleteSource(id: ID!): Boolean! createAlternateCourse(input: CreateAlternateCourseInput!): CreateAlternateCourseResponse! createLesson(input: CreateLessonInput!): Lesson! updateLesson(id: ID!, input: UpdateLessonInput!): Lesson! deleteLesson(id: ID!): Boolean! createContent(input: CreateContentInput!): Content! updateContent(id: ID!, input: UpdateContentInput!): Content! deleteContent(id: ID!): Boolean! createQuestion(input: CreateQuestionInput!): Question! updateQuestion(id: ID!, input: UpdateQuestionInput!): Question! deleteQuestion(id: ID!): Boolean! deleteQuestionsForContent(contentId: ID!): Int! addImageCreditsToQuestion(id: ID!): AddImageCreditsResult! addImageCreditsToAllQuestions(contentId: ID!, overrideFlag: Boolean = false): BatchAddImageCreditsResult! addImageCreditsToAllQuestionsInCourse(courseId: ID!, overrideFlag: Boolean = false): BatchAddImageCreditsResult! patchAlternateCourseQuestionHtml(altCourseId: ID!, addImageCredits: Boolean = false): PatchAlternateCourseResult! createStandard(input: CreateStandardInput!): Standard! updateStandard(id: ID!, input: UpdateStandardInput!): Standard! deleteStandard(id: ID!): Boolean! createTest(input: CreateTestInput!): Test! updateTest(id: ID!, input: UpdateTestInput!): Test! deleteTest(id: ID!): Boolean! createTestQuestion(input: CreateTestQuestionInput!): TestQuestion! updateTestQuestion(id: ID!, input: UpdateTestQuestionInput!): TestQuestion! deleteTestQuestion(id: ID!): Boolean! processTestPdfs(filters: ProcessTestsFiltersInput, config: PdfSplitterConfigInput!, options: ProcessTestsOptionsInput): ProcessTestsResponse! processQuestionPdfs(filters: ProcessQuestionsFiltersInput, config: PdfToQtiConfigInput!, options: ProcessQuestionsOptionsInput): ProcessQuestionsResponse! getQuestionDetail(questionId: ID!, config: PdfToQtiConfigInput!): QuestionDetail! processQuestionDetails(filters: ProcessQuestionsFiltersInput, config: PdfToQtiConfigInput!, options: ProcessQuestionsOptionsInput): ProcessQuestionDetailResponse! """ Extract images from test PDFs and analyze them using AI (OLD single-process system). DEPRECATED: Use parsePdfAndExtractImages instead. """ extractTestImages(filters: ExtractImagesFiltersInput, config: ExtractImagesConfigInput!, options: ExtractImagesOptionsInput): ExtractImagesResponse! """ Parse PDF and extract images (Process 1 of two-process architecture). Saves parsed PDF data to Test document and images to Image Bank with Extend analysis. """ parsePdfAndExtractImages(testId: ID!, forceReparse: Boolean = false): PdfParsingResult! """ Parse PDFs and extract images in batch (Process 1 of two-process architecture). Accepts filters to process multiple tests matching criteria (subject, grade, language, etc.). """ parsePdfAndExtractImagesBatch(filters: ParsePdfFiltersInput, options: ParsePdfOptionsInput): ParsePdfBatchResponse! """ Analyze images with GPT-5 Vision AI (Process 2 of two-process architecture). Filters educational content, generates detailed descriptions, and extracts concepts. Can provide specific image IDs or batch size to analyze unanalyzed images. """ analyzeImages( """ Specific image IDs to analyze. If not provided, will analyze unanalyzed images. """ imageIds: [ID!] """Number of images to analyze in this batch (if imageIds not provided)""" batchSize: Int = 10 """ Filter by source name (matches contentReferences.sourceName, e.g., "CK-12", "Khan Academy"). Case-insensitive partial match. """ source: String ): ImageAnalysisResponse! """ Segment a single test's parsed PDF into individual questions using AI. Requires the test to have parsedPdf data (from Extend API parsing). Creates new questions if they don't exist, or updates existing questions with questionMd. Uses AI (Gemini or GPT) to identify question boundaries and validate splits. """ segmentTestMd( """ID of the test to segment""" testId: ID! """Whether to overwrite existing questions (default: false)""" overwrite: Boolean = false """AI model provider to use for segmentation: 'gemini' (default) or 'gpt'""" modelProvider: String = "gemini" ): SegmentTestResult! """ Segment multiple tests' parsed PDFs into questions in batch. Processes tests that have parsedPdf data and creates TestQuestion records. """ segmentTestsMdBatch( """Filters to select which tests to process""" filters: SegmentTestsFiltersInput """Processing options for batch operation""" options: SegmentTestsOptionsInput ): SegmentTestsBatchResponse! """ Segment tests in bulk, processing them sequentially to avoid AI rate limits. Automatically finds tests that need segmentation (no questionMd or no testQuestions). Designed for long-running operations over many tests. Example - Process all TX elementary science tests: ```graphql mutation { segmentTestsMdBulkSequential( filters: { state: "TX" educationLevel: elementary subject: science } options: { delayBetweenTests: 5000 modelProvider: "gemini" maxTests: 100 } ) { totalEligible totalProcessed successCount errorCount completed results { testId success questionsSegmented } } } ``` """ segmentTestsMdBulkSequential( """ Filters to select which tests to process. Only tests that need segmentation (no questionMd or no testQuestions) will be processed. """ filters: SegmentTestsFiltersInput! """Options for the bulk sequential operation""" options: BulkSequentialSegmentOptionsInput ): BulkSequentialSegmentResponse! """ Generate QTI XML for a single test's markdown questions using AI. Requires questions to have questionMd data (from MD Segmenter). Uses AI (Gemini or OpenAI) to convert markdown to QTI 3.0 XML format. """ generateTestQti( """ID of the test to generate QTI for""" testId: ID! """Whether to overwrite existing QTI XML (default: false)""" overwrite: Boolean = false """ AI model provider to use for QTI generation: 'gemini' (default) or 'gpt' """ modelProvider: String = "gemini" ): GenerateQtiResult! """ Generate QTI XML for multiple tests' markdown questions in batch. Processes tests that have questions with questionMd data. Uses AI (Gemini or OpenAI) to convert markdown to QTI 3.0 XML format. Supports modelProvider option: 'gemini' (default) or 'gpt'. """ generateTestsQtiBatch( """ Filters to select which tests to process (same as segmentation filters) """ filters: SegmentTestsFiltersInput """ Processing options for batch operation (same as segmentation options, including modelProvider) """ options: SegmentTestsOptionsInput ): GenerateQtiBatchResponse! """ Parse question content (Markdown or HTML) to QTI XML for a single test. Supports both questionMd and questionHtml fields via originField parameter. Uses AI (Gemini or OpenAI) to convert content to QTI 3.0 XML format. Example - Parse HTML questions: ```graphql mutation { parseTestToQti( testId: "test123" originField: questionHtml modelProvider: "gemini" ) { success questionsProcessed qtiGenerated } } ``` """ parseTestToQti( """ID of the test to parse""" testId: ID! """Field to read content from: questionMd (default) or questionHtml""" originField: OriginFieldType = questionMd """Whether to overwrite existing QTI XML (default: false)""" overwrite: Boolean = false """AI model provider to use: 'gemini' (default) or 'gpt'""" modelProvider: String = "gemini" ): GenerateQtiResult! """ Parse question content (Markdown or HTML) to QTI XML for multiple tests in batch. Supports both questionMd and questionHtml fields via originField option. Uses AI (Gemini or OpenAI) to convert content to QTI 3.0 XML format. Example - Parse HTML questions in batch: ```graphql mutation { parseTestsToQtiBatch( filters: { subject: "math", state: "TX" } options: { originField: questionHtml, modelProvider: "gpt" } ) { totalProcessed successCount totalQtiGenerated } } ``` """ parseTestsToQtiBatch( """Filters to select which tests to process""" filters: SegmentTestsFiltersInput """ Processing options including originField, modelProvider, concurrency, etc. """ options: SegmentTestsOptionsInput ): GenerateQtiBatchResponse! """Create a new image""" createImage(input: CreateImageInput!): Image! """Update an existing image""" updateImage(id: ID!, input: UpdateImageInput!): Image! """Delete an image""" deleteImage(id: ID!): Boolean! """Bulk create multiple images (useful for batch ingestion from PDFs)""" bulkCreateImages(input: BulkCreateImagesInput!): BulkCreateImagesResult! """ Detect incomplete/cut-off images. Processes images without isIncomplete field and marks them as incomplete or complete. Can process all images (auto-fetch) or specific images by ID. """ detectIncompleteImages( """ Optional: Specific image IDs to check. If not provided, will auto-fetch images. """ imageIds: [ID!] """Number of images to process if imageIds not provided (default: 100)""" batchSize: Int ): IncompleteDetectionResult! """ Curate images marked as incomplete with stricter verification. Re-evaluates images flagged as incomplete using a more conservative prompt to verify they are truly incomplete in a way that prevents comprehension. This helps reduce false positives from the initial detection. """ curateIncompleteImages( """Number of incomplete images to curate (default: 1000)""" batchSize: Int ): IncompleteDetectionResult! """ Synchronizes a single course and all its components (units, lessons, content) from the platform to TimeBack. If the course or its components are already synced (have a timebackId), they will be skipped (updated later). This is an all-or-nothing operation for the full course sync. """ syncCourseToTimeBack( platformCourseId: ID! overwrite: Boolean openaiApiKey: String youtubeApiKey: String """ Lesson type for quiz content. Defaults to 'powerpath-100'. Pass 'quiz' to use the quiz lesson type. """ lessonType: String """ If true, reading content will use QTI stimulus with sanitized HTML (legacy behavior). Defaults to false - reading content HTML is uploaded to S3 as-is and used directly. """ useQtiStimulus: Boolean ): SyncedCourse """ Finds all courses on the platform that do not have a timebackId and syncs them to TimeBack. Returns a list of courses that were attempted to be synced. Each course sync is an all-or-nothing operation. """ syncAllNewCoursesToTimeBack: [SyncedCourse!] """ Synchronizes a single test (and all its test questions) to TimeBack QTI API. Uses filters to find the test: testId, state, grade, or subject. Creates QTI Assessment Items for each test question and a QTI Assessment containing all items. If overwrite is true, updates existing tests/questions; otherwise skips already synced items. """ syncTestToTimeBack(testId: ID, state: String, grade: String, subject: String, overwrite: Boolean): SyncedTest """ Synchronizes multiple tests based on filters to TimeBack QTI API. Processes tests matching the provided filters (testId, state, grade, subject). Each test sync creates QTI Assessment Items and a QTI Assessment. If overwrite is true, updates existing tests/questions; otherwise skips already synced items. If bluebonnet is true, only syncs tests from Bluebonnet (originUrl contains 'bluebonnet'). """ syncTestsToTimeBack(testId: ID, state: String, grade: String, subject: String, overwrite: Boolean, bluebonnet: Boolean): [SyncedTest!] """ Updates a single QTI resource (stimulus or item) in the QTI API. Use this to fix wrong QTI stimuli (for reading content) or QTI items (for questions). Pass either contentId (for reading/mixed content) or questionId (for questions). This will look up the existing TimeBack IDs and update the corresponding QTI resource. """ updateQtiResource(contentId: ID, questionId: ID, openaiApiKey: String): QtiUpdateResult """ Deletes an orphaned simplified HTML segment from TimeBack and QTI API. Use this when a segment was removed from MongoDB but still exists in TimeBack. Deletes ComponentResource, Resource, and QTI Stimulus (if exists). """ deleteOrphanedSegment(contentId: ID!, segmentOrder: Int!): DeleteSegmentResult """ Validate a single QTI question using AI. Checks content completeness, media integrity, structure validity, and parse quality. The AI analyzes the QTI XML and any referenced images to ensure: - Question stem and choices/interactions are complete - Referenced images exist and match the question context - QTI structure is valid for the detected interaction type - No parsing artifacts, garbled text, or contamination Example: ```graphql mutation { validateQtiQuestion( questionId: "507f1f77bcf86cd799439011" modelProvider: "gemini" ) { isValid overallScore contentCompleteness { passed, score, issues } mediaIntegrity { passed, score, issues } structureValidity { passed, score, issues } parseQuality { passed, score, issues } detectedQuestionType aiReasoning } } ``` """ validateQtiQuestion( """ID of the TestQuestion to validate""" questionId: ID! """AI model provider: 'gemini' (default) or 'gpt'""" modelProvider: String = "gemini" ): ValidateQuestionResult! """ Validate multiple QTI questions in batch using AI. Processes questions matching the filters with concurrency control. Example - Validate all questions for a test: ```graphql mutation { validateQtiQuestionsBatch( filters: { testId: "507f1f77bcf86cd799439011" } options: { modelProvider: "gpt", concurrency: 5 } ) { totalProcessed validCount invalidCount errorCount results { questionId isValid overallScore error } } } ``` """ validateQtiQuestionsBatch( """Filters to select which questions to validate""" filters: ValidateQtiFiltersInput """Options for batch validation""" options: ValidateQtiOptionsInput ): ValidateQtiBatchResponse! """Creates a new atom""" createAtom(input: CreateAtomInput!): Atom! """Updates an existing atom by MongoDB ID""" updateAtom(id: ID!, input: UpdateAtomInput!): Atom! """Updates an existing atom by atomId""" updateAtomByAtomId(atomId: String!, input: UpdateAtomInput!): Atom! """Deletes an atom by MongoDB ID""" deleteAtom(id: ID!): Boolean! """Deletes an atom by atomId""" deleteAtomByAtomId(atomId: String!): Boolean! """ Bulk imports atoms from JSON array. Uses upsert logic - creates new atoms or updates existing ones by atomId. """ bulkImportAtoms(atoms: [BulkAtomInput!]!): BulkImportResult! """Creates a new exemplar question""" createExemplarQuestion(input: CreateExemplarQuestionInput!): ExemplarQuestion! """Updates an existing exemplar question by MongoDB ID""" updateExemplarQuestion(id: ID!, input: UpdateExemplarQuestionInput!): ExemplarQuestion! """Updates an exemplar question by source question ID (upsert)""" upsertExemplarQuestion(sourceQuestionId: ID!, input: CreateExemplarQuestionInput!): ExemplarQuestion! """Deletes an exemplar question by MongoDB ID""" deleteExemplarQuestion(id: ID!): Boolean! """Deletes an exemplar question by source question ID""" deleteExemplarQuestionBySource(sourceQuestionId: ID!): Boolean! } """ Information about pagination in a connection. Used for all list queries to enable efficient navigation through large result sets. """ type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } """Academic subject areas for educational content.""" enum SubjectType { """Mathematics - algebra, geometry, statistics, calculus, etc.""" math """English Language Arts - reading, writing, literature, language""" ela """Science - physics, chemistry, biology, earth science""" science """Social Studies - history, geography, civics, economics""" social_studies """Other subjects not covered by main categories""" other } """K-12 education level groupings.""" enum EducationLevelType { """Elementary School - typically grades K-5""" elementary """Middle School - typically grades 6-8""" middle_school """High School - typically grades 9-12""" high_school } """Represents a content source for courses.""" type Source { id: ID! name: String! url: String createdAt: DateTime! updatedAt: DateTime! } """Represents a source reference embedded within a course.""" type SourceRef { id: ID! name: String! url: String } """ Represents a unit or module within a course. Units are organizational containers for lessons. """ type Unit { id: ID! title: String! description: String orderIndex: Int! url: String } """ Represents a course containing units and lessons. A course is the top-level educational entity in the system. """ type Course { id: ID! sourceRef: SourceRef! title: String! description: String url: String! units: [Unit!] createdAt: DateTime! updatedAt: DateTime! subject: SubjectType """ Sub-subject specification like "algebra", "geometry" for math; "chemistry", "biology" for science; etc. """ subSubject: String grade: String educationLevel: EducationLevelType } type CourseEdge { cursor: String! node: Course! } """ A Relay-style connection for paginating through courses. Example query: ``` query { courses(first: 10) { edges { node { id title description sourceRef { id name } } } pageInfo { hasNextPage endCursor } totalCount } } ``` """ type CourseConnection { edges: [CourseEdge!]! pageInfo: PageInfo! totalCount: Int! } type CourseComparisonReport { originalCourseId: ID! replicaCourseId: ID! isComplete: Boolean! missingUnits: [MissingUnitInfo!]! missingLessons: [MissingLessonInfo!]! missingContent: [MissingContentInfo!]! incompleteQuizzes: [IncompleteQuizInfo!]! } type MissingUnitInfo { unitTitle: String! orderIndex: Int! originalUnitId: ID! } type MissingLessonInfo { lessonTitle: String! orderIndex: Int! originalLessonId: ID! originalUnitTitle: String! originalUnitId: ID! } type MissingContentInfo { contentTitle: String! orderIndex: Int! originalContentId: ID! originalLessonTitle: String! originalLessonId: ID! } type IncompleteQuizInfo { contentTitle: String! orderIndex: Int! originalContentId: ID! originalLessonTitle: String! originalLessonId: ID! originalQuestionCount: Int! replicaQuestionCount: Int! } input UnitInput { id: ID title: String! description: String orderIndex: Int! url: String } input SourceRefInput { id: ID! name: String! url: String } input CreateCourseInput { sourceRef: SourceRefInput! title: String! description: String url: String! units: [UnitInput!] subject: SubjectType """Sub-subject like "algebra", "geometry", "chemistry", "biology", etc.""" subSubject: String grade: String educationLevel: EducationLevelType } input UpdateCourseInput { sourceRef: SourceRefInput title: String description: String url: String units: [UnitInput!] subject: SubjectType """Sub-subject like "algebra", "geometry", "chemistry", "biology", etc.""" subSubject: String grade: String educationLevel: EducationLevelType } input CreateSourceInput { name: String! url: String } input UpdateSourceInput { name: String url: String } input CreateAlternateCourseInput { courseId: ID! openaiApiKey: String! suffix: String lessonId: ID unitId: ID altCourseId: ID stopOnError: Boolean resume: Boolean processSingleLesson: Boolean } type CreateAlternateCourseResponse { course: Course! transformationsApplied: Int! readingContentTransformed: Int! quizContentTransformed: Int! imagesConvertedToBase64: Int! } """ Represents a lesson within a unit of a course. Lessons contain educational content and may represent quizzes. """ type Lesson { id: ID! courseId: ID! unitId: ID! title: String! description: String orderIndex: Int! url: String! pdfUrl: String isQuiz: Boolean createdAt: DateTime! updatedAt: DateTime! } type LessonEdge { cursor: String! node: Lesson! } """ A Relay-style connection for paginating through lessons. Example query: ``` query { lessons(courseId: "course-id", first: 10) { edges { node { id title unitId isQuiz } } pageInfo { hasNextPage endCursor } } } ``` """ type LessonConnection { edges: [LessonEdge!]! pageInfo: PageInfo! totalCount: Int! } input CreateLessonInput { courseId: ID! unitId: ID! title: String! description: String orderIndex: Int! url: String! pdfUrl: String isQuiz: Boolean } input UpdateLessonInput { courseId: ID unitId: ID title: String description: String orderIndex: Int url: String pdfUrl: String isQuiz: Boolean } """ Represents the type of content (e.g., reading material, video). Used to categorize and filter content items. """ enum ContentType { reading video audio quiz mixed } type LinkedVideo { youtubeUrl: String! transcript: String } type LinkedAudio { s3Url: String! duration: Float } type LinkedArticle { title: String! htmlBody: String url: String } """ Represents a simplified HTML segment with metadata for interactive content. """ type SimplifiedHtmlSegment { html: String! timeLength: Float wordLength: Int xp: Int timebackId: String timebackResourceId: String qtiStimulusId: String order: Int! } """ Represents a piece of content within a lesson (e.g., reading, video, quiz). Content is the actual educational material presented to users. """ type Content { id: ID! lessonId: ID! title: String! type: ContentType! category: String url: String htmlBody: String simplifiedHtml: [SimplifiedHtmlSegment!] pdfUrl: String youtubeUrl: String audioUrl: String transcript: String linkedVideos: [LinkedVideo!] linkedAudios: [LinkedAudio!] linkedArticles: [LinkedArticle!] questionIds: [ID!] standards: [StandardRef!] orderIndex: Int! duration: Float timeLength: Float wordLength: Int xp: Int validationScore: Int validationReason: String createdAt: DateTime! updatedAt: DateTime! } type ContentEdge { cursor: String! node: Content! } """ A Relay-style connection for paginating through content. Example query: ``` query { contents(lessonId: "lesson-id-here", first: 10) { edges { node { id title type htmlBody youtubeUrl } } } } ``` """ type ContentConnection { edges: [ContentEdge!]! pageInfo: PageInfo! totalCount: Int! } input LinkedVideoInput { youtubeUrl: String! transcript: String } input LinkedAudioInput { s3Url: String! duration: Float } input LinkedArticleInput { title: String! htmlBody: String url: String } input SimplifiedHtmlSegmentInput { html: String! timeLength: Float wordLength: Int xp: Int timebackId: String timebackResourceId: String qtiStimulusId: String order: Int! } input CreateContentInput { lessonId: ID! title: String! type: ContentType! category: String url: String htmlBody: String simplifiedHtml: [SimplifiedHtmlSegmentInput!] pdfUrl: String youtubeUrl: String audioUrl: String transcript: String linkedVideos: [LinkedVideoInput!] linkedAudios: [LinkedAudioInput!] linkedArticles: [LinkedArticleInput!] questionIds: [ID!] standards: [StandardRefInput!] orderIndex: Int! duration: Float timeLength: Float wordLength: Int xp: Int } input UpdateContentInput { lessonId: ID title: String type: ContentType category: String url: String htmlBody: String simplifiedHtml: [SimplifiedHtmlSegmentInput!] pdfUrl: String youtubeUrl: String audioUrl: String transcript: String linkedVideos: [LinkedVideoInput!] linkedAudios: [LinkedAudioInput!] linkedArticles: [LinkedArticleInput!] questionIds: [ID!] standards: [StandardRefInput!] orderIndex: Int duration: Float timeLength: Float wordLength: Int xp: Int validationScore: Int validationReason: String } """ Represents a question, typically part of a quiz or content item. Questions have HTML-formatted question text and optional answer explanations. """ type Question { id: ID! contentId: ID! orderIndex: Int! questionHtml: String! answerHtml: String qtiXml: String versionNumber: Int difficulty: DifficultyType validationScore: Int validationReason: String createdAt: DateTime! updatedAt: DateTime! } type QuestionEdge { cursor: String! node: Question! } """ A Relay-style connection for paginating through questions. Example query: ``` query { questions(contentId: "content-id-here", first: 20) { edges { node { id questionHtml answerHtml orderIndex } } } } ``` """ type QuestionConnection { edges: [QuestionEdge!]! pageInfo: PageInfo! totalCount: Int! } input CreateQuestionInput { contentId: ID! orderIndex: Int! questionHtml: String! answerHtml: String qtiXml: String versionNumber: Int difficulty: DifficultyType } input UpdateQuestionInput { contentId: ID orderIndex: Int questionHtml: String answerHtml: String qtiXml: String versionNumber: Int difficulty: DifficultyType validationScore: Int validationReason: String } type AddImageCreditsResult { success: Boolean! message: String! question: Question creditsAdded: Int! } type BatchAddImageCreditsResult { success: Boolean! message: String! totalQuestions: Int! questionsProcessed: Int! totalCreditsAdded: Int! errors: [String!]! results: [QuestionCreditResult!]! } type QuestionCreditResult { questionId: ID! success: Boolean! message: String! creditsAdded: Int! } type PatchAlternateCourseResult { success: Boolean! message: String! questionsPatched: Int! creditsAdded: Int! errors: [String!]! results: [QuestionPatchResult!]! } type QuestionPatchResult { questionId: ID! success: Boolean! message: String! htmlPatched: Boolean! creditsAdded: Int! } """ Represents the set or source of educational standards. Used to categorize and filter standards. """ enum StandardSetType { NGSS CCSS } """ Represents an educational standard. Standards define what students should know and be able to do at specific grade levels. """ type Standard { id: ID! setId: String! standardId: String! description: String! createdAt: DateTime! updatedAt: DateTime! } type StandardRef { id: ID! setId: String! standardId: String! } type StandardEdge { cursor: String! node: Standard! } """A Relay-style connection for paginating through standards.""" type StandardConnection { edges: [StandardEdge!]! pageInfo: PageInfo! totalCount: Int! } input CreateStandardInput { setId: String! standardId: String! description: String! } input UpdateStandardInput { setId: String standardId: String description: String } input StandardRefInput { id: ID setId: String! standardId: String! description: String } """Grade levels for K-12 education and standardized tests.""" enum GradeType { """Kindergarten""" K """1st Grade""" G1 """2nd Grade""" G2 """3rd Grade""" G3 """4th Grade""" G4 """5th Grade""" G5 """6th Grade""" G6 """7th Grade""" G7 """8th Grade""" G8 """9th Grade (Freshman)""" G9 """10th Grade (Sophomore)""" G10 """11th Grade (Junior)""" G11 """12th Grade (Senior)""" G12 """Advanced Placement""" AP """SAT standardized test""" SAT } """Language codes following ISO 639-1 standard.""" enum LanguageType { """English""" en """Spanish (Español)""" es """French (Français)""" fr """German (Deutsch)""" de """Italian (Italiano)""" it """Portuguese (Português)""" pt """Chinese (中文)""" zh """Japanese (日本語)""" ja """Korean (한국어)""" ko """Arabic (العربية)""" ar """Hindi (हिन्दी)""" hi """Russian (Русский)""" ru } """Difficulty levels for test questions.""" enum DifficultyType { """Easy difficulty""" easy """Medium difficulty""" medium """Hard difficulty""" hard } type Test { id: ID! title: String! originUrl: String pdfUrl: String state: String grade: GradeType educationLevel: EducationLevelType subject: SubjectType! """ Sub-subject specification like "algebra", "geometry" for math; "chemistry", "biology" for science; etc. """ subSubject: String language: LanguageType releaseYear: Int """Source of the test (e.g., "bluebonnet")""" source: String testQuestionIds: [ID!]! """TimeBack integration ID""" timebackId: String """Flag indicating if images have been extracted from this test""" extractedImages: Boolean """Metadata about the PDF parsing from Extend API (Process 1)""" parsedPdfMetadata: ParsedPdfMetadata """ Full parsed PDF data from Extend API (Process 1). This is a large field and only returned when explicitly requested. """ parsedPdf: JSON """S3 URL for parsed PDF storage (alternative to inline storage)""" parsedPdfS3Url: String """Flag indicating if images have been analyzed with GPT""" imagesAnalyzedWithAI: Boolean """Flag indicating if parsedPdf is known to be structurally unreliable""" invalidParsedPdf: Boolean createdAt: DateTime! updatedAt: DateTime! """ On demand, returns coverage, rigor, and alignment stats for the questions inside this Test. Computed dynamically; no cost if the client doesn't ask. """ questionSummary: TestQuestionSummary! } type TestQuestion { id: ID! testId: ID! orderIndex: Int! title: String description: String questionType: String questionHtml: String """Markdown content from MD Segmenter (raw markdown, not HTML)""" questionMd: String """ MD validation status - true = passed, false = failed, null = not yet validated """ questionMdValidated: Boolean answerHtml: String qtiXml: String """Original QTI 2.2 XML (for imported items from sources like Bluebonnet)""" qti22Xml: String pdfUrl: String """Flag indicating if correct response has not been verified""" unverifiedCorrectResponse: Boolean """Whether this question is intended for student-facing use""" isStudentFacing: Boolean """PDF-to-QTI validation results""" questionValidation: QuestionValidation questionDetails: QuestionDetail """ AI validation result - true = passed, false = failed, null = not yet validated """ aiValidated: Boolean """AI reasoning explaining why question was approved or rejected""" aiValidationReason: String """Difficulty level of the question""" difficulty: DifficultyType """TimeBack integration ID""" timebackId: String createdAt: DateTime! updatedAt: DateTime! } type TestEdge { cursor: String! node: Test! } type TestConnection { edges: [TestEdge!]! pageInfo: PageInfo! totalCount: Int! """ Summary statistics for the entire filtered set of tests, not just the current page. """ summary: TestsSummary } type TestQuestionEdge { cursor: String! node: TestQuestion! } type TestQuestionConnection { edges: [TestQuestionEdge!]! pageInfo: PageInfo! totalCount: Int! } """ Minimal metadata about the Test a question belongs to. Returned alongside each question so a consumer can understand grade, subject, etc. without an extra fetch. """ type TestContext { id: ID! title: String! subject: SubjectType! subSubject: String grade: GradeType educationLevel: EducationLevelType state: String releaseYear: Int language: LanguageType source: String } """ A TestQuestion enriched with its parent-test context. This is the primary node returned by the searchTestQuestions query. """ type TestQuestionWithContext { id: ID! testId: ID! orderIndex: Int! title: String description: String questionType: String questionHtml: String """Markdown content from MD Segmenter (raw markdown, not HTML)""" questionMd: String """ MD validation status - true = passed, false = failed, null = not yet validated """ questionMdValidated: Boolean answerHtml: String qtiXml: String """Original QTI 2.2 XML (for imported items from sources like Bluebonnet)""" qti22Xml: String pdfUrl: String """Flag indicating if correct response has not been verified""" unverifiedCorrectResponse: Boolean """Whether this question is intended for student-facing use""" isStudentFacing: Boolean """PDF-to-QTI validation results""" questionValidation: QuestionValidation questionDetails: QuestionDetail """ AI validation result - true = passed, false = failed, null = not yet validated """ aiValidated: Boolean """AI reasoning explaining why question was approved or rejected""" aiValidationReason: String """Difficulty level of the question""" difficulty: DifficultyType """TimeBack integration ID""" timebackId: String createdAt: DateTime! updatedAt: DateTime! testContext: TestContext! } type TestQuestionWithContextEdge { cursor: String! node: TestQuestionWithContext! } type RigorBucket { level: Int! questionCount: Int! } """ Per-standard statistics for a single Test. Gives you how many questions align to the standard, the highest cognitive-rigor available, and the distribution of questions across rigor levels. """ type TestStandardSummary { standardCode: String! questionCount: Int! highestRigorAvailable: Int! rigorDistribution: [RigorBucket!]! } """ Aggregate information about all questions that belong to a Test. Returned only if the client explicitly requests the questionSummary field on Test. """ type TestQuestionSummary { totalQuestions: Int! questionsWithQtiXml: Int! standards: [TestStandardSummary!]! } """ Coverage stats for a single standard. • highestRigorAvailable – max cognitiveRigorCell among filtered questions. • rigorDistribution – count of questions at each rigor level present. """ type StandardSummary { standardCode: String! highestRigorAvailable: Int rigorDistribution: [RigorBucket!]! } """Count of questions grouped by source""" type SourceCount { source: String! count: Int! } """Count of questions grouped by grade level""" type GradeCount { grade: GradeType! count: Int! } """Count of questions grouped by state""" type StateCount { state: String! count: Int! } """Count of questions grouped by subject""" type SubjectCount { subject: SubjectType! count: Int! } """Count of questions grouped by language""" type LanguageCount { language: String! count: Int! } """ High-level report returned with every searchTestQuestions call so clients can see coverage, rigor distribution, and QTI readiness at a glance. """ type SearchSummary { standardSummaries: [StandardSummary!]! missingStandards: [String!]! totalQuestions: Int! questionsWithQtiXml: Int! """Breakdown of questions by source (e.g., bluebonnet)""" sourceBreakdown: [SourceCount!]! """Breakdown of questions by grade level""" gradeBreakdown: [GradeCount!]! """Breakdown of questions by state""" stateBreakdown: [StateCount!]! """Breakdown of questions by subject""" subjectBreakdown: [SubjectCount!]! """Breakdown of questions by language""" languageBreakdown: [LanguageCount!]! } """ Relay-style connection for paginated TestQuestionWithContext edges, plus a summary object with aggregated statistics over the full result set (not just the current page). """ type TestQuestionWithContextConnection { edges: [TestQuestionWithContextEdge!]! pageInfo: PageInfo! totalCount: Int! summary: SearchSummary } """A standard with its question count""" type StandardWithCount { standardSet: String! standardCode: String! questionCount: Int! } """A standard set with its question count""" type StandardSetWithCount { standardSet: String! questionCount: Int! } input CreateTestInput { title: String! originUrl: String pdfUrl: String state: String grade: GradeType educationLevel: EducationLevelType subject: SubjectType! """Sub-subject like "algebra", "geometry", "chemistry", "biology", etc.""" subSubject: String language: LanguageType releaseYear: Int """Source of the test (e.g., "bluebonnet")""" source: String testQuestionIds: [ID!] timebackId: String extractedImages: Boolean imagesAnalyzedWithAI: Boolean invalidParsedPdf: Boolean } input UpdateTestInput { title: String originUrl: String pdfUrl: String state: String grade: GradeType educationLevel: EducationLevelType subject: SubjectType """Sub-subject like "algebra", "geometry", "chemistry", "biology", etc.""" subSubject: String language: LanguageType releaseYear: Int """Source of the test (e.g., "bluebonnet")""" source: String testQuestionIds: [ID!] timebackId: String extractedImages: Boolean imagesAnalyzedWithAI: Boolean invalidParsedPdf: Boolean } input CreateTestQuestionInput { testId: ID! orderIndex: Int! title: String description: String questionType: String questionHtml: String questionMd: String answerHtml: String qtiXml: String qti22Xml: String pdfUrl: String unverifiedCorrectResponse: Boolean isStudentFacing: Boolean difficulty: DifficultyType timebackId: String } input UpdateTestQuestionInput { orderIndex: Int title: String description: String questionType: String questionHtml: String questionMd: String questionMdValidated: Boolean answerHtml: String qtiXml: String qti22Xml: String pdfUrl: String unverifiedCorrectResponse: Boolean isStudentFacing: Boolean aiValidated: Boolean aiValidationReason: String difficulty: DifficultyType timebackId: String } input PdfSplitterConfigInput { openaiApiKey: String! awsCredentials: AwsCredentialsInput! s3Config: S3ConfigInput! } input AwsCredentialsInput { accessKeyId: String! secretAccessKey: String! sessionToken: String region: String! } input S3ConfigInput { bucketName: String! pathPrefix: String! } input ProcessTestsFiltersInput { testId: ID subject: SubjectType subSubject: String state: String grade: GradeType language: LanguageType } input ProcessTestsOptionsInput { overwrite: Boolean concurrency: Int batchSize: Int delayBetweenBatches: Int } input ParsePdfFiltersInput { testId: ID subject: SubjectType subSubject: String state: String grade: GradeType language: LanguageType releaseYear: Int } input ParsePdfOptionsInput { forceReparse: Boolean concurrency: Int batchSize: Int delayBetweenBatches: Int """ Only process tests with complete QTI XML coverage (all student-facing questions have qtiXml). If true, skips tests that require PDF extraction. """ qtiXmlOnly: Boolean } type ParsePdfBatchResult { testId: ID! success: Boolean! parsing: ParsingMetrics! images: ImageExtractionMetrics! parsedPdfSaved: Boolean! processingTimeSeconds: Float! errors: [String!] } type ParsePdfBatchResponse { results: [ParsePdfBatchResult!]! totalProcessed: Int! successCount: Int! errorCount: Int! } type ProcessTestResult { testId: ID! success: Boolean! questionsProcessed: Int! questionUrls: [String!]! questionsDeleted: Int error: String } type ProcessTestsResponse { results: [ProcessTestResult!]! totalProcessed: Int! successCount: Int! errorCount: Int! } input PdfToQtiConfigInput { openaiApiKey: String! awsCredentials: AwsCredentialsInput s3Config: S3ConfigInput validationEndpoint: String } input ProcessQuestionsFiltersInput { questionId: ID testId: ID hasPdfUrl: Boolean missingTitle: Boolean missingDescription: Boolean state: String subject: SubjectType subSubject: String grade: GradeType language: LanguageType releaseYear: Int } input ProcessQuestionsOptionsInput { overwrite: Boolean concurrency: Int batchSize: Int delayBetweenBatches: Int getDetails: Boolean } type ProcessQuestionResult { questionId: ID! success: Boolean! skipped: Boolean title: String description: String questionType: String qtiXmlUpdated: Boolean! error: String } type ProcessQuestionsResponse { results: [ProcessQuestionResult!]! totalProcessed: Int! successCount: Int! errorCount: Int! skippedCount: Int! } type QuestionDetailStandard { standardSet: String! standardCode: String! } type QuestionDetailCorrectAnswerKey { obtentionMethod: String! answer: JSON! } type QuestionDetail { qtiInteractionType: [String!]! mediaTypesPresent: [String!]! stemWordCount: Int! estimatedTimeSeconds: Int! dokLevel: Int! dokReason: String bloomLevel: String! bloomReason: String cognitiveRigorCell: Int! standards: [QuestionDetailStandard!]! correctAnswerKey: QuestionDetailCorrectAnswerKey! } """PDF-to-QTI validation results for a question""" type QuestionValidation { success: Boolean! validation_passed: Boolean! overall_score: Float! completeness_score: Float! accuracy_score: Float! visual_score: Float! functionality_score: Float! issues_found: [String!] missing_elements: [String!] recommendations: [String!] validation_summary: String } type ProcessQuestionDetailResult { questionId: ID! success: Boolean! detailSaved: Boolean error: String } type ProcessQuestionDetailResponse { results: [ProcessQuestionDetailResult!]! totalProcessed: Int! successCount: Int! errorCount: Int! } input ExtractImagesConfigInput { openaiApiKey: String! awsCredentials: AwsCredentialsInput s3Config: S3ConfigInput } input ExtractImagesFiltersInput { testId: ID subject: SubjectType subSubject: String state: String grade: GradeType language: LanguageType releaseYear: Int hasQtiXml: Boolean } input ExtractImagesOptionsInput { overwrite: Boolean concurrency: Int batchSize: Int delayBetweenBatches: Int } type ExtractedImage { imageUrl: String! imageHash: String! pageNumber: Int! concept: String! type: String! detailedDescription: String! subjectBranch: String tags: [String!]! } type ExtractImagesResult { testId: ID! success: Boolean! imagesExtracted: Int! imagesSaved: Int! duplicateCount: Int! images: [ExtractedImage!]! error: String } type ExtractImagesResponse { results: [ExtractImagesResult!]! totalProcessed: Int! successCount: Int! errorCount: Int! totalImagesExtracted: Int! totalImagesSaved: Int! } type ParsingMetrics { processingTimeMs: Int! pageCount: Int! creditsUsed: Int! } type ImageExtractionMetrics { total: Int! uploaded: Int! savedToImageBank: Int! imageIds: [ID!]! } type PdfParsingResult { success: Boolean! testId: ID! parsing: ParsingMetrics! images: ImageExtractionMetrics! parsedPdfSaved: Boolean! processingTimeSeconds: Float! errors: [String!] } type ParsedPdfMetadata { processingTimeMs: Int! pageCount: Int! creditsUsed: Int! figuresFound: Int! parsedAt: DateTime! extendApiVersion: String! } type TestParsingStatus { testId: ID! parsedPdfMetadata: ParsedPdfMetadata imageCount: Int! imagesAnalyzedCount: Int! } type ImageAnalysisResult { imageId: ID! educational: Boolean! analyzed: Boolean! concept: String confidence: Float error: String } type ImageAnalysisResponse { success: Boolean! imagesProcessed: Int! queued: Int! failed: Int! failedImageIds: [String!] status: String! queueUrl: String! processingTimeSeconds: Float! errors: [String!] } input SegmentTestsFiltersInput { """Specific test ID to process""" testId: ID """Filter by subject""" subject: SubjectType """Filter by sub-subject (e.g., algebra, biology)""" subSubject: String """Filter by state code""" state: String """Filter by grade level""" grade: GradeType """Filter by education level (elementary, middle_school, high_school)""" educationLevel: EducationLevelType """Filter by language""" language: LanguageType """Filter by release year""" releaseYear: Int """Filter by source (e.g., bluebonnet)""" source: String } """ Origin field for QTI parsing - specifies which TestQuestion field to read content from. """ enum OriginFieldType { """Read from questionMd field (Markdown format)""" questionMd """Read from questionHtml field (HTML format)""" questionHtml } input SegmentTestsOptionsInput { """Whether to overwrite existing questions (default: false)""" overwrite: Boolean """Number of tests to process concurrently (default: 5)""" concurrency: Int """Number of tests per batch (default: 10)""" batchSize: Int """Delay in milliseconds between batches (default: 3000)""" delayBetweenBatches: Int """AI model provider to use: 'gemini' (default) or 'gpt'""" modelProvider: String """ Origin field to read content from: 'questionMd' (default) or 'questionHtml'. Used by parseTestToQti and parseTestsToQtiBatch mutations. """ originField: OriginFieldType } """ Options for bulk sequential segmentation. Designed for processing many tests while respecting AI rate limits. """ input BulkSequentialSegmentOptionsInput { """Delay in milliseconds between each test (default: 5000 = 5 seconds)""" delayBetweenTests: Int """AI model provider to use: 'gemini' (default) or 'gpt'""" modelProvider: String """Whether to overwrite existing questionMd (default: false)""" overwrite: Boolean """ Maximum number of tests to process (default: no limit, processes all matching tests) """ maxTests: Int } """ Response for bulk sequential segmentation operation. Includes detailed progress information for long-running operations. """ type BulkSequentialSegmentResponse { """Results for each test processed""" results: [SegmentTestResult!]! """Total number of tests that matched the filters and needed processing""" totalEligible: Int! """Total number of tests actually processed""" totalProcessed: Int! """Number of tests successfully segmented""" successCount: Int! """Number of tests that failed""" errorCount: Int! """Number of tests skipped (already had questionMd or other reason)""" skippedCount: Int! """Total questions segmented across all tests""" totalQuestionsSegmented: Int! """ Whether all eligible tests were processed (false if maxTests limit was hit) """ completed: Boolean! } """ Result of segmenting a single test. Creates new questions if they don't exist, or updates existing questions with questionMd. When updating existing questions, validates that segmented count matches existing count. """ type SegmentTestResult { """ID of the processed test""" testId: ID! """Whether segmentation was successful""" success: Boolean! """Whether the test was skipped (e.g., already has questionMd)""" skipped: Boolean """Total number of questions identified by segmentation""" questionsSegmented: Int! """Number of questions that passed validation""" validatedQuestions: Int! """Number of questions that failed validation""" unvalidatedQuestions: Int! """Number of shared contexts identified""" sharedContexts: Int! """Number of questions created or updated with questionMd""" questionsUpdated: Int! """Error message if segmentation failed""" error: String """List of validation errors (for partial failures)""" errors: [String!] } """Response for batch segmentation operation""" type SegmentTestsBatchResponse { """Results for each test processed""" results: [SegmentTestResult!]! """Total number of tests processed""" totalProcessed: Int! """Number of tests successfully segmented""" successCount: Int! """Number of tests that failed""" errorCount: Int! """Number of tests skipped""" skippedCount: Int! """Total questions segmented across all tests""" totalQuestionsSegmented: Int! } """Result of generating QTI for a single test""" type GenerateQtiResult { """ID of the processed test""" testId: ID! """Whether QTI generation was successful""" success: Boolean! """Whether the test was skipped (e.g., no questions need QTI)""" skipped: Boolean """Number of questions processed""" questionsProcessed: Int! """Number of QTI items generated""" qtiGenerated: Int! """Number of questions updated with QTI XML""" qtiUpdated: Int! """Error message if QTI generation failed""" error: String """List of errors (for partial failures)""" errors: [String!] } """Response for batch QTI generation operation""" type GenerateQtiBatchResponse { """Results for each test processed""" results: [GenerateQtiResult!]! """Total number of tests processed""" totalProcessed: Int! """Number of tests successfully processed""" successCount: Int! """Number of tests that failed""" errorCount: Int! """Number of tests skipped""" skippedCount: Int! """Total QTI items generated across all tests""" totalQtiGenerated: Int! } """ Summary statistics for a collection of tests. Provides high-level metrics about test coverage, question availability, and QTI readiness. """ type TestsSummary { totalTests: Int! testsWithQuestions: Int! testsWithQti: Int! testsFullyQti: Int! } """Classification of visual content types found in standardized tests.""" enum ImageType { """ Data visualization with axes (line graph, bar graph, scatter plot, etc.) """ graph """Annotated diagram with labels pointing to parts""" labeled_diagram """Diagram with multiple-choice labels or interactive elements""" labeled_diagram_with_options """Pie chart, organizational chart, or other non-graph visual data""" chart """Tabular data with rows and columns""" table """Drawing or artistic representation""" illustration """Real-world photograph""" photograph """Geographic map or spatial representation""" map """Chronological sequence visualization""" timeline """Process or system flow diagram""" flow_chart """Logical relationship diagram with overlapping circles""" venn_diagram """Visual content not fitting other categories""" other } """Bounding box coordinates from Extend API""" type BoundingBox { left: Float! top: Float! right: Float! bottom: Float! } """Analysis data from Extend API (Process 1: PDF parsing)""" type ExtendAnalysis { """Figure type classification from Extend API""" figureType: String! """AI-generated caption from Extend API""" caption: String """Bounding box coordinates in the PDF""" boundingBox: BoundingBox } """Analysis data from GPT-5 Vision API (Process 2: AI analysis)""" type GptAnalysis { """Main educational concept (2-5 words)""" concept: String! """Extremely detailed description (150-300 words)""" detailedDescription: String! """ Specific academic sub-field (e.g., biology, chemistry, algebra, geometry) """ subjectBranch: String """Searchable keywords (5-10 tags)""" tags: [String!]! """Confidence score (0-1)""" confidence: Float """Timestamp when analyzed""" analyzedAt: DateTime! """Model used (e.g., "gpt-5-2025-08-07")""" model: String! } """Reference to a test that uses this image""" type TestReference { """Test ID""" testId: ID! """Grade level for this specific test""" grade: GradeType """Subject for this specific test""" subject: SubjectType """State code for this specific test""" state: String """Page number in this specific test's PDF""" pageNumber: Int """When this test reference was added""" addedAt: DateTime! } type Image { id: ID! """Aggregated array of all grade levels using this image""" grades: [GradeType!] """Aggregated array of all subjects using this image""" subjects: [SubjectType!] """ Sub-field specification like "physics", "chemistry", "biology" for science; "algebra", "geometry" for math; etc. """ subjectBranch: String """ Main concept depicted in the image (e.g., "water cycle", "action reaction", "food chain"). Filled by GPT-5 in Process 2, so may be null for newly extracted images. """ concept: String """ Detailed description of what the image shows. Filled by GPT-5 in Process 2, so may be null for newly extracted images. """ detailedDescription: String """Type of image (graph, diagram, chart, etc.)""" type: ImageType! """URL or S3 path to the actual image file""" imageUrl: String! """Content-based hash/fingerprint for duplicate detection""" imageHash: String! """Array of test references - all tests that use this image""" testReferences: [TestReference!]! """Searchable tags for flexible categorization""" tags: [String!] """ Flexible field for additional metadata (width, height, aspectRatio, etc.) """ metadata: JSON """ Analysis from Extend API (Process 1: PDF parsing). Always present after extraction. """ extendAnalysis: ExtendAnalysis """ Analysis from GPT-5 Vision API (Process 2: AI analysis). Only present after AI analysis is complete. """ gptAnalysis: GptAnalysis """Flag indicating if this image has been analyzed with GPT-5 (Process 2)""" aiAnalyzed: Boolean! """ Flag indicating if this image is educational content (vs logo, branding, etc.). Set by GPT-5-mini educational filter in Process 2. """ isEducational: Boolean! """ Flag indicating if this image is incomplete or cut-off. Set by GPT-5-mini incomplete filter. Optional field with no default. """ isIncomplete: Boolean createdAt: DateTime! updatedAt: DateTime! } type ImageEdge { cursor: String! node: Image! } type ImageConnection { edges: [ImageEdge!]! pageInfo: PageInfo! totalCount: Int! } input CreateImageInput { grade: GradeType subject: SubjectType """ Sub-field like "physics", "chemistry", "biology", "algebra", "geometry", etc. """ subjectBranch: String """ Main concept depicted (e.g., "water cycle", "action reaction", "food chain"). Optional - filled by GPT-5 in Process 2. """ concept: String """ Detailed description of what the image shows. Optional - filled by GPT-5 in Process 2. """ detailedDescription: String """Type of image""" type: ImageType! """URL or S3 path to the image file""" imageUrl: String! """Content-based hash for duplicate detection""" imageHash: String! """Reference to the test this image came from""" testId: ID """Page number in the source PDF""" pageNumber: Int """Searchable tags""" tags: [String!] """Additional metadata""" metadata: JSON } input UpdateImageInput { grade: GradeType subject: SubjectType subjectBranch: String concept: String detailedDescription: String type: ImageType imageUrl: String imageHash: String testId: ID pageNumber: Int tags: [String!] metadata: JSON } input BulkCreateImagesInput { images: [CreateImageInput!]! } type BulkCreateImagesResult { successCount: Int! failureCount: Int! duplicateCount: Int! createdImages: [Image!]! errors: [String!] } type IncompleteDetectionResult { """Whether the operation was successful""" success: Boolean! """Number of images processed""" imagesProcessed: Int! """Number of incomplete images found""" incomplete: Int! """Number of complete images found""" complete: Int! """Number of images that failed processing""" failed: Int! """Status of the operation""" status: String! """Processing time in seconds""" processingTimeSeconds: Float! """Any errors encountered""" errors: [String!]! } """ Represents a Course entity from our platform, extended with its TimeBack sync status. """ type SyncedCourse { id: ID! title: String! url: String timebackId: String isSyncedToTimeBack: Boolean! units: [SyncedUnit!] } """ Represents a Test entity from our platform, extended with its TimeBack sync status. """ type SyncedTest { id: ID! title: String! pdfUrl: String originUrl: String state: String grade: String subject: String! language: String releaseYear: Int timebackId: String isSyncedToTimeBack: Boolean! questionCount: Int! error: String } """Represents a Unit entity within a SyncedCourse.""" type SyncedUnit { id: ID! title: String! timebackId: String lessons: [SyncedLesson!] } """Represents a Lesson entity within a SyncedUnit.""" type SyncedLesson { id: ID! title: String! timebackId: String activities: [SyncedContentActivity!] } """Represents a Content/Activity entity within a SyncedLesson.""" type SyncedContentActivity { id: ID! title: String! type: String timebackId: String timebackResourceId: String isAssessmentBank: Boolean versionCount: Int questionCount: Int } """Result of updating a QTI resource (stimulus or item).""" type QtiUpdateResult { success: Boolean! id: ID! title: String! type: String! qtiId: String qtiUrl: String timebackId: String error: String } """Result of deleting an orphaned segment.""" type DeleteSegmentResult { success: Boolean! message: String! deletedComponentResourceId: String deletedResourceId: String deletedQtiStimulusId: String error: String } """Controls how batch validation handles already-validated questions.""" enum ValidationOverwriteMode { """ Skip all questions that have any validation result (aiValidated is true OR false). Only validate questions that have never been validated (aiValidated is null/undefined). """ NONE """ Only re-validate questions that previously failed (aiValidated === false). Skip questions that passed or were never validated. """ FAILED_ONLY """ Re-validate all matching questions regardless of previous validation status. """ ALL } """ Result of a single validation check. Includes pass/fail status, score, and specific issues found. """ type QtiValidationCheckResult { """Whether this validation check passed""" passed: Boolean! """Score from 0-100 for this check""" score: Int! """List of specific issues found during validation""" issues: [String!]! """Additional details about the validation""" details: String } """ Detailed validation result for a single media item. Used when validating images referenced in QTI questions. """ type QtiMediaValidationDetail { """URL of the media item""" url: String! """Whether the media resource exists and loads""" exists: Boolean! """Whether the image content matches the question context (AI-determined)""" contextuallyCorrect: Boolean """Issues found with this media item""" issues: [String!]! """AI analysis of the image content""" aiAnalysis: String } """ Complete validation result for a QTI question. Contains results for all validation categories plus overall assessment. """ type QtiValidationResult { """Question identifier""" questionId: ID! """ Overall validation result - true if question passes all critical checks """ isValid: Boolean! """Overall quality score from 0-100""" overallScore: Int! """Content completeness validation (stem, choices, interactions present)""" contentCompleteness: QtiValidationCheckResult! """Media integrity validation (images exist and are contextually correct)""" mediaIntegrity: QtiValidationCheckResult! """ Structure validity validation (QTI structure complete for question type) """ structureValidity: QtiValidationCheckResult! """Parse quality validation (no contamination, garbled text, artifacts)""" parseQuality: QtiValidationCheckResult! """Detailed validation results for each media item""" mediaDetails: [QtiMediaValidationDetail!]! """ Detected QTI interaction type (e.g., choiceInteraction, textEntryInteraction) """ detectedQuestionType: String """AI reasoning explaining the validation result""" aiReasoning: String """Any errors encountered during validation""" errors: [String!]! } """ Result of validating a single question. Extends QtiValidationResult with success status for batch operations. """ type ValidateQuestionResult { """Question identifier""" questionId: ID! """ Whether the validation process completed successfully (not the same as isValid) """ success: Boolean! """ Overall validation result - true if question passes all critical checks """ isValid: Boolean! """Overall quality score from 0-100""" overallScore: Int! """Content completeness validation result""" contentCompleteness: QtiValidationCheckResult! """Media integrity validation result""" mediaIntegrity: QtiValidationCheckResult! """Structure validity validation result""" structureValidity: QtiValidationCheckResult! """Parse quality validation result""" parseQuality: QtiValidationCheckResult! """Detected QTI interaction type""" detectedQuestionType: String """AI reasoning for the validation result""" aiReasoning: String """Error message if validation failed""" error: String } """Input filters for batch QTI validation.""" input ValidateQtiFiltersInput { """Specific question ID to validate""" questionId: ID """Validate all questions for a specific test""" testId: ID """Filter by subject""" subject: SubjectType """Filter by state""" state: String """Filter by grade""" grade: GradeType """Filter by language""" language: LanguageType } """Options for batch QTI validation.""" input ValidateQtiOptionsInput { """AI model provider to use: 'gemini' (default), 'gpt', or 'opus'""" modelProvider: String """Number of questions to process concurrently (default: 5)""" concurrency: Int """Number of questions per batch (default: 10)""" batchSize: Int """Delay in milliseconds between batches (default: 2000)""" delayBetweenBatches: Int """ How to handle already-validated questions (default: NONE - skip validated). - NONE: Skip questions that have been validated (aiValidated is true or false) - FAILED_ONLY: Only re-validate questions where aiValidated === false - ALL: Re-validate all matching questions """ overwriteMode: ValidationOverwriteMode } """Response for batch QTI validation operation.""" type ValidateQtiBatchResponse { """Results for each question validated""" results: [ValidateQuestionResult!]! """Total number of questions processed""" totalProcessed: Int! """Number of questions that passed validation""" validCount: Int! """Number of questions that failed validation""" invalidCount: Int! """Number of questions where validation encountered errors""" errorCount: Int! """Number of questions skipped due to overwriteMode setting""" skippedCount: Int! } """ Visual benefit classification - whether visual aids help with this atom """ enum VisualBenefitType { positive neutral negative } """Atom publication status""" enum AtomStatusType { draft published archived } """ Difficulty rubric describing what Easy/Medium/Hard looks like for an atom. Used for tagging questions with appropriate difficulty levels. """ type DifficultyRubric { """Description of what an Easy question looks like for this atom""" easy: String! """Description of what a Medium question looks like for this atom""" medium: String! """Description of what a Hard question looks like for this atom""" hard: String! } """ A Knowledge Graph Atom - the smallest unit of teachable knowledge. Atoms are linked to educational standards and contain difficulty rubrics that describe what Easy/Medium/Hard questions look like for this concept. Example atom: "Identify needs solvable by magnets" (3-PS2-4_atom_1) """ type Atom { id: ID! """Unique atom identifier (e.g., '3-PS2-4_atom_1', '5-ESS1-2_atom_15')""" atomId: String! """Human-readable title""" title: String! """Detailed description of the learning objective""" description: String! """Publication status""" status: AtomStatusType! """Semantic version (e.g., '1.0.0')""" version: String! """Grade levels this atom applies to (e.g., ['3'], ['3', '4', '5'])""" grades: [String!]! """Difficulty rubric describing Easy/Medium/Hard for this atom""" difficulty: DifficultyRubric! """Prerequisite atom IDs (other atoms that should be mastered first)""" prerequisites: [String!]! """Standard codes that are potential prerequisites""" potentialPrerequisiteStandards: [String!]! """Standard codes this atom supports/assesses""" supports: [String!]! """Whether visual aids help with this atom""" visualBenefit: VisualBenefitType! """Subject area (derived from standard code)""" subject: SubjectType """Sub-subject/domain (e.g., 'physical_science', 'earth_science')""" subjectBranch: String """Whether structural-type hard questions are allowed for this atom""" allowStructuralHard: Boolean createdAt: DateTime! updatedAt: DateTime! } type AtomEdge { cursor: String! node: Atom! } """A Relay-style connection for paginating through atoms.""" type AtomConnection { edges: [AtomEdge!]! pageInfo: PageInfo! totalCount: Int! } """Input for difficulty rubric""" input DifficultyRubricInput { easy: String! medium: String! hard: String! } """Input for creating a new atom""" input CreateAtomInput { atomId: String! title: String! description: String! status: AtomStatusType version: String grades: [String!]! difficulty: DifficultyRubricInput! prerequisites: [String!] potentialPrerequisiteStandards: [String!] supports: [String!]! visualBenefit: VisualBenefitType subject: SubjectType subjectBranch: String allowStructuralHard: Boolean } """Input for updating an existing atom""" input UpdateAtomInput { atomId: String title: String description: String status: AtomStatusType version: String grades: [String!] difficulty: DifficultyRubricInput prerequisites: [String!] potentialPrerequisiteStandards: [String!] supports: [String!] visualBenefit: VisualBenefitType subject: SubjectType subjectBranch: String allowStructuralHard: Boolean } """Input for bulk importing atoms from JSON""" input BulkAtomInput { atom_id: String! title: String! description: String! status: AtomStatusType version: String grades: [String!]! difficulty: DifficultyRubricInput! prerequisites: [String!] potential_prerequisite_standards: [String!] supports: [String!]! visual_benefit: VisualBenefitType allow_structural_hard: Boolean } """Result of bulk import operation""" type BulkImportResult { imported: Int! updated: Int! errors: [String!]! } """Categorical difficulty for exemplar questions""" enum ExemplarDifficultyType { Easy Medium Hard } """Source type for the original question""" enum ExemplarSourceType { TestQuestion Question } """Difficulty area assignments for detailed difficulty breakdown""" type DifficultyAreaAssignments { claimPrecision: String evidenceSelection: String linkStrength: String } """Pipeline stage completion tracking""" type PipelineStages { imagesMigrated: Boolean! difficultyTagged: Boolean! feedbackGenerated: Boolean! validated: Boolean! } """ An enriched question from the exemplar pipeline. Contains original question data plus difficulty tagging, feedback, and validation. Pipeline stages: 1. FETCH: From CommonCoreCrawl API 2. IMAGES: Migrate base64 → S3 URLs 3. DIFFICULTY: Tag with KG atoms 4. FEEDBACK: Generate inline feedback + solutions 5. VALIDATE: LLM judge verification """ type ExemplarQuestion { id: ID! """Reference to the original TestQuestion""" sourceQuestionId: ID! """Type of source (TestQuestion or Question)""" sourceType: ExemplarSourceType! """Original QTI XML (with S3 images)""" qtiXml: String! """Enriched QTI XML with inline feedback and solution block""" qtiXmlWithFeedback: String """Position within the original test""" orderIndex: Int! """Question title""" title: String """Interaction type (e.g., 'choice', 'extended_text')""" questionType: String """Standard code (e.g., '5-PS1-3', '3-5-ETS1-2')""" standard: String """Whether standard was inferred by LLM""" standardInferred: Boolean """Subject area""" subject: SubjectType """Grade level""" grade: GradeType """State code (2-char, e.g., 'TX', 'CA')""" state: String """Categorical difficulty based on atom rubrics""" difficulty: ExemplarDifficultyType """Atom ID used for difficulty evaluation""" difficultyAtom: String """LLM reasoning for difficulty assignment""" difficultyReasoning: String """Numeric difficulty (1-10 scale)""" difficultyScore: Int """Breakdown by difficulty dimensions""" difficultyAreaAssignments: DifficultyAreaAssignments """Detailed reasoning for difficulty score""" difficultyEvaluationReasoning: String """Schema version for difficulty profile""" difficultyProfileVersion: String """Completion status of each pipeline stage""" pipelineStages: PipelineStages """Pipeline version used for processing""" pipelineVersion: String """Validation errors or warnings""" validationNotes: [String!] createdAt: DateTime! updatedAt: DateTime! } type ExemplarQuestionEdge { cursor: String! node: ExemplarQuestion! } """A Relay-style connection for paginating through exemplar questions.""" type ExemplarQuestionConnection { edges: [ExemplarQuestionEdge!]! pageInfo: PageInfo! totalCount: Int! } """Input for difficulty area assignments""" input DifficultyAreaAssignmentsInput { claimPrecision: String evidenceSelection: String linkStrength: String } """Input for pipeline stages""" input PipelineStagesInput { imagesMigrated: Boolean difficultyTagged: Boolean feedbackGenerated: Boolean validated: Boolean } """Input for creating a new exemplar question""" input CreateExemplarQuestionInput { sourceQuestionId: ID! sourceType: ExemplarSourceType qtiXml: String! qtiXmlWithFeedback: String orderIndex: Int! title: String questionType: String standard: String standardInferred: Boolean subject: SubjectType grade: GradeType state: String difficulty: ExemplarDifficultyType difficultyAtom: String difficultyReasoning: String difficultyScore: Int difficultyAreaAssignments: DifficultyAreaAssignmentsInput difficultyEvaluationReasoning: String difficultyProfileVersion: String pipelineStages: PipelineStagesInput pipelineVersion: String validationNotes: [String!] } """Input for updating an existing exemplar question""" input UpdateExemplarQuestionInput { qtiXml: String qtiXmlWithFeedback: String title: String questionType: String standard: String standardInferred: Boolean subject: SubjectType grade: GradeType state: String difficulty: ExemplarDifficultyType difficultyAtom: String difficultyReasoning: String difficultyScore: Int difficultyAreaAssignments: DifficultyAreaAssignmentsInput difficultyEvaluationReasoning: String difficultyProfileVersion: String pipelineStages: PipelineStagesInput pipelineVersion: String validationNotes: [String!] }