Spaces:
Sleeping
Sleeping
File size: 7,627 Bytes
cd470c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | # Analytical Benchmark Suite for MaterialForge.
# This script runs batch evaluations to provide "Evidence of Improvement" for the hackathon.
import asyncio
import os
import sys
import random
import csv
import json
import statistics
import time
import subprocess
import socket
from datetime import datetime
from typing import List, Dict
# Standardize environment variables to prevent quoting errors from terminal shells.
for key in ["API_BASE_URL", "API_KEY", "MODEL_NAME"]:
val = os.environ.get(key)
if val:
# Strip literal quotes that CMD sometimes includes
os.environ[key] = val.strip('"').strip("'")
# Configuration defaults for local testing with Ollama.
if "API_BASE_URL" not in os.environ:
os.environ["API_BASE_URL"] = "http://127.0.0.1:11434/v1"
if "API_KEY" not in os.environ:
os.environ["API_KEY"] = "ollama"
if "MODEL_NAME" not in os.environ:
os.environ["MODEL_NAME"] = "qwen3.5:latest"
# Ensure the root project directory is in the path for internal imports.
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from inference import (
MaterialForgeEnv,
run_episode,
start_environment_server,
TASKS as ORIGINAL_TASKS,
MODEL_NAME,
SUCCESS_THRESHOLD
)
from openai import OpenAI
# Connectivity utility: checks if a specific local port is available for the server.
def is_port_open(port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(('127.0.0.1', port)) == 0
# Automation: automatically launches the Ollama inference server if not already running.
def start_ollama_server():
if is_port_open(11434):
print("[INFO] Ollama server is already running on port 11434.")
return None
print("[INFO] Ollama server not detected. Starting 'ollama serve'...")
try:
proc = subprocess.Popen(
["ollama", "serve"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS if os.name == 'nt' else 0
)
# Wait up to 30 seconds for Ollama to initialize.
start_time = time.time()
while time.time() - start_time < 30:
if is_port_open(11434):
print("[INFO] Ollama server started successfully.")
time.sleep(2)
return proc
time.sleep(1)
print("[WARNING] Ollama server start timed out. It may still be warming up.")
return proc
except Exception as e:
print(f"[ERROR] Failed to start Ollama server: {e}")
return None
# Main Execution Engine: manages the full benchmarking lifecycle (Startup -> Trials -> Analysis).
async def run_analytical_suite(num_trials: int = 100, mode: str = "llm"):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
report_file = f"tests/benchmarks/benchmark_{mode}_{timestamp}.csv"
summary_file = f"tests/benchmarks/summary_{mode}_{timestamp}.txt"
# Ensure the benchmarks directory exists before saving logs.
os.makedirs("tests/benchmarks", exist_ok=True)
ollama_proc = None
if mode == "llm":
ollama_proc = start_ollama_server()
print(f"π Starting Analytical Benchmark Task ({mode.upper()} mode)")
print(f"π Timestamp: {timestamp}")
print(f"π Logging results to: {report_file}")
api_url = os.environ.get("API_BASE_URL", "http://localhost:11434/v1")
api_key = os.environ.get("API_KEY", "ollama")
# Client initialization: bypasses LLM if mode is set to 'heuristic'.
client = OpenAI(base_url=api_url, api_key=api_key) if mode == "llm" else None
if mode == "heuristic":
print("π Mode: Pure Heuristic (LLM Bypassed)")
else:
print(f"π€ Model: {os.getenv('MODEL_NAME', MODEL_NAME)}")
# Environment Setup: launches the FastAPI simulator on port 7860.
server_proc = start_environment_server(port=7860)
env = MaterialForgeEnv(base_url="http://127.0.0.1:7860")
try:
await env.connect()
except Exception as e:
print(f"β Connection failed: {e}. Is the server running?")
if server_proc: server_proc.terminate()
return
results = []
successes = 0
print("\n" + "="*50)
print(f"{'Trial':<8} | {'Seed':<8} | {'Score':<8} | {'Status':<10}")
print("-" * 50)
# Trial Loop: iterates through random seeds to evaluate agent stability and generalization.
with open(report_file, mode='w', newline='') as csvfile:
fieldnames = ['trial', 'seed', 'score', 'success', 'timestamp']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for i in range(1, num_trials + 1):
seed = random.randint(1000, 9999)
template = random.choice(ORIGINAL_TASKS)
task = {
"name": f"analytical-{i}",
"difficulty": template["difficulty"],
"seed": seed
}
try:
# Executes the episode and captures the normalized performance score.
score = await run_episode(env, client, task)
is_success = score >= SUCCESS_THRESHOLD
results.append(score)
if is_success:
successes += 1
status = "β
PASS" if is_success else "β FAIL"
print(f"{i:<8} | {seed:<8} | {score:<8.3f} | {status:<10}")
writer.writerow({
'trial': i, 'seed': seed, 'score': f"{score:.3f}",
'success': is_success, 'timestamp': datetime.now().isoformat()
})
except Exception as e:
print(f"Trial {i} Error: {e}")
# Aggregated Analysis: computes high-level metrics for the final presentation.
summary_data = [
"="*50,
"π FINAL ANALYTICAL REPORT",
"="*50,
f"Mode: {mode.upper()}",
f"Model: {os.getenv('MODEL_NAME', MODEL_NAME) if mode == 'llm' else 'Heuristic-Only'}",
f"Total Trials: {num_trials}",
f"Success Rate: {(successes/num_trials)*100:.1f}%",
f"Average Score: {statistics.mean(results):.3f}",
f"Max Score: {max(results):.3f}",
f"Min Score: {min(results):.3f}",
"="*50
]
report_text = "\n".join(summary_data)
print("\n" + report_text)
with open(summary_file, 'w') as f:
f.write(report_text)
# Resource Cleanup: shuts down the environment server and local inference engine.
await env.close()
if server_proc:
server_proc.terminate()
server_proc.wait()
if ollama_proc:
print("[INFO] Terminating local Ollama server...")
ollama_proc.terminate()
try:
ollama_proc.wait(timeout=5)
except subprocess.TimeoutExpired:
ollama_proc.kill()
# Entry point for the CLI benchmark tool.
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--trials", type=int, default=100)
parser.add_argument("--mode", type=str, choices=["llm", "heuristic"], default="llm")
args = parser.parse_args()
asyncio.run(run_analytical_suite(args.trials, mode=args.mode))
|