Verify Generated Artifacts #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Verify Generated Artifacts | |
| on: | |
| schedule: | |
| # Run at 8 AM UTC daily (2 hours after nightly e2e tests) | |
| - cron: '0 8 * * *' | |
| workflow_dispatch: # Allow manual trigger | |
| jobs: | |
| verify: | |
| name: Verify Artifacts | |
| runs-on: ubuntu-latest | |
| if: github.repository == 'teng-lin/notebooklm-py' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| cache: 'pip' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e "." | |
| - name: Verify artifacts exist | |
| env: | |
| NOTEBOOKLM_AUTH_JSON: ${{ secrets.NOTEBOOKLM_AUTH_JSON }} | |
| NOTEBOOKLM_GENERATION_NOTEBOOK_ID: ${{ secrets.NOTEBOOKLM_GENERATION_NOTEBOOK_ID }} | |
| run: | | |
| python -c " | |
| import asyncio | |
| import os | |
| import sys | |
| from notebooklm import NotebookLMClient | |
| # Type ID to display name mapping | |
| TYPE_NAMES = { | |
| 1: 'Audio', | |
| 2: 'Report', # Study Guide, Briefing Doc, Blog Post | |
| 3: 'Video', | |
| 4: 'Quiz/Flashcards', | |
| 5: 'Mind Map', | |
| 7: 'Infographic', | |
| 8: 'Slide Deck', | |
| 9: 'Data Table', | |
| } | |
| # Expected type IDs from generation tests | |
| # Note: Type 2 is Reports (study guide), Type 4 is Quiz+Flashcards | |
| EXPECTED_TYPES = {1, 2, 3, 4, 5, 7, 8, 9} | |
| async def verify(): | |
| async with await NotebookLMClient.from_storage() as client: | |
| nb_id = os.environ['NOTEBOOKLM_GENERATION_NOTEBOOK_ID'] | |
| print(f'Checking notebook: {nb_id}') | |
| # List artifacts | |
| artifacts = await client.artifacts.list(nb_id) | |
| print(f'\nTotal artifacts: {len(artifacts)}') | |
| # Group by type and status | |
| by_type = {} | |
| for a in artifacts: | |
| key = a._artifact_type | |
| if key not in by_type: | |
| by_type[key] = [] | |
| by_type[key].append(a) | |
| print('\nArtifacts by type:') | |
| for t in sorted(by_type.keys()): | |
| items = by_type[t] | |
| type_name = TYPE_NAMES.get(t, f'Unknown({t})') | |
| print(f' {type_name} (type {t}): {len(items)}') | |
| for a in items: | |
| status = a.status_str | |
| variant_info = f', variant={a._variant}' if a._variant else '' | |
| print(f' - {a.title} ({status}{variant_info})') | |
| # Check expected types | |
| found = set(by_type.keys()) | |
| missing = EXPECTED_TYPES - found | |
| print(f'\nExpected types: {len(EXPECTED_TYPES)}') | |
| print(f'Found types: {len(found)}') | |
| if missing: | |
| missing_names = [TYPE_NAMES.get(t, str(t)) for t in missing] | |
| print(f'\nWARNING: Missing artifact types: {missing_names}') | |
| # Check for completed artifacts | |
| completed = sum(1 for a in artifacts if a.is_completed) | |
| processing = sum(1 for a in artifacts if a.is_processing) | |
| failed = sum(1 for a in artifacts if a.is_failed) | |
| print(f'\nStatus summary:') | |
| print(f' Completed: {completed}') | |
| print(f' Processing: {processing}') | |
| print(f' Failed: {failed}') | |
| # List notes | |
| notes = await client.notes.list(nb_id) | |
| print(f'\nTotal notes: {len(notes)}') | |
| for n in notes[:10]: | |
| print(f' - {n.title or \"(untitled)\"}') | |
| if len(notes) > 10: | |
| print(f' ... and {len(notes) - 10} more') | |
| # Fail if too many failures or no artifacts at all | |
| if len(artifacts) == 0: | |
| print('\nERROR: No artifacts found!') | |
| sys.exit(1) | |
| if failed > len(artifacts) // 2: | |
| print(f'\nERROR: Too many failed artifacts ({failed}/{len(artifacts)})') | |
| sys.exit(1) | |
| print('\nVerification complete!') | |
| asyncio.run(verify()) | |
| " |