Skip to content

Commit 8f506aa

Browse files
committed
first commit
0 parents  commit 8f506aa

243 files changed

Lines changed: 55930 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# LHAW Environment Variables
2+
# Copy to .env, fill in your values, then: source .env
3+
4+
# Required
5+
export LLM_API_KEY=your-api-key
6+
7+
# Optional (e.g. for LiteLLM proxy): LiteLLM proxy URL (omit to call providers directly via API key)
8+
# export LLM_BASE_URL=https://your-litellm-proxy.com
9+
10+
# Optional: model for LLM-as-judge evaluation grading in TAC (default: gpt-4o)
11+
# export LLM_EVAL_MODEL=gpt-4o

.gitignore

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Runtime artifacts
2+
experiments/agentcompany/runs/
3+
experiments/agentcompany/temp_tasks/
4+
experiments/agentcompany/data/
5+
experiments/agentcompany/reports/
6+
experiments/agentcompany/golden_trajectories/
7+
experiments/swebench/golden_trajectories/
8+
experiments/swebench/runs/
9+
*.log
10+
11+
# Synthetic pipeline outputs
12+
synthetic/outputs/
13+
14+
# Docs (local working notes, not tracked)
15+
docs/
16+
17+
# Archived scripts and docs (kept locally, not tracked)
18+
scripts/_archived/
19+
docs/_archived/
20+
21+
# Claude Code instructions (contains env-specific paths)
22+
CLAUDE.md
23+
24+
# Experiment outputs (large, regenerable)
25+
outputs/
26+
27+
# Data files (task lists, variants)
28+
data/
29+
initial-data/
30+
31+
# Generated underspecified variants
32+
task_pairs_agentcompany/underspecified/
33+
34+
# TAC data files (download from S3, see README)
35+
experiments/agentcompany/tac-openhands/
36+
37+
# SWEBench repo + evaluation + user simulator code
38+
swebenchpro/SWE-bench_Pro-os
39+
40+
# MCP Atlas repo (clone separately, see experiments/mcpatlas/README.md)
41+
experiments/mcpatlas/mcp-atlas/
42+
43+
# MCP Atlas large dataset (download from S3)
44+
experiments/mcpatlas/MCP-Atlas.csv
45+
46+
# MCP Atlas generated data
47+
experiments/mcpatlas/trajectories/
48+
experiments/mcpatlas/inference_data/
49+
experiments/mcpatlas/underspec_output/
50+
experiments/mcpatlas/underspec_test/
51+
experiments/mcpatlas/underspec_full/
52+
experiments/mcpatlas/full_inference_data/
53+
experiments/mcpatlas/underspec_datasets/
54+
experiments/mcpatlas/experiments/
55+
experiments/mcpatlas/runs/
56+
experiments/mcpatlas/reports/
57+
# Virtual environment
58+
.venv311/
59+
60+
hf_variants/

README.md

Lines changed: 360 additions & 0 deletions
Large diffs are not rendered by default.

__init__.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""
2+
LHAW: Long-Horizon Autonomous Workflows Benchmark
3+
=================================================
4+
5+
A benchmark for evaluating AI agents on strategic clarification
6+
in underspecified, long-horizon enterprise workflows.
7+
8+
Key Components:
9+
- synthetic: Unified pipeline for generating underspecified task variants
10+
11+
Usage:
12+
from lhaw.synthetic import SyntheticPipeline, Severity
13+
14+
pipeline = SyntheticPipeline()
15+
16+
# TAC tasks
17+
result = pipeline.process_tac_task("finance_check_attendance_payroll")
18+
19+
"""
20+
21+
from .synthetic import (
22+
Dimension,
23+
PipelineResult,
24+
Segment,
25+
Severity,
26+
SyntheticPipeline,
27+
UnderspecVariant,
28+
UnifiedTask,
29+
)
30+
31+
__all__ = [
32+
"Dimension",
33+
"Severity",
34+
"Segment",
35+
"UnderspecVariant",
36+
"UnifiedTask",
37+
"PipelineResult",
38+
"SyntheticPipeline",
39+
]
40+
41+
__version__ = "0.0.0" # Prototyping phase

constants.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
"""
2+
LHAW Constants
3+
==============
4+
5+
Centralized defaults for all scripts. Import these instead of hardcoding values.
6+
7+
Usage:
8+
from constants import DEFAULTS, MODELS
9+
10+
parser.add_argument("--num_trials", type=int, default=DEFAULTS["num_trials"])
11+
"""
12+
13+
import os
14+
15+
# =============================================================================
16+
# DEFAULT CONFIGURATION
17+
# =============================================================================
18+
# Canonical values used across all entrypoints. Change here to affect everything.
19+
20+
DEFAULTS = {
21+
# Trials
22+
"num_trials": 3, # Number of rollouts per task/variant (for pass@k)
23+
# Parallelism
24+
"parallel_variants": 1, # Sequential by default to avoid OOM; override with PARALLEL_VARIANTS=N
25+
# Agent limits
26+
"max_iterations": 100, # Max agent steps per task
27+
# Underspec generation
28+
"max_level": 2, # Segments to remove together (1=single, 2=pairs)
29+
"max_variants": None, # None = no limit (use top_k_per_level in pipeline)
30+
"severity": "delete", # Removal strategy: delete, vaguify, genericize
31+
# Default model for agent evaluation
32+
"model": "opus_4_5",
33+
# Docker cleanup
34+
"cleanup_interval": 10, # Prune containers every N completed variants
35+
# Benchmark filtering
36+
"max_total": 100, # Max total samples in filtered benchmark
37+
"max_per_task_tac": 10, # Max samples per original task (TAC)
38+
"max_per_task_swebench": 8, # Max samples per original instance (SWE-bench)
39+
"seed": 42, # Random seed for reproducibility
40+
# Container timeouts (SWE-bench)
41+
"startup_timeout": 600, # Container startup timeout in seconds
42+
"runtime_timeout": 900, # Container runtime timeout in seconds
43+
# Parallelism
44+
"concurrency": 10, # Parallel completions (MCP-Atlas, SWE-bench)
45+
}
46+
47+
# =============================================================================
48+
# MODEL SHORTCUTS
49+
# =============================================================================
50+
# Short names → full model identifiers for LiteLLM/OpenAI API
51+
52+
MODELS = {
53+
# Standard LiteLLM model identifiers (provider/model-name).
54+
# When LLM_BASE_URL is set (proxy mode), "litellm_proxy/" is auto-prepended
55+
# at runtime so LiteLLM routes through the proxy. For direct provider access,
56+
# leave LLM_BASE_URL unset and set provider-specific API keys instead
57+
# (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY).
58+
#
59+
# Anthropic
60+
"opus_4_5": "anthropic/claude-opus-4-5-20251101",
61+
"sonnet_4_5": "anthropic/claude-sonnet-4-5-20250929",
62+
"opus_4_6": "anthropic/claude-opus-4-6-20260205",
63+
"sonnet_4_6": "anthropic/claude-sonnet-4-6",
64+
"sonnet_4": "anthropic/claude-sonnet-4-20250514",
65+
"haiku_4_5": "anthropic/claude-haiku-4-5-20251001",
66+
# OpenAI
67+
"gpt_5_2": "openai/gpt-5.2-2025-12-11",
68+
"gpt_5_1": "openai/gpt-5.1-2025-11-13",
69+
"gpt_5": "openai/gpt-5-2025-08-07",
70+
"o3_pro": "openai/o3-pro-2025-06-10",
71+
"o3": "openai/o3-2025-04-16",
72+
"gpt_4_1_mini": "openai/gpt-4.1-mini",
73+
# Google
74+
"gemini_3_pro": "gemini/gemini-3-pro-preview",
75+
"gemini_3_flash": "gemini/gemini-3-flash-preview",
76+
"gemini_3_1_pro": "gemini/gemini-3.1-pro-preview",
77+
"gemini_3_1_flash_lite": "gemini/gemini-3.1-flash-lite-preview",
78+
# Other
79+
"kimi_k2": "fireworks_ai/kimi-k2-instruct-0905",
80+
"qwen3_235b": "fireworks_ai/qwen3-235b-a22b",
81+
"llama4_maverick": "fireworks_ai/llama4-maverick-instruct-basic",
82+
"glm_4p5_air": "fireworks_ai/glm-4p5-air",
83+
"nova_2_lite": "bedrock/global.amazon.nova-2-lite-v1:0",
84+
}
85+
86+
# =============================================================================
87+
# PATH CONSTANTS
88+
# =============================================================================
89+
# Relative to REPO_ROOT (computed at import time by each script)
90+
91+
PATHS = {
92+
"synthetic_outputs": "synthetic/outputs",
93+
"tac_tasks": "task_pairs_agentcompany",
94+
"runs_dir": "experiments/agentcompany/runs",
95+
"tac_golden_trajectories": "experiments/agentcompany/golden_trajectories",
96+
}
97+
98+
# =============================================================================
99+
# LHAW BENCHMARK TASKS
100+
# =============================================================================
101+
# 13 TAC tasks selected for LHAW (avg_ckpt_acc >= 0.5 across reference models)
102+
103+
SELECTED_TASKS = [
104+
"ds_answer_numerical_data_question",
105+
"ds_fix_table_values_and_missing_answers",
106+
"ds_format_excel_sheets",
107+
"ds_predictive_modeling",
108+
"ds_visualize_data_in_pie_and_bar_chart",
109+
"finance_budget_variance",
110+
"finance_check_attendance_payroll",
111+
"finance_expense_validation",
112+
"hr_check_attendance_multiple_days",
113+
"hr_check_attendance_one_day",
114+
"hr_create_employee_manual",
115+
"hr_new_grad_job_description",
116+
"sde_create_sqlite_database",
117+
]
118+
119+
120+
VARIANT_DELIMITER = "__V_"
121+
122+
123+
def parse_variant_id(instance_id: str) -> tuple:
124+
"""Parse a SWE-bench variant instance ID into (original_id, variant_suffix).
125+
126+
Returns (original_id, variant_suffix) where variant_suffix is "" for originals.
127+
Example: "inst_foo__V_S1_delete" → ("inst_foo", "S1_delete")
128+
"inst_foo" → ("inst_foo", "")
129+
"""
130+
if VARIANT_DELIMITER in instance_id:
131+
original_id, variant_suffix = instance_id.split(VARIANT_DELIMITER, 1)
132+
return original_id, variant_suffix
133+
return instance_id, ""
134+
135+
136+
def print_bash_models() -> None:
137+
"""Print MODELS as a bash associative array declaration.
138+
139+
Shell scripts source this via:
140+
eval "$(python3 constants.py bash_models)"
141+
"""
142+
pairs = " ".join(f'["{k}"]="{v}"' for k, v in MODELS.items())
143+
print(f"declare -A MODELS=({pairs})")
144+
145+
146+
def get_model(key: str) -> str:
147+
"""Get full model identifier from shortcut, or return as-is if not found."""
148+
return MODELS.get(key, key)
149+
150+
151+
def uses_litellm_proxy() -> bool:
152+
"""Check if LLM_BASE_URL points to a LiteLLM proxy.
153+
154+
Returns True when LLM_BASE_URL contains "litellm" (e.g.
155+
``https://litellm-proxy.{DOMAIN}.com/v1``). The ``litellm_proxy/``
156+
model prefix is only needed when OpenHands routes through such a proxy.
157+
"""
158+
base_url = os.environ.get("LLM_BASE_URL", "").lower()
159+
return "litellm-proxy" in base_url
160+
161+
162+
def resolve_model(key: str) -> str:
163+
"""Get model identifier with litellm_proxy/ prefix when using a LiteLLM proxy.
164+
165+
When LLM_BASE_URL points to a LiteLLM proxy, the model needs the
166+
``litellm_proxy/`` prefix so the LiteLLM SDK routes through the proxy.
167+
Otherwise, returns the standard LiteLLM identifier for direct provider
168+
access (e.g. ``anthropic/claude-sonnet-4-6``).
169+
"""
170+
model = get_model(key)
171+
if uses_litellm_proxy() and not model.startswith("litellm_proxy/"):
172+
return f"litellm_proxy/{model}"
173+
return model
174+
175+
176+
if __name__ == "__main__":
177+
import sys
178+
179+
if len(sys.argv) > 1 and sys.argv[1] == "bash_models":
180+
print_bash_models()
181+
else:
182+
print(f"Usage: {sys.argv[0]} bash_models", file=sys.stderr)
183+
sys.exit(1)

evaluation/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""
2+
LHAW Evaluation Module
3+
======================
4+
5+
Evaluation utilities and scoring system for LHAW benchmark tasks.
6+
7+
Key exports:
8+
- Checkpoint, Result: Core scoring types (from TAC)
9+
- grader: Decorator for checkpoint grading functions
10+
- pass_at_k, pass_hat_k: Pass metrics from τ-bench
11+
"""
12+
13+
from .common import (
14+
compare_images_with_llm,
15+
evaluate_with_llm,
16+
grader,
17+
llm_complete,
18+
verify_image_file,
19+
)
20+
from .pass_k_metrics import (
21+
TaskTrialResults,
22+
TrialResult,
23+
aggregate_pass_metrics,
24+
compute_task_metrics,
25+
pass_at_k,
26+
pass_hat_k,
27+
)
28+
from .scoring import Checkpoint, Result, bonus_for_completing_any, bonus_for_completing_final
29+
30+
__all__ = [
31+
# Scoring
32+
"Checkpoint",
33+
"Result",
34+
"bonus_for_completing_any",
35+
"bonus_for_completing_final",
36+
# Grading
37+
"grader",
38+
"evaluate_with_llm",
39+
"compare_images_with_llm",
40+
"verify_image_file",
41+
"llm_complete",
42+
# Pass metrics
43+
"pass_at_k",
44+
"pass_hat_k",
45+
"TrialResult",
46+
"TaskTrialResults",
47+
"compute_task_metrics",
48+
"aggregate_pass_metrics",
49+
]

0 commit comments

Comments
 (0)