Skip to content

feat(data-warehouse): implement helicone import source - #70714

Merged
talyn-app[bot] merged 5 commits into
masterfrom
posthog-code/helicone-warehouse-source
Jul 15, 2026
Merged

feat(data-warehouse): implement helicone import source#70714
talyn-app[bot] merged 5 commits into
masterfrom
posthog-code/helicone-warehouse-source

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

Helicone is a scaffolded warehouse source (registered with unreleasedSource=True and an empty stub) with no sync logic. Teams using Helicone for LLM observability can't join their LLM request, cost, and usage data with their product data in PostHog.

Why

Fills in the scaffolded Helicone connector so LLM observability data (requests, sessions, per-user usage, prompts) can be imported into the Data warehouse.

Changes

Implements the Helicone source end-to-end as a ResumableSource, following the standard source.py / settings.py / helicone.py split:

  • Tables: requests (the logged LLM request stream via the bulk POST /v1/request/query-clickhouse endpoint), sessions (POST /v1/session/query), users (per-user usage aggregates via POST /v1/user/query), and prompts (POST /v1/prompt-2025/query).
  • Incremental sync on requests only, using a server-side request_created_at gte condition in Helicone's filter AST with ascending created_at sort and limit/offset paging. The filter rides in every page's body, so pagination stays bounded by the watermark. First incremental sync is capped at 365 days. sessions/users/prompts are aggregates or metadata that change in place with no changed-since filter, so they're full refresh only.
  • Resumable: offset-based resume state saved after each yielded page, with the incremental cutoff (and the sessions time window) pinned in the state so a resumed attempt pages the same result set.
  • Regional hosts: region select (US api.helicone.ai / EU eu.api.helicone.ai) alongside the API key, mirroring the Amplitude source's pattern.
  • All HTTP goes through make_tracked_session(); retries via tenacity on 429/5xx/transient transport errors; 401/403 on both regional hosts registered as non-retryable errors.
  • canonical_descriptions.py for all four tables (from the official API docs), lists_tables_without_credentials = True so the public docs render the table catalog, and the official Helicone icon mark (SVG, from their Apache-2.0 repo).
  • Regenerated HeliconeSourceConfig, moved helicone into the Implemented table in SOURCES.md.

Per the rollout plan the source stays behind unreleasedSource=True with releaseStatus=ReleaseStatus.ALPHA for now.

Note

Webhook support was deliberately left out despite Helicone having a manageable webhook API. Helicone's webhook payload (verified against their open-source sender) carries only request_id, truncated bodies, and optional model/provider/token metadata, with no creation timestamp and a different shape than the pull rows, so merging webhook deliveries into the request_created_at-partitioned requests table would produce partition-key-less, schema-divergent rows. The append-only request log makes incremental pull a safe substitute for deltas.

The user-facing doc is in PostHog/posthog.com#18459; docsUrl matches its slug and audit_source_docs passes for helicone against that checkout.

How did you test this code?

  • Verified endpoint behavior against the live API docs and with curl probes of both regional hosts (401 error shapes, endpoint existence). I don't have Helicone credentials, so authenticated responses (row shape, sort/filter honoring) are per the published OpenAPI specs, with the uncertainty noted in code comments; the alpha flag reflects that.
  • pytest products/.../sources/helicone/tests/ - 48 tests pass. Transport tests catch: wrong filter-AST shape (which Helicone answers with silently empty data), pagination not terminating or not resuming from saved offset, resume recomputing the time window and shifting rows across page boundaries, sync-mode/primary-key/partition metadata regressions, and non-retryable error keys drifting from the message raise_for_status actually produces. Source tests cover schema advertising, credential-validation plumbing, and full-refresh runs dropping the watermark.
  • test_source_categories.py and test_source_config_generator.py (1537 tests) pass.
  • ruff check / ruff format clean. hogli isn't available in this environment, so ci:preflight couldn't run.

Automatic notifications

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

Docs update

Doc added in PostHog/posthog.com#18459.

🤖 Agent context

Autonomy: Fully autonomous

  • Built with Claude Code (PostHog Code cloud task). Skills invoked: /implementing-warehouse-sources, /documenting-warehouse-sources, /writing-tests.
  • Modeled on the Amplitude source (closest shape: API key + region select, ResumableSource). Chose pull-only over the suggested ResumableSource+WebhookSource combination after verifying from Helicone's OSS webhook sender that payloads lack the partition timestamp needed for safe merges (details in the note above).
  • Running generate_source_configs produced unrelated drift in generated_configs.py (including a semantic change to StripeAuthMethodConfig optionality that would break Stripe config parsing), so only the Helicone hunk was kept, exactly as the generator emitted it. The generator/committed-file drift may be worth a separate look.
  • No new env vars. No migrations needed (the enum landed with the scaffold).

Created with PostHog Code

@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 17e894a · box box-5b4ef1cfe98a · ready in 745s (push → usable) · build log · rebuilds on every push, torn down on close

@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.1 KiB (+0.0%)

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

Total: 1312.48 MiB · 🔺 +1.1 KiB (+0.0%)

Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 95.0% of changed backend lines covered — 21 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 95.0% (450 / 471)

File Patch Uncovered changed lines
products/warehouse_sources/backend/temporal/data_imports/sources/helicone/helicone.py 86.7% 62, 83, 98, 101–102, 230, 277, 297–298, 303, 305–306, 316–321, 323
products/warehouse_sources/backend/temporal/data_imports/sources/helicone/source.py 94.3% 95, 99

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 29458579227 -n patch-coverage), or the coverage-data block at the end of this comment.

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.2% 230,698 / 239,749
feature_flags ███████████████████░ 96.3% 16,002 / 16,625
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

@veria-ai

veria-ai Bot commented Jul 14, 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: 2 · PR risk: 0/10

@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 Helicone warehouse source as a ResumableSource pulling
requests (incremental on request_created_at via the filter AST),
sessions, users, and prompts, with regional US/EU hosts, tracked HTTP
transport, canonical table descriptions, and offset-based resume state.

Generated-By: PostHog Code
Task-Id: b085672b-ea5a-42d3-8a00-d49175254f10
…ting

Declare region as a connection host field so editing it forces
re-entering the API key instead of reusing the stored secret against
the other regional host (matches the Datadog/AssemblyAI pattern).

Generated-By: PostHog Code
Task-Id: b085672b-ea5a-42d3-8a00-d49175254f10
Helicone request pages carry request_body/response_body containing customer
prompts and model output. Pass capture=False on both the sync and
credential-validation tracked sessions so those bodies never land in diagnostic
HTTP samples, and redact the API key from logged URLs and samples for defense in
depth.

Generated-By: PostHog Code
Task-Id: 7a31032b-1697-4380-89d2-c521e498c9dc
Remove unreleasedSource=True so the finished source is visible in the
connector catalog, and drop any test asserting the hidden state.
The prompts endpoint is page-based, but resume state stored the cumulative
row count and derived the page via offset // pageSize. That drifts once any
page comes back short, re-requesting an already-paged page on resume. Store
the next page index directly in the resume state instead.

Generated-By: PostHog Code
Task-Id: 00dc30e7-8768-485d-937f-5be541b5b0a5
@talyn-app
talyn-app Bot merged commit 5e1164c into master Jul 15, 2026
262 checks passed
@talyn-app
talyn-app Bot deleted the posthog-code/helicone-warehouse-source branch July 15, 2026 23:49
@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 00:21 UTC Run
prod-us ✅ Deployed 2026-07-16 00:40 UTC Run
prod-eu ✅ Deployed 2026-07-16 00:41 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