feat(data-warehouse): implement hyperspell import source - #69620
Conversation
|
Hey @Gilbert09! 👋 It looks like your git author email on this PR isn't your
You can fix it for this repo with: git config user.email "you@posthog.com"Or set it globally with |
🦔 Hogbox preview · ✅ ready▶ Open the preview
commit |
|
🤖 CI report✅ Bundle size — no changeUncompressed size of every built Total: 64.76 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 budgetHow 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
🟢 Largest files eagerly shipped from
|
| 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 |
|---|---|
| 280.3 KiB | ../node_modules/.pnpm/posthog-js@1.400.1/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.7 KiB | ../node_modules/.pnpm/posthog-js@1.400.1/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.9 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.6 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1307.79 MiB · 🔺 +1.6 KiB (+0.0%)
⚠️ Backend coverage — 99.0% of changed backend lines covered — 3 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ████████████████████ 99.0% (376 / 379)
| File | Patch | Uncovered changed lines |
|---|---|---|
products/warehouse_sources/backend/temporal/data_imports/sources/hyperspell/hyperspell.py |
96.8% | 158–159, 174 |
🤖 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 29405511751 -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,425 / 37,703 |
signals |
████████████████░░░░ 79.0% |
19,009 / 24,055 |
data_modeling |
████████████████░░░░ 80.0% |
4,834 / 6,045 |
cdp |
████████████████░░░░ 80.7% |
3,118 / 3,864 |
notebooks |
█████████████████░░░ 83.8% |
6,086 / 7,259 |
agent_platform |
█████████████████░░░ 84.2% |
3,112 / 3,695 |
cohorts |
█████████████████░░░ 86.0% |
3,989 / 4,639 |
actions |
█████████████████░░░ 86.6% |
717 / 828 |
product_tours |
█████████████████░░░ 87.5% |
1,266 / 1,447 |
exports |
██████████████████░░ 88.3% |
6,861 / 7,769 |
conversations |
██████████████████░░ 88.9% |
16,028 / 18,034 |
dashboards |
██████████████████░░ 89.1% |
5,648 / 6,337 |
mcp_analytics |
██████████████████░░ 89.1% |
2,502 / 2,807 |
error_tracking |
██████████████████░░ 89.6% |
9,749 / 10,883 |
engineering_analytics |
██████████████████░░ 89.8% |
4,861 / 5,414 |
streamlit_apps |
██████████████████░░ 90.4% |
2,499 / 2,764 |
slack_app |
██████████████████░░ 90.6% |
9,511 / 10,503 |
marketing_analytics |
██████████████████░░ 90.8% |
11,514 / 12,684 |
alerts |
██████████████████░░ 90.9% |
3,416 / 3,760 |
product_analytics |
██████████████████░░ 91.1% |
5,527 / 6,068 |
data_warehouse |
██████████████████░░ 92.0% |
17,807 / 19,349 |
workflows |
██████████████████░░ 92.0% |
4,795 / 5,210 |
ai_observability |
███████████████████░ 92.7% |
14,670 / 15,822 |
web_analytics |
███████████████████░ 92.7% |
13,607 / 14,674 |
surveys |
███████████████████░ 92.9% |
5,660 / 6,094 |
posthog_ai |
███████████████████░ 93.2% |
1,312 / 1,408 |
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,475 / 6,848 |
logs |
███████████████████░ 95.3% |
9,476 / 9,942 |
experiments |
███████████████████░ 95.6% |
24,005 / 25,112 |
replay_vision |
███████████████████░ 95.7% |
13,300 / 13,896 |
feature_flags |
███████████████████░ 96.1% |
14,721 / 15,325 |
warehouse_sources |
███████████████████░ 96.1% |
217,424 / 226,136 |
annotations |
███████████████████░ 96.2% |
732 / 761 |
user_interviews |
███████████████████░ 96.4% |
2,242 / 2,325 |
data_catalog |
███████████████████░ 97.2% |
1,642 / 1,689 |
customer_analytics |
███████████████████░ 97.3% |
7,442 / 7,648 |
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.
272cdc9 to
ffd5263
Compare
|
Re: the |
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 2 · PR risk: 0/10 |
|
Note: the |
CI status updateAll checks owned by this PR are green:
The earlier The one remaining red check, |
76990a6 to
ed56ce8
Compare
…k on missing source icons The regenerated StripeAuthMethodConfig incorrectly made both stripe_secret_key and stripe_integration_id required, breaking Config.from_dict() for every existing Stripe source (API-key connections lack an integration id and OAuth connections lack a secret key). Restore both to optional to match the generator output on master. The 25 new scaffolded sources reference icon PNGs that aren't shipped yet, which would render as broken images in the "coming soon" source picker. Add an onError fallback in SourceIcon so a missing icon degrades to the placeholder hedgehog. Generated-By: PostHog Code Task-Id: 65c6cedb-aadf-4f89-914d-de1c069bbe3c
Fills in the scaffolded Hyperspell source: six endpoints (memories, connections, integrations, entities, queries, context_documents) synced over cursor pagination as a ResumableSource, with region (US/EU) and optional per-user (X-As-User) config, tracked HTTP transport, canonical table descriptions, an official SVG icon, and behavior tests. Generated-By: PostHog Code Task-Id: 56c0c569-970d-421a-8251-e4eee2fe59d1
mypy infers the ternary's type from the else branch as dict[str, str], rejecting the list/None values in the 200-status body. Generated-By: PostHog Code Task-Id: 56c0c569-970d-421a-8251-e4eee2fe59d1
…d changes Add region and user_id to HyperspellSource.connection_host_fields so changing either on an existing source forces the API key to be re-entered. region selects the host the key is sent to (US vs EU) and user_id sets the X-As-User identity the key acts as, so leaving them out would let an editor who cannot read the stored key retarget it at another Hyperspell user's data. Generated-By: PostHog Code Task-Id: 59a91ac9-398b-4f07-b28d-aa108546fdd7
Remove unreleasedSource=True so the finished source is visible in the connector catalog, and drop any test asserting the hidden state.
Memory documents and query logs contain arbitrary user-authored content that the name-based sample scrubbers can't recognise, so exclude these sessions from HTTP sample capture (still metered and logged). Generated-By: PostHog Code Task-Id: 669fcd37-c252-4cd8-abe9-241f97ae3a5d
1cecf68 to
e0322f2
Compare
Generated-By: PostHog Code Task-Id: d8b56dc0-a85a-47a4-aeb4-a246bbf25a46
Problem
Hyperspell (an API-based memory layer for AI apps and agents) was scaffolded as a data warehouse source in #69517 but had no sync logic: empty fields, no schemas, no pipeline wiring. Users can't yet pull their agent-memory corpus into PostHog to analyze it alongside product data.
Why: part of the batch effort to turn the scaffolded SaaS/AI source stubs into working connectors, so this PR targets the scaffold branch and will retarget to master when #69517 merges.
Changes
Implements the source end to end following the
source.py/settings.py/hyperspell.pyarchitecture:memories,connections,integrations,entities,queries(query log),context_documents. All full refresh - no Hyperspell list endpoint exposes a server-side updated-since filter (memories only filter on source/collection/status/metadata), so no endpoint claimssupports_incremental.ResumableSourceusing Hyperspell'scursor/next_cursorpagination. The cursor of the page most recently yielded is saved after each batch, so a resume re-fetches that page and merge-dedupes rather than risking skipped rows.connectionsandintegrationsare single unpaginated responses.X-As-Userheader syncs one user's data; blank syncs app-scoped data only.make_tracked_session()with key redaction, bounded tenacity retries on 429/5xx, and non-retryable 401/403 mappings.ingested_at,created_at,time) - never moving fields likelast_modified_at. Composite primary key[source, resource_id]on memories sinceresource_idis only unique within its provider.memorieslowers the per-chunk byte cap to 100 MiB since rows embed the full nested document payload.lists_tables_without_credentials = Trueso the docs table list renders, generatedHyperspellSourceConfig, SOURCES.md entry, and the official Hyperspell SVG icon (their favicon, with light/dark support).unreleasedSource=TruewithreleaseStatus="alpha"per the batch rollout plan.How did you test this code?
pytest products/warehouse_sources/backend/temporal/data_imports/sources/hyperspell/tests/- 58 tests pass. They cover: cursor pagination followsnext_cursorand terminates, resume state is saved per yielded page and honored on restart, unpaginated endpoints make exactly one request, per-endpoint page-size param names (sizevslimit), region base URL selection,X-As-Userheader wiring, credential validation status mapping, retryable vs non-retryable error classification, and the SourceResponse shape (primary keys, partitioning, sort mode, chunk byte cap) per endpoint.pytest .../sources/common/test/- 362 pass (10 pre-existing errors in MinIO-backed webhook tests that need a live MinIO container, unrelated).SourceConfigGeneratoragainst the registry to confirm the committedHyperspellSourceConfigmatches generator output exactly.ruff check/ruff formatclean;hogli ci:preflight --fixreports 0 failures.Automatic notifications
Docs update
The user-facing doc lives in the posthog.com repo, which isn't available in this environment.
docsUrlis set tohttps://posthog.com/docs/cdp/sources/hyperspell; the doc below needs to land atcontents/docs/cdp/sources/hyperspell.md(andaudit_source_docsrun) as a follow-up.Drafted doc content for posthog.com
🤖 Agent context
Autonomy: Fully autonomous
implementing-warehouse-sources,documenting-warehouse-sources,writing-tests.requests+ tracked session +ResumableSourceManager) rather than the declarativerest_source.RESTClientpath - Hyperspell's per-endpoint quirks (different page-size param names, different response wrapper keys, two unpaginated endpoints) fit a small endpoint-config dataclass better than the generic REST config.ingested_atfor pseudo-incremental sync of memories; rejected it since the API documents no ordering guarantee for/memories/list, so a watermark could silently skip rows. Full refresh is the honest mode.HyperspellSourceConfigingenerated_configs.pywas first hand-written, then verified byte-for-byte by runningSourceConfigGeneratordirectly in auv syncvenv (the management command itself needs a DB connection this environment doesn't have).Created with PostHog Code