Skip to content

feat(data-warehouse): implement llama_cloud import source - #70712

Merged
talyn-app[bot] merged 11 commits into
masterfrom
posthog-code/llama-cloud-source
Jul 16, 2026
Merged

feat(data-warehouse): implement llama_cloud import source#70712
talyn-app[bot] merged 11 commits into
masterfrom
posthog-code/llama-cloud-source

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

LlamaCloud (LlamaIndex's hosted document-processing and RAG platform, home of LlamaParse) was scaffolded as a warehouse source but had no sync logic. Teams running document pipelines on LlamaCloud can't analyze their parse/extract/classify jobs, pipelines, or credit consumption alongside their product data in PostHog.

Changes

Implements the llama_cloud source end to end, following the standard source.py / settings.py / llama_cloud.py split:

  • Ten endpoints: parse_jobs, extract_jobs, classify_jobs, batches, split_jobs, sheets_jobs (incremental on created_at), usage_metrics (incremental on day, with credit consumption), and projects, pipelines, files (full refresh, the API exposes no timestamp filter for them).
  • ResumableSource with page_size/page_token cursor pagination. The resume token is persisted after each yielded page, so a crashed sync re-yields the last page (merge dedupes) instead of restarting.
  • Incremental sync uses the server-side inclusive created_at_on_or_after / day_on_or_after filters. The list endpoints accept no sort param and don't document ordering, so incremental endpoints report sort_mode="desc" and the watermark persists only when a sync completes, which is correct regardless of actual server order. Job schemas declare a 24h default incremental lookback so status transitions on recently created jobs are picked up (jobs are keyed and merged on id).
  • Region support: LlamaCloud API keys are region-specific, so the form has a region select (NA/EU) mapping to the two regional hosts.
  • usage_metrics requires an organization_id query param, resolved at sync time from the key's project via /api/v2/projects.
  • All HTTP goes through make_tracked_session(); retryable 429/5xx handling layered with tenacity on top of the tracked transport's urllib3 retry.
  • validate_credentials probes /api/v2/projects and surfaces the API's own error detail (which points at region mismatches, the most common failure). get_non_retryable_errors stops retries on 401/403 for both regional hosts.
  • Canonical table/column descriptions from the official API reference plus lists_tables_without_credentials = True, so the public doc renders the full table catalog.
  • Official LlamaIndex logo mark added as frontend/public/services/llama_cloud.svg.
  • SOURCES.md updated (moved from Scaffolded to Implemented: HTTP, requests, tracked).
  • Stays behind unreleasedSource=True with releaseStatus=alpha.

Companion doc PR: PostHog/posthog.com#18456 (docsUrl points at /docs/cdp/sources/llama-cloud).

How did you test this code?

  • Verified endpoint behavior against the live API: fetched the production OpenAPI 3.1 spec and confirmed pagination params, created_at_on_or_after/day_on_or_after filters, response shapes (items/next_page_token/total_size), and item schemas per endpoint; curl-probed both regional hosts unauthenticated and with an invalid key to confirm the 401 semantics used by validate_credentials and get_non_retryable_errors.
  • I did not run a live sync against a real LlamaCloud account (no credentials available), so per-endpoint response ordering couldn't be observed. That's why incremental endpoints conservatively use sort_mode="desc" (watermark persisted only at successful completion) rather than trusting ascending order.
  • tests/test_llama_cloud_source.py (26 cases): schema catalog and incremental semantics per endpoint (guards a full-refresh endpoint silently advertising incremental), non-retryable error matching for both regional hosts (guards endless retries on bad keys), region plumbing into validation and the pipeline, and the watermark being dropped on full refresh.
  • tests/test_llama_cloud.py (33 cases): page-token pagination and termination, resume-state saved after yield not before (guards data loss on crash), resume seeding, incremental filter formatting for datetime and date cursors, usage-metrics org-id resolution and failure, bare-array pipelines endpoint, retryable vs non-retryable status classification, credential-validation status mapping, and SourceResponse shape (sort mode, partitioning, primary keys).
  • All 59 targeted tests pass, plus the registry-wide guards (test_source_categories, test_source_config_generator, 1537 tests). ruff check/format and hogli ci:preflight --fix clean. audit_source_docs against the posthog.com checkout reports no issues for this source.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

User-facing doc added in PostHog/posthog.com#18456.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Authored with PostHog Code (Claude). Skills invoked: /implementing-warehouse-sources, /writing-tests, /documenting-warehouse-sources. Decisions worth knowing: chose ResumableSource over the generic rest_source.RESTClient config path because the page-token resume, the usage-metrics org-id resolution, and per-endpoint filter formatting were simpler to express directly (klaviyo-style raw requests, yielding list[dict] per page, no source-level batching); chose sort_mode="desc" for incremental endpoints because the API has no sort param and ordering is undocumented; reverted an env-dependent regeneration of generated_configs.py (the generator's output for the Stripe config varies with local OAuth env vars) and applied only the LlamaCloud class change, byte-identical to the generator's output for it. The source stays behind unreleasedSource=True deliberately since it hasn't synced against a live account yet.

@Gilbert09 Gilbert09 self-assigned this Jul 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ✅ ready

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds — a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit cac9f9e · box box-5f38850ef784 · ready in 740s (push → usable) · build log · rebuilds on every push, torn down on close

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 14, 2026 13:04
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 64.77 MiB · no change

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.22 MiB · 22 files no change ███░░░░░░░ 28.4% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.13 MiB · 2,978 files no change █████████░ 87.9% of 9.25 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
281.3 KiB ../node_modules/.pnpm/posthog-js@1.402.3/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
235.5 KiB src/taxonomy/core-filter-definitions-by-group.json
222.9 KiB ../node_modules/.pnpm/posthog-js@1.402.3/node_modules/posthog-js/dist/module.js
164.0 KiB src/queries/validators.js
154.3 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
105.8 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
92.7 KiB ../packages/quill/packages/quill/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Dist folder size — 🔺 +1.9 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1312.64 MiB · 🔺 +1.9 KiB (+0.0%)

Playwright — all passed

All tests passed.

View test results →

Backend coverage — all changed backend lines covered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ████████████████████ 100.0% (444 / 444)

All changed backend lines are covered ✅

Per-product line coverage (touched products)
Product Coverage Lines
demo ███████████░░░░░░░░░ 56.2% 1,497 / 2,663
tasks █████████████░░░░░░░ 67.4% 25,560 / 37,895
signals ████████████████░░░░ 79.1% 19,033 / 24,073
data_modeling ████████████████░░░░ 80.0% 4,834 / 6,045
cdp ████████████████░░░░ 80.7% 3,118 / 3,864
notebooks █████████████████░░░ 84.3% 6,343 / 7,520
agent_platform █████████████████░░░ 84.7% 3,273 / 3,862
cohorts █████████████████░░░ 86.1% 4,022 / 4,671
actions █████████████████░░░ 86.6% 717 / 828
product_tours █████████████████░░░ 87.5% 1,266 / 1,447
exports ██████████████████░░ 88.3% 6,891 / 7,800
conversations ██████████████████░░ 88.9% 16,129 / 18,133
dashboards ██████████████████░░ 89.1% 5,719 / 6,418
mcp_analytics ██████████████████░░ 89.1% 2,502 / 2,807
error_tracking ██████████████████░░ 89.6% 9,718 / 10,852
alerts ██████████████████░░ 89.9% 3,638 / 4,046
engineering_analytics ██████████████████░░ 90.1% 5,105 / 5,665
streamlit_apps ██████████████████░░ 90.4% 2,499 / 2,764
slack_app ██████████████████░░ 90.6% 9,511 / 10,503
marketing_analytics ██████████████████░░ 90.8% 11,514 / 12,684
product_analytics ██████████████████░░ 91.1% 5,599 / 6,143
data_warehouse ██████████████████░░ 92.1% 18,133 / 19,683
workflows ██████████████████░░ 92.4% 5,148 / 5,574
web_analytics ███████████████████░ 92.7% 13,624 / 14,691
ai_observability ███████████████████░ 92.8% 14,868 / 16,019
surveys ███████████████████░ 92.9% 5,687 / 6,120
posthog_ai ███████████████████░ 93.2% 1,322 / 1,418
approvals ███████████████████░ 93.3% 3,395 / 3,640
reminders ███████████████████░ 93.4% 468 / 501
early_access_features ███████████████████░ 93.8% 848 / 904
endpoints ███████████████████░ 94.1% 8,606 / 9,143
skills ███████████████████░ 94.4% 2,827 / 2,995
revenue_analytics ███████████████████░ 94.5% 3,598 / 3,809
review_hog ███████████████████░ 94.6% 6,532 / 6,905
logs ███████████████████░ 95.3% 9,528 / 9,994
experiments ███████████████████░ 95.6% 24,171 / 25,288
replay_vision ███████████████████░ 95.7% 13,354 / 13,952
annotations ███████████████████░ 96.2% 732 / 761
warehouse_sources ███████████████████░ 96.3% 233,491 / 242,583
feature_flags ███████████████████░ 96.3% 16,038 / 16,661
user_interviews ███████████████████░ 96.4% 2,242 / 2,325
data_catalog ███████████████████░ 97.1% 2,034 / 2,095
customer_analytics ███████████████████░ 97.2% 7,480 / 7,698

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

@trunk-io

trunk-io Bot commented Jul 14, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@danielcarletti danielcarletti left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Implements the LlamaCloud (LlamaIndex) import source end to end: resumable
cursor-paginated transport over the tracked HTTP session, ten endpoints
(parse/extract/classify/split/sheets jobs, batches, projects, pipelines,
files, usage metrics), server-side incremental sync on created_at/day,
canonical table descriptions, icon, and source + transport tests.
Ships behind unreleasedSource with releaseStatus=alpha.

Generated-By: PostHog Code
Task-Id: 0d0ea3f2-4ebc-47f9-ad3b-bab99644f935
Bind tenacity's __wrapped__ once with the established type-ignore pattern
and pass the response kwarg requests' HTTPError stub requires.

Generated-By: PostHog Code
Task-Id: 0d0ea3f2-4ebc-47f9-ad3b-bab99644f935
Covers the _parse_error_detail fallback path where a gateway returns a
non-JSON error body, so validate_credentials surfaces the raw text instead
of crashing.

Generated-By: PostHog Code
Task-Id: 9cc974e0-19d5-49ff-a187-c3c942af5bd7
Remove unreleasedSource=True so the finished source is visible in the
connector catalog, and drop any test asserting the hidden state.
@veria-ai

veria-ai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 4 · PR risk: 0/10

…ines

The pipelines endpoint returns full pipeline definitions whose nested
embedding and data-sink configs can carry third-party credentials
(provider API keys, sink connection secrets). Yielding them unchanged
would persist those secrets into the warehouse.

Redact auth-bearing keys by name before yielding, reusing the same
denylist the HTTP sample-capture path already scrubs by, gated behind a
per-endpoint flag so only the config-style pipelines endpoint pays for it.

Generated-By: PostHog Code
Task-Id: 21f24636-a5ba-426b-ae64-c182fc60db03
…crets

Project the pipelines endpoint onto its documented, non-sensitive fields
instead of relying on a key-name denylist. A denylist can't enumerate every
secret-bearing shape (generic token/secret fields, credentials objects,
auth headers), so an allowlist keeps them out of the warehouse structurally.
Also opt the endpoint out of HTTP sample capture, since the sampler observes
the upstream response before row projection runs.

Generated-By: PostHog Code
Task-Id: 21f24636-a5ba-426b-ae64-c182fc60db03
… webhook secrets

The /api/v1/sheets/jobs listing can carry webhook credentials under nested
parameters.webhook_configurations (signing secrets, auth headers). With
output_fields unset the whole object was persisted, exposing those secrets to
warehouse readers. Project sheets_jobs rows onto the documented, non-sensitive
job metadata (matching canonical_descriptions) and disable HTTP sample capture,
mirroring the pipelines endpoint fix.

Generated-By: PostHog Code
Task-Id: 19edced1-886c-4d07-af72-3439c70ab1bc
Generated-By: PostHog Code
Task-Id: 3719b52a-2e64-4237-b7f3-5c103a41aac3
The files endpoint returns rows with a presigned download_url (and form fields)
that grant access to the private source document. Project files rows onto the
documented metadata fields and disable HTTP sample capture, matching the
treatment already applied to the pipelines and sheets endpoints, so those
credentials never reach the warehouse.

Generated-By: PostHog Code
Task-Id: c45c82e7-4037-4cdd-8bdd-2bb47a1a97bc
…closed

Job and config endpoints can return arbitrary customer document content (e.g.
parse-job user_metadata, extraction results) or embedded credentials that the
name-based sample scrubbers can't remove. With capture_http_samples defaulting
to True, those endpoints inherited capture-on. Flip the default to False and
opt in only the metadata-only endpoints (projects, usage_metrics) whose response
schema is limited to safe metadata. Add a regression test locking the fail-closed
default and the capturing-endpoint set.

Generated-By: PostHog Code
Task-Id: 19edced1-886c-4d07-af72-3439c70ab1bc
@github-actions
github-actions Bot requested a deployment to preview-pr-70712 July 16, 2026 02:16 In progress
Generated-By: PostHog Code
Task-Id: 3719b52a-2e64-4237-b7f3-5c103a41aac3
@talyn-app
talyn-app Bot merged commit 2197999 into master Jul 16, 2026
257 checks passed
@talyn-app
talyn-app Bot deleted the posthog-code/llama-cloud-source branch July 16, 2026 03:04
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-16 03:33 UTC Run
prod-us ✅ Deployed 2026-07-16 03:47 UTC Run
prod-eu ✅ Deployed 2026-07-16 03:48 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants