feat: add PrimingGroup and MLPipeline SDK methods#420
feat: add PrimingGroup and MLPipeline SDK methods#420brandon-wada wants to merge 22 commits intomainfrom
Conversation
New Groundlight client methods: - list_detector_pipelines(detector) -> List[MLPipeline] - list_priming_groups() -> List[PrimingGroup] - create_priming_group(name, source_ml_pipeline_id, canonical_query, disable_shadow_pipelines) -> PrimingGroup - get_priming_group(priming_group_id) -> PrimingGroup - delete_priming_group(priming_group_id) New pydantic models in generated/model.py: MLPipeline, PrimingGroup, PaginatedMLPipelineList, PaginatedPrimingGroupList. PrimingGroups let users seed new detectors with a pre-trained model binary so they skip the cold-start period. Detectors created with priming_group_id (already supported in create_detector) will start with the primed model active.
…gs to PrimingGroup model
Resolved conflict in experimental_api.py by keeping both the edge_base_url utility method from main and the new ML Pipeline / PrimingGroup methods from this branch. Made-with: Cursor
|
This line in the description confuses me: active = next(p for p in pipelines if p.is_active_pipeline and p.cached_vizlogic_key)Should we have a function for that? gl.get_active_pipeline(pipelines)? ...actually, I realize now that this code block is invalid. I get this error: |
| @pytest.mark.expensive | ||
| def test_get_priming_group_unknown_raises(gl_experimental: ExperimentalApi): | ||
| with pytest.raises(NotFoundError): | ||
| gl_experimental.get_priming_group("pgp_doesnotexist000000000000") |
There was a problem hiding this comment.
I think this is a typo? pg instead of pgp
|
There are multiple docstrings that say something to the effect of: Should this be updated now that users can self-service? |
|
From Claude:
|
|
It seems there is no way to connect the dots between a detector and it's related priming group. Below is a detector that I created on a particular priming group, but there is no way (that I can find) to know that they are connected. {
"id": "pg_3C39sYIOAs1Joc0h3plHuw3V8ua",
"name": "Car Detector - Priming Group",
"is_global": false,
"canonical_query": "Draw a box around each car in the image",
"active_pipeline_config": null,
"priming_group_specific_shadow_pipeline_configs": [],
"disable_shadow_pipelines": true,
"created_at": "2026-04-07T23:13:13.338272Z"
}
{
"id": "det_3C3A1WilrnS9cT7dO7MPJ5hbr1d",
"type": "detector",
"created_at": "2026-04-07T23:14:24.750278Z",
"name": "Car Detector - 2026-04-07 23:14:24",
"query": "Draw a box around each car in the image",
"group_name": "Default",
"confidence_threshold": 0.9,
"patience_time": 30.0,
"metadata": null,
"mode": "BOUNDING_BOX",
"mode_configuration": {
"class_name": "car",
"max_num_bboxes": 10.0
},
"status": "ON",
"escalation_type": "STANDARD"
}Maybe Detector should be updated with priming_group_id and/or the PrimingGroup should be updated to include detectors[] |
|
Are there any safeguards to ensure that users don't try to associate detectors of different types to the same PrimingGroup? I ran a little test and there doesn't seem to be. I assume we want the service to reject this, because detectors of different types can't (necessarily) use the same pipelines. |
|
The PR description says "gl = Groundlight()" but it should say "gl = ExperimentalApi" |
|
I just did a bit of testing and found that when I prime a detector from a mature detector, the primed detector doesn't get the mature weights downloaded to run on edge. In other words, the primed detector seems naive on edge, and doesn't return confident answers. I'm not exactly sure what the solution for this should be, but I think we should consider this before merging this PR. |
Summary
Adds SDK methods for the new PrimingGroup public API. Priming Groups are groupings of detectors that share a common semantic query and can utilize a common base model. In the future, priming groups may allow for cross detector learning and other multi-task and meta-learning techniques.
In introducing priming groups, we also expose MLPipelines. Every detector has one active MLPipeline but constantly runs multiple MLPipelines under the hood that are compared against the top performing active model. Whenever Groundlight determines that there is a better performing model, we will switch that model to be the active model.
Currently, a priming group can be created from an existing and trained MLPipeline, which one can get given an active detector. The new PrimingGroup will use a frozen version of the MLPipeline for the first inference on a new detector created using the PrimingGroups. If shadow pipelines are disallowed on the PrimingGroup, that frozen model will be used indefinitely by the detector. Otherwise, the frozen model will be used until one of the other MLPipelines on the detector is evaluated to be a better fit.
We add the following methods in this PR
list_detector_pipelines(detector)— list all MLPipelines for a detectorcreate_priming_group(name, source_ml_pipeline_id, ...)— create a PrimingGroup seeded from an existing trained modellist_priming_groups()— list all priming groups owned by this accountget_priming_group(priming_group_id)— retrieve by IDdelete_priming_group(priming_group_id)— soft-deleteWe also add new pydantic models:
MLPipeline,PrimingGroupingenerated/model.py.Usage
```python
gl = Groundlight()
pipelines = gl.list_detector_pipelines(detector)
active = next(p for p in pipelines if p.is_active_pipeline and p.cached_vizlogic_key)
pg = gl.create_priming_group(
name="door-detector-primer",
source_ml_pipeline_id=active.id,
canonical_query="Is the door open?",
disable_shadow_pipelines=True,
)
new_detector = gl.create_detector(
name="new-door-detector",
query="Is the door open?",
priming_group_id=pg.id,
)
```