Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0a00bcd
Refactor Fireworks client integration
dphuang2 Jan 7, 2026
d465a89
remove launch.json
dphuang2 Jan 7, 2026
348bb58
Add .vscode/launch.json to .gitignore
dphuang2 Jan 7, 2026
acaa901
Enhance environment variable loading in auth module
dphuang2 Jan 7, 2026
4b71ddb
Add evaluator version creation in evaluation module
dphuang2 Jan 7, 2026
3dbcd59
test
dphuang2 Jan 8, 2026
532e071
REVERT this later
dphuang2 Jan 8, 2026
5e7a5fa
Merge branch 'main' into dhuang/dxe-478-implement-evaluator-versions
dphuang2 Jan 8, 2026
060d72c
fix mock tests
dphuang2 Jan 9, 2026
bc31c9f
Add error handling for evaluator creation in evaluation module
dphuang2 Jan 9, 2026
ea08062
Support EP_REMOTE_API_KEY
dphuang2 Jan 9, 2026
f246087
Merge branch 'main' into dhuang/dxe-478-implement-evaluator-versions
dphuang2 Jan 9, 2026
6b53ac1
include launch.json.backup
dphuang2 Jan 12, 2026
ec0c8ca
rename to .example and add docker run extra arg
dphuang2 Jan 12, 2026
fc036f5
use ignore-docker by default
dphuang2 Jan 12, 2026
4566584
delete backup
dphuang2 Jan 13, 2026
f103b69
ignore-docker by default in dev
dphuang2 Jan 13, 2026
9c3e417
Refactor evaluator function calls to use Fireworks directly for metho…
dphuang2 Jan 13, 2026
ea673f4
use in-flight SDK version
dphuang2 Jan 13, 2026
26fbc2d
Enhance evaluator handling by returning version ID on creation and up…
dphuang2 Jan 13, 2026
4702307
update
dphuang2 Jan 13, 2026
9d1bc74
use published a22 of fireworks-ai
dphuang2 Jan 13, 2026
3314bec
uv lock
dphuang2 Jan 13, 2026
66f191a
Refactor dotenv handling in auth module and integrate environment var…
dphuang2 Jan 14, 2026
165afe1
add create rft launch configuration
dphuang2 Jan 14, 2026
838c7a5
Refactor dotenv handling in auth module and integrate environment var…
dphuang2 Jan 14, 2026
71599e6
Merge branch 'pass-dot-env-to-docker-container' into dhuang/dxe-478-i…
dphuang2 Jan 14, 2026
0144c9f
actually not necessary for local test since local-test mounts the wor…
dphuang2 Jan 14, 2026
c8774a6
increase sql retries
dphuang2 Jan 14, 2026
2076f0a
Refactor dotenv loading to use explicit paths in CLI and API modules
dphuang2 Jan 14, 2026
8acdc35
Merge branch 'main' into dhuang/dxe-478-implement-evaluator-versions
dphuang2 Jan 14, 2026
432a649
Refactor dotenv loading to use explicit paths in CLI and API modules
dphuang2 Jan 14, 2026
ab04086
Merge branch 'ensure-explicit-dotenv' into dhuang/dxe-478-implement-e…
dphuang2 Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,5 @@ package.json
tau2-bench
*.err
eval-protocol

.vscode/launch.json
1 change: 1 addition & 0 deletions .vscode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!launch.json.backup
39 changes: 0 additions & 39 deletions .vscode/launch.json

This file was deleted.

60 changes: 60 additions & 0 deletions .vscode/launch.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "EP: Upload",
"type": "python",
"request": "launch",
"module": "eval_protocol.cli",
"args": ["upload"],
"console": "integratedTerminal",
"justMyCode": false,
"cwd": "<REPLACE_WITH_YOUR_EVALUATOR_DIRECTORY>",
"env": {
"PYTHONPATH": "${workspaceFolder}",
"FIREWORKS_API_KEY": "${env:FIREWORKS_API_KEY}",
"FIREWORKS_BASE_URL": "${env:FIREWORKS_BASE_URL}",
"FIREWORKS_EXTRA_HEADERS": "{\"x-api-key\": \"${env:FIREWORKS_API_KEY}\", \"X-Fireworks-Gateway-Secret\": \"${env:FIREWORKS_GATEWAY_SECRET}\"}"
}
},
{
"name": "EP: Local Test",
"type": "python",
"request": "launch",
"module": "eval_protocol.cli",
"args": ["local-test", "--ignore-docker"],
"console": "integratedTerminal",
"justMyCode": false,
"cwd": "<REPLACE_WITH_YOUR_EVALUATOR_DIRECTORY>",
"env": {
"PYTHONPATH": "${workspaceFolder}",
"FIREWORKS_API_KEY": "${env:FIREWORKS_API_KEY}",
"FIREWORKS_BASE_URL": "${env:FIREWORKS_BASE_URL}",
"FIREWORKS_EXTRA_HEADERS": "{\"x-api-key\": \"${env:FIREWORKS_API_KEY}\", \"X-Fireworks-Gateway-Secret\": \"${env:FIREWORKS_GATEWAY_SECRET}\"}"
}
},
{
"name": "EP: Create RFT",
"type": "python",
"request": "launch",
"module": "eval_protocol.cli",
"args": [
"create",
"rft",
"--base-model",
"accounts/fireworks/models/qwen3-0p6b",
"--chunk-size",
"10"
],
"console": "integratedTerminal",
"justMyCode": false,
"cwd": "<REPLACE_WITH_YOUR_EVALUATOR_DIRECTORY>",
"env": {
"PYTHONPATH": "${workspaceFolder}",
"FIREWORKS_API_KEY": "${env:FIREWORKS_API_KEY}",
"FIREWORKS_BASE_URL": "${env:FIREWORKS_BASE_URL}",
"FIREWORKS_EXTRA_HEADERS": "{\"x-api-key\": \"${env:FIREWORKS_API_KEY}\", \"X-Fireworks-Gateway-Secret\": \"${env:FIREWORKS_GATEWAY_SECRET}\"}"
}
}
]
}
74 changes: 72 additions & 2 deletions eval_protocol/auth.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,75 @@
import logging
import os
from typing import Optional
from typing import Dict, Optional

import requests
from dotenv import dotenv_values, find_dotenv, load_dotenv

logger = logging.getLogger(__name__)


def find_dotenv_path(search_path: Optional[str] = None) -> Optional[str]:
"""
Find the .env file path, searching .env.dev first, then .env.

Args:
search_path: Directory to search from. If None, uses current working directory.

Returns:
Path to the .env file if found, otherwise None.
"""
# If a specific search path is provided, look there first
if search_path:
env_dev_path = os.path.join(search_path, ".env.dev")
if os.path.isfile(env_dev_path):
return env_dev_path
env_path = os.path.join(search_path, ".env")
if os.path.isfile(env_path):
return env_path
return None

# Otherwise use find_dotenv to search up the directory tree
env_dev_path = find_dotenv(filename=".env.dev", raise_error_if_not_found=False, usecwd=True)
if env_dev_path:
return env_dev_path
env_path = find_dotenv(filename=".env", raise_error_if_not_found=False, usecwd=True)
if env_path:
return env_path
return None


def get_dotenv_values(search_path: Optional[str] = None) -> Dict[str, Optional[str]]:
"""
Get all key-value pairs from the .env file.

Args:
search_path: Directory to search from. If None, uses current working directory.

Returns:
Dictionary of environment variable names to values.
"""
dotenv_path = find_dotenv_path(search_path)
if dotenv_path:
return dotenv_values(dotenv_path)
return {}


# --- Load .env files ---
# Attempt to load .env.dev first, then .env as a fallback.
# This happens when the module is imported.
# We use override=False (default) so that existing environment variables
# (e.g., set in the shell) are NOT overridden by .env files.
_DOTENV_PATH = find_dotenv_path()
if _DOTENV_PATH:
load_dotenv(dotenv_path=_DOTENV_PATH, override=False)
logger.debug(f"eval_protocol.auth: Loaded environment variables from: {_DOTENV_PATH}")
else:
logger.debug(
"eval_protocol.auth: No .env.dev or .env file found. Relying on shell/existing environment variables."
)
# --- End .env loading ---


def get_fireworks_api_key() -> Optional[str]:
"""
Retrieves the Fireworks API key.
Expand Down Expand Up @@ -73,6 +136,8 @@ def verify_api_key_and_get_account_id(
Args:
api_key: Optional explicit API key. When None, resolves via get_fireworks_api_key().
api_base: Optional explicit API base. When None, resolves via get_fireworks_api_base().
If api_base is api.fireworks.ai, it is used directly. Otherwise, defaults to
dev.api.fireworks.ai for the verification call.

Returns:
The resolved account id if verification succeeds and the header is present; otherwise None.
Expand All @@ -81,7 +146,12 @@ def verify_api_key_and_get_account_id(
resolved_key = api_key or get_fireworks_api_key()
if not resolved_key:
return None
resolved_base = api_base or get_fireworks_api_base()
provided_base = api_base or get_fireworks_api_base()
# Use api.fireworks.ai if explicitly provided, otherwise fall back to dev
if "api.fireworks.ai" in provided_base:
resolved_base = provided_base
else:
resolved_base = "https://dev.api.fireworks.ai"
Copy link

Choose a reason for hiding this comment

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

Verification endpoint defaults to dev for custom API bases

Medium Severity

The verify_api_key_and_get_account_id function now falls back to https://dev.api.fireworks.ai for the verification call when the API base URL doesn't contain "api.fireworks.ai". This causes authentication failures for users with custom endpoints (proxies, internal deployments) who have production API keys, since the verification request goes to dev which rejects production credentials. The fallback should likely use production (https://api.fireworks.ai) instead of dev, or the user's configured base URL should be respected.

Fix in Cursor Fix in Web


from .common_utils import get_user_agent

Expand Down
18 changes: 11 additions & 7 deletions eval_protocol/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,12 @@ def _configure_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParse
"--env-file",
help="Path to .env file containing secrets to upload (default: .env in current directory)",
)
upload_parser.add_argument(
"--force",
action="store_true",
help="Overwrite existing evaluator with the same ID",
)

# Auto-generate flags from SDK Fireworks().evaluators.create() signature
# Note: We use Fireworks() directly here instead of create_fireworks_client()
# because we only need the method signature for introspection, not a fully
# authenticated client. create_fireworks_client() would trigger an HTTP request
# to verify the API key, causing delays even for --help invocations.
create_evaluator_fn = Fireworks().evaluators.create

upload_skip_fields = {
Expand Down Expand Up @@ -137,7 +136,6 @@ def _configure_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParse

rft_parser.add_argument("--yes", "-y", action="store_true", help="Non-interactive mode")
rft_parser.add_argument("--dry-run", action="store_true", help="Print planned SDK call without sending")
rft_parser.add_argument("--force", action="store_true", help="Overwrite existing evaluator with the same ID")
rft_parser.add_argument("--skip-validation", action="store_true", help="Skip local dataset/evaluator validation")
rft_parser.add_argument(
"--ignore-docker",
Expand Down Expand Up @@ -198,6 +196,10 @@ def _configure_parser(parser: argparse.ArgumentParser) -> argparse.ArgumentParse
"loss_config.method": "RL loss method for underlying trainers. One of {grpo,dapo}.",
}

# Note: We use Fireworks() directly here instead of create_fireworks_client()
# because we only need the method signature for introspection, not a fully
# authenticated client. create_fireworks_client() would trigger an HTTP request
# to verify the API key, causing delays even for --help invocations.
create_rft_job_fn = Fireworks().reinforcement_fine_tuning_jobs.create

add_args_from_callable_signature(
Expand Down Expand Up @@ -284,8 +286,10 @@ def main():
from dotenv import load_dotenv

# .env.dev for development-specific overrides, .env for general
# Use explicit paths to avoid find_dotenv() searching up the directory tree
# and potentially finding a different .env file (e.g., in some other repo)
load_dotenv(dotenv_path=Path(".") / ".env.dev", override=True)
load_dotenv(override=True)
load_dotenv(dotenv_path=Path(".") / ".env", override=True)
except ImportError:
pass

Expand Down
Loading
Loading