Skip to content

Commit 0772a5f

Browse files
Revert to state of branch main
1 parent de9837a commit 0772a5f

File tree

4 files changed

+68
-7
lines changed

4 files changed

+68
-7
lines changed

codeflash/api/aiservice.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,49 @@
3232

3333
class AiServiceClient:
3434
def __init__(self) -> None:
35+
# API key validation is deferred to first use (in the headers property)
3536
self.base_url = self.get_aiservice_base_url()
36-
self.headers = {"Authorization": f"Bearer {get_codeflash_api_key()}", "Connection": "close"}
37-
37+
3838
def get_aiservice_base_url(self) -> str:
3939
if os.environ.get("CODEFLASH_AIS_SERVER", default="prod").lower() == "local":
4040
logger.info("Using local AI Service at http://localhost:8000")
4141
console.rule()
4242
return "http://localhost:8000"
4343
return "https://app.codeflash.ai"
44+
45+
def validate_api_key(self) -> None:
46+
"""Validate API key. Raises OSError if invalid."""
47+
if hasattr(self, '_api_key_validated') and self._api_key_validated:
48+
return
49+
50+
try:
51+
from codeflash.api.cfapi import get_user_id # noqa: PLC0415
52+
53+
# Try to get user ID, if it fails the API key is invalid
54+
user_id = get_user_id()
55+
if user_id is None:
56+
msg = (
57+
"Invalid Codeflash API key. The API key you provided is not valid.\n"
58+
"Please generate a new one at https://app.codeflash.ai/app/apikeys ,\n"
59+
"then set it as a CODEFLASH_API_KEY environment variable.\n"
60+
"For more information, refer to the documentation at \n"
61+
"https://docs.codeflash.ai/optimizing-with-codeflash/codeflash-github-actions#manual-setup\n"
62+
"or\n"
63+
"https://docs.codeflash.ai/optimizing-with-codeflash/codeflash-github-actions#automated-setup-recommended"
64+
)
65+
raise OSError(msg)
66+
except ImportError:
67+
# If cfapi is not available, skip validation
68+
pass
69+
70+
self._api_key_validated = True
71+
72+
@property
73+
def headers(self) -> dict[str, str]:
74+
"""Get headers with API key. Validates API key on first access."""
75+
# Lazily validate API key on first use
76+
self.validate_api_key()
77+
return {"Authorization": f"Bearer {get_codeflash_api_key()}", "Connection": "close"}
4478

4579
def make_ai_service_request(
4680
self,

codeflash/api/cfapi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def make_cfapi_request(
8989
def get_user_id(api_key: Optional[str] = None) -> Optional[str]:
9090
"""Retrieve the user's userid by making a request to the /cfapi/cli-get-user endpoint.
9191
92+
:param api_key: The API key to use. If None, uses get_codeflash_api_key().
9293
:return: The userid or None if the request fails.
9394
"""
9495
if not api_key and not ensure_codeflash_api_key():
@@ -119,7 +120,6 @@ def get_user_id(api_key: Optional[str] = None) -> Optional[str]:
119120
logger.error(f"Failed to look up your userid; is your CF API key valid? ({response.reason})")
120121
return None
121122

122-
123123
def suggest_changes(
124124
owner: str,
125125
repo: str,

codeflash/optimization/function_optimizer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def __init__(
213213
) -> None:
214214
self.project_root = test_cfg.project_root_path
215215
self.test_cfg = test_cfg
216-
self.aiservice_client = aiservice_client if aiservice_client else AiServiceClient()
216+
self._aiservice_client = aiservice_client # Can be None for lazy initialization
217217
self.function_to_optimize = function_to_optimize
218218
self.function_to_optimize_source_code = (
219219
function_to_optimize_source_code
@@ -247,6 +247,13 @@ def __init__(
247247
max_workers=n_tests + 2 if self.experiment_id is None else n_tests + 3
248248
)
249249

250+
@property
251+
def aiservice_client(self) -> AiServiceClient:
252+
"""Lazy initialization of AiServiceClient to delay API key validation."""
253+
if self._aiservice_client is None:
254+
self._aiservice_client = AiServiceClient()
255+
return self._aiservice_client
256+
250257
def can_be_optimized(self) -> Result[tuple[bool, CodeOptimizationContext, dict[Path, str]], str]:
251258
should_run_experiment = self.experiment_id is not None
252259
logger.debug(f"Function Trace ID: {self.function_trace_id}")

codeflash/optimization/optimizer.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import ast
4+
import contextlib
45
import copy
56
import os
67
import tempfile
@@ -49,7 +50,7 @@ def __init__(self, args: Namespace) -> None:
4950
benchmark_tests_root=args.benchmarks_root if "benchmark" in args and "benchmarks_root" in args else None,
5051
)
5152

52-
self.aiservice_client = AiServiceClient()
53+
self._aiservice_client: AiServiceClient | None = None
5354
self.experiment_id = os.getenv("CODEFLASH_EXPERIMENT_ID", None)
5455
self.local_aiservice_client = LocalAiServiceClient() if self.experiment_id else None
5556
self.replay_tests_dir = None
@@ -60,6 +61,13 @@ def __init__(self, args: Namespace) -> None:
6061
self.original_args_and_test_cfg: tuple[Namespace, TestConfig] | None = None
6162
self.patch_files: list[Path] = []
6263

64+
@property
65+
def aiservice_client(self) -> AiServiceClient:
66+
"""Lazy initialization of AiServiceClient to delay API key validation."""
67+
if self._aiservice_client is None:
68+
self._aiservice_client = AiServiceClient()
69+
return self._aiservice_client
70+
6371
def run_benchmarks(
6472
self, file_to_funcs_to_optimize: dict[Path, list[FunctionToOptimize]], num_optimizable_functions: int
6573
) -> tuple[dict[str, dict[BenchmarkKey, float]], dict[BenchmarkKey, float]]:
@@ -260,6 +268,10 @@ def run(self) -> None:
260268
console.rule()
261269
if not env_utils.ensure_codeflash_api_key():
262270
return
271+
272+
# Validate API key before starting optimization
273+
self.aiservice_client.validate_api_key()
274+
263275
if self.args.no_draft and is_pr_draft():
264276
logger.warning("PR is in draft mode, skipping optimization")
265277
return
@@ -499,5 +511,13 @@ def run_with_args(args: Namespace) -> None:
499511
logger.warning("Keyboard interrupt received. Cleaning up and exiting, please wait…")
500512
if optimizer:
501513
optimizer.cleanup_temporary_paths()
502-
503-
raise SystemExit from None
514+
raise SystemExit(0) from None
515+
except (OSError, Exception) as e:
516+
# Log the error if we have an optimizer instance with cleanup
517+
if optimizer:
518+
logger.error(f"Error during optimization: {e}")
519+
with contextlib.suppress(Exception):
520+
optimizer.cleanup_temporary_paths()
521+
else:
522+
logger.error(f"Error initializing optimizer: {e}")
523+
raise

0 commit comments

Comments
 (0)