Skip to content

Commit 99df2ac

Browse files
groupthinkingdependabot[bot]claudegithub-advanced-security[bot]Copilot
authored
Enhance NotebookLM integration and frontend video ingestion pipeline (#51)
* feat: Initialize PGLite v17 database data files for the dataconnect project. * feat: enable automatic outline generation for Gemini Code Assist in VS Code settings. * feat: Add NotebookLM integration with a new processor and `analyze_video_with_notebooklm` MCP tool. * feat: Add NotebookLM profile data and an ingestion test. * chore: Update and add generated browser profile files for notebooklm development. * Update `notebooklm_chrome_profile` internal state and add architectural context documentation and video asset. * feat: Add various knowledge prototypes for MCP servers and universal automation, archive numerous scripts and documentation, and update local browser profile data. * chore: Add generated browser profile cache and data for notebooklm. * Update notebooklm Chrome profile preferences, cache, and session data. * feat: Update NotebookLM Chrome profile with new cache, preferences, and service worker data. * feat: Add generated Chrome profile cache and code cache files and update associated profile data. * Update `notebooklm` Chrome profile cache, code cache, GPU cache, and safe browsing data. * chore(deps): bump the npm_and_yarn group across 4 directories with 5 updates Bumps the npm_and_yarn group with 3 updates in the / directory: [ajv](https://github.com/ajv-validator/ajv), [hono](https://github.com/honojs/hono) and [qs](https://github.com/ljharb/qs). Bumps the npm_and_yarn group with 3 updates in the /docs/knowledge_prototypes/mcp-servers/fetch-mcp directory: [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk), [ajv](https://github.com/ajv-validator/ajv) and [hono](https://github.com/honojs/hono). Bumps the npm_and_yarn group with 1 update in the /scripts/archive/software-on-demand directory: [ajv](https://github.com/ajv-validator/ajv). Bumps the npm_and_yarn group with 2 updates in the /scripts/archive/supabase_cleanup directory: [next](https://github.com/vercel/next.js) and [qs](https://github.com/ljharb/qs). Updates `ajv` from 8.17.1 to 8.18.0 - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](ajv-validator/ajv@v8.17.1...v8.18.0) Updates `hono` from 4.11.7 to 4.12.1 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](honojs/hono@v4.11.7...v4.12.1) Updates `qs` from 6.14.1 to 6.15.0 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](ljharb/qs@v6.14.1...v6.15.0) Updates `@modelcontextprotocol/sdk` from 1.25.2 to 1.26.0 - [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases) - [Commits](modelcontextprotocol/typescript-sdk@v1.25.2...v1.26.0) Updates `ajv` from 8.17.1 to 8.18.0 - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](ajv-validator/ajv@v8.17.1...v8.18.0) Updates `hono` from 4.11.5 to 4.12.1 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](honojs/hono@v4.11.7...v4.12.1) Updates `qs` from 6.14.1 to 6.15.0 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](ljharb/qs@v6.14.1...v6.15.0) Updates `ajv` from 8.17.1 to 8.18.0 - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](ajv-validator/ajv@v8.17.1...v8.18.0) Updates `next` from 15.4.10 to 15.5.10 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](vercel/next.js@v15.4.10...v15.5.10) Updates `qs` from 6.14.1 to 6.15.0 - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](ljharb/qs@v6.14.1...v6.15.0) --- updated-dependencies: - dependency-name: ajv dependency-version: 8.18.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: hono dependency-version: 4.12.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: qs dependency-version: 6.15.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: "@modelcontextprotocol/sdk" dependency-version: 1.26.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: ajv dependency-version: 8.18.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: hono dependency-version: 4.12.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: qs dependency-version: 6.15.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: ajv dependency-version: 8.18.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: next dependency-version: 15.5.10 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: qs dependency-version: 6.15.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump minimatch Bumps the npm_and_yarn group with 1 update in the /scripts/archive/supabase_cleanup directory: [minimatch](https://github.com/isaacs/minimatch). Updates `minimatch` from 3.1.2 to 3.1.4 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v3.1.2...v3.1.4) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): bump the npm_and_yarn group across 2 directories with 1 update Bumps the npm_and_yarn group with 1 update in the / directory: [hono](https://github.com/honojs/hono). Bumps the npm_and_yarn group with 1 update in the /docs/knowledge_prototypes/mcp-servers/fetch-mcp directory: [hono](https://github.com/honojs/hono). Updates `hono` from 4.12.1 to 4.12.2 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](honojs/hono@v4.12.1...v4.12.2) Updates `hono` from 4.12.1 to 4.12.2 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](honojs/hono@v4.12.1...v4.12.2) --- updated-dependencies: - dependency-name: hono dependency-version: 4.12.2 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: hono dependency-version: 4.12.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * feat: enable frontend-only video ingestion pipeline for Vercel deployment The core pipeline previously required the Python backend to be running. When deployed to Vercel (https://v0-uvai.vercel.app/), the backend is unavailable, causing all video analysis to fail immediately. Changes: - /api/video: Falls back to frontend-only pipeline (transcribe + extract) when the Python backend is unreachable, with 15s timeout - /api/transcribe: Adds Gemini fallback when OpenAI is unavailable, plus 8s timeout on backend probe to avoid hanging on Vercel - layout.tsx: Loads Google Fonts via <link> instead of next/font/google to avoid build failures in offline/sandboxed CI environments - page.tsx: Replace example URLs with technical content (3Blue1Brown neural networks, Karpathy LLM intro) instead of rick roll / zoo videos - gemini_service.py: Gate Vertex AI import behind GOOGLE_CLOUD_PROJECT env var to prevent 30s+ hangs on the GCE metadata probe - agent_gap_analyzer.py: Fix f-string backslash syntax errors (Python 3.11) https://claude.ai/code/session_015Pd3a6hinTenCNrPRGiZqE * Potential fix for code scanning alert no. 4518: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Initial plan * Potential fix for code scanning alert no. 4517: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Initial plan * Fix review feedback: timeout cleanup, transcript_segments shape, ENABLE_VERTEX_AI boolean parsing Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> * fix: clearTimeout in finally blocks, transcript_segments shape, ENABLE_VERTEX_AI boolean parsing Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> * Update src/youtube_extension/services/ai/gemini_service.py Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> * Update apps/web/src/app/api/video/route.ts Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> * Update apps/web/src/app/api/video/route.ts Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> * Initial plan * Initial plan * Fix: move clearTimeout into .finally() to prevent timer leaks on fetch abort/error Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> * Fix clearTimeout not called in finally blocks for AbortController timeouts Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> * Fix: Relative URLs in server-side fetch calls fail in production - fetch('/api/transcribe') and fetch('/api/extract-events') use relative URLs which don't resolve correctly in server-side Next.js code on production deployments like Vercel. This commit fixes the issue reported at apps/web/src/app/api/video/route.ts:101 ## Bug Analysis **Why it happens:** In Next.js API routes running on the server (Node.js runtime), the `fetch()` API requires absolute URLs. Unlike browsers which have an implicit base URL (the current origin), server-side code has no context for resolving relative URLs like `/api/transcribe`. The Node.js fetch implementation will fail to resolve these relative paths, resulting in TypeError or connection errors. **When it manifests:** - **Development (localhost:3000)**: Works accidentally because the request URL contains the host - **Production (Vercel)**: Fails because the relative URL cannot be resolved to a valid absolute URL without proper host context **What impact it has:** The frontend-only pipeline fallback (Strategy 2) in lines 101-132 is completely broken in production. When the backend is unavailable (common on Vercel), the code attempts to use `/api/transcribe` and `/api/extract-events` serverless functions but fails due to unresolvable relative URLs. This causes the entire video analysis endpoint to fail when the backend is unavailable. ## Fix Explanation **Changes made:** 1. Added a `getBaseUrl(request: Request)` helper function that extracts the absolute base URL from the incoming request object using `new URL(request.url)` 2. Updated line 108: `fetch('/api/transcribe', ...)` → `fetch(`${baseUrl}/api/transcribe`, ...)` 3. Updated line 127: `fetch('/api/extract-events', ...)` → `fetch(`${baseUrl}/api/extract-events`, ...)` **Why it solves the issue:** - The incoming `request` object contains the full URL including protocol and host - By constructing an absolute URL from the request, we ensure the fetch calls work in both development and production - This approach is more reliable than environment variables because it uses the actual request context, handling reverse proxies and different deployment configurations correctly Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: groupthinking <garveyht@gmail.com> * Initial plan * chore(deps): bump the npm_and_yarn group across 1 directory with 1 update Bumps the npm_and_yarn group with 1 update in the /docs/knowledge_prototypes/mcp-servers/fetch-mcp directory: [minimatch](https://github.com/isaacs/minimatch). Updates `minimatch` from 3.1.2 to 3.1.5 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v3.1.2...v3.1.5) Updates `minimatch` from 5.1.6 to 5.1.9 - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](isaacs/minimatch@v3.1.2...v3.1.5) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: minimatch dependency-version: 5.1.9 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * fix: validate BACKEND_URL before using it Skip backend calls entirely when BACKEND_URL is not configured or contains an invalid value (like a literal ${...} template string). This prevents URL parse errors on Vercel where the env var may not be set. https://claude.ai/code/session_015Pd3a6hinTenCNrPRGiZqE * fix: resolve embeddings package build errors (#41) - Create stub types for Firebase Data Connect SDK in src/dataconnect-generated/ - Fix import path from ../dataconnect-generated to ./dataconnect-generated (rootDir constraint) - Add explicit type assertions for JSON responses (predictions, access_token) - All 6 TypeScript errors resolved, clean build verified Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: Gemini SDK upgrade + VideoPack schema alignment (#43) * chore: Update generated Chrome profile cache and session data for notebooklm. * chore: refresh notebooklm Chrome profile data, including Safe Browsing lists, caches, and session files. * Update local application cache and database files within the NotebookLM Chrome profile. * chore: update Chrome profile cache and Safe Browsing data files. * feat: upgrade Gemini to @google/genai SDK with structured output, search grounding, video URL processing, and extend VideoPack schema - Upgrade extract-events/route.ts from @google/generative-ai to @google/genai - Add Gemini responseSchema with Type system for structured output enforcement - Add Google Search grounding (googleSearch tool) to Gemini calls - Upgrade transcribe/route.ts to @google/genai with direct YouTube URL processing via fileData - Add Gemini video URL fallback chain: direct video → text+search → other strategies - Extend VideoPackV0 schema with Chapter, CodeCue, Task models - Update versioning shim for new fields - Export new types from videopack __init__ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: wire CloudEvents pipeline + Chrome Built-in AI fallback (#44) - Add TypeScript CloudEvents publisher (apps/web/src/lib/cloudevents.ts) emitting standardized events at each video processing stage - Wire CloudEvents into /api/video route (both backend + frontend strategies) - Wire CloudEvents into FastAPI backend router (process_video_v1 endpoint) - Add Chrome Built-in AI service (Prompt API + Summarizer API) for on-device client-side transcript analysis when API keys are unavailable - Add useBuiltInAI React hook for component integration - Add .next/ to .gitignore Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: wire A2A inter-agent messaging into orchestrator + API (#45) - Add A2AContextMessage dataclass to AgentOrchestrator for lightweight inter-agent context sharing during parallel task execution - Auto-broadcast agent results to peer agents after parallel execution - Add send_a2a_message() and get_a2a_log() methods to orchestrator - Add POST /api/v1/agents/a2a/send endpoint for frontend-to-agent messaging - Add GET /api/v1/agents/a2a/log endpoint to query message history - Extend frontend agentService with sendA2AMessage() and getA2ALog() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: add LiteRT-LM setup script and update README (#46) - Add setup.sh to download lit CLI binary and .litertlm model - Support macOS arm64 and x86_64 architectures - Auto-generate .env with LIT_BINARY_PATH and LIT_MODEL_PATH - Add .gitignore for bin/, models/, .env - Update README with Quick Setup section Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: implement Gemini agentic video analysis with Google Search grounding (#47) - Create gemini-video-analyzer.ts: single Gemini call with googleSearch tool for transcript extraction AND event analysis (PK=998 pattern) - Add youtube-metadata.ts: scrapes title, description, chapters from YouTube without API key - Update /api/video: Gemini agentic analysis as primary strategy, transcribe→extract chain as fallback - Fix /api/transcribe: remove broken fileData.fileUri, use Gemini Google Search grounding as primary, add metadata context, filter garbage OpenAI results - Fix /api/extract-events: accept videoUrl without requiring transcript, direct Gemini analysis via Google Search when no transcript available Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: support Vertex_AI_API_KEY as Gemini key fallback Create shared gemini-client.ts that resolves API key from: GEMINI_API_KEY → GOOGLE_API_KEY → Vertex_AI_API_KEY All API routes now use the shared client instead of hardcoding process.env.GEMINI_API_KEY. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: use Vertex AI Express Mode for Vertex_AI_API_KEY When only Vertex_AI_API_KEY is set (no GEMINI_API_KEY), the client now initializes in Vertex AI mode with vertexai: true + apiKey. Uses project uvai-730bb and us-central1 as defaults. Also added GOOGLE_CLOUD_PROJECT env var to Vercel. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: Vertex AI Express Mode compatibility — remove responseSchema+googleSearch conflict (#48) Vertex AI does not support controlled generation (responseSchema) combined with the googleSearch tool. This caused 400 errors on every Gemini call. Changes: - gemini-client.ts: Prioritize Vertex_AI_API_KEY, support GOOGLE_GENAI_USE_VERTEXAI env var - gemini-video-analyzer.ts: Remove responseSchema, enforce JSON via prompt instructions - extract-events/route.ts: Same fix for extractWithGemini and inline Gemini calls - Strip markdown code fences from responses before JSON parsing Tested end-to-end with Vertex AI Express Mode key against multiple YouTube videos. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: restore full PK=998 pattern — responseSchema + googleSearch + gemini-3-pro-preview (#49) The previous fix (PR #48) was a shortcut — it removed responseSchema when the real issue was using gemini-2.5-flash which doesn't support responseSchema + googleSearch together on Vertex AI. gemini-3-pro-preview DOES support the combination. This commit restores the exact PK=998 pattern: - gemini-video-analyzer.ts: Restored responseSchema with Type system, responseMimeType, e22Snippets field, model → gemini-3-pro-preview - extract-events/route.ts: Restored geminiResponseSchema, Type import, responseMimeType, model → gemini-3-pro-preview - transcribe/route.ts: model → gemini-3-pro-preview Tested with Vertex AI Express Mode key on two YouTube videos. Both return structured JSON with events, transcript, actions, codeMapping, cloudService, e22Snippets, architectureCode, ingestScript. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: end-to-end pipeline — YouTube URL to deployed software (#50) - Add /api/pipeline route for full end-to-end pipeline (video analysis → code generation → GitHub repo → Vercel deploy) - Add deployPipeline() action to dashboard store with stage tracking - Add 🚀 Deploy button to dashboard alongside Analyze - Show pipeline results (live URL, GitHub repo, framework) in video cards - Fix deployment_manager import path in video_processing_service - Wire pipeline to backend /api/v1/video-to-software endpoint - Fallback to Gemini-only analysis when no backend available Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: add writable directories to Docker image for deployment pipeline Create /app/generated_projects, /app/youtube_processed_videos, and /tmp/uvai_data directories in Dockerfile to fix permission denied errors in the deployment and video processing pipeline on Railway. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: security hardening, video-specific codegen, API consistency - CORS: replace wildcard/glob with explicit allowed origins in both entry points - Rate limiting: enable 60 req/min with 15 burst on backend - API auth: add optional X-API-Key middleware for pipeline endpoints - Codegen: generate video-specific HTML/CSS/JS from analysis output - API: accept both 'url' and 'video_url' via Pydantic alias - Deploy: fix Vercel REST API payload format (gitSource instead of gitRepository) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: Vercel deployment returning empty live_url Root causes fixed: - Case mismatch in _poll_deployment_status: compared lowercased status against uppercase success_statuses list, so READY was never matched - Vercel API returns bare domain URLs without https:// prefix; added _ensure_https() to normalize them - Poll requests were missing auth headers, causing 401 failures - _deploy_files_directly fallback returned fake simulated URLs that masked real failures; removed in favor of proper error reporting - _generate_deployment_urls only returned URLs from 'success' status deployments, discarding useful fallback URLs from failed deployments Improvements: - On API failure (permissions, plan limits), return a Vercel import URL the user can click to deploy manually instead of an empty string - Support VERCEL_ORG_ID team scoping on deploy and poll endpoints - Use readyState field (Vercel v13 API) for initial status check - Add 'canceled' to failure status list in poll loop - Poll failures are now non-fatal; initial URL is used as fallback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: harden slim entry point — CORS, rate limiting, auth, security headers - Add uvaiio.vercel.app to CORS allowed origins - Add slowapi rate limiting (60 req/min) - Add API key auth middleware (optional via EVENTRELAY_API_KEY) - Add security headers (X-Content-Type-Options, X-Frame-Options, X-XSS-Protection) - Fixes production gap where slim main.py had none of the backend/main.py protections Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: resolve Pydantic Config/model_config conflict breaking Railway deploy The VideoToSoftwareRequest model had both 'model_config = ConfigDict(...)' and 'class Config:' which Pydantic v2 rejects. Merged into single model_config. This was causing the v1 router to fail loading, making /api/v1/health return 404. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8820c5b commit 99df2ac

12,310 files changed

Lines changed: 346724 additions & 814 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.firebase/.graphqlrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"schema":["../dataconnect/.dataconnect/**/*.gql","../dataconnect/schema/**/*.gql"],"document":["../dataconnect/example/**/*.gql"]}

.firebaserc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"projects": {
3+
"default": "uvai-730bb"
4+
},
5+
"targets": {},
6+
"etags": {}
7+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,4 @@ UVAI_Digital_Refinery_Blueprint.pdf
155155
*.db
156156
.vercel
157157
.env*.local
158+
.next/

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,6 @@
8181
"dart.enableCompletionCommitCharacters": true,
8282
"geminicodeassist.codeGenerationPaneViewEnabled": true,
8383
"geminicodeassist.inlineSuggestions.nextEditPredictions": true,
84-
"geminicodeassist.inlineSuggestions.suggestionSpeed": "Fast"
84+
"geminicodeassist.inlineSuggestions.suggestionSpeed": "Fast",
85+
"geminicodeassist.outlines.automaticOutlineGeneration": true
8586
}

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ COPY --chown=uvai:uvai src/ ./src/
4949
COPY --chown=uvai:uvai pyproject.toml ./
5050

5151
# Create data directories
52-
RUN mkdir -p /app/data/enhanced_analysis /app/data/cache /app/logs && \
53-
chown -R uvai:uvai /app/data /app/logs
52+
RUN mkdir -p /app/data/enhanced_analysis /app/data/cache /app/logs /app/generated_projects /app/youtube_processed_videos /tmp/uvai_data && \
53+
chown -R uvai:uvai /app/data /app/logs /app/generated_projects /app/youtube_processed_videos /tmp/uvai_data
5454

5555
# Switch to non-root user
5656
USER uvai

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@google/genai": "^1.43.0",
1213
"@google/generative-ai": "^0.24.1",
1314
"@stripe/stripe-js": "^2.0.0",
1415
"@supabase/supabase-js": "^2.39.0",

apps/web/src/app/api/extract-events/route.ts

Lines changed: 103 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
import OpenAI from 'openai';
2-
import { GoogleGenerativeAI } from '@google/generative-ai';
2+
import { Type } from '@google/genai';
33
import { NextResponse } from 'next/server';
4+
import { getGeminiClient, hasGeminiKey } from '@/lib/gemini-client';
45

56
let _openai: OpenAI | null = null;
67
function getOpenAI() {
78
if (!_openai) _openai = new OpenAI();
89
return _openai;
910
}
1011

11-
let _gemini: GoogleGenerativeAI | null = null;
12-
function getGemini() {
13-
if (!_gemini) _gemini = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || '');
14-
return _gemini;
15-
}
16-
17-
// JSON Schema for structured extraction via Responses API
12+
// JSON Schema for structured extraction via OpenAI Responses API
1813
const extractionSchema = {
1914
type: 'object' as const,
2015
properties: {
@@ -54,6 +49,43 @@ const extractionSchema = {
5449
additionalProperties: false,
5550
};
5651

52+
// Gemini responseSchema using @google/genai Type system
53+
const geminiResponseSchema = {
54+
type: Type.OBJECT,
55+
properties: {
56+
events: {
57+
type: Type.ARRAY,
58+
items: {
59+
type: Type.OBJECT,
60+
properties: {
61+
type: { type: Type.STRING, enum: ['action', 'topic', 'insight', 'tool', 'resource'] },
62+
title: { type: Type.STRING },
63+
description: { type: Type.STRING },
64+
timestamp: { type: Type.STRING, nullable: true },
65+
priority: { type: Type.STRING, enum: ['high', 'medium', 'low'] },
66+
},
67+
required: ['type', 'title', 'description', 'priority'],
68+
},
69+
},
70+
actions: {
71+
type: Type.ARRAY,
72+
items: {
73+
type: Type.OBJECT,
74+
properties: {
75+
title: { type: Type.STRING },
76+
description: { type: Type.STRING },
77+
category: { type: Type.STRING, enum: ['setup', 'build', 'deploy', 'learn', 'research', 'configure'] },
78+
estimatedMinutes: { type: Type.NUMBER, nullable: true },
79+
},
80+
required: ['title', 'description', 'category'],
81+
},
82+
},
83+
summary: { type: Type.STRING },
84+
topics: { type: Type.ARRAY, items: { type: Type.STRING } },
85+
},
86+
required: ['events', 'actions', 'summary', 'topics'],
87+
};
88+
5789
const SYSTEM_PROMPT = `You are an expert content analyst. Extract structured data from video transcripts.
5890
Be specific and practical — no vague or generic items.
5991
For events: classify type (action/topic/insight/tool/resource) and priority (high/medium/low).
@@ -94,54 +126,91 @@ async function extractWithOpenAI(trimmed: string, videoTitle?: string, videoUrl?
94126
}
95127

96128
async function extractWithGemini(trimmed: string, videoTitle?: string, videoUrl?: string) {
97-
const model = getGemini().getGenerativeModel({
98-
model: 'gemini-2.0-flash',
99-
generationConfig: {
100-
responseMimeType: 'application/json',
129+
const ai = getGeminiClient();
130+
const response = await ai.models.generateContent({
131+
model: 'gemini-3-pro-preview',
132+
contents: `${SYSTEM_PROMPT}\n\n${buildUserPrompt(trimmed, videoTitle, videoUrl)}`,
133+
config: {
101134
temperature: 0.3,
135+
responseMimeType: 'application/json',
136+
responseSchema: geminiResponseSchema,
137+
tools: [{ googleSearch: {} }],
102138
},
103139
});
104-
const result = await model.generateContent(`${SYSTEM_PROMPT}\n\n${buildUserPrompt(trimmed, videoTitle, videoUrl)}`);
105-
const text = result.response.text();
140+
const text = response.text ?? '';
106141
return JSON.parse(text);
107142
}
108143

109144
export async function POST(request: Request) {
110145
try {
111146
const { transcript, videoTitle, videoUrl } = await request.json();
112147

113-
if (!transcript || typeof transcript !== 'string') {
148+
// Accept either transcript text OR videoUrl for direct Gemini analysis
149+
if ((!transcript || typeof transcript !== 'string') && !videoUrl) {
114150
return NextResponse.json(
115-
{ error: 'transcript (string) is required' },
151+
{ error: 'transcript (string) or videoUrl is required' },
116152
{ status: 400 }
117153
);
118154
}
119155

120-
const trimmed = transcript.slice(0, 8000);
121156
let parsed;
122157
let provider = 'openai';
123158

124-
// Try OpenAI first, fall back to Gemini on quota/auth errors
125-
if (process.env.OPENAI_API_KEY) {
126-
try {
127-
parsed = await extractWithOpenAI(trimmed, videoTitle, videoUrl);
128-
} catch (err) {
129-
const msg = err instanceof Error ? err.message : '';
130-
if ((msg.includes('429') || msg.includes('quota') || msg.includes('rate')) && process.env.GEMINI_API_KEY) {
131-
console.warn('OpenAI quota hit, falling back to Gemini');
132-
parsed = await extractWithGemini(trimmed, videoTitle, videoUrl);
133-
provider = 'gemini';
134-
} else {
135-
throw err;
159+
// If we have transcript text, use the existing extraction logic
160+
if (transcript && typeof transcript === 'string' && transcript.length > 50) {
161+
const trimmed = transcript.slice(0, 8000);
162+
163+
if (process.env.OPENAI_API_KEY) {
164+
try {
165+
parsed = await extractWithOpenAI(trimmed, videoTitle, videoUrl);
166+
} catch (err) {
167+
const msg = err instanceof Error ? err.message : '';
168+
if ((msg.includes('429') || msg.includes('quota') || msg.includes('rate')) && hasGeminiKey()) {
169+
console.warn('OpenAI quota hit, falling back to Gemini');
170+
parsed = await extractWithGemini(trimmed, videoTitle, videoUrl);
171+
provider = 'gemini';
172+
} else {
173+
throw err;
174+
}
136175
}
176+
} else if (hasGeminiKey()) {
177+
parsed = await extractWithGemini(trimmed, videoTitle, videoUrl);
178+
provider = 'gemini';
137179
}
138-
} else if (process.env.GEMINI_API_KEY) {
139-
parsed = await extractWithGemini(trimmed, videoTitle, videoUrl);
140-
provider = 'gemini';
141-
} else {
180+
}
181+
182+
// If no transcript but have videoUrl + Gemini, do direct video analysis via Google Search
183+
if (!parsed && videoUrl && hasGeminiKey()) {
184+
try {
185+
const ai = getGeminiClient();
186+
const response = await ai.models.generateContent({
187+
model: 'gemini-3-pro-preview',
188+
contents: `${SYSTEM_PROMPT}\n\nAnalyze this YouTube video and extract structured data.
189+
Use your Google Search tool to find the video's transcript, description, and chapter content.
190+
191+
Video URL: ${videoUrl}
192+
${videoTitle ? `Video Title: ${videoTitle}` : ''}
193+
194+
Extract events, actions, summary, and topics from the actual video content found via search.`,
195+
config: {
196+
temperature: 0.3,
197+
responseMimeType: 'application/json',
198+
responseSchema: geminiResponseSchema,
199+
tools: [{ googleSearch: {} }],
200+
},
201+
});
202+
const text = response.text ?? '';
203+
parsed = JSON.parse(text);
204+
provider = 'gemini-search';
205+
} catch (e) {
206+
console.warn('Gemini direct video extraction failed:', e);
207+
}
208+
}
209+
210+
if (!parsed) {
142211
return NextResponse.json({
143212
success: false,
144-
error: 'No AI API key configured. Set OPENAI_API_KEY or GEMINI_API_KEY.',
213+
error: 'No AI API key configured or all extraction attempts failed. Set GEMINI_API_KEY.',
145214
data: { events: [], actions: [], summary: '', topics: [] },
146215
});
147216
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { NextResponse } from 'next/server';
2+
import { publishEvent, EventTypes } from '@/lib/cloudevents';
3+
import { analyzeVideoWithGemini } from '@/lib/gemini-video-analyzer';
4+
import { hasGeminiKey } from '@/lib/gemini-client';
5+
6+
const rawBackendUrl = process.env.BACKEND_URL || '';
7+
const BACKEND_URL = rawBackendUrl.startsWith('http') ? rawBackendUrl : 'http://localhost:8000';
8+
const BACKEND_AVAILABLE = rawBackendUrl.startsWith('http');
9+
10+
/**
11+
* POST /api/pipeline
12+
*
13+
* End-to-end pipeline: YouTube URL → Video Analysis → Code Generation → Deployment → Live URL
14+
*
15+
* This is the FULL pipeline that the user's notes describe (PK=999, PK=1021):
16+
* Ingest → Translate → Transport → Execute
17+
*
18+
* Strategies:
19+
* 1. Backend pipeline (FastAPI /api/v1/video-to-software) — full pipeline with agents
20+
* 2. Gemini analysis + frontend deployment — when no backend is available
21+
*/
22+
export async function POST(request: Request) {
23+
let videoUrl: string | undefined;
24+
try {
25+
const body = await request.json();
26+
const { url, project_type = 'web', deployment_target = 'vercel', features } = body;
27+
videoUrl = url;
28+
29+
if (!url) {
30+
return NextResponse.json({ error: 'Video URL is required' }, { status: 400 });
31+
}
32+
33+
await publishEvent(EventTypes.VIDEO_RECEIVED, { url, pipeline: 'end-to-end' }, url);
34+
35+
// ── Strategy 1: Full backend pipeline (FastAPI video-to-software) ──
36+
if (BACKEND_AVAILABLE) {
37+
try {
38+
const controller = new AbortController();
39+
const timeout = setTimeout(() => controller.abort(), 300_000); // 5 min for full pipeline
40+
41+
let response: Response;
42+
try {
43+
response = await fetch(`${BACKEND_URL}/api/v1/video-to-software`, {
44+
method: 'POST',
45+
headers: { 'Content-Type': 'application/json' },
46+
body: JSON.stringify({
47+
video_url: url,
48+
project_type,
49+
deployment_target,
50+
features: features || ['responsive_design', 'modern_ui'],
51+
}),
52+
signal: controller.signal,
53+
});
54+
} finally {
55+
clearTimeout(timeout);
56+
}
57+
58+
if (response.ok) {
59+
const result = await response.json();
60+
61+
await publishEvent(EventTypes.PIPELINE_COMPLETED, {
62+
strategy: 'backend-pipeline',
63+
success: result.status === 'success',
64+
live_url: result.live_url,
65+
github_repo: result.github_repo,
66+
build_status: result.build_status,
67+
}, url);
68+
69+
return NextResponse.json({
70+
id: `pipeline_${Date.now().toString(36)}`,
71+
status: result.status || 'complete',
72+
pipeline: 'backend',
73+
processing_time: result.processing_time,
74+
result: {
75+
live_url: result.live_url,
76+
github_repo: result.github_repo,
77+
build_status: result.build_status,
78+
video_analysis: result.video_analysis,
79+
code_generation: result.code_generation,
80+
deployment: result.deployment,
81+
features_implemented: result.features_implemented,
82+
},
83+
});
84+
}
85+
console.warn(`Backend pipeline returned ${response.status}, falling back`);
86+
} catch (e) {
87+
console.log('Backend pipeline unavailable:', e);
88+
}
89+
}
90+
91+
// ── Strategy 2: Gemini analysis (video intelligence only, no deployment) ──
92+
if (hasGeminiKey()) {
93+
try {
94+
const startTime = Date.now();
95+
const analysis = await analyzeVideoWithGemini(url);
96+
const elapsed = Date.now() - startTime;
97+
98+
await publishEvent(EventTypes.PIPELINE_COMPLETED, {
99+
strategy: 'gemini-analysis-only',
100+
success: true,
101+
note: 'Backend unavailable — analysis only, no deployment',
102+
}, url);
103+
104+
return NextResponse.json({
105+
id: `pipeline_${Date.now().toString(36)}`,
106+
status: 'partial',
107+
pipeline: 'gemini-only',
108+
processing_time: `${(elapsed / 1000).toFixed(1)}s`,
109+
result: {
110+
live_url: null,
111+
github_repo: null,
112+
build_status: 'not_attempted',
113+
video_analysis: {
114+
title: analysis.title,
115+
summary: analysis.summary,
116+
events: analysis.events,
117+
actions: analysis.actions,
118+
topics: analysis.topics,
119+
architectureCode: analysis.architectureCode,
120+
},
121+
code_generation: null,
122+
deployment: null,
123+
message: 'Backend pipeline unavailable. Video analysis complete but code generation and deployment require the Python backend.',
124+
},
125+
});
126+
} catch (e) {
127+
console.error('Gemini analysis failed:', e);
128+
}
129+
}
130+
131+
return NextResponse.json(
132+
{ error: 'No pipeline available. Configure BACKEND_URL for full pipeline or GEMINI_API_KEY for analysis only.' },
133+
{ status: 503 },
134+
);
135+
} catch (error) {
136+
console.error('Pipeline error:', error);
137+
await publishEvent(EventTypes.PIPELINE_FAILED, { error: String(error) }, videoUrl).catch(() => {});
138+
return NextResponse.json(
139+
{ error: 'Pipeline failed', details: String(error) },
140+
{ status: 500 },
141+
);
142+
}
143+
}
144+
145+
export async function GET() {
146+
return NextResponse.json({
147+
name: 'EventRelay End-to-End Pipeline',
148+
version: '1.0.0',
149+
description: 'YouTube URL → Video Analysis → Code Generation → Deployment → Live URL',
150+
pipeline_stages: [
151+
'1. Ingest: Gemini analyzes video content with Google Search grounding',
152+
'2. Translate: Structured output → VideoPack artifact',
153+
'3. Transport: CloudEvents published at each stage',
154+
'4. Execute: Agents generate code, create repo, deploy to Vercel',
155+
],
156+
backend_available: BACKEND_AVAILABLE,
157+
gemini_available: hasGeminiKey(),
158+
endpoints: {
159+
pipeline: 'POST /api/pipeline - Full end-to-end pipeline',
160+
video: 'POST /api/video - Video analysis only',
161+
},
162+
});
163+
}

0 commit comments

Comments
 (0)