ctfy.sdk.resources.vendor
client.vendor — read-only vendor tenancy (authenticate with a pv_ token).
1"""``client.vendor`` — read-only vendor tenancy (authenticate with a ``pv_`` token).""" 2 3from __future__ import annotations 4 5import builtins 6 7from ctfy.core.state.models import ( 8 EvalCampaignState, 9 EvalModelState, 10 EvalRunState, 11 VendorKeyState, 12 VendorState, 13) 14from ctfy.sdk._helpers import _raise_for_status 15from ctfy.sdk.base import BaseHttpClient 16from ctfy.server.models import NodeInfo, VendorStandardSet 17 18 19class VendorResource: 20 """The calling vendor's own profile / models / runs. 21 22 Requires a ``pv_`` bearer (minted by an admin); the token resolves to a 23 single vendor and exposes only that vendor's data — never the platform's. 24 """ 25 26 def __init__(self, http: BaseHttpClient) -> None: 27 self._http = http 28 29 def me(self) -> VendorState: 30 resp = self._http.request("GET", "/vendor/me") 31 _raise_for_status(resp) 32 return VendorState.model_validate(resp.json()) 33 34 def models(self) -> builtins.list[EvalModelState]: 35 resp = self._http.request("GET", "/vendor/models") 36 _raise_for_status(resp) 37 return [EvalModelState.model_validate(row) for row in resp.json()] 38 39 def runs(self, *, limit: int = 100) -> builtins.list[EvalRunState]: 40 resp = self._http.request("GET", "/vendor/runs", params={"limit": limit}) 41 _raise_for_status(resp) 42 return [EvalRunState.model_validate(row) for row in resp.json()] 43 44 def standard_sets(self) -> builtins.list[VendorStandardSet]: 45 """The operator eval standard sets a vendor may launch a campaign over 46 (id / title / challenge count / corpus pin — the launch-picker source).""" 47 resp = self._http.request("GET", "/vendor/standard-sets") 48 _raise_for_status(resp) 49 return [VendorStandardSet.model_validate(row) for row in resp.json()] 50 51 def nodes(self) -> builtins.list[NodeInfo]: 52 """The vendor's own worker nodes (bring-your-own-node), with live 53 health — so the vendor can confirm a node is online before launching a 54 BYO-node campaign that would run on it.""" 55 resp = self._http.request("GET", "/vendor/nodes") 56 _raise_for_status(resp) 57 return [NodeInfo.model_validate(row) for row in resp.json()] 58 59 def register_model( 60 self, 61 *, 62 id: str, 63 flavor: str, 64 display_name: str = "", 65 provider_model: str = "", 66 base_url: str = "", 67 notes: str = "", 68 ) -> EvalModelState: 69 """Self-register (or edit) one of the vendor's own models. Requires a 70 ``submit``-scoped ``pv_`` token. The model lands ``pending`` review — 71 an operator must approve it before it can run official campaigns.""" 72 resp = self._http.request( 73 "POST", 74 "/vendor/models", 75 json={ 76 "id": id, 77 "flavor": flavor, 78 "display_name": display_name, 79 "provider_model": provider_model, 80 "base_url": base_url, 81 "notes": notes, 82 }, 83 ) 84 _raise_for_status(resp) 85 return EvalModelState.model_validate(resp.json()) 86 87 def upload_key(self, *, flavor: str, api_key: str, label: str = "") -> VendorKeyState: 88 """Upload (or replace) the vendor's model API key for a flavor. Requires 89 a ``submit``-scoped token. The key is encrypted at rest; the returned 90 row carries only metadata (never the ciphertext).""" 91 resp = self._http.request( 92 "POST", 93 "/vendor/keys", 94 json={"flavor": flavor, "api_key": api_key, "label": label}, 95 ) 96 _raise_for_status(resp) 97 return VendorKeyState.model_validate(resp.json()) 98 99 def keys(self) -> builtins.list[VendorKeyState]: 100 """List the vendor's uploaded key metadata (ciphertext blanked).""" 101 resp = self._http.request("GET", "/vendor/keys") 102 _raise_for_status(resp) 103 return [VendorKeyState.model_validate(row) for row in resp.json()] 104 105 def delete_key(self, vendor_key_id: str) -> bool: 106 resp = self._http.request("DELETE", f"/vendor/keys/{vendor_key_id}") 107 _raise_for_status(resp) 108 return bool(resp.json().get("deleted")) 109 110 def campaigns(self) -> builtins.list[EvalCampaignState]: 111 """The vendor's own campaigns (sandbox + self-reported), newest first.""" 112 resp = self._http.request("GET", "/vendor/campaigns") 113 _raise_for_status(resp) 114 return [EvalCampaignState.model_validate(row) for row in resp.json()] 115 116 def submit_campaign( 117 self, 118 *, 119 model_id: str, 120 standard_set_id: str, 121 name: str = "", 122 num_samples: int = 1, 123 max_cost_usd: float = 0.0, 124 publish: bool = False, 125 ) -> EvalCampaignState: 126 """Self-trigger a batch over an operator standard set on the vendor's 127 own key. ``publish=False`` → a private ``sandbox`` run; ``publish=True`` 128 → a public but unverified ``self_reported`` run (requires an approved 129 model). Requires a ``submit``-scoped token + an uploaded key for the 130 model's flavor. Poll :meth:`campaigns` for progress.""" 131 resp = self._http.request( 132 "POST", 133 "/vendor/campaigns", 134 json={ 135 "model_id": model_id, 136 "standard_set_id": standard_set_id, 137 "name": name, 138 "num_samples": num_samples, 139 "max_cost_usd": max_cost_usd, 140 "publish": publish, 141 }, 142 ) 143 _raise_for_status(resp) 144 return EvalCampaignState.model_validate(resp.json())
20class VendorResource: 21 """The calling vendor's own profile / models / runs. 22 23 Requires a ``pv_`` bearer (minted by an admin); the token resolves to a 24 single vendor and exposes only that vendor's data — never the platform's. 25 """ 26 27 def __init__(self, http: BaseHttpClient) -> None: 28 self._http = http 29 30 def me(self) -> VendorState: 31 resp = self._http.request("GET", "/vendor/me") 32 _raise_for_status(resp) 33 return VendorState.model_validate(resp.json()) 34 35 def models(self) -> builtins.list[EvalModelState]: 36 resp = self._http.request("GET", "/vendor/models") 37 _raise_for_status(resp) 38 return [EvalModelState.model_validate(row) for row in resp.json()] 39 40 def runs(self, *, limit: int = 100) -> builtins.list[EvalRunState]: 41 resp = self._http.request("GET", "/vendor/runs", params={"limit": limit}) 42 _raise_for_status(resp) 43 return [EvalRunState.model_validate(row) for row in resp.json()] 44 45 def standard_sets(self) -> builtins.list[VendorStandardSet]: 46 """The operator eval standard sets a vendor may launch a campaign over 47 (id / title / challenge count / corpus pin — the launch-picker source).""" 48 resp = self._http.request("GET", "/vendor/standard-sets") 49 _raise_for_status(resp) 50 return [VendorStandardSet.model_validate(row) for row in resp.json()] 51 52 def nodes(self) -> builtins.list[NodeInfo]: 53 """The vendor's own worker nodes (bring-your-own-node), with live 54 health — so the vendor can confirm a node is online before launching a 55 BYO-node campaign that would run on it.""" 56 resp = self._http.request("GET", "/vendor/nodes") 57 _raise_for_status(resp) 58 return [NodeInfo.model_validate(row) for row in resp.json()] 59 60 def register_model( 61 self, 62 *, 63 id: str, 64 flavor: str, 65 display_name: str = "", 66 provider_model: str = "", 67 base_url: str = "", 68 notes: str = "", 69 ) -> EvalModelState: 70 """Self-register (or edit) one of the vendor's own models. Requires a 71 ``submit``-scoped ``pv_`` token. The model lands ``pending`` review — 72 an operator must approve it before it can run official campaigns.""" 73 resp = self._http.request( 74 "POST", 75 "/vendor/models", 76 json={ 77 "id": id, 78 "flavor": flavor, 79 "display_name": display_name, 80 "provider_model": provider_model, 81 "base_url": base_url, 82 "notes": notes, 83 }, 84 ) 85 _raise_for_status(resp) 86 return EvalModelState.model_validate(resp.json()) 87 88 def upload_key(self, *, flavor: str, api_key: str, label: str = "") -> VendorKeyState: 89 """Upload (or replace) the vendor's model API key for a flavor. Requires 90 a ``submit``-scoped token. The key is encrypted at rest; the returned 91 row carries only metadata (never the ciphertext).""" 92 resp = self._http.request( 93 "POST", 94 "/vendor/keys", 95 json={"flavor": flavor, "api_key": api_key, "label": label}, 96 ) 97 _raise_for_status(resp) 98 return VendorKeyState.model_validate(resp.json()) 99 100 def keys(self) -> builtins.list[VendorKeyState]: 101 """List the vendor's uploaded key metadata (ciphertext blanked).""" 102 resp = self._http.request("GET", "/vendor/keys") 103 _raise_for_status(resp) 104 return [VendorKeyState.model_validate(row) for row in resp.json()] 105 106 def delete_key(self, vendor_key_id: str) -> bool: 107 resp = self._http.request("DELETE", f"/vendor/keys/{vendor_key_id}") 108 _raise_for_status(resp) 109 return bool(resp.json().get("deleted")) 110 111 def campaigns(self) -> builtins.list[EvalCampaignState]: 112 """The vendor's own campaigns (sandbox + self-reported), newest first.""" 113 resp = self._http.request("GET", "/vendor/campaigns") 114 _raise_for_status(resp) 115 return [EvalCampaignState.model_validate(row) for row in resp.json()] 116 117 def submit_campaign( 118 self, 119 *, 120 model_id: str, 121 standard_set_id: str, 122 name: str = "", 123 num_samples: int = 1, 124 max_cost_usd: float = 0.0, 125 publish: bool = False, 126 ) -> EvalCampaignState: 127 """Self-trigger a batch over an operator standard set on the vendor's 128 own key. ``publish=False`` → a private ``sandbox`` run; ``publish=True`` 129 → a public but unverified ``self_reported`` run (requires an approved 130 model). Requires a ``submit``-scoped token + an uploaded key for the 131 model's flavor. Poll :meth:`campaigns` for progress.""" 132 resp = self._http.request( 133 "POST", 134 "/vendor/campaigns", 135 json={ 136 "model_id": model_id, 137 "standard_set_id": standard_set_id, 138 "name": name, 139 "num_samples": num_samples, 140 "max_cost_usd": max_cost_usd, 141 "publish": publish, 142 }, 143 ) 144 _raise_for_status(resp) 145 return EvalCampaignState.model_validate(resp.json())
The calling vendor's own profile / models / runs.
Requires a pv_ bearer (minted by an admin); the token resolves to a
single vendor and exposes only that vendor's data — never the platform's.
45 def standard_sets(self) -> builtins.list[VendorStandardSet]: 46 """The operator eval standard sets a vendor may launch a campaign over 47 (id / title / challenge count / corpus pin — the launch-picker source).""" 48 resp = self._http.request("GET", "/vendor/standard-sets") 49 _raise_for_status(resp) 50 return [VendorStandardSet.model_validate(row) for row in resp.json()]
The operator eval standard sets a vendor may launch a campaign over (id / title / challenge count / corpus pin — the launch-picker source).
52 def nodes(self) -> builtins.list[NodeInfo]: 53 """The vendor's own worker nodes (bring-your-own-node), with live 54 health — so the vendor can confirm a node is online before launching a 55 BYO-node campaign that would run on it.""" 56 resp = self._http.request("GET", "/vendor/nodes") 57 _raise_for_status(resp) 58 return [NodeInfo.model_validate(row) for row in resp.json()]
The vendor's own worker nodes (bring-your-own-node), with live health — so the vendor can confirm a node is online before launching a BYO-node campaign that would run on it.
60 def register_model( 61 self, 62 *, 63 id: str, 64 flavor: str, 65 display_name: str = "", 66 provider_model: str = "", 67 base_url: str = "", 68 notes: str = "", 69 ) -> EvalModelState: 70 """Self-register (or edit) one of the vendor's own models. Requires a 71 ``submit``-scoped ``pv_`` token. The model lands ``pending`` review — 72 an operator must approve it before it can run official campaigns.""" 73 resp = self._http.request( 74 "POST", 75 "/vendor/models", 76 json={ 77 "id": id, 78 "flavor": flavor, 79 "display_name": display_name, 80 "provider_model": provider_model, 81 "base_url": base_url, 82 "notes": notes, 83 }, 84 ) 85 _raise_for_status(resp) 86 return EvalModelState.model_validate(resp.json())
Self-register (or edit) one of the vendor's own models. Requires a
submit-scoped pv_ token. The model lands pending review —
an operator must approve it before it can run official campaigns.
88 def upload_key(self, *, flavor: str, api_key: str, label: str = "") -> VendorKeyState: 89 """Upload (or replace) the vendor's model API key for a flavor. Requires 90 a ``submit``-scoped token. The key is encrypted at rest; the returned 91 row carries only metadata (never the ciphertext).""" 92 resp = self._http.request( 93 "POST", 94 "/vendor/keys", 95 json={"flavor": flavor, "api_key": api_key, "label": label}, 96 ) 97 _raise_for_status(resp) 98 return VendorKeyState.model_validate(resp.json())
Upload (or replace) the vendor's model API key for a flavor. Requires
a submit-scoped token. The key is encrypted at rest; the returned
row carries only metadata (never the ciphertext).
100 def keys(self) -> builtins.list[VendorKeyState]: 101 """List the vendor's uploaded key metadata (ciphertext blanked).""" 102 resp = self._http.request("GET", "/vendor/keys") 103 _raise_for_status(resp) 104 return [VendorKeyState.model_validate(row) for row in resp.json()]
List the vendor's uploaded key metadata (ciphertext blanked).
111 def campaigns(self) -> builtins.list[EvalCampaignState]: 112 """The vendor's own campaigns (sandbox + self-reported), newest first.""" 113 resp = self._http.request("GET", "/vendor/campaigns") 114 _raise_for_status(resp) 115 return [EvalCampaignState.model_validate(row) for row in resp.json()]
The vendor's own campaigns (sandbox + self-reported), newest first.
117 def submit_campaign( 118 self, 119 *, 120 model_id: str, 121 standard_set_id: str, 122 name: str = "", 123 num_samples: int = 1, 124 max_cost_usd: float = 0.0, 125 publish: bool = False, 126 ) -> EvalCampaignState: 127 """Self-trigger a batch over an operator standard set on the vendor's 128 own key. ``publish=False`` → a private ``sandbox`` run; ``publish=True`` 129 → a public but unverified ``self_reported`` run (requires an approved 130 model). Requires a ``submit``-scoped token + an uploaded key for the 131 model's flavor. Poll :meth:`campaigns` for progress.""" 132 resp = self._http.request( 133 "POST", 134 "/vendor/campaigns", 135 json={ 136 "model_id": model_id, 137 "standard_set_id": standard_set_id, 138 "name": name, 139 "num_samples": num_samples, 140 "max_cost_usd": max_cost_usd, 141 "publish": publish, 142 }, 143 ) 144 _raise_for_status(resp) 145 return EvalCampaignState.model_validate(resp.json())
Self-trigger a batch over an operator standard set on the vendor's
own key. publish=False → a private sandbox run; publish=True
→ a public but unverified self_reported run (requires an approved
model). Requires a submit-scoped token + an uploaded key for the
model's flavor. Poll campaigns() for progress.