Skip to content

SDK Configures Edge#419

Open
timmarkhuff wants to merge 16 commits intomainfrom
tim/sdk-configures-edge
Open

SDK Configures Edge#419
timmarkhuff wants to merge 16 commits intomainfrom
tim/sdk-configures-edge

Conversation

@timmarkhuff
Copy link
Copy Markdown
Contributor

@timmarkhuff timmarkhuff commented Mar 20, 2026

Summary

Adds SDK methods for reading and modifying edge endpoint configuration at runtime, accessible via a namespaced gl.edge pattern. Depends on companion Edge PR.

gl = ExperimentalApi(endpoint="http://my-edge-endpoint:30101")

config = gl.edge.get_config()
gl.edge.set_config(new_config, timeout_sec=600)

New code

EdgeAPI class (groundlight/edge/api.py)

Provides three methods:

  • get_config() -- calls GET /edge-config, returns EdgeEndpointConfig
  • set_config(config, timeout_sec) -- calls PUT /edge-config, then polls GET /edge-detector-readiness until all desired detectors are ready (or timeout)
  • get_detector_readiness() -- calls GET /edge-detector-readiness, returns dict[str, bool]

All HTTP calls go through _request(), which raises EdgeNotAvailableError on 404 or connection failure with an informative message guiding the user to check their endpoint configuration.

EdgeNotAvailableError

New exception in groundlight.client. Raised when an edge-only method is called against a non-edge endpoint (cloud API, unreachable host). The error message includes a hint about the GROUNDLIGHT_ENDPOINT env var.

gl.edge access pattern

ExperimentalApi.__init__ creates an EdgeAPI instance as self.edge. Edge methods are only accessible through this namespace.

Version

Bumped to 0.26.0 (minor version bump for new public API surface).

Release order

The companion edge-endpoint PR must be deployed first. The gl.edge.* methods call endpoints (/edge-config, /edge-detector-readiness) that only exist after the edge PR is deployed. Releasing this SDK version first would cause those methods to raise EdgeNotAvailableError (404).

Authentication note

These methods do not add authentication headers beyond what the SDK already sends (the standard x-api-token). The edge endpoint does not currently enforce auth on the config or readiness endpoints. The SDK sends the token in case auth is added in the future, but it is not required today.

Copy link
Copy Markdown
Collaborator

@brandon-wada brandon-wada left a comment

Choose a reason for hiding this comment

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

Couple of fixes to take a look at, mostly to ensure some stylistic rules

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe worth a small mocked test on the put method and detector readiness method

self.detector_reset_api = DetectorResetApi(self.api_client)

self.edge_api = EdgeApi(self.api_client)
self.edge = EdgeAPI(self)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Something has gone very wrong here. I think what we're looking for is for edge_base_url to be defined in the EdgeApi, and for there to only be one EdgeAPI defined on the experimental client

pass


class EdgeNotAvailableError(GroundlightClientError):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there a reason to define this in client rather than experimental_api? This error should only be possible in while using the experimental client, no?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct that it is only be possible from the experimental client at this point.

We don't currently have any exceptions that are specific to the experimental client, so I decided not to put it there. I considered grouping it with the edge stuff, e.g. groundlight.edge.exceptions, but I decided not to introduce a new pattern for a single exception.

deadline = time.time() + timeout_sec
while time.time() < deadline:
readiness = self.get_detector_readiness()
if desired_ids and all(readiness.get(did, False) for did in desired_ids):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Via Claude: edge condition if the detector list is empty. It should either return immediately or return a different error rather than timeout

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

headers = self._client.get_raw_headers()
try:
response = requests.request(
method, url, headers=headers, verify=self._client.configuration.verify_ssl, timeout=10, **kwargs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Claude has a nit: 10 second default is fine but should be overridable via kwargs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think this would add complexity without any value to the SDK user. All of these methods should return very quickly. 10 seconds is a reasonable and generous timeout. If the request is taking longer than this, there is something wrong with the network, which won't be fixed by adjusting the timeout.

self._request("PUT", "/edge-config", json=config.to_payload())

poll_interval_seconds = 1
desired_ids = {d.detector_id for d in config.detectors if d.detector_id}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Claude: d.detector_id should never be falsey, it's misleading to have the check here
Me: However, in the current pydantic config I don't think we actually forbid the empty string. We probably should though

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