feat(python-sdk): add api manager and event queue layer - #3
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces the API and event layers for the Python SDK, building upon the existing deterministic core and in-memory evaluation managers. It integrates functionalities such as synchronous configuration fetching from the CDN and managing grouped visitor tracking payloads, mirroring the behavior found in the JavaScript SDK. This enhancement significantly advances the SDK's capability to interact with external services and handle internal events. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces new ApiManager and EventManager components to the SDK, along with a new http_client utility and SystemEvents enum. The ApiManager handles queuing and sending tracking events, and fetching configuration, while the EventManager provides a mechanism for event handling. Several improvements were suggested, including addressing potential runtime errors due to empty default API endpoints, ensuring the _sdk_key remains consistent after data updates, adhering to Python's snake_case convention for visitorId, refining type hints for event_manager, preventing a KeyError when accessing sdkKeySecret, and narrowing the generic Exception catch in release_queue to HttpError for more specific error handling.
| DEFAULT_CONFIG_ENDPOINT = os.getenv("CONFIG_ENDPOINT", "") | ||
| DEFAULT_TRACK_ENDPOINT = os.getenv("TRACK_ENDPOINT", "") |
There was a problem hiding this comment.
The default values for DEFAULT_CONFIG_ENDPOINT and DEFAULT_TRACK_ENDPOINT are empty strings if the respective environment variables are not set. This can lead to request calls with an invalid base_url, which will likely result in runtime errors when attempting to make HTTP requests. It's safer to either raise an error during initialization if these critical endpoints are not configured or provide a more robust default that prevents silent failures.
| if not self._sdk_key: | ||
| self._sdk_key = self._build_default_sdk_key() |
There was a problem hiding this comment.
The logic for rebuilding _sdk_key in set_data only triggers if _sdk_key is falsy. If _sdk_key was initially provided (and thus truthy) but _account_id or _project_id are subsequently changed via set_data, the _sdk_key might become stale if it was originally derived from these IDs. To ensure consistency, _sdk_key should be rebuilt unconditionally if _account_id or _project_id are updated, or if sdkKey is not explicitly provided in the initial config.
self._sdk_key = config.get("sdkKey") or self._build_default_sdk_key()|
|
||
| @dataclass | ||
| class VisitorQueueItem: | ||
| visitorId: str |
There was a problem hiding this comment.
| self, | ||
| config: Mapping[str, Any] | None = None, | ||
| *, | ||
| event_manager: Any | None = None, |
There was a problem hiding this comment.
| self._default_headers["Authorization"] = ( | ||
| f"Bearer {config['sdkKeySecret']}" | ||
| ) |
There was a problem hiding this comment.
Accessing config['sdkKeySecret'] directly after checking config.get("sdkKeySecret") can lead to a KeyError if config.get("sdkKeySecret") returns a falsy value (e.g., 0 or False) but the key is not actually present. It's safer to store the result of config.get("sdkKeySecret") in a variable and use that variable.
| self._default_headers["Authorization"] = ( | |
| f"Bearer {config['sdkKeySecret']}" | |
| ) | |
| sdk_key_secret = config.get("sdkKeySecret") | |
| if sdk_key_secret: | |
| self._default_headers["Authorization"] = f"Bearer {sdk_key_secret}" |
| }, | ||
| self._mapper(payload), | ||
| ) | ||
| except Exception as error: |
There was a problem hiding this comment.
Catching a generic Exception can mask other programming errors and make debugging more difficult. It's generally better to catch more specific exceptions. In this context, HttpError (which is raised by _request_sender) would be a more appropriate exception to catch for network-related issues.
| except Exception as error: | |
| except HttpError as error: |
…n round-trip — tests (verification) Beads: ai-driven-product-dev-xuci Verification task (Task 4): no production code change; proves FR25 determinism, AC#5 overlay/persisted distinction, AC#2/#3 round-trip via qs-06 fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the existing diagnostic surface (4-1 log_safe / 4-2 _Diagnostic.details) so each diagnostic carries the partial cross-SDK-comparable field set: reason, environment, bucket_value, variation_key, hashed visitor_ref (fingerprint_visitor). Additive: optional environment kwarg on Context.__init__ wired from Core.create_context. Frozen ExperienceResult shape and None-returning callers preserved. Deferred AC-1 fields (config_version/bucketing_inputs/experience_key) and AC#2/#3 honored (4.5/5.1). Beads: ai-driven-product-dev-klm5 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the existing diagnostic surface (4-1 log_safe / 4-2 _Diagnostic.details) so each diagnostic carries the partial cross-SDK-comparable field set: reason, environment, bucket_value, variation_key, hashed visitor_ref (fingerprint_visitor). Additive: optional environment kwarg on Context.__init__ wired from Core.create_context. Frozen ExperienceResult shape and None-returning callers preserved. Deferred AC-1 fields (config_version/bucketing_inputs/experience_key) and AC#2/#3 honored (4.5/5.1). Beads: ai-driven-product-dev-klm5 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
This PR adds the API and event layer for the Python SDK on top of the deterministic core and in-memory evaluation managers built in earlier PRs. It ports the JS SDK’s sync config fetch and tracking queue behavior so the Python implementation can request project config from the CDN and prepare/send grouped visitor tracking payloads.
What’s Included
Added
ApiManagerfor:/config/{sdkKey}/track/{sdkKey}Added
EventManagerfor:Added HTTP utility helpers for:
Added
SystemEventsenum values needed by the queue/event flowExported the new API/event modules through the package entrypoint
Behavior Covered
This PR now supports:
sdkKeyTesting
Added unit coverage for:
Verification:
.venv/bin/pytest -q63 passedOut of Scope
This PR intentionally does not include:
ConvertSDKContextThose should come in the next PR, where the public Python SDK surface is wired on top of the managers introduced so far.