Skip to content

Commit d54f91c

Browse files
committed
use FireworkS SDK for Secret automation
1 parent 97f056b commit d54f91c

File tree

1 file changed

+62
-121
lines changed

1 file changed

+62
-121
lines changed

eval_protocol/platform_api.py

Lines changed: 62 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
# eval_protocol/platform_api.py
22
import logging
33
import sys
4-
from typing import Any, Dict, Optional
4+
from typing import Optional
55

6-
import requests
76
from dotenv import find_dotenv, load_dotenv
87

98
from eval_protocol.auth import (
109
get_fireworks_account_id,
1110
get_fireworks_api_base,
1211
get_fireworks_api_key,
1312
)
14-
from eval_protocol.common_utils import get_user_agent
13+
from fireworks.types import Secret
14+
from fireworks import Fireworks, FireworksError, NotFoundError, InternalServerError
1515

1616
logger = logging.getLogger(__name__)
1717

@@ -88,47 +88,31 @@ def create_or_update_fireworks_secret(
8888
resolved_api_key = api_key or get_fireworks_api_key()
8989
resolved_api_base = api_base or get_fireworks_api_base()
9090
resolved_account_id = account_id # Must be provided
91+
client = Fireworks(api_key=resolved_api_key, account_id=resolved_account_id, base_url=resolved_api_base)
9192

9293
if not all([resolved_api_key, resolved_api_base, resolved_account_id]):
9394
logger.error("Missing Fireworks API key, base URL, or account ID for creating/updating secret.")
9495
return False
9596

96-
headers = {
97-
"Authorization": f"Bearer {resolved_api_key}",
98-
"Content-Type": "application/json",
99-
"User-Agent": get_user_agent(),
100-
}
101-
102-
# The secret_id for GET/PATCH/DELETE operations is the key_name.
103-
# The 'name' field in the gatewaySecret model for POST/PATCH is a bit ambiguous.
104-
# For POST (create), the body is gatewaySecret, which has 'name', 'keyName', 'value'.
105-
# 'name' in POST body is likely just the 'keyName' or 'secret_id' for creation context,
106-
# as the full resource name 'accounts/.../secrets/...' is server-generated.
107-
# Let's assume for POST, we send 'keyName' and 'value'.
108-
# For PATCH, the path contains {secret_id} which is the key_name. The body is also gatewaySecret.
109-
11097
# Check if secret exists using GET (path uses normalized resource id)
11198
resource_id = _normalize_secret_resource_id(key_name)
11299
secret_exists = False
113100
try:
114-
url = f"{resolved_api_base}/v1/accounts/{resolved_account_id}/secrets/{resource_id}"
115-
response = requests.get(url, headers=headers, timeout=10)
116-
if response.status_code == 200:
101+
secret = client.secrets.get(resource_id)
102+
if secret:
117103
secret_exists = True
118104
logger.info(f"Secret '{key_name}' already exists. Will attempt to update.")
119-
elif response.status_code == 404:
120-
logger.info(f"Secret '{key_name}' does not exist. Will attempt to create.")
121-
secret_exists = False
122-
elif response.status_code == 500: # As per user feedback, 500 on GET might mean not found
123-
logger.warning(
124-
f"Received 500 error when checking for secret '{key_name}'. Assuming it does not exist and will attempt to create. Response: {response.text}"
125-
)
126-
secret_exists = False
127-
else:
128-
logger.error(f"Error checking for secret '{key_name}': {response.status_code} - {response.text}")
129-
return False
130-
except requests.exceptions.RequestException as e:
131-
logger.error(f"Request exception while checking for secret '{key_name}': {e}")
105+
except NotFoundError:
106+
# Secret doesn't exist, proceed with creation
107+
secret_exists = False
108+
except InternalServerError as e:
109+
# As per user feedback, 500 on GET might mean not found, treat as not found
110+
logger.warning(
111+
f"Received 500 error when checking for secret '{key_name}'. Assuming it doesn't exist. Response: {e}"
112+
)
113+
secret_exists = False
114+
except FireworksError as e:
115+
logger.error(f"Error checking for secret '{key_name}': {e}")
132116
return False
133117

134118
if secret_exists:
@@ -144,31 +128,15 @@ def create_or_update_fireworks_secret(
144128
)
145129
payload_key_name = "EP_SECRET" # Fallback, though unlikely needed with .upper()
146130

147-
payload = {"keyName": payload_key_name, "value": secret_value}
148131
try:
149-
logger.debug(f"PATCH payload for '{key_name}': {payload}")
150-
url = f"{resolved_api_base}/v1/accounts/{resolved_account_id}/secrets/{resource_id}"
151-
response = requests.patch(url, json=payload, headers=headers, timeout=30)
152-
response.raise_for_status()
132+
logger.debug(f"PATCH payload for '{key_name}': key_name={payload_key_name}")
133+
client.secrets.update(resource_id, key_name=payload_key_name, value=secret_value)
153134
logger.info(f"Successfully updated secret '{key_name}' on Fireworks platform.")
154135
return True
155-
except requests.exceptions.HTTPError as e:
156-
logger.error(f"HTTP error updating secret '{key_name}': {e.response.status_code} - {e.response.text}")
157-
return False
158-
except requests.exceptions.RequestException as e:
159-
logger.error(f"Request exception updating secret '{key_name}': {e}")
136+
except FireworksError as e:
137+
logger.error(f"Error updating secret '{key_name}': {e}")
160138
return False
161139
else:
162-
# Create new secret (POST)
163-
# Body for POST is gatewaySecret. 'name' field in payload is the resource path.
164-
# Let's assume for POST, the 'name' in payload can be omitted or is the key_name.
165-
# The API should ideally use 'keyName' from URL or a specific 'secretId' in payload for creation if 'name' is server-assigned.
166-
# Given the Swagger, 'name' is required in gatewaySecret.
167-
# Let's try with 'name' being the 'key_name' for the payload, as the full path is not known yet.
168-
# This might need adjustment based on actual API behavior.
169-
# Construct the full 'name' path for the POST payload as per Swagger's title for 'name'
170-
full_resource_name_for_payload = f"accounts/{resolved_account_id}/secrets/{resource_id}"
171-
172140
# Transform key_name for payload "keyName" field: uppercase and underscores
173141
payload_key_name = key_name.upper().replace("-", "_")
174142
if not payload_key_name or not payload_key_name[0].isupper():
@@ -177,26 +145,12 @@ def create_or_update_fireworks_secret(
177145
)
178146
payload_key_name = "EP_SECRET"
179147

180-
payload = {
181-
"name": full_resource_name_for_payload, # This 'name' is the resource path
182-
"keyName": payload_key_name, # This 'keyName' is the specific field with new rules
183-
"value": secret_value,
184-
}
185148
try:
186-
logger.debug(f"POST payload for '{key_name}': {payload}")
187-
url = f"{resolved_api_base}/v1/accounts/{resolved_account_id}/secrets"
188-
response = requests.post(url, json=payload, headers=headers, timeout=30)
189-
response.raise_for_status()
190-
logger.info(
191-
f"Successfully created secret '{key_name}' on Fireworks platform. Full name: {response.json().get('name')}"
192-
)
149+
logger.debug(f"POST payload for '{key_name}': {payload_key_name}")
150+
client.secrets.create(key_name=payload_key_name, value=secret_value, name=resource_id)
193151
return True
194-
except requests.exceptions.HTTPError as e:
195-
logger.error(f"HTTP error creating secret '{key_name}': {e.response.status_code} - {e.response.text}")
196-
# If error is due to 'name' field, this log will show it.
197-
return False
198-
except requests.exceptions.RequestException as e:
199-
logger.error(f"Request exception creating secret '{key_name}': {e}")
152+
except FireworksError as e:
153+
logger.error(f"Error creating secret '{key_name}': {e}")
200154
return False
201155

202156

@@ -205,7 +159,7 @@ def get_fireworks_secret(
205159
key_name: str, # This is the identifier for the secret
206160
api_key: Optional[str] = None,
207161
api_base: Optional[str] = None,
208-
) -> Optional[Dict[str, Any]]:
162+
) -> Optional[Secret]:
209163
"""
210164
Retrieves a secret from the Fireworks AI platform by its keyName.
211165
Note: This typically does not return the secret's actual value for security reasons,
@@ -215,30 +169,25 @@ def get_fireworks_secret(
215169
resolved_api_base = api_base or get_fireworks_api_base()
216170
resolved_account_id = account_id
217171

218-
if not all([resolved_api_key, resolved_api_base, resolved_account_id]):
219-
logger.error("Missing Fireworks API key, base URL, or account ID for getting secret.")
220-
return None
221-
222-
headers = {
223-
"Authorization": f"Bearer {resolved_api_key}",
224-
"User-Agent": get_user_agent(),
225-
}
172+
client = Fireworks(api_key=resolved_api_key, account_id=resolved_account_id, base_url=resolved_api_base)
226173
resource_id = _normalize_secret_resource_id(key_name)
227174

228175
try:
229-
url = f"{resolved_api_base}/v1/accounts/{resolved_account_id}/secrets/{resource_id}"
230-
response = requests.get(url, headers=headers, timeout=10)
231-
if response.status_code == 200:
176+
secret = client.secrets.get(resource_id)
177+
if secret:
232178
logger.info(f"Successfully retrieved secret '{key_name}'.")
233-
return response.json()
234-
elif response.status_code == 404:
235-
logger.info(f"Secret '{key_name}' not found.")
236-
return None
237-
else:
238-
logger.error(f"Error getting secret '{key_name}': {response.status_code} - {response.text}")
239-
return None
240-
except requests.exceptions.RequestException as e:
241-
logger.error(f"Request exception while getting secret '{key_name}': {e}")
179+
return secret
180+
except NotFoundError:
181+
logger.info(f"Secret '{key_name}' not found.")
182+
return None
183+
except InternalServerError as e:
184+
# As per user feedback, 500 on GET might mean not found
185+
logger.warning(
186+
f"Received 500 error when getting secret '{key_name}'. Assuming it doesn't exist. Response: {e}"
187+
)
188+
return None
189+
except FireworksError as e:
190+
logger.error(f"Error getting secret '{key_name}': {e}")
242191
return None
243192

244193

@@ -259,33 +208,24 @@ def delete_fireworks_secret(
259208
logger.error("Missing Fireworks API key, base URL, or account ID for deleting secret.")
260209
return False
261210

262-
headers = {
263-
"Authorization": f"Bearer {resolved_api_key}",
264-
"User-Agent": get_user_agent(),
265-
}
211+
client = Fireworks(api_key=resolved_api_key, account_id=resolved_account_id, base_url=resolved_api_base)
266212
resource_id = _normalize_secret_resource_id(key_name)
267213

268214
try:
269-
url = f"{resolved_api_base}/v1/accounts/{resolved_account_id}/secrets/{resource_id}"
270-
response = requests.delete(url, headers=headers, timeout=30)
271-
if response.status_code == 200 or response.status_code == 204: # 204 No Content is also success for DELETE
272-
logger.info(f"Successfully deleted secret '{key_name}'.")
273-
return True
274-
elif response.status_code == 404:
275-
logger.info(f"Secret '{key_name}' not found, nothing to delete.")
276-
return True
277-
elif (
278-
response.status_code == 500
279-
): # As per user feedback, 500 on GET might mean not found, apply same logic for DELETE
280-
logger.warning(
281-
f"Received 500 error when deleting secret '{key_name}'. Assuming it might not have existed. Response: {response.text}"
282-
)
283-
return True # Consider deletion successful if it results in non-existence
284-
else:
285-
logger.error(f"Error deleting secret '{key_name}': {response.status_code} - {response.text}")
286-
return False
287-
except requests.exceptions.RequestException as e:
288-
logger.error(f"Request exception while deleting secret '{key_name}': {e}")
215+
client.secrets.delete(resource_id, account_id=resolved_account_id)
216+
logger.info(f"Successfully deleted secret '{key_name}'.")
217+
return True
218+
except NotFoundError:
219+
logger.info(f"Secret '{key_name}' not found, nothing to delete.")
220+
return True
221+
except InternalServerError as e:
222+
# As per user feedback, 500 on GET might mean not found, apply same logic for DELETE
223+
logger.warning(
224+
f"Received 500 error when deleting secret '{key_name}'. Assuming it might not have existed. Response: {e}"
225+
)
226+
return True # Consider deletion successful if it results in non-existence
227+
except FireworksError as e:
228+
logger.error(f"Error deleting secret '{key_name}': {e}")
289229
return False
290230

291231

@@ -319,8 +259,6 @@ def delete_fireworks_secret(
319259
logger.error(
320260
"CRITICAL: FIREWORKS_API_KEY and FIREWORKS_API_BASE must be correctly set in environment or .env file to run this test."
321261
)
322-
import sys # Make sure sys is imported if using sys.exit
323-
324262
sys.exit(1)
325263

326264
test_secret_key_name = "rewardkit-test-secret-delete-me" # Changed to be valid
@@ -331,7 +269,7 @@ def delete_fireworks_secret(
331269

332270
# 1. Ensure it doesn't exist initially (or delete if it does from a previous failed run)
333271
logger.info(f"\n[Test Step 0] Attempting to delete '{test_secret_key_name}' if it exists (cleanup)...")
334-
delete_fireworks_secret(test_account_id, test_secret_key_name)
272+
delete_fireworks_secret(account_id=test_account_id, key_name=test_secret_key_name)
335273
retrieved = get_fireworks_secret(test_account_id, test_secret_key_name)
336274
if retrieved is None:
337275
logger.info(f"Confirmed secret '{test_secret_key_name}' does not exist before creation test.")
@@ -351,8 +289,11 @@ def delete_fireworks_secret(
351289
logger.info(f"Retrieved secret metadata: {retrieved_after_create}")
352290
# Assert against the transformed keyName that's expected in the payload/response body
353291
expected_payload_key_name = test_secret_key_name.upper().replace("-", "_")
354-
assert retrieved_after_create.get("keyName") == expected_payload_key_name
355-
assert retrieved_after_create.get("value") == test_secret_value # Also check value if returned
292+
assert retrieved_after_create.key_name == expected_payload_key_name
293+
# Note: value is typically not returned in GET responses for security reasons
294+
# The value field will be None or empty string, so we don't assert on it
295+
if retrieved_after_create.value:
296+
logger.info(f"Note: Secret value was returned (unusual): {retrieved_after_create.value[:10]}...")
356297
else:
357298
logger.error(f"Failed to retrieve secret '{test_secret_key_name}' after creation.")
358299

0 commit comments

Comments
 (0)