Technology Stack
Versions captured 2026-06-24 from the repos’ manifest files.
Backend (pw-enterprise)
Section titled “Backend (pw-enterprise)”| Technology | Version | Role |
|---|---|---|
| Python | 3.12 | Language / runtime |
| Flask | 3.1.2 | Web framework (Blueprints + MethodView) |
| SQLAlchemy | 2.0.46 | ORM |
| psycopg2 | 2.9.11 | PostgreSQL driver / raw pool |
| Redis (client lib) | 7.1.0 | Python Redis client (cache + RQ) |
| RQ | 2.6.1 | Background job queue |
| Elasticsearch (client) | 9.2.1 | Search / time-series |
| Gunicorn | 24.1.1 | WSGI server |
| Marshmallow | 3.26.2 | Serialization schemas |
| Sentry SDK | 2.51.0 | Error / performance monitoring |
📎 Evidence
- Python 3.12 runtime —
pw-enterprise/.python-version:1(3.12) andpw-enterprise/pyproject.toml:10(requires-python = ">=3.12") both pin Python 3.12. ✅ - Flask 3.1.2 —
pw-enterprise/pyproject.toml:44(flask>=3.1.2) andpw-enterprise/uv.lock:1452-1453resolve to3.1.2. ✅ - SQLAlchemy 2.0.46 —
pw-enterprise/uv.lock:6393-6394resolves to2.0.46; transitive viaflask-sqlalchemy(pyproject.toml:47), not declared directly. ✅ - psycopg2 2.9.11 —
pw-enterprise/pyproject.toml:70(psycopg2-binary>=2.9.11) and:103(psycopg2>=2.9.11);uv.lock:4690-4691resolves to2.9.11. ✅ - redis client 7.1.0 —
pw-enterprise/uv.lock:5730-5731resolves to7.1.0;cache.py:10(import redis) confirms direct use. Transitive (via rq), not inpyproject.toml. ✅ - RQ 2.6.1 —
pw-enterprise/pyproject.toml:82(rq>=2.6.1) anduv.lock:6097-6098resolve to2.6.1. ✅ - Elasticsearch client 9.2.1 —
pw-enterprise/uv.lock:1132-1133resolves to9.2.1;pyproject.toml:37floor is>=9.2.0(one patch below). ✅ - Gunicorn 24.1.1 —
pw-enterprise/uv.lock:2097-2098resolves to24.1.1;pyproject.toml:51floor is>=23.0.0(a major below the resolved version). ✅ - Marshmallow 3.26.2 —
pw-enterprise/uv.lock:3447-3448resolves to3.26.2; transitive viaflask-marshmallow/marshmallow-sqlalchemy, not declared directly. ✅ - Sentry SDK 2.51.0 —
pw-enterprise/pyproject.toml:85(sentry-sdk[langchain,langgraph,rq]>=2.47.0) anduv.lock:6278-6279resolve to2.51.0. ✅
AI / LLM (pw-enterprise)
Section titled “AI / LLM (pw-enterprise)”| Technology | Version | Role |
|---|---|---|
| LangChain | 1.2.7 | LLM abstraction / orchestration |
| LangGraph | 1.0.7 | Stateful multi-agent graphs |
| CrewAI | 1.6.1 | Role-based agent framework |
| OpenAI SDK | 2.16.0 | LLM vendor |
| Anthropic SDK | 0.76.0 | LLM vendor |
| Groq | 0.37.1 | Fast-inference LLM vendor |
| google-genai (Gemini) | 1.60.0 | LLM / document extraction |
| Cohere | 5.20.2 | Embeddings / LLM vendor |
| Langfuse | 4.5.1 | LLM tracing / observability |
📎 Evidence
- LangChain 1.2.7 —
pw-enterprise/pyproject.toml:12(langchain>=1.2.6) anduv.lock:2644-2645resolve to1.2.7. ✅ - LangGraph 1.0.7 —
pw-enterprise/pyproject.toml:16(langgraph>=1.0.6) anduv.lock:2834-2835resolve to1.0.7. ✅ - CrewAI 1.6.1 —
pw-enterprise/pyproject.toml:33(crewai>=1.6.1) anduv.lock:853-854resolve to1.6.1. ✅ - OpenAI SDK 2.16.0 —
pw-enterprise/pyproject.toml:63(openai>=2.9.0) anduv.lock:3999-4000resolve to2.16.0. ✅ - Anthropic SDK 0.76.0 —
pw-enterprise/uv.lock:154-155resolves to0.76.0; transitive vialangchain-anthropic(pyproject.toml:13);.env:136ANTHROPIC_API_KEYconfirms wiring. ✅ - Groq 0.37.1 —
pw-enterprise/uv.lock:1955-1956resolves to0.37.1;v3/video_analysis.py:25(from groq import Groq) and.env:132GROQ_API_KEYconfirm use. ✅ - google-genai 1.60.0 —
pw-enterprise/pyproject.toml:97(google-genai>=1.60.0) anduv.lock:1850-1851resolve to1.60.0. ✅ - Cohere 5.20.2 —
pw-enterprise/pyproject.toml:32(cohere>=5.20.0) anduv.lock:813-814resolve to5.20.2. ✅ - Langfuse 4.5.1 —
pw-enterprise/pyproject.toml:105(langfuse>=4.5.1) anduv.lock:2815-2816resolve to4.5.1. ✅
Frontend (pw-enterprise-frontend)
Section titled “Frontend (pw-enterprise-frontend)”| Technology | Version | Role |
|---|---|---|
| React / React DOM | 19.1.0 | UI framework |
Vite+ (vite-plus) |
latest | Build toolchain (wraps Vite/Vitest/Oxlint/Oxfmt) |
| TypeScript | 5.9.3 | Language |
| React Router | 7.5.1 | Routing |
| TanStack React Query | 4.32.0 | Server-state / data fetching |
| Zustand | 5.0.8 | Client state |
| nuqs | 2.4.3 | URL-state |
| Tailwind CSS | 4.2.2 | Styling (+ shadcn/Radix) |
| Highcharts / Recharts | 10.1.0 / 2.15.3 | Charting |
| Axios | 1.8.4 | HTTP client |
| Sentry React | 9.13.0 | Error / performance monitoring |
vite-plus floats to latest in package.json (the Vite+ toolchain self-manages its tool versions); all other dependencies use caret (^) ranges that allow minor/patch upgrades — the numbers above are the declared floors.
📎 Evidence
- React / React DOM 19.1.0 —
pw-enterprise-frontend/package.json:108(react: ^19.1.0) and:114(react-dom: ^19.1.0). Caret range, not exact pin. ✅ - vite-plus pinned
latest—pw-enterprise-frontend/package.json:171("vite-plus": "latest");:168aliasesvitetonpm:@voidzero-dev/vite-plus-core@latest. ✅ - Vite+ unified toolchain —
pw-enterprise-frontend/CLAUDE.md:1describes it built on Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt;package.json:177overridesvitestto@voidzero-dev/vite-plus-test. ✅ - TypeScript 5.9.3 —
pw-enterprise-frontend/package.json:167("typescript": "^5.9.3") in devDependencies. Caret range. ✅ - React Router 7.5.1 —
pw-enterprise-frontend/package.json:125("react-router": "^7.5.1"). ✅ - TanStack React Query 4.32.0 —
pw-enterprise-frontend/package.json:79("@tanstack/react-query": "^4.32.0"). ✅ - Zustand 5.0.8 —
pw-enterprise-frontend/package.json:146("zustand": "^5.0.8"). ✅ - nuqs 2.4.3 —
pw-enterprise-frontend/package.json:105("nuqs": "^2.4.3"). ✅ - Tailwind CSS 4.2.2 —
pw-enterprise-frontend/package.json:166("tailwindcss": "^4.2.2") and:153(@tailwindcss/vite: ^4.2.2). ✅ - Highcharts 10.1.0 —
pw-enterprise-frontend/package.json:90("highcharts": "^10.1.0"). ✅ - Recharts 2.15.3 —
pw-enterprise-frontend/package.json:133("recharts": "^2.15.3"). ✅ - Axios 1.8.4 —
pw-enterprise-frontend/package.json:81("axios": "^1.8.4"). ✅ - Sentry React 9.13.0 —
pw-enterprise-frontend/package.json:75("@sentry/react": "^9.13.0"). ✅ - Versions are caret (
^) floors, not exact pins —pw-enterprise-frontend/package.jsonuses^ranges throughout (e.g.:108react: ^19.1.0,:125react-router: ^7.5.1); onlyvite-plus(:171) floats tolatest. Footnote corrected accordingly. ✅
Data & Infrastructure
Section titled “Data & Infrastructure”| Technology | Version | Role |
|---|---|---|
| PostgreSQL | server 12.12 · client 14.23 | System of record (local on each VM, ×2 instances; + GCP Cloud SQL via proxy on dev) |
| Redis | server 8.8.0 (dev) / 6.0.16 (prod) · 7.x (client) | Cache + RQ queues |
| MySQL | server 8.0.46 | Secondary local DB on the VMs |
| Elasticsearch | 9.2.x–9.4.x (client) | Search / time-series — remote (not hosted on the VMs) |
| RabbitMQ | 3.9.27 (dev only) | Message broker (likely Airflow/Celery; no app call-site verified) |
| Apache HTTP Server | 2.4.52 | Web server / ingress on both VMs (no nginx) |
| BigQuery | (GCP, client 3.40.0) | Analytics warehouse |
| Cloudflare (Wrangler) | 4.81.1 | Pages + Workers + Tunnel hosting / deploy |
| Node.js | 22 (CI lts/*) · 18.17.0 dev VM / 26.2.0 prod VM |
Build (CI) and on-host runtimes |
Server versions confirmed from the VMs on 2026-06-25 (see Environments & Infrastructure → Cloud Inventory). Postgres servers are local 12.12 on each box; Redis server major differs by environment (8.x dev, 6.x prod); Elasticsearch is a remote/managed cluster (not listening on either VM).
📎 Evidence
- PostgreSQL server 12.12 (confirmed) —
sudo -u postgres psql -tc 'select version();'on both VMs (2026-06-25) =PostgreSQL 12.12 (Ubuntu 12.12-0ubuntu0.20.04.1);ss -tlnpshows two local postgres instances per box (:5432,:5433). Client ispsql 14.23..env:140-141POSTGRES_HOST/POSTGRES_PORTconfirm wiring. ✅ - Redis server major differs by env (confirmed) —
redis-server --version: pw-devv=8.8.0, pw-prodv=6.0.16; Pythonredisclient pinned7.1.0(prod) /7.4.0(test) /5.2.1(dev) per the deployeduv.locks..env:41-43confirm connectivity. ✅ - Elasticsearch is remote (confirmed) — client pinned
9.2.1(prod) /9.4.0(test) /8.17.1(dev) in the deployeduv.locks;:9200is absent fromss -tlnpon both VMs → managed/remote cluster, not VM-hosted..env:26elastic_keyconfirms connectivity. ✅ - MySQL 8.0.46, Apache 2.4.52, RabbitMQ 3.9.27 (from VMs) —
mysql --version=8.0.46,apache2 -v=Apache/2.4.52,sudo rabbitmqctl version(pw-dev) =3.9.27. RabbitMQ listens only on pw-dev (beam.smpAMQP:5672); likely the Airflow/Celery broker — no app call-site verified. ⚠️ - BigQuery client 3.40.0 —
pw-enterprise/pyproject.toml:100(google-cloud-bigquery>=3.39.0) anduv.lock:1745-1746resolve to3.40.0. Doc correctly labels it ‘client’. ✅ - Wrangler 4.81.1 (Cloudflare Pages) —
pw-enterprise-frontend/package.json:172("wrangler": "^4.81.1"), deploy script:15(wrangler pages deploy dist),wrangler.toml:1-5setspages_build_output_dir = "dist". ✅ - Node version: CI uses
lts/*(Node 22) —pw-enterprise-frontend/.github/workflows/playwright.yml:26usesnode-version: lts/*; no.nvmrc/.node-version/enginesfield is declared, so the project floor is inferred (package-lock.json:2044shows a transitive"node": ">=18.0.0"). Table now cites the CI version. ✅
Third-party services & integrations
Section titled “Third-party services & integrations”External services the platform calls out to, identified from the backend .env and the library/ integration wrappers. (Names only — no credentials.)
LLM & model providers
Section titled “LLM & model providers”| Service | Used for |
|---|---|
| OpenAI | LLM / embeddings |
| Anthropic (Claude) | LLM |
| Groq | Fast-inference LLM |
| Google Gemini | LLM / document extraction |
| Cohere | Embeddings / LLM |
| Cerebras | Fast-inference LLM |
| DeepSeek | LLM |
| Perplexity | LLM / answer engine |
| OpenRouter | Multi-model gateway |
| SambaNova (Sambaverse) | LLM |
| LlamaIndex (local library) | Indexing / retrieval |
📎 Evidence
- OpenAI LLM provider —
pw-enterprise/library/llm_completions.py:13importsfrom openai import OpenAIandPROVIDER_CONFIGS["openai"]wiresapi_key_env="openai_api_key";.env:3,129defineopenai_api_key/OPENAI_API_KEY. ✅ - Anthropic / Claude LLM —
pw-enterprise/ai_twin/respondents/agents.py:19,46importsChatAnthropicand readsclaude_key;pw-enterprise/v3/tools.py:8048-8049instantiatesChatAnthropic(model_name="claude-sonnet-4-5", api_key=claude_key). ✅ - Groq fast-inference LLM —
pw-enterprise/library/llm_completions.py:33-35configapi_key_env="groq_api_key", base_url="https://api.groq.com/openai/v1";pw-enterprise/ai_twin/group/main.py:9,1489imports and instantiatesChatGroq. ✅ - Google Gemini LLM —
pw-enterprise/app.py:5859-5860buildsChatGoogleGenerativeAI(model="gemini-3-flash-preview");gemini_grounded_calldefined inpw-enterprise/library/ai_services.py:63and imported inapp.py:166. ✅ - Cohere —
pw-enterprise/api/planner.py:12,63doesimport cohereandcohere_api_key = os.environ["cohere_api_key"];.env:4defines the key. ✅ - Cerebras fast-inference LLM —
pw-enterprise/v3/langraph_main.py:30,397-399readsCEREBRAS-API-KEY, importsfrom cerebras.cloud.sdk import Cerebras, and callscerebras_client.chat.completions.create; dep inpyproject.toml:30. ✅ - DeepSeek LLM —
pw-enterprise/api/assets/routers/transparency.py:2280-2284activeChatOpenAI(openai_api_key=deepseek_api_key, model="deepseek-chat", base_url="https://api.deepseek.com"); secret mapped inapp.py:43. ✅ - Perplexity answer engine —
pw-enterprise/library/ai_services.py:311-316readsperplexity_api_keyand POSTs tohttps://api.perplexity.ai/chat/completionswith a Bearer header. ✅ - OpenRouter gateway —
pw-enterprise/config/constants.py:48readsOPENROUTER_API_KEY = os.environ.get("openrouter", "");api/assets/routers/conversations.py:4126setsopenai_api_base="https://openrouter.ai/api/v1". ✅ - SambaNova LLM —
pw-enterprise/api/planner.py:9591-9598activeChatOpenAI(openai_api_key=SAMBAVERSE_API_KEY, openai_api_base="https://api.sambanova.ai/v1", tags=[..., "sambanova"]); secret mapped inapp.py:46. ✅ - LlamaIndex (local library) —
pw-enterprise/api/assets/routers/assets_uploads.py:110importsfrom llama_index.core import SimpleDirectoryReader;app.py:32loadsLLAMAINDEX-API-KEYbut no LlamaIndex Cloud endpoint call was found — so this is the local library, as the table now states. ✅
Models in use
Section titled “Models in use”The providers above are reached through concrete model IDs. The list below covers only IDs referenced in active (non-commented) code on the app (prod) branch, grouped by provider. The active model-ID inventory is nearly identical across the three deploy branches — but the wiring is not: which call site or feature selects which model diverges in ~20 places (7 of them same-call-site model swaps, the rest branch-only features). All per-branch differences are catalogued in Model differences across branches below. Two facts frame the whole table:
gemini-3-flash-previewis the Gemini workhorse (~53 active call sites) andgpt-4.1is the most-referenced model overall (~350 active call sites) — together they carry the bulk of production generation.- Selection is mostly hardcoded per call site. There is no single global model registry; each module names its model inline. The only centralized controls are the frontend-facing passthrough allow-list (
library/llm_completions.py:32-64) and two module-level constants (MAVEN_MODEL_SPEC,GPT5_MODEL) — see the Evidence block.
Counts are approximate active-reference tallies (~refs), not exact.
OpenAI
Section titled “OpenAI”| Model ID | Type | ~refs | Used for |
|---|---|---|---|
gpt-4.1 |
chat | ~350 | Primary chat model across twin / maven / v3 / api / cron |
gpt-4.1-mini |
chat | ~43 | Secondary chat; also the openai passthrough default |
gpt-4.1-nano |
chat | 4 | Small/cheap variant (passthrough allow-list) |
gpt-4o |
chat | ~16 | Meetings insight generation + fallback |
gpt-4o-mini |
chat | 7 | Survey / intent-insights chat |
gpt-4o-2024-08-06 |
chat (pinned) | 1 | Product intelligence (dated snapshot) |
gpt-4 |
chat | 2 | Maven LangGraph env default |
gpt-4.5-preview |
chat | 1 | v3 “AI storm” multi-model ensemble |
gpt-3.5-turbo |
chat | 4 | Reports + conversations |
gpt-3.5-turbo-instruct |
completion | ~14 | Legacy completions-style planner engine |
gpt-3.5-turbo-0125 |
base | ~10 | Base for fine-tuned ad-creative models |
gpt-3.5-turbo-1106 |
base / tokenizer | ~10 | Token-count helper + base for model-router fine-tune |
gpt-5 |
chat | 3 | Twin respondent prompt composer (CLI) |
gpt-5-mini |
chat | 3 | Twin respondent agents + maven scope classification |
gpt-5.1 |
chat | 1 | Maven deep-agent tool call |
gpt-5.4 |
chat | 3 | Maven orchestrator (MAVEN_MODEL_SPEC) + passthrough allow-list |
gpt-5.4-mini |
chat | 3 | Passthrough allow-list |
gpt-5.5 |
chat | 1 | Premium report design (env-overridable) |
gpt-realtime-1.5 |
realtime voice | 1 | Realtime model reference (also called from the browser) |
o1 |
reasoning | 4 | Persona generation reasoning |
o3 |
reasoning | 6 | Persona cues + meetings reasoning |
o3-mini |
reasoning | 3 | LinkedIn + homepage reasoning |
o4-mini |
reasoning | 1 | Misc reasoning task |
text-embedding-3-large |
embeddings | 7 | Vector search / report summarization |
text-embedding-3-small |
embeddings | 2 | Maven embeddings + persona clustering |
text-embedding-ada-002 |
embeddings | 2 | Legacy embeddings (homepage, FB audiences) |
tts-1 |
speech (TTS) | 3 | Persona / creative-evaluation voice |
gpt-image-2 |
image gen | 4 | Ad creative image generation |
Fine-tuned OpenAI models (ProfitWheel org):
| Fine-tuned model | Base | Used for |
|---|---|---|
ft:…:google-ad-genius:9hGemqwG |
gpt-3.5-turbo-0125 |
Google ad creative generation |
ft:…:instagram-v2:9Zx2tg5u |
gpt-3.5-turbo-0125 |
Instagram creative generation |
ft:…:model-router:9GgS0ypq |
gpt-3.5-turbo-1106 |
“Crew” feature model router |
ft:…:funnel:7qbzYPcw |
gpt-3.5-turbo-0613 |
Google Ads funnel-stage classification |
ft:…:audiencename:AYBws9Ok |
gpt-4o-mini-2024-07-18 |
Audience-name quality scoring |
Anthropic (Claude)
Section titled “Anthropic (Claude)”| Model ID | Type | ~refs | Used for |
|---|---|---|---|
claude-sonnet-4-5 |
chat | 20 | Twin respondents, planner, meetings, transparency, IAM |
claude-3-7-sonnet-latest |
chat | 1 | Meetings analysis |
claude-3-5-haiku-latest |
chat | 1 | Planner lightweight task |
Google (Gemini / Gemma)
Section titled “Google (Gemini / Gemma)”| Model ID | Type | ~refs | Used for |
|---|---|---|---|
gemini-3-flash-preview |
chat | ~53 | Dominant Gemini model — meetings, surveys, product intel, v3, twin |
gemini-2.5-pro |
chat | 10 | File analysis, product intelligence |
gemini-2.5-flash |
chat | 2 | Persona generation |
gemini-2.5-flash-image |
image gen | 2 | Image-capable Gemini variant |
gemini-2.0-flash |
chat | 5 | Maven tools |
gemini-3.1-flash-lite |
chat | 3 | Lightweight Gemini variant |
google/gemma-4-26b-a4b-it |
chat | 1 | Twin conversation LLM (user persona) |
Groq / Meta & Cerebras (Llama, Mixtral)
Section titled “Groq / Meta & Cerebras (Llama, Mixtral)”| Model ID | Provider | ~refs | Used for |
|---|---|---|---|
llama-3.3-70b-versatile |
Groq | 16 | Twin group sims, planner, meetings; Groq passthrough default |
llama3.3-70b |
Cerebras | 7 | Report-resilience fallback model |
llama-4-scout |
Groq | 1 | Persona-name generation (primary) |
llama-3.3-70b |
Groq | 1 | Research agent tools |
whisper-large-v3 |
Groq | 2 | Audio transcription for video analysis |
llama-3.1-8b-instant, llama3-70b-8192, llama3-8b-8192, mixtral-8x7b-32768, openai/gpt-oss-20b |
Groq | — | Passthrough allow-list only (not directly invoked) |
DeepSeek & Perplexity
Section titled “DeepSeek & Perplexity”| Model ID | Provider | ~refs | Used for |
|---|---|---|---|
deepseek-chat |
DeepSeek | 1 | Transparency-report LLM call |
sonar |
Perplexity | 2 | Web-search chat (primary) |
sonar-pro |
Perplexity | 2 | Web-search chat (fallback) |
Cloudflare Workers AI (server-side)
Section titled “Cloudflare Workers AI (server-side)”| Model ID | Type | ~refs | Used for |
|---|---|---|---|
@cf/black-forest-labs/flux-1-schnell |
image gen | 1 | Persona profile-picture generation (dev env) |
Frontend (browser-side) model calls
Section titled “Frontend (browser-side) model calls”The React app calls a few models directly from the browser, in addition to the server-side models above:
| Model ID | Provider / transport | Used for |
|---|---|---|
gpt-realtime-1.5 |
OpenAI Realtime (WebRTC) | AI-twin live audio call |
gpt-4o-transcribe |
OpenAI (realtime transcription) | Speech-to-text for the audio call |
llama-3.3-70b-versatile |
Groq (browser SDK) | Chat sidebar, activity summaries, survey parsing |
gpt-4o (fallback) |
via backend /chat_gpt_response |
Streaming twin chat (model passed as query param) |
📎 Evidence
Representative call site per model ID (one active, non-commented site each; grep the exact ID for every occurrence):
| Model ID | Representative call site |
|---|---|
gpt-4.1 |
ai_twin/respondents/universe_stage_fetcher.py:683 |
gpt-4.1-mini |
ai_twin/group/main.py:882 |
gpt-4.1-nano |
library/llm_completions.py:56 |
gpt-4o |
api/product.py:3173 |
gpt-4o-mini |
api/survey.py:3790 |
gpt-4o-2024-08-06 |
api/product.py:3198 |
gpt-4 |
maven/deep_agent_langgraph.py:145 |
gpt-4.5-preview |
v3/tools.py:8046 |
gpt-3.5-turbo |
api/reports.py:3236 |
gpt-3.5-turbo-instruct |
api/planner.py:1039 |
gpt-3.5-turbo-0125 |
api/planner_creative.py:9462 |
gpt-3.5-turbo-1106 |
library/openai_helpers.py:112 |
gpt-5 |
ai_twin/respondents/respondent_prompt_composer.py:55 |
gpt-5-mini |
ai_twin/respondents/agents.py:330 |
gpt-5.1 |
maven/deep_tools.py:733 |
gpt-5.4 |
maven/deep_agent_profiles.py:16 |
gpt-5.4-mini |
library/llm_completions.py:60 |
gpt-5.5 |
maven/report_designer.py:217 |
gpt-realtime-1.5 |
api/misc.py:2645 (backend) · pw-enterprise-frontend/src/components/atoms/AitwinAudioCall.jsx:737 (browser) |
gpt-4o-transcribe |
pw-enterprise-frontend/src/components/atoms/AitwinAudioCall.jsx:742 (browser) |
o1 |
api/assets/routers/personas.py:3204 |
o3 |
api/assets/routers/meetings.py:9196 |
o3-mini |
api/linkedin.py:1452 |
o4-mini |
api/assets/routers/misc.py:236 |
text-embedding-3-large |
cron/run_reports.py:169 |
text-embedding-3-small |
maven/deep_utils.py:685 |
text-embedding-ada-002 |
api/homepage.py:1731 |
tts-1 |
app.py:3818 |
gpt-image-2 |
api/planner_creative.py:6455 |
ft:…:google-ad-genius:9hGemqwG |
api/planner_creative.py:9083 |
ft:…:instagram-v2:9Zx2tg5u |
api/planner_creative.py:1655 |
ft:…:model-router:9GgS0ypq |
api/misc.py:2227 |
ft:…:funnel:7qbzYPcw |
api/google_ads.py:1715 |
ft:…:audiencename:AYBws9Ok |
library/audience_buckets.py:158 |
claude-sonnet-4-5 |
ai_twin/respondents/agents.py:338 |
claude-3-7-sonnet-latest |
api/meetings.py:2956 |
claude-3-5-haiku-latest |
api/planner.py:9694 |
gemini-3-flash-preview |
library/ai_services.py:267 |
gemini-2.5-pro |
api/files.py:121 |
gemini-2.5-flash |
api/assets/routers/personas.py:507 |
gemini-2.5-flash-image |
api/creative_consumrai.py:636 |
gemini-2.0-flash |
maven/tools.py:3349 |
gemini-3.1-flash-lite |
maven/generative_ui.py:198 |
google/gemma-4-26b-a4b-it |
api/assets/routers/conversations.py:4121 |
llama-3.3-70b-versatile |
ai_twin/group/main.py:1491 (backend) · useGroq.js:49, useGroqEventSummary.js:66, useRespondents.js:739 (browser) |
llama3.3-70b (Cerebras) |
library/report_resilience.py:620 |
llama-4-scout |
library/report_resilience.py:618 |
llama-3.3-70b (Groq) |
research_setup/utils/functions.py:322 |
whisper-large-v3 |
v3/video_analysis.py:171 |
Groq passthrough allow-list (llama-3.1-8b-instant, llama3-70b-8192, llama3-8b-8192, mixtral-8x7b-32768, openai/gpt-oss-20b) |
library/llm_completions.py:40-44 |
deepseek-chat |
api/assets/routers/transparency.py:2283 |
sonar |
library/ai_services.py:321 |
sonar-pro |
library/ai_services.py:332 |
@cf/black-forest-labs/flux-1-schnell |
api/assets/routers/personas.py:5431 |
gpt-4o (browser fallback) |
pw-enterprise-frontend/src/hooks/conversation/useConversationStreaming.js:3590 |
Paths are relative to pw-enterprise/ unless prefixed with pw-enterprise-frontend/.
gpt-4.1primary workhorse — activeChatOpenAI(model="gpt-4.1", …)atpw-enterprise/ai_twin/respondents/universe_stage_fetcher.py:683; ~350 active (non-commented) references across the backend. ✅gemini-3-flash-previewGemini workhorse —pw-enterprise/library/ai_services.py:267(model_name="gemini-3-flash-preview"); ~53 active references. ✅claude-sonnet-4-5—pw-enterprise/ai_twin/respondents/agents.py:338(model_name="claude-sonnet-4-5"); 20 active references. ✅- Passthrough allow-list (no direct browser provider calls) —
pw-enterprise/library/llm_completions.py:32-64definesPROVIDER_CONFIGS:groq→ defaultllama-3.3-70b-versatile(allow-list lines 39-44);openai→ defaultgpt-4.1-mini(allow-list lines 54-60). Module docstring (llm_completions.py:1-6) states the frontend must not call providers directly. ✅ - Central model constants (only two) —
MAVEN_MODEL_SPEC = "openai:gpt-5.4"atpw-enterprise/maven/deep_agent_profiles.py:16;GPT5_MODEL = "gpt-5"atpw-enterprise/ai_twin/respondents/respondent_prompt_composer.py:55(same-line comment notes it as a placeholder alias). No model defaults exist underconfig/. ✅ - Fine-tuned models —
google-ad-geniusactive atpw-enterprise/api/planner_creative.py:9083;instagram-v2at:1655;model-routeratapi/misc.py:2227;funnelatapi/google_ads.py:1715;audiencenameatlibrary/audience_buckets.py:158. ✅ - Server-side Flux —
pw-enterprise/api/assets/routers/personas.py:5431(model_name = "@cf/black-forest-labs/flux-1-schnell"). ✅ - Workers AI catalog is generated, not used —
pw-enterprise-frontend/worker-configuration.d.ts:2header:// Generated by Wrangler by running \wrangler types`; theAiModelsinterface (:3767+) is a TypeScript catalog of every Workers-AI-supported model. GreppingAI.run(/env.AI.run(/.run(“@cf/acrosssrc/andfunctions/returns zero hits — theAIbinding (declaredfunctions/_middleware.ts:10,wrangler.toml:11-12`) is never invoked. ✅ - Browser-side model calls — OpenAI Realtime
gpt-realtime-1.5/gpt-4o-transcribeatpw-enterprise-frontend/src/components/atoms/AitwinAudioCall.jsx:737,742; Groqllama-3.3-70b-versatileatuseGroq.js:49,useGroqEventSummary.js:66,useRespondents.js:739; backend passthrough fallbackgpt-4oatuseConversationStreaming.js:3590. ✅ - Commented-out (excluded) refs — e.g.
claude-haiku-4-5(maven/deep_agent.py:601),dall-e-3(api/planner_creative.py:6444,6492, superseded bygpt-image-2),gemini-3.1-pro-preview, and ~14 commentedgpt-4.1lines are not counted as active. ✅
Model differences across branches (dev · test · prod)
Section titled “Model differences across branches (dev · test · prod)”The tables above reflect the app (prod) branch. The three deploy branches share a near-identical model inventory — comparing every active model-ID literal, only 3 IDs are active on some branches but not all (plus one commented-only). But inventory parity is not wiring parity: ~20 shared call sites, features, or whole files invoke a different model — or a model prod doesn’t call at all — depending on branch. The consistent pattern: dev/test run ahead of prod app (newer features, plus a partial migration of some Gemini calls to newer/cheaper models), while app is the conservative baseline; dev occasionally diverges from test too.
Re-verified 2026-07-15 against freshly-fetched refs — origin/dev @ 6f58193, origin/test @ d463768, origin/app @ 59d0803. These are unchanged from the 2026-06-24 capture: no new commits have landed on any deploy branch, so this is already the latest code. Curated invoked-model counts are dev 56, test 55, prod 55 (a broad literal grep returns ~77/75/75, but ~20 per branch are token-cost dict keys, truncated gemini-2.x version-prefix partials, env-var names like DEEPSEEK-API-KEY, and helper identifiers — not model invocations).
There are three kinds of difference:
1 · Inventory differences — a model ID active on some branches, not all
Section titled “1 · Inventory differences — a model ID active on some branches, not all”| Model ID | prod (app) |
test |
dev |
Why |
|---|---|---|---|---|
gpt-4o-transcribe |
✓ | ✗ | ✗ | prod-only; dev/test transcribe with whisper-large-v3 at the same transcribe_audio() — a swap (Table 2) |
gemini-2.5-flash-lite |
✗ | ✓ | ✓ | dev/test swapped 4 shared gemini-2.0-flash call sites to it (Table 2) |
claude-3-5-sonnet-20241022 |
✗ | ✗ | ✓ | only in dev-only files (Table 3) |
claude-3-5-sonnet-latest |
✗ | ✗ | commented | two #-commented refs in dev’s v3/maven.py — excluded |
2 · Model swaps — the same shared call site invokes a different model per branch
Section titled “2 · Model swaps — the same shared call site invokes a different model per branch”This is the class the old inventory-only view missed. The IDs all exist on every branch, so a set diff can’t see them — but the model chosen at a given call site diverges:
| Feature — call site | prod (app) |
test |
dev |
|---|---|---|---|
Video transcription — transcribe_audio · v3/video_analysis.py |
gpt-4o-transcribe |
whisper-large-v3 |
whisper-large-v3 |
Focus-group AEO responses — get_llm_responses_for_questions · api/assets/routers/meetings.py |
gemini-2.0-flash |
gemini-2.5-flash-lite |
gemini-2.5-flash-lite |
Focus-group executor — ai_twin_focused_group_execute_plan_func · api/meetings.py |
gemini-2.0-flash |
gemini-2.5-flash-lite |
gemini-2.5-flash-lite |
Keyword-plan SEM filter — create_keyword_plan · api/intent_planner.py |
gemini-2.0-flash |
gemini-2.5-flash-lite |
gemini-2.5-flash-lite |
Plan-input generator — generate_plan_inputs · v3/tools.py |
gemini-2.0-flash |
gemini-2.5-flash-lite |
gemini-2.5-flash-lite |
Grounded web search — gemini_grounded_call · library/ai_services.py |
gemini-3-flash-preview (Gemini REST) |
gpt-4o-mini (OpenAI) |
gpt-4o-mini (OpenAI) |
Custom grounded search — gemini_custom_grounded_func · api/misc.py |
gemini-3-flash-preview |
gemini-3-flash-preview |
gemini-2.5-flash |
Two themes dominate: dev/test migrating gemini-2.0-flash → gemini-2.5-flash-lite across the focus-group / keyword / planning features, and the grounded-search helper re-platformed from Gemini to OpenAI gpt-4o-mini on dev/test. The last row shows dev alone diverging from both prod and test.
3 · Branch-only features — a call site or whole file present on only some branches
Section titled “3 · Branch-only features — a call site or whole file present on only some branches”| Feature | On | Model(s) | Nature |
|---|---|---|---|
Creative-intelligence params & scoring — ci_select_parameters, performance_elements… · api/assets/routers/meetings.py |
dev + test | gpt-4.1-mini, gpt-4.1 |
new call sites (absent on app) |
Content-creator tools — code/blog/email/messaging/ads_creator · v3/tools.py |
dev only | gpt-4.1 |
new call sites |
Survey Action Plan — GenerateSurveyActionPlan.post · api/reports.py |
dev + test | gpt-4o |
new call site |
Maven schedule-prompt validate/enhance · api/scheduled_report.py |
dev + test | gpt-5-mini, gpt-4.1-mini |
new call sites |
Premium report cover image — _generate_maven_premium_report_cover_url · research_setup/api/routes.py |
test only | gpt-image-2 |
new call site |
Segmentation→JSON — run_survey_and_segmentation · research_setup/utils/functions.py |
app only | llama-3.3-70b-versatile |
call site removed on dev/test |
Predictive-twin package · predictive_twin/*.py |
dev + test | gpt-4.1, o3 |
whole package absent on app |
Maven v3 graph · v3/maven.py |
dev only | gpt-4.1, gemini-2.0-flash, llama3.3-70b, claude-3-5-sonnet-20241022 |
whole file absent on app/test |
Question classification · fastapi_app/services/question_classification.py |
dev only | claude-3-5-sonnet-20241022 |
whole file absent on app/test |
Respondent memory summarizer · ai_twin/respondents/memory_summarizer.py |
dev only | gemini-2.5-flash |
whole file absent on app/test |
📎 Evidence
Verified 2026-07-15 by reading each call site on origin/app, origin/dev, origin/test (47-agent cross-check; every finding independently re-derived, raw-diff noise refuted). Paths relative to pw-enterprise/.
Inventory (set-membership) diffs — app\(dev∪test) = gpt-4o-transcribe, dev\(app∪test) = claude-3-5-sonnet-20241022, (dev∩test)\app = gemini-2.5-flash-lite. Prod is not a strict subset. Arithmetic: shared core 54 + prod’s gpt-4o-transcribe = 55; + dev’s two extras = 56; + test’s one extra = 55. ✅
Swaps (same call site, different model per branch):
transcribe_audio—app:v3/video_analysis.py:789→gpt-4o-transcribe;dev/testsame function →whisper-large-v3. ✅get_llm_responses_for_questions(AEOgeminislot) —app:api/assets/routers/meetings.py:3115gemini-2.0-flash→dev:4522/test:4589gemini-2.5-flash-lite. ✅ai_twin_focused_group_execute_plan_func—app:api/meetings.py:1509gemini-2.0-flash→dev:1515/test:1517gemini-2.5-flash-lite. ✅create_keyword_plan—app:api/intent_planner.py:957gemini-2.0-flash→dev:958/test:957gemini-2.5-flash-lite. ✅generate_plan_inputs—v3/tools.py:5024gemini-2.0-flash(app) →gemini-2.5-flash-lite(dev/test). ✅gemini_grounded_call—app:library/ai_services.py:69gemini-3-flash-preview(Gemini REST) →dev:117/test:120gpt-4o-minivia aGROUNDED_MODELconstant on the OpenAI Responses API (function re-platformed, signature preserved). ✅gemini_custom_grounded_func—app/test:api/misc.pygemini-3-flash-preview→dev:2794gemini-2.5-flash(dev-only divergence). ✅
Branch-only call sites & files:
api/assets/routers/meetings.py—ci_select_parametersgpt-4.1-mini(dev:846/test:848) + CI scoringgpt-4.1(dev:8292/test:8370); none onapp. ✅v3/tools.py— content-creator toolsgpt-4.1atdev:12544/13118/13189/13260/13339; absent onappandtest. ✅api/reports.py:5002—GenerateSurveyActionPlan.postgpt-4oondev/test; absent onapp. ✅api/scheduled_report.py:122,187—gpt-5-mini/gpt-4.1-miniondev/test; absent onapp. ✅research_setup/api/routes.py:149—gpt-image-2ontestonly. ✅research_setup/utils/functions.py:3051—llama-3.3-70b-versatileonapponly (site removed ondev/test). ✅predictive_twin/{intent_forecast,mentions_forecast,predictive_prompt_composer}.py—gpt-4.1+o3;git cat-file -efails onorigin/app. ✅v3/maven.py(dev-only) +fastapi_app/services/question_classification.py:24(dev-only) →claude-3-5-sonnet-20241022;ai_twin/respondents/memory_summarizer.py(dev-only) →gemini-2.5-flash. ✅- Refuted as noise —
daily_failed_reports_alert.py(no model call at all),v3/langraph_main.py(all models identical; raw-diff hits were line-offset artifacts), andmain.py/library/utils.py(DEEPSEEK-API-KEY/LLAMAINDEX-API-KEYare Key-Vault secret names, not model IDs) were flagged by the literal diff but carry no real per-branch model difference. ✅
AI media generation
Section titled “AI media generation”| Service | Used for |
|---|---|
| fal.ai | Image / media generation |
| Replicate | Model hosting / generation |
| Stability AI | Image generation |
| Leonardo AI | Image generation |
| Midjourney (GoAPI / TheNextLeg) | Image generation |
| Napkin | Visual / diagram generation |
| Tavus | AI video / avatars |
| Whisper API | Audio transcription |
📎 Evidence
- fal.ai media generation —
pw-enterprise/api/meetings.py:3105,3251importsfal_clientand callsfal_client.submit("fal-ai/flux-pro/v1.1", ...); dep inpyproject.toml:39; key loaded inapp.py:35. ✅ - Replicate generation —
pw-enterprise/api/planner_creative.py:29,93,7149-7150doesimport replicate, readsREPLICATE_API_TOKEN, and callsreplicate.run(...); dep inpyproject.toml:80. ✅ - Stability AI image gen —
pw-enterprise/api/assets/routers/personas.py:4232,4234POSTs tohttps://api.stability.ai/v2beta/stable-image/generate/corewithBearer {stability_ai_token}. ✅ - Leonardo AI image gen —
pw-enterprise/api/planner_creative.py:92,7551,7565readsleonardo_keyand callshttps://cloud.leonardo.ai/api/rest/v1/generationswithBearer {leonardo_key}. ✅ - Midjourney via GoAPI + TheNextLeg —
pw-enterprise/api/planner_creative.py:90,94readthe_next_legandgo_api_midjourney; GoAPI endpointhttps://api.midjourneyapi.xyz/mj/v2/imagineat 6641 and TheNextLeg Bearer headers at 6566/6607. ✅ - Napkin (key only, no call site) —
napkin_api_keyis read at module load inpw-enterprise/config/constants.py:52andapi/assets/main.py:58, but a repo-wide grep found NO HTTP call to any Napkin endpoint. ⚠️ Integration is provisioned but no active call site was found; confirm before listing as an active service. - Tavus AI video/avatars —
pw-enterprise/api/assets/routers/conversations.py:2941,2958,3042,3058callshttps://tavusapi.com/v2/personasand/v2/conversationswithx-api-key: TAVUS_API_KEY. ✅ - Whisper transcription —
pw-enterprise/api/planner_creative.py:95,3258readswhisperapi_keyand posts tohttps://transcribe.whisperapi.comwith a Bearer header. ✅
AI observability
Section titled “AI observability”| Service | Used for |
|---|---|
| Langfuse | LLM tracing (separate App / Dev / Test projects) |
| LangSmith | LangChain tracing |
📎 Evidence
- Langfuse tracing, separate App/Dev/Test —
.env:152-166has four key-pairs (local, DEV, TEST, APP);library/langsmith_metadata.py:16-34callsget_client()+LangChainInstrumentor().instrument();scripts/push_see_trace_prompt.py:100-105selectsLANGFUSE_*_KEY_{DEV,TEST,APP}. ✅ - LangSmith tracing —
pw-enterprise/library/langsmith_metadata.py:37-57setup_langsmith_tracing()requiresLANGCHAIN_API_KEYthen setsLANGCHAIN_TRACING_V2/LANGSMITH_TRACING; deplangsmithinpyproject.toml:17. ✅
Search, web & enrichment
Section titled “Search, web & enrichment”| Service | Used for |
|---|---|
| Tavily, Serper, SerpWow, ValueSERP | Search APIs |
| DataForSEO | SEO / SERP data |
| ZenRows, WebScrapingAPI, ScrapingNarf, Unwrangle | Web scraping |
| RapidAPI | API marketplace (various) |
| Klazify | Domain / company classification |
| BuzzSumo | Content / social analytics |
| SMMRY | Text summarization |
| NewsData | News feeds |
| US Census API | Demographics |
| Precisely | Geo / data enrichment |
| OpenWeatherMap | Weather data |
| Google Maps | Geocoding / maps |
📎 Evidence
- Tavily (env key only — no code usage) —
.env:137definesTAVILY_API_KEYbut a repo-wide grep fortavilyacross all.py(incl.v3/,ai_twin/,research_setup/) returned ZERO matches: no import, no HTTP call, no LangChain tool. 🔍 Needs a confirmed call site (or a runtime trace) before listing Tavily as an active Search API; currently only a provisioned key. - Serper search API —
pw-enterprise/api/intent_insights.py:7766,7768POSTs tohttps://google.serper.dev/searchwithX-API-KEY: serper_api_key. ✅ - SerpWow search API —
pw-enterprise/library/google_ads_package.py:4949callshttps://api.serpwow.com/live/search?api_key={}...withserpwow_api_key. ✅ - ValueSERP search API —
pw-enterprise/library/ai_services.py:226,237readsvalueserp_api_keyand callshttps://api.valueserp.com/search. ✅ - DataForSEO SERP data (actively called) —
pw-enterprise/api/intent_insights.py:9635callshttps://api.dataforseo.com/v3/keywords_data/bing/search_volume/live(plus 9362, 9775, 10153, 12256, andv3/tools.py:11984); secret loaded inapp.py:36. ✅ - ZenRows scraping —
pw-enterprise/api/misc.py:2044,2065sendsapikey: zenrow_tokentohttps://api.zenrows.com/v1/. ✅ - WebScrapingAPI scraping —
pw-enterprise/api/intent_insights.py:2265buildshttps://api.webscrapingapi.com/v1?...&api_key={webscrapingapi}&render_js=1. ✅ - ScrapingNarf (key read; call site not pinned) —
pw-enterprise/app.py:309readsscraping_narf_ai_key = os.environ["scraping_narf_ai_key"]and.env:27defines it; finder asserts use in 10+ modules but cited only the env read, not an HTTP endpoint. ⚠️ Confirm an actual scrapingnarf API call site. - Unwrangle extraction —
pw-enterprise/api/product.py:96,1590,1598readsunwrangle_api_keyand GETshttps://data.unwrangle.com/api/getter/. ✅ - RapidAPI marketplace —
pw-enterprise/library/google_ads_package.py:4814,4819callshttps://youtube-v31.p.rapidapi.com/searchwithx-rapidapi-keyheader. ✅ - Klazify classification —
pw-enterprise/api/intent_insights.py:155,2017,2061readsklazify_keyand callsklazify_fresh_urls()/klazify_url_fallback(). ✅ - BuzzSumo analytics —
pw-enterprise/api/intent_insights.py:623,630sendsapi_key: buzz_sumotohttps://api.buzzsumo.com/search/articles.json. ✅ - SMMRY summarization —
pw-enterprise/library/algorithms.py:24,258,266definessmmry()posting tohttps://api.smmry.comwithSM_API_KEY: smmry_key. ✅ - NewsData feeds —
pw-enterprise/cron/top_daily_news_by_country_cron.py:16,86readsnewsdata_api_keyand GETshttps://newsdata.io/api/1/latest. ✅ - US Census demographics —
pw-enterprise/ai_twin/respondents/universe_stage_fetcher.py:63,247,310readsCENSUS_API_KEYand callshttps://api.census.gov/data/{acs_year}/acs/acs1. ✅ - Precisely geo enrichment —
pw-enterprise/api/household.py:200,207precisely_household()callshttps://api.precisely.com/demographics-segmentation/v1/...; secrets loaded inapp.py:33-34. ✅ - OpenWeatherMap weather —
pw-enterprise/api/misc.py:90,2607readsopenweathermap_api_keyand callshttps://api.openweathermap.org/data/3.0/onecall. ✅ - Google Maps geocoding —
pw-enterprise/api/misc.py:2382callshttps://maps.googleapis.com/maps/api/geocode/json?latlng=...&key={pb_google_api_key}. ✅
Retail / commerce data (Traject Data family)
Section titled “Retail / commerce data (Traject Data family)”| Service | Marketplace |
|---|---|
| Rainforest | Amazon |
| BigBox | Home Depot |
| BlueCart | Walmart |
| RedCircle | Target |
📎 Evidence
- Rainforest Amazon data —
pw-enterprise/api/product.py:311,482have activerequests.get("https://api.rainforestapi.com/request", ...)(finder’s cited line 118 is commented, but 311/482 are live). ✅ - BigBox Home Depot data —
pw-enterprise/api/social_insights.py:79,670,676readsbigbox_keyand GETshttps://api.bigboxapi.com/request; secret mapped inapp.py:37. ✅ - BlueCart Walmart data —
pw-enterprise/api/social_insights.py:80,530,536readsbluecart_keyand GETshttps://api.bluecartapi.com/request; secret mapped inapp.py:38. ✅ - RedCircle Target data —
pw-enterprise/api/social_insights.py:81,603readsredcircle_keyand GETshttps://api.redcircleapi.com/request; secret mapped inapp.py:39. ✅
Ad & social platforms
Section titled “Ad & social platforms”| Platform | Integration |
|---|---|
| Meta / Facebook | Ads, Business Manager, Power Editor |
| Google Ads / DV360 | Ads |
| Snapchat | Ads (dev + prod apps) |
| TikTok | Ads |
| Ads | |
| Twitter / X | Ads / data |
| Ads (in-house wrapper) | |
| YouTube | Data / ads |
| Spotify | Data (in-house wrapper) |
| Google Analytics / GA4 | Analytics |
📎 Evidence
- Meta / Facebook Ads + BM + Power Editor —
pw-enterprise/library/facebook_package.py:51,75readsfb_versionand callshttps://graph.facebook.com/{fb_version}/me?fields=adaccounts;.env:39-40,10-15,20define facebook + BM + power-editor creds. ✅ - Google Ads / DV360 —
pw-enterprise/library/google_ads_package.py:51,66readsgoogle_client_secret+google_ads_auth();library/dv360_package.py:32-33,36readsdv360_client_id/secret+dv360_auth(); depgoogle-adsinpyproject.toml:49. ✅ - Snapchat Ads (dev + prod apps) —
pw-enterprise/library/snapchat_package.py:13-16,33-36readsdev_/prod_snapchat_client_id/secretand branches on environment;.env:29-34define all three pairs. ✅ - TikTok Ads —
pw-enterprise/library/tiktok_package.py:20-21,155readstiktok_client_id/secretand callshttps://business-api.tiktok.com/open_api/v1.3/ad/audience_size/estimate/. ✅ - Pinterest Ads —
pw-enterprise/library/pinterest_package.py:18-19,43readsPINTEREST_CLIENT_ID/SECRETand callshttps://api.pinterest.com/v5/oauth/token. ✅ - Twitter / X Ads —
pw-enterprise/api/twitter.py:12,14-15readstwitter_consumer_key,twitter_access_token(_secret); deptwitter-adsinpyproject.toml:91; five creds in.env:60-64. ✅ - LinkedIn Ads (in-house wrapper) —
pw-enterprise/library/linkedin_package.py:19-20,41,157does OAuth athttps://www.linkedin.com/oauth/v2/accessTokenand ad targeting athttps://api.linkedin.com/rest/adTargetingEntities. ✅ Note: lines 19-20 contain hardcoded default client_id/secret fallbacks (a security smell worth flagging separately). - YouTube data —
pw-enterprise/library/youtube_package.py:14,57readsyoutube_client_secretand buildsbuild("youtube", "v3", credentials=credentials). ✅ - Spotify (in-house wrapper) —
pw-enterprise/library/spotify_package.py:6-7spotify_access_token()callshttps://accounts.spotify.com/api/token; no dedicated.envkey (creds likely in GCP Secret Manager, per finder note). ✅ - GA4 analytics —
pw-enterprise/library/google_analytics_4_package.py:33,44hasgoogle_analytics_auth()+get_kv_user_platform_token(..., "google_analytics"); a separatelibrary/google_analytics_package.py(GA3) also exists. ✅
Cloud, infrastructure & ops
Section titled “Cloud, infrastructure & ops”| Service | Used for |
|---|---|
| Google Cloud Platform | Compute Engine (the VMs), Cloud SQL (managed Postgres), Cloud Ops/OpenTelemetry agents, BigQuery, Secret Manager, Translate |
| Cloudflare | R2, Images, Browser Rendering, Workers/Pages, Tunnel (VM ingress) |
| Microsoft Azure | Key Vault, Blob Storage (legacy) — migrated off, but Azure Monitor agents still run on the VMs |
| Sentry | Error / performance monitoring |
| Datadog | Metrics / monitoring (agent present on the dev VM) |
| Mailgun | Transactional email |
📎 Evidence
- GCP BigQuery / Secret Manager / Translate —
library/gcp_secrets.py:13,28instantiatesSecretManagerServiceClient;api/common.py:4importsbigquery;api/planner_creative.py:107importstranslate_v2; deps inpyproject.toml:100-104. ✅ - Cloudflare R2 + Images + Browser Rendering —
library/storage.py:37,104usesr2.cloudflarestorage.comandapi.cloudflare.com/.../images/v1;api/misc.py:2131calls.../browser-rendering/screenshot;.env:118,120,124-125,144-148define R2/Images/Browser/general keys. ✅ (Workers/Pages hosting is via frontend Wrangler — not evidenced in backend, but consistent with tech-stack table.) - Azure Key Vault + Blob (legacy) —
library/storage.py:6-7states the backend “migrated from Azure Blob Storage to Cloudflare R2”;library/secrets.py:109-116Key-Vault-named functions now delegate to GCP Secret Manager; deps remain inpyproject.toml:27-28but NOfrom azureimport exists in any.py. ✅ (legacy is accurate) - Sentry monitoring —
pw-enterprise/library/sentry_client.py:41,62is a dedicated client usingSENTRY_API_BASE_URL+SENTRY_API_TOKEN; depsentry-sdk[langchain,langgraph,rq]inpyproject.toml:85. ✅ - Datadog monitoring —
pw-enterprise/cron/iam_checks.py:17-18,106,109reads both Datadog keys and callshttps://api.us3.datadoghq.com/api/v1/synthetics/...withDD-API-KEY/DD-APPLICATION-KEY. ✅ - Mailgun email —
pw-enterprise/airflow_monitor.py:20-22,180-181reads Mailgun creds and sends viasmtplib.SMTP("smtp.mailgun.org", 587)with.login(...). ✅
Complete dependency inventory
Section titled “Complete dependency inventory”The full set of declared dependencies from each repo’s manifest, grouped by purpose. Collapsed by default.
Backend — pw-enterprise (Python, ~100 deps)
- Web / API: flask, flask-cors, flask-sqlalchemy, flask-marshmallow, flask-sse, fastapi[standard], uvicorn, gunicorn, flasgger, security
- AI frameworks: langchain (+ -anthropic, -community, -groq, -openai, -google-community, -google-genai, -text-splitters, -core, -classic), langgraph (+ -prebuilt, -cli[inmem]), langsmith, crewai, llama-index, langfuse, openinference-instrumentation-langchain
- LLM / model SDKs: openai, cohere, cerebras-cloud-sdk, google-genai, replicate, fal-client, faiss-cpu
- Cloud SDKs: google-cloud-bigquery, google-cloud-secret-manager, google-cloud-translate, google-cloud, google-api-python-client, google-ads, boto3, cloudflare, azure-identity, azure-keyvault-secrets, oauth2client, apiclient
- Ad / social clients: google-ads, twitter-ads (plus in-house
library/wrappers: facebook, linkedin, snapchat, tiktok, pinterest, youtube, spotify, dv360, google-analytics / GA4) - Jobs / async: rq, nest-asyncio (plus an in-house Airflow REST client
library/airflow_client.py— Airflow itself runs externally and is not a PyPI dependency) - Data / search: psycopg2 / psycopg2-binary, sqlalchemy-utils, marshmallow-sqlalchemy, elasticsearch, pandas, db-dtypes
- Media / docs / OCR: pillow, opencv-python-headless, moviepy, librosa, pozalabs-pydub, pytesseract, cairosvg, svglib, cssutils, imagehash, imutils, favicon; python-docx, python-pptx, pypdf, pdfkit, reportlab, unstructured, tabulate, xmltodict
- Auth / security: pyjwt, python-jose[cryptography], pyotp, webauthn, security, bandit (dev)
- ML / numeric: scikit-learn, llvmlite (dev/experimental branches add tensorflow, tensorflow-hub, prophet, matplotlib, crawl4ai)
- Utilities: python-dotenv, decouple, inflect, feedparser, pytrends, tld, tldextract, user-agents, psutil, icecream, libcst, imagehash, xmltodict
- Monitoring: sentry-sdk[langchain,langgraph,rq]
- Tooling: uv, ruff, langsmith-fetch (dev)
Frontend — pw-enterprise-frontend (React, ~150 packages)
- Core: react 19, react-dom, react-router 7
- Build / deploy: vite-plus (
@voidzero-dev/vite-plus-core), @vitejs/plugin-react, @rolldown/plugin-babel, vite-plugin-svgr, vite-plugin-devtools-json, wrangler, @cloudflare/workers-types, typescript, sass - State / data / forms: @tanstack/react-query, zustand, nuqs, react-hook-form, @hookform/resolvers, zod
- UI: @mui/material (+ icons-material), @radix-ui/* (26 primitives), tailwindcss 4 (+ animate, typography), lucide-react, react-icons, @emotion/react + styled, next-themes, sonner, cmdk, vaul, class-variance-authority, clsx, tailwind-merge
- Tables / grids: @ag-grid-community/, @ag-grid-enterprise/, @tanstack/react-table, react-window
- Charts / viz: highcharts (+ react-official, map-collection), @amcharts/amcharts5 (+ geodata), @nivo/*, recharts, @hophiphip/react-fishbone, google-map-react
- AI (client): @langchain/core, @langchain/langgraph (+ -api, -cli, -sdk), groq-sdk
- Media: wavesurfer.js, howler, react-media-recorder, react-medium-image-zoom, yet-another-react-lightbox, embla-carousel-react, react-slick, slick-carousel
- Export / docs: @react-pdf/renderer, jspdf, pdfmake, xlsx (SheetJS), file-saver, downloadjs, html2canvas, html2canvas-pro
- Auth: @simplewebauthn/browser, otpauth, jose, jwt-decode, react-cookie
- Dates: date-fns, moment, react-date-range, react-datepicker, react-day-picker
- HTTP / markdown / misc: axios, query-string, react-markdown (+ rehype-raw, remark-gfm), dompurify, motion, nanoid, uuid, react-beautiful-dnd, react-resizable-panels, input-otp, react-qr-code, react-number-format
- Monitoring / test: @sentry/react, @sentry/vite-plugin, @playwright/test, allure-playwright
📎 Evidence
- Web/API group —
pw-enterprise/pyproject.toml:40,43-48,51,84,94— flask, flask-cors, flask-sqlalchemy, flask-marshmallow, flask-sse, fastapi[standard], uvicorn, gunicorn, flasgger, security all present. ✅ - AI frameworks group —
pw-enterprise/pyproject.toml:12-25,33,57,105-106— langchain(+sub-packages), langgraph(+prebuilt,cli), langsmith, crewai, llama-index, langfuse, openinference-instrumentation-langchain all present. ✅ - Airflow is an in-house REST client, not a PyPI dep —
pw-enterprise/pyproject.tomlhas noapache-airflow;library/airflow_client.py(HTTP wrapper) is imported atapi/product_health.py:46. Inventory now states this. ✅ - ~100 backend deps —
pw-enterprise/pyproject.tomlapp branch: 95 in[project].dependencies+ 3 in[dependency-groups].dev= 98 (~100). ✅ - ~150 frontend pkgs —
pw-enterprise-frontend/package.json: 126 dependencies + 25 devDependencies = 151 (~150). ✅ - FE Core —
pw-enterprise-frontend/package.json:108,114,125(react ^19.1.0, react-dom ^19.1.0, react-router ^7.5.1) — all present. ✅ - FE Build/deploy —
pw-enterprise-frontend/package.json:149,159,74,164,167,169-172— all 9 named packages present in devDependencies. ✅ - Radix = 26 primitives —
pw-enterprise-frontend/package.json:47-72contains exactly 26@radix-ui/*packages; inventory now says 26. ✅ - FE AI client —
pw-enterprise-frontend/package.json:38-42,89(@langchain/core, @langchain/langgraph + -api/-cli/-sdk, groq-sdk) — all present. ✅
Versioning across environments
Section titled “Versioning across environments”Declared differences across branches (verified 2026-06-24)
Section titled “Declared differences across branches (verified 2026-06-24)”Comparing the committed manifests across branches, dev and test carry a newer backend dependency set than prod app — most notably a bumped LangChain / LangGraph / LangSmith / OpenAI cluster plus a few extra packages. app is the lowest-version baseline.
Backend — shared dependencies bumped on dev (and test) vs prod app:
| Package | dev / test | prod (app) |
|---|---|---|
langchain |
>=1.3.1 |
>=1.2.6 |
langgraph |
>=1.2.0 |
>=1.0.6 |
langchain-core |
>=1.4.0 |
>=1.2.7 |
langsmith |
>=0.8.5 |
>=0.6.4 |
langchain-anthropic |
>=1.4.3 |
>=1.3.1 |
langchain-openai |
>=1.2.1 |
>=1.1.7 |
langchain-google-community |
>=4.0.0 |
>=3.0.5 |
openai |
>=2.26.0 |
>=2.9.0 |
The full langchain-* / langgraph-* family moves together; the table lists the headline bumps.
Backend — extra packages on dev / test beyond prod app:
| Environment | Adds (beyond app) |
|---|---|
Dev (dev) |
deepagents==0.6.2 + the bumped LangChain/LangGraph cluster above |
Test (test) |
deepagents==0.6.2, crawl4ai==0.8.0, cryptography>=46.0.6 + the same bumped cluster |
Prod (app) |
— (baseline) |
tensorflow,tensorflow-hub,prophet, andmatplotliblive in the opt-in[dependency-groups].airflowgroup (not the core[project].dependencies), so they are not installed by default in any environment.db-dtypesis in theappbaseline, not a dev-only extra.
Frontend — only the Vite+ toolchain differs (dev and test share one package.json):
| Package | dev / test | prod (main) |
|---|---|---|
vite-plus (@voidzero-dev/vite-plus-core) |
^0.1.18 |
latest |
@vitejs/plugin-react |
^5.2.0 |
^6.0.1 |
All other frontend dependencies are identical across Dev / Test / Prod.
Net: the frontend differs only in the Vite+ toolchain, but the backend dev/test branches run a materially newer LangChain / LangGraph / OpenAI stack than prod app — a real source-level version difference, not parity.
📎 Evidence
- FE vite-plus dev/test ^0.1.18 —
git show origin/dev:package.json&origin/test:package.json(line 171) both"vite-plus": "^0.1.18", vite alias@^0.1.18. ✅ - FE vite-plus main latest —
pw-enterprise-frontend/package.json:171on origin/main"vite-plus": "latest"(vite alias@latest). ✅ - @vitejs/plugin-react 5.2.0 vs 6.0.1 —
git diff origin/main origin/dev -- package.jsonshows^6.0.1→^5.2.0; test identical to dev. ✅ - FE otherwise identical —
git diff origin/main origin/dev -- package.jsonshows only @vitejs/plugin-react, vite-plus, vite alias version changes + xlsx/overrides reorder. ✅ - Shared-dependency bumps on dev/test —
git diff origin/app origin/dev -- pyproject.tomlshows langchain1.2.6→1.3.1, langgraph1.0.6→1.2.0, langchain-core1.2.7→1.4.0, langsmith0.6.4→0.8.5, langchain-anthropic1.3.1→1.4.3, langchain-openai1.1.7→1.2.1, langchain-google-community3.0.5→4.0.0, openai2.9.0→2.26.0;origin/testbumps the same cluster. Section now documents these. ✅ - Dev/test extra packages —
git diff origin/app origin/devand…origin/test -- pyproject.toml: both adddeepagents==0.6.2;testalso addscrawl4ai==0.8.0+cryptography>=46.0.6. The earlierbigquery>=0.0.1entry did not exist (onlygoogle-cloud-bigquery>=3.39.0, present inapptoo) and has been removed. ✅ tensorflow/matplotlibare opt-in,db-dtypesis baseline — they live in[dependency-groups].airflow, anddb-dtypesis in theapp[project].dependencies(:35), not a dev-only extra; the note now states this. ✅- App baseline —
git show origin/app:pyproject.tomllackscrawl4ai,deepagents,cryptography>=46and runs the lower LangChain/LangGraph floors; it is the lowest-version baseline. ✅
Deployed runtime versions (captured from VMs 2026-06-25)
Section titled “Deployed runtime versions (captured from VMs 2026-06-25)”Resolved from each environment’s deployed uv.lock on the VMs (read-only). These are the exact installed versions, not the pyproject.toml floors above. Host topology: live dev checkout = /home/coder/served/pw-enterprise on pw-dev; test + prod co-located on pw-prod (/opt/test, /opt/production).
| Library | Dev (pw-dev /home/coder/served/pw-enterprise) |
Test (pw-prod /opt/test) |
Prod (pw-prod /opt/production) |
|---|---|---|---|
| python (system) | 3.12.7 | 3.12.7 | 3.12.7 |
| node | 18.17.0 | 26.2.0 | 26.2.0 |
| flask | 3.1.0 | 3.1.3 | 3.1.2 |
| sqlalchemy | 2.0.38 | 2.0.49 | 2.0.46 |
| redis (client) | 5.2.1 | 7.4.0 | 7.1.0 |
| rq | 2.1.0 | 2.8.0 | 2.6.1 |
| elasticsearch | 8.17.1 | 9.4.0 | 9.2.1 |
| gunicorn | 21.2.0 (sys) | 26.0.0 | 24.1.1 |
| uvicorn | 0.34.0 | 0.46.0 | 0.40.0 |
| langchain | 0.3.18 | 1.3.11 | 1.2.7 |
| langgraph | 0.2.72 | 1.2.6 | 1.0.7 |
| openai | 1.63.0 | 2.36.0 | 2.16.0 |
| anthropic | 0.45.2 | 0.102.0 | 0.76.0 |
Still uncaptured: deployed commit SHA per host (git rev-parse HEAD) and the Cloudflare Pages deployment ID for each frontend env.
📎 Evidence
- Test & Prod deployed versions (valid) —
uv.lockextracts viaawk … | grep …on pw-prod/opt/production/pw-enterprise/uv.lockand/opt/test/pw-enterprise/uv.lock(2026-06-25). Full table indocs/vm-tech-inventory.md. ✅ - node versions —
node --version: pw-devv18.17.0, pw-prodv26.2.0. ✅ - Dev deployed versions —
langchain==0.3.18 / langgraph==0.2.72read from the served dev checkout/home/coder/served/pw-enterprise/uv.lockon pw-dev (2026-06-25). ✅ - Still pending — git SHA per host and Cloudflare Pages deploy IDs were not captured. 🔍 needs
git rev-parse HEADon each checkout + Pages dashboard. ✅