Spaces:
Sleeping
Sleeping
Lucifer-cyber007 commited on
Commit Β·
07d4313
1
Parent(s): aa0c12c
Update API fallback logic for Groq and Gemini
Browse files- baseline.py +64 -22
- free_review.py +76 -42
- inference.py +82 -29
baseline.py
CHANGED
|
@@ -17,6 +17,8 @@ import sys
|
|
| 17 |
import json
|
| 18 |
import argparse
|
| 19 |
from typing import Dict, Any
|
|
|
|
|
|
|
| 20 |
|
| 21 |
from openai import OpenAI
|
| 22 |
from environment import CodeReviewEnv
|
|
@@ -103,28 +105,70 @@ def parse_llm_response(content: str) -> Action:
|
|
| 103 |
)
|
| 104 |
|
| 105 |
|
| 106 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
env = CodeReviewEnv(task_id=task_id)
|
| 108 |
obs = env.reset(task_id=task_id)
|
| 109 |
|
| 110 |
if verbose:
|
| 111 |
print(f"\n{'='*60}\n Task: {task_id.upper()} β {obs.file_name}\n{'='*60}")
|
| 112 |
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
{"role": "system", "content": SYSTEM_PROMPT},
|
| 118 |
-
{"role": "user", "content": build_user_prompt(obs.model_dump())},
|
| 119 |
-
],
|
| 120 |
-
temperature=0.0,
|
| 121 |
-
max_tokens=2000,
|
| 122 |
-
)
|
| 123 |
-
action = parse_llm_response(response.choices[0].message.content)
|
| 124 |
-
except Exception as e:
|
| 125 |
if verbose:
|
| 126 |
-
print(f" [
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
_, reward, _, info = env.step(action)
|
| 130 |
episode_history = [{
|
|
@@ -161,21 +205,19 @@ def main():
|
|
| 161 |
parser.add_argument("--output-json", action="store_true")
|
| 162 |
args = parser.parse_args()
|
| 163 |
|
| 164 |
-
|
| 165 |
-
print("ERROR: No API_KEY or GEMINI_API_KEY env variable found.", file=sys.stderr)
|
| 166 |
-
sys.exit(1)
|
| 167 |
|
| 168 |
-
client = OpenAI(api_key=API_KEY, base_url=API_BASE_URL)
|
| 169 |
task_ids = [args.task] if args.task else ["easy", "medium", "hard"]
|
| 170 |
-
results = [run_task(
|
| 171 |
|
| 172 |
if args.output_json:
|
|
|
|
| 173 |
print(json.dumps({
|
| 174 |
"scores": [{"task_id": r["task_id"], "task_name": r["task_name"],
|
| 175 |
"difficulty": r["difficulty"], "score": r["score"],
|
| 176 |
"feedback": r["feedback"]} for r in results],
|
| 177 |
-
"model_used":
|
| 178 |
-
"note": "Temperature=0. Provider:
|
| 179 |
}))
|
| 180 |
else:
|
| 181 |
print(f"\n{'='*60}\n BASELINE SCORES\n{'='*60}")
|
|
|
|
| 17 |
import json
|
| 18 |
import argparse
|
| 19 |
from typing import Dict, Any
|
| 20 |
+
from dotenv import load_dotenv
|
| 21 |
+
load_dotenv()
|
| 22 |
|
| 23 |
from openai import OpenAI
|
| 24 |
from environment import CodeReviewEnv
|
|
|
|
| 105 |
)
|
| 106 |
|
| 107 |
|
| 108 |
+
def get_providers(model_arg):
|
| 109 |
+
providers = []
|
| 110 |
+
|
| 111 |
+
# 1. Groq
|
| 112 |
+
if os.environ.get("GROQ_API_KEY"):
|
| 113 |
+
providers.append({
|
| 114 |
+
"name": "Groq",
|
| 115 |
+
"api_key": os.environ.get("GROQ_API_KEY"),
|
| 116 |
+
"base_url": "https://api.groq.com/openai/v1",
|
| 117 |
+
"model": "llama-3.3-70b-versatile"
|
| 118 |
+
})
|
| 119 |
+
|
| 120 |
+
# 2. Gemini
|
| 121 |
+
if os.environ.get("GEMINI_API_KEY"):
|
| 122 |
+
providers.append({
|
| 123 |
+
"name": "Gemini",
|
| 124 |
+
"api_key": os.environ.get("GEMINI_API_KEY"),
|
| 125 |
+
"base_url": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
| 126 |
+
"model": "gemini-2.0-flash"
|
| 127 |
+
})
|
| 128 |
+
|
| 129 |
+
# 3. Default
|
| 130 |
+
if not providers:
|
| 131 |
+
providers.append({
|
| 132 |
+
"name": "Default",
|
| 133 |
+
"api_key": API_KEY,
|
| 134 |
+
"base_url": API_BASE_URL,
|
| 135 |
+
"model": model_arg
|
| 136 |
+
})
|
| 137 |
+
|
| 138 |
+
return providers
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def run_task(task_id: str, providers: list, verbose: bool = True) -> Dict[str, Any]:
|
| 142 |
env = CodeReviewEnv(task_id=task_id)
|
| 143 |
obs = env.reset(task_id=task_id)
|
| 144 |
|
| 145 |
if verbose:
|
| 146 |
print(f"\n{'='*60}\n Task: {task_id.upper()} β {obs.file_name}\n{'='*60}")
|
| 147 |
|
| 148 |
+
action = None
|
| 149 |
+
|
| 150 |
+
for provider in providers:
|
| 151 |
+
client = OpenAI(api_key=provider["api_key"], base_url=provider["base_url"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
if verbose:
|
| 153 |
+
print(f" [INFO] Attempting inference with {provider['name']} ({provider['model']})", flush=True)
|
| 154 |
+
|
| 155 |
+
try:
|
| 156 |
+
response = client.chat.completions.create(
|
| 157 |
+
model=provider["model"],
|
| 158 |
+
messages=[
|
| 159 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 160 |
+
{"role": "user", "content": build_user_prompt(obs.model_dump())},
|
| 161 |
+
],
|
| 162 |
+
temperature=0.0,
|
| 163 |
+
max_tokens=2000,
|
| 164 |
+
)
|
| 165 |
+
action = parse_llm_response(response.choices[0].message.content)
|
| 166 |
+
break # Success
|
| 167 |
+
except Exception as e:
|
| 168 |
+
if verbose:
|
| 169 |
+
print(f" [ERROR] {provider['name']} failed: {e}")
|
| 170 |
+
action = Action(comments=[], verdict="comment", summary=f"Error: {e}")
|
| 171 |
+
continue # Try next provider
|
| 172 |
|
| 173 |
_, reward, _, info = env.step(action)
|
| 174 |
episode_history = [{
|
|
|
|
| 205 |
parser.add_argument("--output-json", action="store_true")
|
| 206 |
args = parser.parse_args()
|
| 207 |
|
| 208 |
+
providers = get_providers(args.model)
|
|
|
|
|
|
|
| 209 |
|
|
|
|
| 210 |
task_ids = [args.task] if args.task else ["easy", "medium", "hard"]
|
| 211 |
+
results = [run_task(t, providers, not args.output_json) for t in task_ids]
|
| 212 |
|
| 213 |
if args.output_json:
|
| 214 |
+
used_model = providers[0]['model'] if providers else args.model
|
| 215 |
print(json.dumps({
|
| 216 |
"scores": [{"task_id": r["task_id"], "task_name": r["task_name"],
|
| 217 |
"difficulty": r["difficulty"], "score": r["score"],
|
| 218 |
"feedback": r["feedback"]} for r in results],
|
| 219 |
+
"model_used": used_model,
|
| 220 |
+
"note": "Temperature=0. Provider: Groq -> Gemini fallback.",
|
| 221 |
}))
|
| 222 |
else:
|
| 223 |
print(f"\n{'='*60}\n BASELINE SCORES\n{'='*60}")
|
free_review.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
| 1 |
from openai import OpenAI
|
| 2 |
import os
|
| 3 |
import json
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
import inference
|
| 6 |
|
| 7 |
-
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
|
| 8 |
-
GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
|
| 9 |
-
DEFAULT_MODEL = os.environ.get("FREE_REVIEW_MODEL", "gemini-2.0-flash")
|
| 10 |
-
|
| 11 |
SYSTEM_PROMPT = """You are an expert code reviewer with 15+ years
|
| 12 |
of experience. Review the provided code and identify ALL issues.
|
| 13 |
|
|
@@ -38,23 +37,43 @@ Respond ONLY with valid JSON, no markdown:
|
|
| 38 |
"summary": "<summary>",
|
| 39 |
"positive_aspects": ["<aspect1>", "<aspect2>"]
|
| 40 |
}"""
|
| 41 |
-
|
| 42 |
def review_free_code(code: str, language: str = "python",
|
| 43 |
context: str = "") -> dict:
|
| 44 |
"""
|
| 45 |
-
Review any arbitrary code using Gemini.
|
| 46 |
Returns structured findings without a grader score.
|
| 47 |
"""
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
if not
|
| 52 |
-
return {"error": "
|
| 53 |
-
|
| 54 |
-
client = OpenAI(
|
| 55 |
-
api_key=api_key_to_use,
|
| 56 |
-
base_url=base_url_to_use
|
| 57 |
-
)
|
| 58 |
|
| 59 |
user_prompt = f"""Language: {language}
|
| 60 |
Context: {context if context else "General code review"}
|
|
@@ -66,30 +85,45 @@ Code to review:
|
|
| 66 |
|
| 67 |
Review this code thoroughly and return JSON only."""
|
| 68 |
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
"
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from openai import OpenAI
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
+
import time
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
load_dotenv()
|
| 7 |
|
| 8 |
import inference
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
SYSTEM_PROMPT = """You are an expert code reviewer with 15+ years
|
| 11 |
of experience. Review the provided code and identify ALL issues.
|
| 12 |
|
|
|
|
| 37 |
"summary": "<summary>",
|
| 38 |
"positive_aspects": ["<aspect1>", "<aspect2>"]
|
| 39 |
}"""
|
|
|
|
| 40 |
def review_free_code(code: str, language: str = "python",
|
| 41 |
context: str = "") -> dict:
|
| 42 |
"""
|
| 43 |
+
Review any arbitrary code using Groq or Gemini.
|
| 44 |
Returns structured findings without a grader score.
|
| 45 |
"""
|
| 46 |
+
providers = []
|
| 47 |
+
|
| 48 |
+
# 1. Primary: Groq
|
| 49 |
+
if os.environ.get("GROQ_API_KEY"):
|
| 50 |
+
providers.append({
|
| 51 |
+
"name": "Groq",
|
| 52 |
+
"api_key": os.environ.get("GROQ_API_KEY"),
|
| 53 |
+
"base_url": "https://api.groq.com/openai/v1",
|
| 54 |
+
"model": "llama-3.3-70b-versatile"
|
| 55 |
+
})
|
| 56 |
+
|
| 57 |
+
# 2. Fallback: Gemini
|
| 58 |
+
if os.environ.get("GEMINI_API_KEY"):
|
| 59 |
+
providers.append({
|
| 60 |
+
"name": "Gemini",
|
| 61 |
+
"api_key": os.environ.get("GEMINI_API_KEY"),
|
| 62 |
+
"base_url": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
| 63 |
+
"model": "gemini-2.0-flash"
|
| 64 |
+
})
|
| 65 |
+
|
| 66 |
+
# 3. Last Resort: Inference Proxy / Hackathon Default
|
| 67 |
+
if not providers and hasattr(inference, '_api_key') and inference._api_key:
|
| 68 |
+
providers.append({
|
| 69 |
+
"name": "Fallback Proxy",
|
| 70 |
+
"api_key": inference._api_key,
|
| 71 |
+
"base_url": inference.API_BASE_URL,
|
| 72 |
+
"model": inference.MODEL_NAME
|
| 73 |
+
})
|
| 74 |
|
| 75 |
+
if not providers:
|
| 76 |
+
return {"error": "API keys not set. Please add GROQ_API_KEY or GEMINI_API_KEY to your .env file."}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
user_prompt = f"""Language: {language}
|
| 79 |
Context: {context if context else "General code review"}
|
|
|
|
| 85 |
|
| 86 |
Review this code thoroughly and return JSON only."""
|
| 87 |
|
| 88 |
+
last_error = ""
|
| 89 |
+
|
| 90 |
+
for provider in providers:
|
| 91 |
+
client = OpenAI(api_key=provider["api_key"], base_url=provider["base_url"])
|
| 92 |
+
print(f"Attempting review with {provider['name']} ({provider['model']})...")
|
| 93 |
+
|
| 94 |
+
provider_attempts = 3
|
| 95 |
+
for attempt in range(provider_attempts):
|
| 96 |
+
try:
|
| 97 |
+
response = client.chat.completions.create(
|
| 98 |
+
model=provider["model"],
|
| 99 |
+
messages=[
|
| 100 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 101 |
+
{"role": "user", "content": user_prompt}
|
| 102 |
+
],
|
| 103 |
+
temperature=0.1,
|
| 104 |
+
max_tokens=3000,
|
| 105 |
+
)
|
| 106 |
+
content = response.choices[0].message.content.strip()
|
| 107 |
+
if content.startswith("```"):
|
| 108 |
+
lines = content.split("\n")
|
| 109 |
+
content = "\n".join(lines[1:])
|
| 110 |
+
if content.strip().endswith("```"):
|
| 111 |
+
content = content.strip()[:-3].strip()
|
| 112 |
+
return json.loads(content)
|
| 113 |
+
except json.JSONDecodeError:
|
| 114 |
+
return {"error": f"Failed to parse JSON from {provider['name']} model",
|
| 115 |
+
"raw": content[:500]}
|
| 116 |
+
except Exception as e:
|
| 117 |
+
err_str = str(e)
|
| 118 |
+
last_error = err_str
|
| 119 |
+
# For Groq or Gemini 429 errors
|
| 120 |
+
if "429" in err_str or "quota" in err_str.lower() or "RESOURCE_EXHAUSTED" in err_str:
|
| 121 |
+
if attempt < provider_attempts - 1:
|
| 122 |
+
wait_time = 10 * (attempt + 1)
|
| 123 |
+
print(f"[{provider['name']}] Rate limit hit. Waiting {wait_time}s before retry ({attempt+1}/{provider_attempts})...")
|
| 124 |
+
time.sleep(wait_time)
|
| 125 |
+
continue
|
| 126 |
+
print(f"[{provider['name']}] Request failed. Falling back to next provider if available.")
|
| 127 |
+
break # Exit retry loop and move to next provider
|
| 128 |
+
|
| 129 |
+
return {"error": f"All fallback APIs failed. Last error: {last_error}"}
|
inference.py
CHANGED
|
@@ -15,6 +15,8 @@ import sys
|
|
| 15 |
import json
|
| 16 |
import argparse
|
| 17 |
from typing import Dict, Any
|
|
|
|
|
|
|
| 18 |
|
| 19 |
from openai import OpenAI
|
| 20 |
from environment import CodeReviewEnv
|
|
@@ -23,14 +25,13 @@ from models import Action, CodeComment, GraderInput
|
|
| 23 |
|
| 24 |
|
| 25 |
# ββ Exactly as required by the Pre-Submission Checklist ββββββββββββββββββ
|
| 26 |
-
API_BASE_URL = os.getenv("API_BASE_URL", "https://
|
| 27 |
-
MODEL_NAME = os.getenv("MODEL_NAME", "
|
| 28 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 29 |
LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
|
| 30 |
|
| 31 |
# Use HF_TOKEN if provided by validator, else fall back to GEMINI_API_KEY
|
| 32 |
-
_api_key = HF_TOKEN or os.getenv("GEMINI_API_KEY", "
|
| 33 |
-
|
| 34 |
|
| 35 |
SYSTEM_PROMPT = """You are an expert code reviewer. You will be given a code diff from a pull request.
|
| 36 |
Your job is to identify ALL bugs, security vulnerabilities, performance issues, and logic errors.
|
|
@@ -100,7 +101,49 @@ def parse_llm_response(content: str) -> Action:
|
|
| 100 |
)
|
| 101 |
|
| 102 |
|
| 103 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
env = CodeReviewEnv(task_id=task_id)
|
| 105 |
obs = env.reset(task_id=task_id)
|
| 106 |
|
|
@@ -109,23 +152,35 @@ def run_task(client: OpenAI, task_id: str, model: str, verbose: bool = True) ->
|
|
| 109 |
|
| 110 |
# REQUIRED: [START] block
|
| 111 |
print(f"[START] task={task_id}", flush=True)
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
messages=[
|
| 118 |
-
{"role": "system", "content": SYSTEM_PROMPT},
|
| 119 |
-
{"role": "user", "content": build_user_prompt(obs.model_dump())},
|
| 120 |
-
],
|
| 121 |
-
temperature=0.0,
|
| 122 |
-
max_tokens=2000,
|
| 123 |
-
)
|
| 124 |
-
action = parse_llm_response(response.choices[0].message.content)
|
| 125 |
-
except Exception as e:
|
| 126 |
if verbose:
|
| 127 |
-
print(f" [
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
_, reward, _, info = env.step(action)
|
| 131 |
|
|
@@ -169,22 +224,20 @@ def main():
|
|
| 169 |
parser.add_argument("--output-json", action="store_true")
|
| 170 |
args = parser.parse_args()
|
| 171 |
|
| 172 |
-
|
| 173 |
-
client = OpenAI(
|
| 174 |
-
api_key=_api_key,
|
| 175 |
-
base_url=API_BASE_URL,
|
| 176 |
-
)
|
| 177 |
|
| 178 |
task_ids = [args.task] if args.task else ["easy", "medium", "hard"]
|
| 179 |
-
results = [run_task(
|
| 180 |
|
| 181 |
if args.output_json:
|
|
|
|
|
|
|
| 182 |
print(json.dumps({
|
| 183 |
"scores": [{"task_id": r["task_id"], "task_name": r["task_name"],
|
| 184 |
"difficulty": r["difficulty"], "score": r["score"],
|
| 185 |
"feedback": r["feedback"]} for r in results],
|
| 186 |
-
"model_used":
|
| 187 |
-
"note": "Temperature=0. Uses
|
| 188 |
}), flush=True)
|
| 189 |
else:
|
| 190 |
print(f"\n{'='*60}\n BASELINE SCORES\n{'='*60}", flush=True)
|
|
|
|
| 15 |
import json
|
| 16 |
import argparse
|
| 17 |
from typing import Dict, Any
|
| 18 |
+
from dotenv import load_dotenv
|
| 19 |
+
load_dotenv()
|
| 20 |
|
| 21 |
from openai import OpenAI
|
| 22 |
from environment import CodeReviewEnv
|
|
|
|
| 25 |
|
| 26 |
|
| 27 |
# ββ Exactly as required by the Pre-Submission Checklist ββββββββββββββββββ
|
| 28 |
+
API_BASE_URL = os.getenv("API_BASE_URL", "https://api.groq.com/openai/v1")
|
| 29 |
+
MODEL_NAME = os.getenv("MODEL_NAME", "llama-3.3-70b-versatile")
|
| 30 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 31 |
LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
|
| 32 |
|
| 33 |
# Use HF_TOKEN if provided by validator, else fall back to GEMINI_API_KEY
|
| 34 |
+
_api_key = HF_TOKEN or os.getenv("GROQ_API_KEY") or os.getenv("GEMINI_API_KEY", "")
|
|
|
|
| 35 |
|
| 36 |
SYSTEM_PROMPT = """You are an expert code reviewer. You will be given a code diff from a pull request.
|
| 37 |
Your job is to identify ALL bugs, security vulnerabilities, performance issues, and logic errors.
|
|
|
|
| 101 |
)
|
| 102 |
|
| 103 |
|
| 104 |
+
def get_providers(model_arg):
|
| 105 |
+
providers = []
|
| 106 |
+
|
| 107 |
+
# 1. Hackathon Proxy environment (if injected by validator)
|
| 108 |
+
if os.getenv("HF_TOKEN") and os.getenv("API_BASE_URL") and "generative" not in os.getenv("API_BASE_URL", "") and "groq" not in os.getenv("API_BASE_URL", ""):
|
| 109 |
+
providers.append({
|
| 110 |
+
"name": "Hackathon Proxy",
|
| 111 |
+
"api_key": os.getenv("HF_TOKEN"),
|
| 112 |
+
"base_url": os.getenv("API_BASE_URL"),
|
| 113 |
+
"model": os.getenv("MODEL_NAME", model_arg)
|
| 114 |
+
})
|
| 115 |
+
|
| 116 |
+
# 2. Main Provider: Groq
|
| 117 |
+
if os.environ.get("GROQ_API_KEY"):
|
| 118 |
+
providers.append({
|
| 119 |
+
"name": "Groq",
|
| 120 |
+
"api_key": os.environ.get("GROQ_API_KEY"),
|
| 121 |
+
"base_url": "https://api.groq.com/openai/v1",
|
| 122 |
+
"model": "llama-3.3-70b-versatile"
|
| 123 |
+
})
|
| 124 |
+
|
| 125 |
+
# 3. Fallback: Gemini
|
| 126 |
+
if os.environ.get("GEMINI_API_KEY"):
|
| 127 |
+
providers.append({
|
| 128 |
+
"name": "Gemini",
|
| 129 |
+
"api_key": os.environ.get("GEMINI_API_KEY"),
|
| 130 |
+
"base_url": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
| 131 |
+
"model": "gemini-2.0-flash"
|
| 132 |
+
})
|
| 133 |
+
|
| 134 |
+
# Fallback to defaults
|
| 135 |
+
if not providers:
|
| 136 |
+
providers.append({
|
| 137 |
+
"name": "Default",
|
| 138 |
+
"api_key": _api_key,
|
| 139 |
+
"base_url": API_BASE_URL,
|
| 140 |
+
"model": model_arg
|
| 141 |
+
})
|
| 142 |
+
|
| 143 |
+
return providers
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def run_task(task_id: str, providers: list, verbose: bool = True) -> Dict[str, Any]:
|
| 147 |
env = CodeReviewEnv(task_id=task_id)
|
| 148 |
obs = env.reset(task_id=task_id)
|
| 149 |
|
|
|
|
| 152 |
|
| 153 |
# REQUIRED: [START] block
|
| 154 |
print(f"[START] task={task_id}", flush=True)
|
| 155 |
+
|
| 156 |
+
action = None
|
| 157 |
+
|
| 158 |
+
for provider in providers:
|
| 159 |
+
client = OpenAI(api_key=provider["api_key"], base_url=provider["base_url"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
if verbose:
|
| 161 |
+
print(f" [INFO] Attempting inference with {provider['name']} ({provider['model']})", flush=True)
|
| 162 |
+
|
| 163 |
+
try:
|
| 164 |
+
response = client.chat.completions.create(
|
| 165 |
+
model=provider["model"],
|
| 166 |
+
messages=[
|
| 167 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 168 |
+
{"role": "user", "content": build_user_prompt(obs.model_dump())},
|
| 169 |
+
],
|
| 170 |
+
temperature=0.0,
|
| 171 |
+
max_tokens=2000,
|
| 172 |
+
)
|
| 173 |
+
action = parse_llm_response(response.choices[0].message.content)
|
| 174 |
+
break # Success, exit provider loop
|
| 175 |
+
except Exception as e:
|
| 176 |
+
err_str = str(e)
|
| 177 |
+
if verbose:
|
| 178 |
+
print(f" [ERROR] {provider['name']} failed: {err_str}", flush=True)
|
| 179 |
+
if "429" in err_str or "quota" in err_str.lower() or "RESOURCE_EXHAUSTED" in err_str:
|
| 180 |
+
if verbose:
|
| 181 |
+
print(f" [INFO] Rate limit reached on {provider['name']}, switching to fallback...", flush=True)
|
| 182 |
+
action = Action(comments=[], verdict="comment", summary=f"Error: {e}")
|
| 183 |
+
continue # Try next provider
|
| 184 |
|
| 185 |
_, reward, _, info = env.step(action)
|
| 186 |
|
|
|
|
| 224 |
parser.add_argument("--output-json", action="store_true")
|
| 225 |
args = parser.parse_args()
|
| 226 |
|
| 227 |
+
providers = get_providers(args.model)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
|
| 229 |
task_ids = [args.task] if args.task else ["easy", "medium", "hard"]
|
| 230 |
+
results = [run_task(t, providers, not args.output_json) for t in task_ids]
|
| 231 |
|
| 232 |
if args.output_json:
|
| 233 |
+
# Just grab the first provider's model as a proxy for what was used across tasks
|
| 234 |
+
used_model = providers[0]['model'] if providers else args.model
|
| 235 |
print(json.dumps({
|
| 236 |
"scores": [{"task_id": r["task_id"], "task_name": r["task_name"],
|
| 237 |
"difficulty": r["difficulty"], "score": r["score"],
|
| 238 |
"feedback": r["feedback"]} for r in results],
|
| 239 |
+
"model_used": used_model,
|
| 240 |
+
"note": "Temperature=0. Uses environment variables with Groq->Gemini fallback.",
|
| 241 |
}), flush=True)
|
| 242 |
else:
|
| 243 |
print(f"\n{'='*60}\n BASELINE SCORES\n{'='*60}", flush=True)
|