feat(flags): expose a flag's evaluation runtime through the SDK - #977
Draft
posthog[bot] wants to merge 2 commits into
Draft
posthog[bot] wants to merge 2 commits into
posthog[bot] wants to merge 2 commits into
Conversation
The local-evaluation payload already carries `evaluation_runtime` per flag, but only on the untyped `client.feature_flags` dicts. There was no typed accessor, no filter and no documentation, so a backend that serves flags to its own frontend had to call `/api/feature_flag/local_evaluation` directly to read the value. Adds `FeatureFlagEvaluationRuntime` and two read methods on the client, both served from the definitions local evaluation already holds: - `get_feature_flag_evaluation_runtime(key)` - `get_feature_flag_keys_by_evaluation_runtime(runtime)` A definition with no runtime reports `ALL`, the default PostHog applies, and a flag set to `ALL` matches both the client and the server runtime. Generated-By: PostHog Desktop Task-Id: f3696295-459a-4066-bb52-5eea7ee16f37
Contributor
posthog-python Compliance ReportDate: 2026-09-21 17:46:09 UTC ✅ All Tests Passed!111/111 tests passed Capture_V1 Tests✅ 94/94 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
|
23 tasks
Both read methods now go through `feature_flags_by_key`, which drops the redundant missing-key guard. The dict is built by iterating the definition list, so load order is unchanged. Generated-By: PostHog Desktop Task-Id: f3696295-459a-4066-bb52-5eea7ee16f37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
/api/feature_flag/local_evaluationby hand to learn a flag's evaluation runtime. That endpoint is a private serializer contract, so they are pinned to a payload shape the team reserves the right to change._update_flag_stateparks the raw local-evaluation dicts onclient.feature_flags, and each dict carriesevaluation_runtime— but that property is untyped, undocumented, and has no filter, so nothing about it is safe to rely on.MinimalFeatureFlagSerializerhas shipped the field for a while, andEvaluationFeatureFlagSerializerextends it for both SDK-facing shapes. No backend change is needed.Note
This adds public API, so per CONTRIBUTING.md the shape needs a maintainer's agreement before it merges. It is a proposal — say the word and the naming or the semantics change. The two design calls worth a look are in "Agent context" below.
posthog-js#5050 mirrors the same surface in posthog-node. The naming and the semantics should match across the two SDKs, so it is worth reviewing them together.
💚 How did you test it?
TestEvaluationRuntime(posthog/test/test_feature_flags.py): each runtime value, a definition withevaluation_runtime: null, a legacy definition with the key absent, an unknown flag key, a client with nothing loaded yet, and a bad runtime argumentload_feature_flags()from a mockedGetResponse, so the assertion runs against the fetch path rather than a hand-setclient.feature_flagshttp.serverserving a local-evaluation body in the trueEvaluationFeatureFlagSerializershape, pointed aPosthog(host=...)at it, and read the runtimes back over a real HTTP requestruff format --check,ruff check,mypy(clean, no new baseline entries),python -W error -c "import posthog", andpytest— 2738 passed, 1 skippedOutput of the end-to-end run
No UI, so no demo.
AsyncClienthas no local-evaluation surface at all, so there was nothing to mirror there.📝 Checklist
If releasing new changes
sampo addto generate a changeset file🤖 Agent context
Autonomy: Fully autonomous
Written by Claude Opus 5 running as a PostHog self-driving task, from an inbox report. No human drove the work, so the PR is left unassigned for the owning team to triage.
Two design calls a reviewer should weigh:
ALLrather thanNone. The column is nullable and older definitions omit the key, andallis the server-side default, so coercing keeps callers from writing the same fallback everywhere.Noneis reserved for "no definition loaded for this key", which is a genuinely different answer. The alternative —Optionalall the way down — was rejected because it makes the filter method ambiguous about null runtimes.ALLmatches symmetrically. A flag set toallis returned for bothCLIENTandSERVER, which is what the customer's use case needs in one call. The cost is that asking forALLreturns every loaded flag rather than only the flags configured asall; that is documented on the method. Exact-equality filtering was tried first and rejected as it forced the caller to union two calls.Also considered and not done: a filter on
get_all_flags()/evaluate_flags(). That changes evaluation behavior rather than adding a read path, which is a much larger surface to agree on, and the report asked for a read path.Created with PostHog Desktop from this inbox report.