diff --git a/.env.example b/.env.example index 919a5c64..940c6bac 100644 --- a/.env.example +++ b/.env.example @@ -21,8 +21,18 @@ NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= CLERK_SECRET_KEY= # OpenAI API (get from https://platform.openai.com/) - OPENAI_API_KEY= +OPENAI_MODEL="gpt-5-mini" + +# Anthropic API (optional — enables Claude models, get from https://console.anthropic.com/) +ANTHROPIC_API_KEY= + +# Google AI API (optional — enables Gemini models, get from https://aistudio.google.com/apikey) +GOOGLE_AI_API_KEY= + +# Ollama (optional — enables local models via Ollama, see https://ollama.com/) +OLLAMA_BASE_URL="http://localhost:11434" +OLLAMA_MODEL="llama3.1:8b" # UploadThing (get from https://uploadthing.com/) UPLOADTHING_SECRET="your_uploadthing_secret" @@ -45,3 +55,11 @@ AZURE_DOC_INTELLIGENCE_KEY="your_azure_doc_intelligence_key" # Inngest (required for background document processing - https://inngest.com/) INNGEST_EVENT_KEY="dev_placeholder" INNGEST_SIGNING_KEY="signkey-dev-xxxxx" + +# Sidecar (optional - get from https://github.com/pdr-ai/sidecar) +SIDECAR_URL="your_sidecar_url" + +# Neo4j (optional - get from https://neo4j.com/) +NEO4J_URI="your_neo4j_uri" +NEO4J_USERNAME="your_neo4j_username" +NEO4J_PASSWORD="your_neo4j_password" diff --git a/.gitignore b/.gitignore index 261762e5..d8641b90 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ yarn-error.log* .idea /.localFiles .windsurf/rules/markdowncreation.md +/public/vad +*.pyc # kiro .kiro diff --git a/README.md b/README.md index 493a9885..76a84e37 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# PDR AI - Professional Document Reader AI +# Launchstack - Professional Document Reader AI -PDR AI is a Next.js platform for role-based document management, AI-assisted Q&A, and predictive document analysis. It combines document upload, optional OCR, embeddings, and retrieval to help teams find gaps and act faster. +Launchstack is a Next.js platform for role-based document management, AI-assisted Q&A, and predictive document analysis. It combines document upload, optional OCR, embeddings, and retrieval to help teams find gaps and act faster. ## Core Features @@ -8,12 +8,83 @@ PDR AI is a Next.js platform for role-based document management, AI-assisted Q&A - Document upload pipeline with optional OCR for scanned PDFs. - PostgreSQL + pgvector semantic retrieval for RAG workflows. - AI chat and predictive document analysis over uploaded content. +- Agent guardrails with PII filtering, grounding checks, and confidence gating. +- Supervisor agent that validates outputs against domain-specific rubrics. +- Marketing pipeline with content generation for Reddit, X, LinkedIn, and Bluesky. - Optional web-enriched analysis with Tavily. - Optional reliability/observability via Inngest and LangSmith. +## Predictive Analysis — Supported Document Types + +Launchstack runs domain-specific analysis tailored to your document type: + +| Type | What It Detects | +|------|----------------| +| **Contract** | Missing exhibits, schedules, addendums, and supporting agreements | +| **Financial** | Missing balance sheets, audit reports, income statements | +| **Technical** | Missing specifications, manuals, diagrams, deliverables | +| **Compliance** | Missing regulatory filings, certifications, policy documents | +| **Educational** | Missing syllabi, handouts, readings, linked resources | +| **HR** | Missing policies, forms, benefits materials, handbooks | +| **Research** | Missing cited papers, datasets, supplementary materials | +| **General** | Any document with cross-references and attachments | + +Each analysis type also extracts insights (deadlines, action items, resources, caveats) and runs chain-of-verification on high-priority predictions. + +## Importing External Knowledge + +Launchstack can ingest content exported from third-party tools. No API keys or OAuth setup required — export your data, upload the files, and the ingestion pipeline handles the rest. + +### Supported Export Formats + +| Source | Export Method | Resulting Format | Launchstack Adapter | +|--------|-------------|-----------------|----------------| +| **Notion** | Settings > Export > Markdown & CSV | `.md`, `.csv` (ZIP) | TextAdapter, SpreadsheetAdapter | +| **Notion** | Page > Export > HTML | `.html` | HtmlAdapter | +| **Google Docs** | File > Download > Microsoft Word | `.docx` | DocxAdapter | +| **Google Sheets** | File > Download > CSV or Excel | `.csv`, `.xlsx` | SpreadsheetAdapter | +| **Google Drive** | Google Takeout (takeout.google.com) | `.docx` (ZIP) | DocxAdapter | +| **Slack** | Workspace Settings > Import/Export > Export | `.json` (ZIP) | JsonExportAdapter | +| **GitHub** | Code > Download ZIP | `.md`, `.txt` (ZIP) | TextAdapter | +| **GitHub** | `gh issue list --json ...` | `.json` | JsonExportAdapter | +| **GitHub** | `gh pr list --json ...` | `.json` | JsonExportAdapter | + +### How to Export + +**Notion** +1. Open your Notion workspace. +2. Click the **...** menu on a page, or go to **Settings & members > Export** for a full workspace export. +3. Select **Markdown & CSV** as the format and check **Include subpages** if needed. +4. Download the ZIP and upload it directly to Launchstack. + +**Google Docs / Sheets** +1. Open the document in Google Docs or Sheets. +2. Go to **File > Download** and choose **Microsoft Word (.docx)** or **CSV / Excel (.xlsx)**. +3. Upload the downloaded file. For bulk exports, use [Google Takeout](https://takeout.google.com) to export your Drive as a ZIP. + +**Slack** +1. Go to **Workspace Settings > Import/Export Data > Export**. +2. Choose a date range and start the export. +3. Download the ZIP and upload it to Launchstack. Each channel's messages will be ingested as a separate document. + +**GitHub** +1. **Repo docs**: Click **Code > Download ZIP** on any GitHub repository. Upload the ZIP — all Markdown and text files will be ingested. +2. **Issues**: Install the [GitHub CLI](https://cli.github.com/) and run: + ```bash + gh issue list --state all --limit 1000 --json number,title,body,state,labels,author,createdAt,closedAt,comments > issues.json + ``` + Upload the resulting `issues.json` file. +3. **Pull requests**: Run: + ```bash + gh pr list --state all --limit 1000 --json number,title,body,state,labels,author,createdAt,mergedAt,comments > prs.json + ``` + Upload the resulting `prs.json` file. + +All uploaded content flows through the standard ingestion pipeline (chunking, embedding, RAG indexing) and becomes searchable alongside your other documents. + ## Architecture -PDR AI follows a three-layer modular architecture: +Launchstack follows a three-layer modular architecture: ```mermaid block-beta @@ -137,6 +208,10 @@ Optional integrations: - `LANDING_AI_API_KEY`, `DATALAB_API_KEY` - `LANGCHAIN_TRACING_V2`, `LANGCHAIN_API_KEY`, `LANGCHAIN_PROJECT` - `DEBUG_PERF` (`1` or `true`) to enable dev perf logs for middleware and key auth/dashboard APIs +- `SIDECAR_URL` +- `NEO4J_URI` +- `NEO4J_USERNAME` +- `NEO4J_PASSWORD` ### 2.1) Configure Vercel Blob Storage @@ -242,6 +317,12 @@ pnpm build pnpm start ``` +## Roadmap — Future Integrations + +- **Notion API-key connector**: Paste your Notion Internal Integration token in settings, select pages to sync. No OAuth required. Contributions welcome. +- **GitHub webhook sync**: Automatically ingest new issues and PRs via repository webhooks. +- **Google Drive watch**: Automatic re-sync when Google Docs are updated, using Drive push notifications. + ## Troubleshooting - Confirm Docker is running before DB startup. diff --git a/docker-compose.yml b/docker-compose.yml index c9442ba6..7b41ca40 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -75,6 +75,12 @@ services: AZURE_DOC_INTELLIGENCE_ENDPOINT: ${AZURE_DOC_INTELLIGENCE_ENDPOINT:-} AZURE_DOC_INTELLIGENCE_KEY: ${AZURE_DOC_INTELLIGENCE_KEY:-} LANDING_AI_API_KEY: ${LANDING_AI_API_KEY:-} + # Anthropic / Google (optional — enables Claude and Gemini models) + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + GOOGLE_AI_API_KEY: ${GOOGLE_AI_API_KEY:-} + # Ollama (optional — set OLLAMA_BASE_URL to an Ollama instance) + OLLAMA_BASE_URL: ${OLLAMA_BASE_URL:-} + OLLAMA_MODEL: ${OLLAMA_MODEL:-} ports: - "3000:3000" diff --git a/docs/Architechture/Overall.md b/docs/Architechture/Overall.md index fb0fcafd..20c63838 100644 --- a/docs/Architechture/Overall.md +++ b/docs/Architechture/Overall.md @@ -1,4 +1,4 @@ -# PDR AI — Modular Architecture Overview +# Launchstack — Modular Architecture Overview ## Three-Layer Architecture diff --git a/docs/deployment.md b/docs/deployment.md index 4ef23d95..92427abc 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,6 +1,6 @@ # Deployment Guide -This document covers deployment options for PDR AI. +This document covers deployment options for Launchstack. ## Prerequisites diff --git a/docs/feature-workflows.md b/docs/feature-workflows.md index e03d6619..1832a4d5 100644 --- a/docs/feature-workflows.md +++ b/docs/feature-workflows.md @@ -1,10 +1,10 @@ # Feature Workflows and Architecture -This document explains how major PDR AI features connect end to end. +This document explains how major Launchstack features connect end to end. ## End-to-end workflow -PDR AI follows this loop: +Launchstack follows this loop: 1. Authenticate user with role context (Employer/Employee) 2. Upload document (cloud or database storage) diff --git a/docs/observability.md b/docs/observability.md index 96513ef2..1bb757dc 100644 --- a/docs/observability.md +++ b/docs/observability.md @@ -1,6 +1,6 @@ # Observability & Metrics -The PDR AI backend exposes Prometheus-compatible metrics for request health, cache efficiency, and search behavior. +The Launchstack backend exposes Prometheus-compatible metrics for request health, cache efficiency, and search behavior. ## Available Metrics diff --git a/drizzle/0004_document_mime_type.sql b/drizzle/0004_document_mime_type.sql new file mode 100644 index 00000000..56cc2e31 --- /dev/null +++ b/drizzle/0004_document_mime_type.sql @@ -0,0 +1 @@ +ALTER TABLE "document" ADD COLUMN "mime_type" varchar(128); diff --git a/next.config.ts b/next.config.ts index 2a4ca279..56fb40bb 100644 --- a/next.config.ts +++ b/next.config.ts @@ -35,6 +35,10 @@ const config: NextConfig = { return webpackConfig; }, + experimental: { + middlewareClientMaxBodySize: "128mb", + }, + eslint: { ignoreDuringBuilds: true }, typescript: { ignoreBuildErrors: true }, diff --git a/package.json b/package.json index d132af48..ae84875a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "pdr_ai_v2", + "name": "launchstack", "version": "0.1.0", "private": true, "type": "module", @@ -34,6 +34,7 @@ "@langchain/core": "^0.3.74", "@langchain/google-genai": "^2.1.3", "@langchain/langgraph": "^0.4.9", + "@langchain/ollama": "^0.1.6", "@langchain/openai": "^0.6.11", "@langchain/textsplitters": "^0.1.0", "@napi-rs/canvas": "^0.1.88", @@ -79,6 +80,7 @@ "cmdk": "^1.1.1", "dayjs": "^1.11.18", "diff": "^8.0.3", + "docxtemplater": "^3.68.3", "dotenv": "^17.3.1", "drizzle-orm": "^0.45.1", "duck-duck-scrape": "^2.2.7", @@ -86,6 +88,7 @@ "formdata-node": "^6.0.3", "framer-motion": "^12.29.2", "geist": "^1.5.1", + "highlight.js": "^11.11.1", "inngest": "^3.49.3", "input-otp": "^1.4.2", "jszip": "^3.10.1", @@ -96,6 +99,7 @@ "mammoth": "^1.11.0", "marked": "^17.0.3", "motion": "^12.29.2", + "neo4j-driver": "^6.0.1", "next": "^15.5.7", "next-themes": "^0.4.6", "node-fetch": "^3.3.2", @@ -108,6 +112,7 @@ "pdf2pic": "^3.2.0", "pdfjs-dist": "^5.4.530", "pdfjs-serverless": "^1.1.0", + "pizzip": "^3.2.0", "postgres": "^3.4.7", "prom-client": "^15.1.3", "re-resizable": "^6.11.2", @@ -174,4 +179,4 @@ "initVersion": "7.38.1" }, "packageManager": "pnpm@10.15.1" -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 897b739a..61d83649 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,7 +34,7 @@ importers: version: 1.3.3(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76))) '@langchain/community': specifier: ^0.3.54 - version: 0.3.54(6d1a988c27bb7046e0625b67df4a39a3) + version: 0.3.54(750a6d03ce53b10eab8b6a936ee9a7b0) '@langchain/core': specifier: ^0.3.74 version: 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)) @@ -44,6 +44,9 @@ importers: '@langchain/langgraph': specifier: ^0.4.9 version: 0.4.9(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.1(zod@3.25.76)) + '@langchain/ollama': + specifier: ^0.1.6 + version: 0.1.6(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76))) '@langchain/openai': specifier: ^0.6.11 version: 0.6.11(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(ws@8.19.0) @@ -179,6 +182,9 @@ importers: diff: specifier: ^8.0.3 version: 8.0.3 + docxtemplater: + specifier: ^3.68.3 + version: 3.68.3 dotenv: specifier: ^17.3.1 version: 17.3.1 @@ -200,6 +206,9 @@ importers: geist: specifier: ^1.5.1 version: 1.5.1(next@15.5.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 inngest: specifier: ^3.49.3 version: 3.49.3(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(next@15.5.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.9.2)(zod@3.25.76) @@ -214,7 +223,7 @@ importers: version: 0.16.25 langchain: specifier: ^0.3.33 - version: 0.3.33(4c63b96815301c04536f0c3ba2bb9f23) + version: 0.3.33(db6e0a2bb4f8eef45b6b578f9c2526eb) lru-cache: specifier: ^11.2.6 version: 11.2.6 @@ -230,6 +239,9 @@ importers: motion: specifier: ^12.29.2 version: 12.29.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + neo4j-driver: + specifier: ^6.0.1 + version: 6.0.1 next: specifier: ^15.5.7 version: 15.5.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.55.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -266,6 +278,9 @@ importers: pdfjs-serverless: specifier: ^1.1.0 version: 1.1.0 + pizzip: + specifier: ^3.2.0 + version: 3.2.0 postgres: specifier: ^3.4.7 version: 3.4.7 @@ -2406,6 +2421,12 @@ packages: zod-to-json-schema: optional: true + '@langchain/ollama@0.1.6': + resolution: {integrity: sha512-hS+xHiRqKpq37eGyZQ0JoTghfNA7hWXK54XbILQ0KVm3v2MMWLKqBAep4LwMLrAr4NE7SIp+SJnQRdsjabR0jw==} + engines: {node: '>=18'} + peerDependencies: + '@langchain/core': '>=0.2.21 <0.4.0' + '@langchain/openai@0.6.11': resolution: {integrity: sha512-BkaudQTLsmdt9mF6tn6CrsK2TEFKk4EhAWYkouGTy/ljJIH/p2Nz9awIOGdrQiQt6AJ5mvKGupyVqy3W/jim2Q==} engines: {node: '>=18'} @@ -4432,6 +4453,10 @@ packages: resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} engines: {node: '>=10.0.0'} + '@xmldom/xmldom@0.9.8': + resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} + engines: {node: '>=14.6'} + abort-controller-x@0.4.3: resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} @@ -5102,6 +5127,10 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + docxtemplater@3.68.3: + resolution: {integrity: sha512-hTZfGcHgN60A09w68Qj0EQRCnF5kf2/ohFlZlUVqAOozCFwx9QMm8naCTvmTsXafuO3nG9qpS4pQWSjFdaCWfQ==} + engines: {node: '>=0.10'} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -5878,6 +5907,10 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -6922,6 +6955,16 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + neo4j-driver-bolt-connection@6.0.1: + resolution: {integrity: sha512-1KyG73TO+CwnYJisdHD0sjUw9yR+P5q3JFcmVPzsHT4/whzCjuXSMpmY4jZcHH2PdY2cBUq4l/6WcDiPMxW2UA==} + + neo4j-driver-core@6.0.1: + resolution: {integrity: sha512-5I2KxICAvcHxnWdJyDqwu8PBAQvWVTlQH2ve3VQmtVdJScPqWhpXN1PiX5IIl+cRF3pFpz9GQF53B5n6s0QQUQ==} + + neo4j-driver@6.0.1: + resolution: {integrity: sha512-8DDF2MwEJNz7y7cp97x4u8fmVIP4CWS8qNBxdwxTG0fWtsS+2NdeC+7uXwmmuFOpHvkfXqv63uWY73bfDtOH8Q==} + engines: {node: '>=18.0.0'} + next-themes@0.4.6: resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: @@ -7049,6 +7092,9 @@ packages: okapibm25@1.4.1: resolution: {integrity: sha512-UHmeH4MAtZXGFVncwbY7pfFvDVNxpsyM3W66aGPU0SHj1+ld59ty+9lJ0ifcrcnPUl1XdYoDgb06ObyCnpTs3g==} + ollama@0.5.18: + resolution: {integrity: sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -7166,6 +7212,9 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -7261,6 +7310,9 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pizzip@3.2.0: + resolution: {integrity: sha512-X4NPNICxCfIK8VYhF6wbksn81vTiziyLbvKuORVAmolvnUzl1A1xmz9DAWKxPRq9lZg84pJOOAMq3OE61bD8IQ==} + pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -8479,6 +8531,9 @@ packages: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -10298,7 +10353,7 @@ snapshots: '@langchain/core': 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)) zod: 3.25.76 - '@langchain/community@0.3.54(6d1a988c27bb7046e0625b67df4a39a3)': + '@langchain/community@0.3.54(750a6d03ce53b10eab8b6a936ee9a7b0)': dependencies: '@browserbasehq/stagehand': 1.9.0(@playwright/test@1.55.0)(deepmerge@4.3.1)(dotenv@17.3.1)(openai@4.104.0(ws@8.19.0)(zod@3.25.76))(zod@3.25.76) '@ibm-cloud/watsonx-ai': 1.3.1 @@ -10310,7 +10365,7 @@ snapshots: flat: 5.0.2 ibm-cloud-sdk-core: 5.1.0 js-yaml: 4.1.0 - langchain: 0.3.33(4c63b96815301c04536f0c3ba2bb9f23) + langchain: 0.3.33(db6e0a2bb4f8eef45b6b578f9c2526eb) langsmith: 0.3.67(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)) openai: 4.104.0(ws@8.19.0)(zod@3.25.76) uuid: 10.0.0 @@ -10327,6 +10382,7 @@ snapshots: jsonwebtoken: 9.0.3 lodash: 4.17.21 mammoth: 1.11.0 + neo4j-driver: 6.0.1 pdf-parse: 1.1.1 playwright: 1.55.0 weaviate-client: 3.8.1 @@ -10407,6 +10463,14 @@ snapshots: - react - react-dom + '@langchain/ollama@0.1.6(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))': + dependencies: + '@langchain/core': 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)) + ollama: 0.5.18 + uuid: 10.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + '@langchain/openai@0.6.11(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(ws@8.19.0)': dependencies: '@langchain/core': 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)) @@ -12666,6 +12730,8 @@ snapshots: '@xmldom/xmldom@0.8.11': {} + '@xmldom/xmldom@0.9.8': {} + abort-controller-x@0.4.3: {} abort-controller@3.0.0: @@ -13350,6 +13416,10 @@ snapshots: dependencies: esutils: 2.0.3 + docxtemplater@3.68.3: + dependencies: + '@xmldom/xmldom': 0.9.8 + dom-accessibility-api@0.5.16: {} dom-accessibility-api@0.6.3: {} @@ -14316,6 +14386,8 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 + highlight.js@11.11.1: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -14370,7 +14442,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.3 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.7.4(debug@4.4.3)) + retry-axios: 2.6.0(axios@1.7.4) tough-cookie: 4.1.4 transitivePeerDependencies: - supports-color @@ -15115,7 +15187,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - langchain@0.3.33(4c63b96815301c04536f0c3ba2bb9f23): + langchain@0.3.33(db6e0a2bb4f8eef45b6b578f9c2526eb): dependencies: '@langchain/core': 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)) '@langchain/openai': 0.6.11(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76)))(ws@8.19.0) @@ -15132,6 +15204,7 @@ snapshots: optionalDependencies: '@langchain/anthropic': 1.3.3(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76))) '@langchain/google-genai': 2.1.3(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76))) + '@langchain/ollama': 0.1.6(@langchain/core@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.210.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0)(zod@3.25.76))) axios: 1.7.4(debug@4.4.3) cheerio: 1.2.0 handlebars: 4.7.8 @@ -15753,6 +15826,20 @@ snapshots: neo-async@2.6.2: optional: true + neo4j-driver-bolt-connection@6.0.1: + dependencies: + buffer: 6.0.3 + neo4j-driver-core: 6.0.1 + string_decoder: 1.3.0 + + neo4j-driver-core@6.0.1: {} + + neo4j-driver@6.0.1: + dependencies: + neo4j-driver-bolt-connection: 6.0.1 + neo4j-driver-core: 6.0.1 + rxjs: 7.8.2 + next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -15883,6 +15970,10 @@ snapshots: okapibm25@1.4.1: {} + ollama@0.5.18: + dependencies: + whatwg-fetch: 3.6.20 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -16006,6 +16097,8 @@ snapshots: pako@1.0.11: {} + pako@2.1.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -16107,6 +16200,10 @@ snapshots: pirates@4.0.7: {} + pizzip@3.2.0: + dependencies: + pako: 2.1.0 + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 @@ -16665,7 +16762,7 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - retry-axios@2.6.0(axios@1.7.4(debug@4.4.3)): + retry-axios@2.6.0(axios@1.7.4): dependencies: axios: 1.7.4(debug@4.4.3) @@ -17537,6 +17634,8 @@ snapshots: dependencies: iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@4.0.0: {} whatwg-url@14.2.0: diff --git a/public/templates/advisory-agreement-template.docx b/public/templates/advisory-agreement-template.docx new file mode 100644 index 00000000..b00d8855 Binary files /dev/null and b/public/templates/advisory-agreement-template.docx differ diff --git a/public/templates/at-will-employment-template.docx b/public/templates/at-will-employment-template.docx new file mode 100644 index 00000000..783e8bb7 Binary files /dev/null and b/public/templates/at-will-employment-template.docx differ diff --git a/public/templates/contractor-agreement-template.docx b/public/templates/contractor-agreement-template.docx new file mode 100644 index 00000000..b275e81f Binary files /dev/null and b/public/templates/contractor-agreement-template.docx differ diff --git a/public/templates/employee-nda-template.docx b/public/templates/employee-nda-template.docx new file mode 100644 index 00000000..b3646b49 Binary files /dev/null and b/public/templates/employee-nda-template.docx differ diff --git a/public/templates/employment-contract-template.docx b/public/templates/employment-contract-template.docx new file mode 100644 index 00000000..3f8d326b Binary files /dev/null and b/public/templates/employment-contract-template.docx differ diff --git a/public/templates/founders-agreement-template.docx b/public/templates/founders-agreement-template.docx new file mode 100644 index 00000000..b0ba22ce Binary files /dev/null and b/public/templates/founders-agreement-template.docx differ diff --git a/public/templates/invention-assignment-template.docx b/public/templates/invention-assignment-template.docx new file mode 100644 index 00000000..76904b06 Binary files /dev/null and b/public/templates/invention-assignment-template.docx differ diff --git a/public/templates/ip-assignment-template.docx b/public/templates/ip-assignment-template.docx new file mode 100644 index 00000000..b3dee3ac Binary files /dev/null and b/public/templates/ip-assignment-template.docx differ diff --git a/public/templates/nda-template.docx b/public/templates/nda-template.docx new file mode 100644 index 00000000..ad612b5c Binary files /dev/null and b/public/templates/nda-template.docx differ diff --git a/public/templates/non-compete-template.docx b/public/templates/non-compete-template.docx new file mode 100644 index 00000000..0d1735eb Binary files /dev/null and b/public/templates/non-compete-template.docx differ diff --git a/public/templates/privacy-policy-template.docx b/public/templates/privacy-policy-template.docx new file mode 100644 index 00000000..91b044bb Binary files /dev/null and b/public/templates/privacy-policy-template.docx differ diff --git a/public/templates/safe-template.docx b/public/templates/safe-template.docx new file mode 100644 index 00000000..e43f9494 Binary files /dev/null and b/public/templates/safe-template.docx differ diff --git a/public/templates/service-agreement-template.docx b/public/templates/service-agreement-template.docx new file mode 100644 index 00000000..a86708e7 Binary files /dev/null and b/public/templates/service-agreement-template.docx differ diff --git a/public/templates/severance-agreement-template.docx b/public/templates/severance-agreement-template.docx new file mode 100644 index 00000000..52429e79 Binary files /dev/null and b/public/templates/severance-agreement-template.docx differ diff --git a/public/templates/termination-letter-template.docx b/public/templates/termination-letter-template.docx new file mode 100644 index 00000000..c8c6c655 Binary files /dev/null and b/public/templates/termination-letter-template.docx differ diff --git a/public/templates/terms-of-service-template.docx b/public/templates/terms-of-service-template.docx new file mode 100644 index 00000000..6f5f053a Binary files /dev/null and b/public/templates/terms-of-service-template.docx differ diff --git a/scripts/run-evals.ts b/scripts/run-evals.ts new file mode 100644 index 00000000..67330cf3 --- /dev/null +++ b/scripts/run-evals.ts @@ -0,0 +1,55 @@ +/** + * Agent Eval Runner + * + * Runs all golden test scenarios against the agent guardrails and + * deterministic extractors. Outputs a summary report to stdout. + * + * Usage: + * npx tsx scripts/run-evals.ts + */ + +import { EVAL_SCENARIOS, runEvals } from '~/lib/agents/evals'; + +function main() { + console.log('=== Launchstack Agent Eval Suite ===\n'); + console.log(`Running ${EVAL_SCENARIOS.length} scenarios...\n`); + + const report = runEvals(EVAL_SCENARIOS); + + console.log('--- Results ---\n'); + + for (const result of report.results) { + const status = result.passed ? 'PASS' : 'FAIL'; + const icon = result.passed ? '✓' : '✗'; + console.log(` ${icon} [${status}] ${result.scenarioName} (${result.domain}) — score: ${result.overallScore}, ${result.duration}ms`); + + for (const metric of result.metrics) { + console.log(` ${metric.name}: ${metric.score}/${metric.maxScore}${metric.details ? ` (${metric.details})` : ''}`); + } + + if (result.errors) { + for (const err of result.errors) { + console.log(` ERROR: ${err}`); + } + } + console.log(); + } + + console.log('--- Summary ---\n'); + console.log(` Total: ${report.totalScenarios}`); + console.log(` Passed: ${report.passed}`); + console.log(` Failed: ${report.failed}`); + console.log(` Score: ${report.overallScore}\n`); + + console.log(' By Domain:'); + for (const [domain, stats] of Object.entries(report.byDomain)) { + if (stats.total === 0) continue; + console.log(` ${domain}: ${stats.passed}/${stats.total} passed (score: ${stats.score})`); + } + + console.log(`\n Timestamp: ${report.timestamp}\n`); + + process.exit(report.failed > 0 ? 1 : 0); +} + +main(); diff --git a/sidecar/app/main.py b/sidecar/app/main.py index 5cfefb75..366b43e0 100644 --- a/sidecar/app/main.py +++ b/sidecar/app/main.py @@ -37,7 +37,7 @@ async def lifespan(app: FastAPI): # --------------------------------------------------------------------------- app = FastAPI( - title="PDR AI Sidecar", + title="Launchstack Sidecar", description="Local ML compute for embedding, reranking, and entity extraction.", version="0.1.0", lifespan=lifespan, diff --git a/src/app/_components/Breadcrumbs.tsx b/src/app/_components/Breadcrumbs.tsx new file mode 100644 index 00000000..00593ebb --- /dev/null +++ b/src/app/_components/Breadcrumbs.tsx @@ -0,0 +1,73 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { ChevronRight, Home } from 'lucide-react'; + +const LABELS: Record = { + pricing: 'Pricing', + about: 'About', + contact: 'Contact', + deployment: 'Deployment Guide', + signin: 'Sign In', + signup: 'Get Started', +}; + +export function Breadcrumbs() { + const pathname = usePathname(); + + if (pathname === '/') return null; + + const segments = pathname.split('/').filter(Boolean); + if (segments.length === 0) return null; + + const crumbs = segments.map((seg, i) => ({ + label: LABELS[seg] ?? seg.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()), + href: '/' + segments.slice(0, i + 1).join('/'), + })); + + const breadcrumbLd = { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: [ + { '@type': 'ListItem', position: 1, name: 'Home', item: process.env.NEXT_PUBLIC_SITE_URL ?? 'https://launchstack.app' }, + ...crumbs.map((c, i) => ({ + '@type': 'ListItem', + position: i + 2, + name: c.label, + item: `${process.env.NEXT_PUBLIC_SITE_URL ?? 'https://launchstack.app'}${c.href}`, + })), + ], + }; + + return ( + <> +