import { S as StreamRunnerOptions } from '../streamRunner-BzlJrAGZ.js'; /** * Media Types — image generation, image search, video search cho học liệu. * Mục tiêu: mọi artifact instruction step-by-step / slide đều có minh họa phù hợp, * sinh bằng AI (Gemini/GPT image) HOẶC tìm kiếm ảnh/video thật có license phù hợp. */ type MediaProvider = 'openai' | 'gemini'; /** Default = 'openai' (gpt-image-1): infographic quality preferred. */ interface GeneratedImage { /** Base64-encoded image data (không kèm prefix data:) */ base64: string; mimeType: string; /** Provider dùng để sinh (audit/debug) */ provider: string; /** Prompt đã được dùng (sau khi enrich theo ngữ cảnh) */ prompt: string; revisedPrompt?: string; } interface StockImage { url: string; thumbnailUrl?: string; width?: number; height?: number; photographer?: string; source: string; license: string; pageUrl?: string; altText?: string; } interface StockVideo { url: string; thumbnailUrl?: string; durationSeconds?: number; width?: number; height?: number; source: string; license: string; pageUrl?: string; } interface GenerateImageOptions { /** Mô tả minh họa cần sinh */ prompt: string; /** Ngữ cảnh bài học để enrich prompt (concept, grade band, style guide excerpt) */ context?: { lessonTitle?: string; concept?: string; gradeBand?: [number, number] | number; pedagogy?: string; styleGuideExcerpt?: string; language?: string; }; /** Tỷ lệ khung: 16:9 cho slide, 4:3/1:1 cho worksheet */ aspectRatio?: '1:1' | '16:9' | '4:3' | '3:4'; /** Phong cách hình: phù hợp giáo dục, rõ ràng, an toàn cho học sinh */ style?: 'educational-diagram' | 'realistic-photo' | 'flat-illustration' | 'technical-schematic'; provider?: MediaProvider; /** API key override (nếu không dùng env) */ apiKey?: string; /** Timeout tổng cho request sinh ảnh (mặc định 420_000ms = 7 phút) */ timeoutMs?: number; } interface SearchImageOptions { query: string; perPage?: number; orientation?: 'landscape' | 'portrait' | 'square'; apiKey?: string; } interface SearchVideoOptions { query: string; perPage?: number; apiKey?: string; } declare class MediaError extends Error { readonly provider?: string | undefined; readonly status?: number | undefined; constructor(message: string, provider?: string | undefined, status?: number | undefined); } /** Enrich prompt gốc với ngữ cảnh sư phạm → prompt sinh ảnh chất lượng, nhất quán phong cách */ declare function buildImagePrompt(options: GenerateImageOptions): string; /** * Sinh ảnh minh họa cho học liệu. Provider mặc định: gemini. * Prompt tự động enrich với ngữ cảnh sư phạm (grade band, style guide, khái niệm bài học). */ declare function generateEducationalImage(options: GenerateImageOptions): Promise; /** * Stock Media Search — tìm ảnh/video minh họa THẬT có license mở (Pexels). * Dùng khi: không cần sinh AI, muốn ảnh thật của phần cứng (ESP32, breadboard…), * hoặc muốn video demo quá trình thực tế cho step-by-step instructions. * API key: PEXELS_API_KEY (miễn phí tại pexels.com/api). */ declare function searchEducationalImages(options: SearchImageOptions): Promise; /** * Pixabay — kho ảnh dự phòng khi Pexels trống/lỗi (mỗi kho có catalog khác nhau, * query tiếng Anh hẹp hay miss trên một kho). Map kết quả về cùng shape StockImage. */ declare function searchPixabayImages(options: SearchImageOptions): Promise; /** * Search-first với fallback: Pexels → Pixabay. Trả về [] (không throw) khi cả * hai kho đều không có ảnh khớp — caller tự quyết định fallback tiếp theo. */ declare function searchStockImage(query: string, apiKeyOverrides?: { pexels?: string; pixabay?: string; }): Promise; declare function searchEducationalVideos(options: SearchVideoOptions): Promise; /** * Media Ledger — sổ cái ảnh per-lesson (JSONL), nguồn chân lý cho quy hoạch ảnh. * Vòng đời entry: pending → approved (Curator chốt) → done | failed (kèm lastError) * Agent tạo artifact chỉ APPEND (pending). Curator quyết định. Orchestrator thực thi. * Idempotent: 1 slug chỉ có 1 entry active; append cùng slug → cập nhật, không trùng. */ type MediaLedgerMode = 'generate' | 'search'; type MediaLedgerStatus = 'pending' | 'approved' | 'rejected' | 'done' | 'failed'; interface MediaLedgerEntry { /** Định danh ảnh trong bài, vd 'img-loop-diagram' — unique per lesson */ slug: string; /** Artifact chứa slot, vd 'LESSON_U01_M01_L01' */ artifactId: string; /** Tên ảnh (human-readable) */ name: string; /** Mô tả chi tiết nội dung ảnh — input chính cho Curator enrich + agent con */ description: string; /** 'generate' (AI) hoặc 'search' (ảnh/video thật) */ mode: MediaLedgerMode; /** Vị trí chèn: markdown heading/anchor gần nhất trong artifact */ anchor?: string; status: MediaLedgerStatus; /** Provider đã dùng (set khi done) */ provider?: string; /** Inline Markdown credit (tác giả + nguồn + license) — từ multimedia-kit AttributionBuilder */ attribution?: string; /** Path tương đối trong project sau khi done: _media/xxx.png */ relPath?: string; /** Prompt cuối (đã enrich) — audit/debug */ finalPrompt?: string; lastError?: string; retryCount?: number; /** Quyết định của Curator: mã lý do (duplicate/content_mismatch/...) — audit + user override */ decisionReasonCode?: string; decisionReason?: string; updatedAt: string; } declare class MediaLedger { readonly filePath: string; private entries; constructor(projectDir: string, lessonId: string); private load; private persist; /** Agent tạo artifact ghi entry mới (hoặc cập nhật nếu slug đã có và chưa done) */ upsert(input: Omit & { status?: MediaLedgerStatus; }): MediaLedgerEntry; get(slug: string): MediaLedgerEntry | undefined; list(): MediaLedgerEntry[]; listByStatus(status: MediaLedgerStatus): MediaLedgerEntry[]; pending(): MediaLedgerEntry[]; setStatus(slug: string, status: MediaLedgerStatus, patch?: Partial): MediaLedgerEntry | undefined; /** Đếm ảnh đã done trong lesson — để Curator tôn trọng maxImages */ doneCount(): number; /** * Scan artifact content và seed pending entries cho mọi placeholder [IMAGE: slug]. * Được gọi sau khi agent sinh artifact (trước khi Curator duyệt) — đây là cầu nối * duy nhất giữa "agent chèn placeholder" và "sổ cái có entry để Curator/Fulfiller xử". * Idempotent: slug đã có entry active (pending/approved/done) → giữ nguyên; * chỉ re-describe khi entry trước bị rejected/failed. */ seedFromArtifact(artifactId: string, content: string): MediaLedgerEntry[]; } type MediaPolicyMode = 'search_only' | 'gen_only' | 'auto'; interface MediaPolicy { /** Giới hạn số ảnh ĐƯỢC GEN (AI) tối đa per artifact — search kho ảnh KHÔNG tốn quota */ maxGenImagesPerArtifact: number; /** Ưu tiên provider khi mode=generate */ preferredProvider?: 'openai' | 'gemini'; /** * Chiến lược nguồn ảnh: * — search_only: chỉ tìm kho ảnh (Pexels), không sinh AI * — gen_only: chỉ sinh AI, bỏ qua kho ảnh * — auto: LUÔN search trước, không có kết quả mới generate (mặc định) */ mode?: MediaPolicyMode; } interface CurationDecision { slug: string; action: 'approve' | 'drop' | 're-describe'; /** Mã lý do máy-đọc-được — bắt buộc khi drop, để audit + user có thể override */ reasonCode?: 'duplicate' | 'content_mismatch' | 'low_quality_description' | 'over_quota' | 'none_needed'; /** Mô tả đã enrich (grade band, style, pedagogy) — chỉ khi approve/re-describe */ enrichedPrompt?: string; reason: string; } interface CurateOptions { ledger: MediaLedger; /** Nội dung artifact ĐÃ PASS judge (markdown, strip YAML) */ artifactContent: string; policy: MediaPolicy; /** Ngữ cảnh dự án: grade band, style guide, tiêu đề bài */ context: { lessonTitle?: string; gradeBand?: [number, number]; styleGuideExcerpt?: string; language?: string; }; llm?: StreamRunnerOptions; } /** * Curator: phân tích toàn bộ sổ cái + nội dung đã chốt → quyết định ảnh nào tạo. * Tôn trọng maxImagesPerArtifact (count cả ảnh done + pending được approve). * LLM trả JSON: [{slug, action, enrichedPrompt, reason}]. Parse lỗi → MediaError rõ ràng. */ declare function curateMediaLedger(options: CurateOptions): Promise; /** Input ranking cho 1 gen candidate */ interface GenCandidate { slug: string; name: string; description: string; } interface GenRanking { /** Slugs được quyền gen (≤ quota) theo thứ tự ưu tiên */ selected: string[]; /** Slots không được gen kèm lý do */ dropped: Array<{ slug: string; rank: number; }>; } /** * Xếp hạng gen candidates khi số slot search-miss (hoặc gen_only tổng slot) VƯỢT quota gen. * Chỉ xếp hạng theo mức cần thiết của minh họa cho hiểu bài — KHÔNG đụng ledger (orchestrate lo persist). * LLM fail → fallback first-come theo thứ tự sổ cái + log warning rõ (degradation có chủ đích, không im lặng). */ declare function rankGenCandidates(candidates: GenCandidate[], quota: number, context: { lessonTitle?: string; gradeBand?: [number, number]; }, llm?: StreamRunnerOptions): Promise; interface FulfillOptions { ledger: MediaLedger; projectDir: string; policy: MediaPolicy; /** Tỷ lệ khung mặc định cho artifact type (SLIDE → 16:9) */ defaultAspect?: '1:1' | '16:9' | '4:3' | '3:4'; apiKeyOverrides?: { openai?: string; gemini?: string; pexels?: string; }; /** Số lần retry tối đa trước khi đánh dấu failed vĩnh viễn */ maxRetries?: number; /** Ngữ cảnh cho gen-ranking (khi search-miss vượt quota gen) */ rankingContext?: { lessonTitle?: string; gradeBand?: [number, number]; }; llm?: StreamRunnerOptions; onProgress?: (slug: string, status: string, detail?: string) => void; } interface FulfillResult { done: string[]; failed: string[]; skipped: string[]; } /** * Thực thi các entry approved trong sổ cái (từng ảnh độc lập — lỗi 1 ảnh không chặn ảnh khác). * generate → base64 → save file _media/; search → lưu URL đầu tiên (metadata vào ledger). * Quá maxRetries → status 'failed', slot giữ placeholder trong markdown (client render). */ declare function fulfillMediaLedger(options: FulfillOptions): Promise; /** Sau khi done: thay placeholder [IMAGE: slug] trong markdown bằng ![](relPath) */ declare function injectMediaIntoMarkdown(markdown: string, ledger: MediaLedger | MediaLedgerEntry[]): string; /** Tạo placeholder hiển thị khi failed/pending — client render rõ trạng thái, không im lặng */ declare function renderMediaPlaceholder(entry: MediaLedgerEntry): string; interface AutoFulfillMediaOptions { markdown: string; artifactId: string; projectDir?: string; policy: MediaPolicy; context?: { lessonTitle?: string; gradeBand?: [number, number]; styleGuideExcerpt?: string; language?: string; }; apiKeyOverrides?: { openai?: string; gemini?: string; pexels?: string; }; defaultAspect?: '1:1' | '16:9' | '4:3' | '3:4'; maxRetries?: number; llm?: StreamRunnerOptions; onProgress?: (slug: string, status: string, detail?: string) => void; } interface AutoFulfillMediaResult { markdown: string; ledger: MediaLedger; fulfillResult: FulfillResult; } /** * End-to-end auto-fulfillment of [IMAGE: slug] placeholders in an approved artifact. * 1. Seeds entries into MediaLedger * 2. Curates pending slots (approves/drops/enriches) * 3. Fulfills approved slots (stock search or AI image generation) * 4. Injects real relative image paths into the markdown document */ declare function autoFulfillArtifactMedia(options: AutoFulfillMediaOptions): Promise; /** * Điền mọi image slot của deck. * Pha 1: query → photo stock (Pexels→Pixabay) → data-URI cho TẤT CẢ slot. * Pha 2: slot còn lại (search miss/lỗi) → gen AI (mặc định openai gpt-image-1, * gemini nếu key đó mới có) với quota maxGenImages. * Mutate deckJson tại chỗ (slide.slots.image / slots.images[i] thành data-URI). * Idempotent: slot đã là data-URI/` void; maxImages?: number; /** Số ảnh AI GEN tối đa/deck (search không tốn quota). Mặc định 3. */ maxGenImages?: number; apiKeyOverrides?: { pexels?: string; pixabay?: string; openai?: string; gemini?: string; }; /** Provider gen AI khi pha 2 chạy (mặc định openai → gpt-image-1). */ genProvider?: MediaProvider; /** Ngữ cảnh enrich prompt gen (đi thẳng vào buildImagePrompt). */ genContext?: { lessonTitle?: string; concept?: string; gradeBand?: [number, number] | number; }; }): Promise<{ filled: number; failed: number; genUsed: number; }>; export { type AutoFulfillMediaOptions, type AutoFulfillMediaResult, type CurateOptions, type CurationDecision, type FulfillOptions, type FulfillResult, type GenCandidate, type GenRanking, type GenerateImageOptions, type GeneratedImage, MediaError, MediaLedger, type MediaLedgerEntry, type MediaLedgerMode, type MediaLedgerStatus, type MediaPolicy, type MediaPolicyMode, type MediaProvider, type SearchImageOptions, type SearchVideoOptions, type StockImage, type StockVideo, autoFulfillArtifactMedia, buildImagePrompt, curateMediaLedger, fulfillMediaLedger, fulfillSlideImageSlots, generateEducationalImage, injectMediaIntoMarkdown, rankGenCandidates, renderMediaPlaceholder, searchEducationalImages, searchEducationalVideos, searchPixabayImages, searchStockImage };