dslighting 1.1.8__tar.gz → 1.3.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dslighting-1.1.8 → dslighting-1.3.6}/PKG-INFO +1 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/llm.py +2 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/llm_single.py +2 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/sandbox.py +34 -5
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/__init__.py +29 -8
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/core/agent.py +122 -23
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/core/config_builder.py +30 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/core/data_loader.py +46 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/utils/defaults.py +1 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting.egg-info/PKG-INFO +1 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/pyproject.toml +1 -1
- {dslighting-1.1.8 → dslighting-1.3.6}/README.md +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/benchmark/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/benchmark/benchmark.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/benchmark/datasci.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/benchmark/mle.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/benchmark/sciencebench.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/common/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/common/constants.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/common/exceptions.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/common/typing.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/config.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/models/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/models/candidates.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/models/formats.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/models/task.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/aflow_ops.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/autokaggle_ops.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/automind_ops.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/base.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/code.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/dsagent_ops.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/operators/llm_basic.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/aflow_prompt.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/aide_prompt.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/autokaggle_prompt.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/automind_prompt.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/common.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/data_interpreter_prompt.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/prompts/dsagent_prompt.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/runner.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/data_analyzer.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/autokaggle_state.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/base.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/dsa_log.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/experience.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/journal.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/states/operator_library.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/vdb.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/services/workspace.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/tasks/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/tasks/handlers.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/templates/open_ended/grade_template.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/tools/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/utils/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/utils/context.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/utils/dynamic_import.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/utils/parsing.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/base.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/factory.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/manual/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/manual/autokaggle_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/manual/data_interpreter_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/manual/deepanalyze_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/manual/dsagent_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/search/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/search/aflow_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/search/aide_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/search/automind_workflow.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/templates/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dsat/workflows/templates/basic_kaggle_loop.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/core/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/core/task_detector.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting/utils/__init__.py +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting.egg-info/SOURCES.txt +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting.egg-info/dependency_links.txt +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting.egg-info/requires.txt +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/dslighting.egg-info/top_level.txt +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/setup.cfg +0 -0
- {dslighting-1.1.8 → dslighting-1.3.6}/tests/test_dslighting_api.py +0 -0
|
@@ -43,7 +43,8 @@ def _load_custom_model_pricing():
|
|
|
43
43
|
config = yaml.safe_load(f)
|
|
44
44
|
return config.get('custom_model_pricing', {})
|
|
45
45
|
else:
|
|
46
|
-
|
|
46
|
+
# Changed to debug to avoid confusing warnings for pip-installed packages
|
|
47
|
+
logger.debug(f"Config file not found at {config_yaml_path} (this is expected for pip-installed packages)")
|
|
47
48
|
return {}
|
|
48
49
|
except Exception as e:
|
|
49
50
|
logger.error(f"Failed to load cost configuration: {e}")
|
|
@@ -43,7 +43,8 @@ def _load_custom_model_pricing():
|
|
|
43
43
|
config = yaml.safe_load(f)
|
|
44
44
|
return config.get('custom_model_pricing', {})
|
|
45
45
|
else:
|
|
46
|
-
|
|
46
|
+
# Changed to debug to avoid confusing warnings for pip-installed packages
|
|
47
|
+
logger.debug(f"Config file not found at {config_yaml_path} (this is expected for pip-installed packages)")
|
|
47
48
|
return {}
|
|
48
49
|
except Exception as e:
|
|
49
50
|
logger.error(f"Failed to load cost configuration: {e}")
|
|
@@ -245,17 +245,46 @@ class ProcessIsolatedNotebookExecutor:
|
|
|
245
245
|
class SandboxService:
|
|
246
246
|
"""
|
|
247
247
|
Provides unified access to isolated script and notebook code execution environments.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
workspace: Workspace service for managing files
|
|
251
|
+
timeout: Default timeout for script execution (seconds)
|
|
252
|
+
auto_matplotlib: Automatically inject matplotlib backend (default: False).
|
|
253
|
+
Set to True for Web UI environments that need visualization.
|
|
254
|
+
Set to False for standalone package usage.
|
|
248
255
|
"""
|
|
249
|
-
def __init__(
|
|
256
|
+
def __init__(
|
|
257
|
+
self,
|
|
258
|
+
workspace: WorkspaceService,
|
|
259
|
+
timeout: int = 600,
|
|
260
|
+
auto_matplotlib: bool = False
|
|
261
|
+
):
|
|
250
262
|
self.workspace = workspace
|
|
251
263
|
self.timeout = timeout
|
|
264
|
+
self.auto_matplotlib = auto_matplotlib # Store matplotlib injection preference
|
|
252
265
|
self.execution_history: List[Dict[str, Any]] = []
|
|
253
266
|
|
|
254
267
|
def run_script(self, script_code: str, timeout: Optional[int] = None) -> ExecutionResult:
|
|
255
|
-
"""
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
268
|
+
"""
|
|
269
|
+
Runs a Python script within the sandbox workspace.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
script_code: Python code to execute
|
|
273
|
+
timeout: Optional timeout override (uses self.timeout if None)
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
ExecutionResult with stdout, stderr, success status, etc.
|
|
277
|
+
"""
|
|
278
|
+
# Optionally inject matplotlib non-interactive backend
|
|
279
|
+
# This is only done if auto_matplotlib=True (used by Web UI for visualization)
|
|
280
|
+
if self.auto_matplotlib:
|
|
281
|
+
# Force non-interactive backend for matplotlib to prevent blocking plt.show()
|
|
282
|
+
fixed_code = "import matplotlib\nmatplotlib.use('Agg')\n" + script_code
|
|
283
|
+
logger.debug("Auto-injected matplotlib non-interactive backend")
|
|
284
|
+
else:
|
|
285
|
+
# Use code as-is without modification (default for DSLighting package)
|
|
286
|
+
fixed_code = script_code
|
|
287
|
+
|
|
259
288
|
script_name = f"_sandbox_script_{uuid.uuid4().hex}.py"
|
|
260
289
|
script_path = self.workspace.run_dir / script_name
|
|
261
290
|
execution_id = uuid.uuid4().hex
|
|
@@ -26,7 +26,7 @@ Advanced Usage:
|
|
|
26
26
|
For more information, see: https://github.com/usail-hkust/dslighting
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
__version__ = "1.
|
|
29
|
+
__version__ = "1.3.6"
|
|
30
30
|
__author__ = "DSLighting Team"
|
|
31
31
|
|
|
32
32
|
# Core API classes
|
|
@@ -60,29 +60,40 @@ def load_data(source, **kwargs):
|
|
|
60
60
|
return loader.load(source, **kwargs)
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def run_agent(data, **kwargs):
|
|
63
|
+
def run_agent(data=None, task_id=None, data_dir=None, keep_workspace=False, keep_workspace_on_failure=True, **kwargs):
|
|
64
64
|
"""
|
|
65
65
|
Quick one-liner: load data and run with defaults.
|
|
66
66
|
|
|
67
67
|
This function creates an Agent with the specified parameters and runs it on the data.
|
|
68
68
|
|
|
69
69
|
Args:
|
|
70
|
-
data:
|
|
70
|
+
data: Optional data source (path, DataFrame, dict, etc.)
|
|
71
|
+
task_id: Task/Competition identifier (e.g., "bike-sharing-demand")
|
|
72
|
+
data_dir: Base data directory (default: "data/competitions")
|
|
73
|
+
keep_workspace: Keep workspace after completion (default: False)
|
|
74
|
+
keep_workspace_on_failure: Keep workspace on failure (default: True)
|
|
71
75
|
**kwargs: Parameters passed to Agent.__init__ and Agent.run
|
|
72
76
|
|
|
73
77
|
Returns:
|
|
74
78
|
AgentResult with output, metrics, and metadata
|
|
75
79
|
|
|
76
80
|
Examples:
|
|
77
|
-
>>> #
|
|
78
|
-
>>> result = dslighting.run_agent(
|
|
81
|
+
>>> # Recommended: using task_id
|
|
82
|
+
>>> result = dslighting.run_agent(
|
|
83
|
+
... task_id="bike-sharing-demand",
|
|
84
|
+
... data_dir="data/competitions"
|
|
85
|
+
... )
|
|
79
86
|
>>> print(f"Score: {result.score}, Cost: ${result.cost}")
|
|
80
87
|
|
|
88
|
+
>>> # Legacy: using data path
|
|
89
|
+
>>> result = dslighting.run_agent("data/titanic")
|
|
90
|
+
|
|
81
91
|
>>> # With custom parameters
|
|
82
92
|
>>> result = dslighting.run_agent(
|
|
83
|
-
... "
|
|
93
|
+
... task_id="bike-sharing-demand",
|
|
84
94
|
... workflow="autokaggle",
|
|
85
|
-
... model="gpt-4o"
|
|
95
|
+
... model="gpt-4o",
|
|
96
|
+
... keep_workspace=True # Keep workspace for debugging
|
|
86
97
|
... )
|
|
87
98
|
"""
|
|
88
99
|
# Extract run-specific parameters if present
|
|
@@ -90,7 +101,7 @@ def run_agent(data, **kwargs):
|
|
|
90
101
|
agent_params = {}
|
|
91
102
|
|
|
92
103
|
# Parameters that should go to run(), not __init__
|
|
93
|
-
run_only_params = {'task_id', 'output_path', 'description'}
|
|
104
|
+
run_only_params = {'task_id', 'data_dir', 'output_path', 'description'}
|
|
94
105
|
|
|
95
106
|
for key, value in kwargs.items():
|
|
96
107
|
if key in run_only_params:
|
|
@@ -98,6 +109,16 @@ def run_agent(data, **kwargs):
|
|
|
98
109
|
else:
|
|
99
110
|
agent_params[key] = value
|
|
100
111
|
|
|
112
|
+
# Add explicit parameters to run_kwargs
|
|
113
|
+
if task_id is not None:
|
|
114
|
+
run_kwargs['task_id'] = task_id
|
|
115
|
+
if data_dir is not None:
|
|
116
|
+
run_kwargs['data_dir'] = data_dir
|
|
117
|
+
|
|
118
|
+
# Add workspace preservation parameters to agent
|
|
119
|
+
agent_params['keep_workspace'] = keep_workspace
|
|
120
|
+
agent_params['keep_workspace_on_failure'] = keep_workspace_on_failure
|
|
121
|
+
|
|
101
122
|
# Create agent and run
|
|
102
123
|
agent = Agent(**agent_params)
|
|
103
124
|
return agent.run(data, **run_kwargs)
|
|
@@ -108,6 +108,8 @@ class Agent:
|
|
|
108
108
|
num_drafts: int = None,
|
|
109
109
|
workspace_dir: str = None,
|
|
110
110
|
run_name: str = None,
|
|
111
|
+
keep_workspace: bool = False,
|
|
112
|
+
keep_workspace_on_failure: bool = True,
|
|
111
113
|
verbose: bool = True,
|
|
112
114
|
**kwargs
|
|
113
115
|
):
|
|
@@ -129,6 +131,8 @@ class Agent:
|
|
|
129
131
|
num_drafts: Number of drafts to generate
|
|
130
132
|
workspace_dir: Custom workspace directory
|
|
131
133
|
run_name: Name for this run
|
|
134
|
+
keep_workspace: Keep workspace after completion (default: False)
|
|
135
|
+
keep_workspace_on_failure: Keep workspace on failure (default: True)
|
|
132
136
|
verbose: Enable verbose logging
|
|
133
137
|
**kwargs: Additional parameters passed to DSATConfig
|
|
134
138
|
"""
|
|
@@ -148,6 +152,8 @@ class Agent:
|
|
|
148
152
|
num_drafts=num_drafts,
|
|
149
153
|
workspace_dir=workspace_dir,
|
|
150
154
|
run_name=run_name,
|
|
155
|
+
keep_workspace=keep_workspace,
|
|
156
|
+
keep_workspace_on_failure=keep_workspace_on_failure,
|
|
151
157
|
**kwargs
|
|
152
158
|
)
|
|
153
159
|
|
|
@@ -161,8 +167,9 @@ class Agent:
|
|
|
161
167
|
|
|
162
168
|
def run(
|
|
163
169
|
self,
|
|
164
|
-
data: Union[str, Path, dict, pd.DataFrame, LoadedData],
|
|
170
|
+
data: Union[str, Path, dict, pd.DataFrame, LoadedData] = None,
|
|
165
171
|
task_id: str = None,
|
|
172
|
+
data_dir: str = None,
|
|
166
173
|
output_path: str = None,
|
|
167
174
|
description: str = None,
|
|
168
175
|
**kwargs
|
|
@@ -175,8 +182,12 @@ class Agent:
|
|
|
175
182
|
result collection.
|
|
176
183
|
|
|
177
184
|
Args:
|
|
178
|
-
data:
|
|
179
|
-
|
|
185
|
+
data: Optional data source (path, DataFrame, dict, or LoadedData).
|
|
186
|
+
If not provided, use task_id + data_dir pattern.
|
|
187
|
+
task_id: Task/Competition identifier (e.g., "bike-sharing-demand").
|
|
188
|
+
Required when using MLE benchmark format.
|
|
189
|
+
data_dir: Base data directory containing competition data.
|
|
190
|
+
Default: "data/competitions"
|
|
180
191
|
output_path: Custom output path for results
|
|
181
192
|
description: Optional task description (overrides detected)
|
|
182
193
|
**kwargs: Additional task parameters
|
|
@@ -185,22 +196,77 @@ class Agent:
|
|
|
185
196
|
AgentResult with output, metrics, and metadata
|
|
186
197
|
|
|
187
198
|
Examples:
|
|
188
|
-
>>>
|
|
189
|
-
>>>
|
|
199
|
+
>>> # Method 1: Recommended - using task_id + data_dir
|
|
200
|
+
>>> result = agent.run(
|
|
201
|
+
... task_id="bike-sharing-demand",
|
|
202
|
+
... data_dir="data/competitions"
|
|
203
|
+
... )
|
|
204
|
+
|
|
205
|
+
>>> # Method 2: Using data path directly
|
|
206
|
+
>>> result = agent.run("path/to/competition")
|
|
190
207
|
|
|
208
|
+
>>> # Method 3: Using DataFrame
|
|
191
209
|
>>> result = agent.run(df, description="Predict price")
|
|
192
|
-
>>> predictions = result.output
|
|
193
210
|
"""
|
|
194
211
|
# Start timing
|
|
195
212
|
start_time = time.time()
|
|
196
213
|
|
|
197
214
|
try:
|
|
198
|
-
#
|
|
199
|
-
if
|
|
215
|
+
# ========== New simplified API: task_id + data_dir ==========
|
|
216
|
+
if task_id:
|
|
217
|
+
# Set default data_dir if not provided
|
|
218
|
+
if data_dir is None:
|
|
219
|
+
data_dir = "data/competitions"
|
|
220
|
+
|
|
221
|
+
self.logger.info(f"Using MLE benchmark format")
|
|
222
|
+
self.logger.info(f" task_id: {task_id}")
|
|
223
|
+
self.logger.info(f" data_dir: {data_dir}")
|
|
224
|
+
|
|
225
|
+
# Resolve paths
|
|
226
|
+
data_dir_path = Path(data_dir).resolve()
|
|
227
|
+
competition_dir = data_dir_path / task_id
|
|
228
|
+
|
|
229
|
+
# Check if task exists in benchmarks registry
|
|
230
|
+
benchmark_dir = self._get_default_benchmark_dir()
|
|
231
|
+
task_registry = benchmark_dir / task_id
|
|
232
|
+
|
|
233
|
+
if not task_registry.exists():
|
|
234
|
+
self.logger.warning(
|
|
235
|
+
f"Task '{task_id}' not found in benchmark registry: {benchmark_dir}"
|
|
236
|
+
)
|
|
237
|
+
self.logger.warning(
|
|
238
|
+
f"This means the task cannot be auto-graded. "
|
|
239
|
+
f"To enable grading, register the task at: {task_registry}"
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
self.logger.info(f" ✓ Task registered: {task_registry}")
|
|
243
|
+
|
|
244
|
+
# Check if data exists
|
|
245
|
+
if not competition_dir.exists():
|
|
246
|
+
raise FileNotFoundError(
|
|
247
|
+
f"Data directory not found: {competition_dir}\n"
|
|
248
|
+
f"Please ensure data is prepared at: {competition_dir}/prepared/"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
self.logger.info(f" Data directory: {competition_dir}")
|
|
252
|
+
|
|
253
|
+
# Load data
|
|
200
254
|
loader = DataLoader()
|
|
201
|
-
loaded_data = loader.load(
|
|
255
|
+
loaded_data = loader.load(competition_dir)
|
|
256
|
+
|
|
257
|
+
# ========== Legacy API: direct data path ==========
|
|
258
|
+
elif data is not None:
|
|
259
|
+
# Load data if not already loaded
|
|
260
|
+
if not isinstance(data, LoadedData):
|
|
261
|
+
loader = DataLoader()
|
|
262
|
+
loaded_data = loader.load(data)
|
|
263
|
+
else:
|
|
264
|
+
loaded_data = data
|
|
202
265
|
else:
|
|
203
|
-
|
|
266
|
+
raise ValueError(
|
|
267
|
+
"Either 'task_id' or 'data' must be provided. "
|
|
268
|
+
"Example: agent.run(task_id='bike-sharing-demand', data_dir='data/competitions')"
|
|
269
|
+
)
|
|
204
270
|
|
|
205
271
|
# Get task information
|
|
206
272
|
task_detection = loaded_data.task_detection
|
|
@@ -365,30 +431,33 @@ class Agent:
|
|
|
365
431
|
|
|
366
432
|
if task_type == "kaggle":
|
|
367
433
|
# MLE/Kaggle format: needs public_data_dir and output_submission_path
|
|
434
|
+
# Follow MLEBenchmark pattern: {data_dir}/prepared/public
|
|
368
435
|
prepared_dir = data_dir / "prepared"
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
payload["public_data_dir"] = str(data_dir)
|
|
436
|
+
public_dir = prepared_dir / "public"
|
|
437
|
+
|
|
438
|
+
# Check if prepared/public exists (MLE format)
|
|
439
|
+
if public_dir.exists():
|
|
440
|
+
payload["public_data_dir"] = str(public_dir.resolve())
|
|
441
|
+
self.logger.info(f"Using MLE prepared data: {public_dir.resolve()}")
|
|
376
442
|
else:
|
|
377
|
-
#
|
|
378
|
-
payload["public_data_dir"] = str(data_dir)
|
|
443
|
+
# Fallback: use data_dir directly
|
|
444
|
+
payload["public_data_dir"] = str(data_dir.resolve())
|
|
445
|
+
self.logger.warning(
|
|
446
|
+
f"Prepared data not found at {public_dir}, using data_dir instead"
|
|
447
|
+
)
|
|
379
448
|
|
|
380
|
-
# Set output path
|
|
449
|
+
# Set output path - use simple filename, will be saved in workspace/sandbox
|
|
381
450
|
if output_path is None:
|
|
382
451
|
# Extract competition_id from data_dir path if possible
|
|
383
452
|
competition_id = data_dir.name
|
|
384
453
|
unique_id = str(uuid.uuid4())[:8]
|
|
385
454
|
output_filename = f"submission_{competition_id}_{unique_id}.csv"
|
|
386
455
|
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
output_path = workspace_dir / output_filename
|
|
456
|
+
# Use just the filename - DSAT will save it in workspace/sandbox
|
|
457
|
+
output_path = Path(output_filename)
|
|
390
458
|
|
|
391
459
|
payload["output_submission_path"] = str(output_path)
|
|
460
|
+
self.logger.info(f"Output submission file: {output_path}")
|
|
392
461
|
else:
|
|
393
462
|
# Other task types: use data_dir
|
|
394
463
|
payload["data_dir"] = str(data_dir)
|
|
@@ -427,6 +496,36 @@ class Agent:
|
|
|
427
496
|
|
|
428
497
|
return workspace_path
|
|
429
498
|
|
|
499
|
+
def _get_default_benchmark_dir(self) -> Path:
|
|
500
|
+
"""
|
|
501
|
+
Get the default benchmark registry directory.
|
|
502
|
+
|
|
503
|
+
This is where task registration files (grade.py, description.md, etc.) are stored.
|
|
504
|
+
Default: benchmarks/mlebench/competitions/
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
Path to benchmark registry directory
|
|
508
|
+
"""
|
|
509
|
+
# Try to get from config
|
|
510
|
+
benchmark_dir = None
|
|
511
|
+
|
|
512
|
+
if hasattr(self, 'config') and hasattr(self.config, 'run'):
|
|
513
|
+
run_config = self.config.run
|
|
514
|
+
if hasattr(run_config, 'parameters') and run_config.parameters:
|
|
515
|
+
benchmark_dir = run_config.parameters.get('benchmark_dir')
|
|
516
|
+
|
|
517
|
+
# Fallback to default benchmark directory
|
|
518
|
+
if benchmark_dir is None:
|
|
519
|
+
# Use relative path from current working directory
|
|
520
|
+
# Default: benchmarks/mlebench/competitions/
|
|
521
|
+
benchmark_dir = "benchmarks/mlebench/competitions"
|
|
522
|
+
|
|
523
|
+
benchmark_path = Path(benchmark_dir).resolve()
|
|
524
|
+
|
|
525
|
+
self.logger.debug(f"Benchmark registry directory: {benchmark_path}")
|
|
526
|
+
|
|
527
|
+
return benchmark_path
|
|
528
|
+
|
|
430
529
|
async def _execute_task(
|
|
431
530
|
self,
|
|
432
531
|
task: TaskDefinition,
|
|
@@ -60,6 +60,8 @@ class ConfigBuilder:
|
|
|
60
60
|
num_drafts: int = None,
|
|
61
61
|
workspace_dir: str = None,
|
|
62
62
|
run_name: str = None,
|
|
63
|
+
keep_workspace: bool = None,
|
|
64
|
+
keep_workspace_on_failure: bool = None,
|
|
63
65
|
**kwargs
|
|
64
66
|
) -> DSATConfig:
|
|
65
67
|
"""
|
|
@@ -76,6 +78,8 @@ class ConfigBuilder:
|
|
|
76
78
|
num_drafts: Number of drafts to generate
|
|
77
79
|
workspace_dir: Workspace directory
|
|
78
80
|
run_name: Name for this run
|
|
81
|
+
keep_workspace: Keep workspace after completion
|
|
82
|
+
keep_workspace_on_failure: Keep workspace on failure
|
|
79
83
|
**kwargs: Additional parameters
|
|
80
84
|
|
|
81
85
|
Returns:
|
|
@@ -100,6 +104,8 @@ class ConfigBuilder:
|
|
|
100
104
|
num_drafts=num_drafts,
|
|
101
105
|
workspace_dir=workspace_dir,
|
|
102
106
|
run_name=run_name,
|
|
107
|
+
keep_workspace=keep_workspace,
|
|
108
|
+
keep_workspace_on_failure=keep_workspace_on_failure,
|
|
103
109
|
**kwargs
|
|
104
110
|
)
|
|
105
111
|
config = self._deep_merge(config, user_config)
|
|
@@ -180,7 +186,22 @@ class ConfigBuilder:
|
|
|
180
186
|
logger.warning("LLM_MODEL_CONFIGS must be a JSON object")
|
|
181
187
|
return {}
|
|
182
188
|
|
|
183
|
-
|
|
189
|
+
# Process each model config
|
|
190
|
+
result = {}
|
|
191
|
+
for k, v in parsed.items():
|
|
192
|
+
if not isinstance(k, str) or not isinstance(v, dict):
|
|
193
|
+
continue
|
|
194
|
+
|
|
195
|
+
# Handle api_key as list (take the first one)
|
|
196
|
+
if "api_key" in v and isinstance(v["api_key"], list):
|
|
197
|
+
if len(v["api_key"]) > 0:
|
|
198
|
+
v = v.copy() # Shallow copy to avoid mutating original
|
|
199
|
+
v["api_key"] = v["api_key"][0]
|
|
200
|
+
logger.debug(f"Model '{k}': using first API key from list of {len(v['api_key'])}")
|
|
201
|
+
|
|
202
|
+
result[k] = v
|
|
203
|
+
|
|
204
|
+
return result
|
|
184
205
|
|
|
185
206
|
def _build_user_config(
|
|
186
207
|
self,
|
|
@@ -194,6 +215,8 @@ class ConfigBuilder:
|
|
|
194
215
|
num_drafts: int = None,
|
|
195
216
|
workspace_dir: str = None,
|
|
196
217
|
run_name: str = None,
|
|
218
|
+
keep_workspace: bool = None,
|
|
219
|
+
keep_workspace_on_failure: bool = None,
|
|
197
220
|
**kwargs
|
|
198
221
|
) -> Dict[str, Any]:
|
|
199
222
|
"""Build user configuration from parameters."""
|
|
@@ -230,6 +253,12 @@ class ConfigBuilder:
|
|
|
230
253
|
if workspace_dir is not None:
|
|
231
254
|
config.setdefault("run", {}).setdefault("parameters", {})["workspace_dir"] = workspace_dir
|
|
232
255
|
|
|
256
|
+
if keep_workspace is not None:
|
|
257
|
+
config.setdefault("run", {})["keep_all_workspaces"] = keep_workspace
|
|
258
|
+
|
|
259
|
+
if keep_workspace_on_failure is not None:
|
|
260
|
+
config.setdefault("run", {})["keep_workspace_on_failure"] = keep_workspace_on_failure
|
|
261
|
+
|
|
233
262
|
# Additional kwargs are added to run.parameters
|
|
234
263
|
if kwargs:
|
|
235
264
|
config.setdefault("run", {}).setdefault("parameters", {}).update(kwargs)
|
|
@@ -269,10 +269,13 @@ class DataLoader:
|
|
|
269
269
|
description = "MLE competition task"
|
|
270
270
|
|
|
271
271
|
if isinstance(source, (str, Path)):
|
|
272
|
-
path = Path(source)
|
|
272
|
+
path = Path(source).resolve() # Convert to absolute path
|
|
273
|
+
self.logger.info(f"Resolved path: {path}")
|
|
274
|
+
|
|
273
275
|
if path.exists():
|
|
274
276
|
if path.is_dir():
|
|
275
277
|
data_dir = path
|
|
278
|
+
self.logger.info(f"Data directory found: {data_dir}")
|
|
276
279
|
# Try to load description
|
|
277
280
|
desc_file = path / "description.md"
|
|
278
281
|
if desc_file.exists():
|
|
@@ -283,6 +286,7 @@ class DataLoader:
|
|
|
283
286
|
pass
|
|
284
287
|
elif path.is_file():
|
|
285
288
|
data_dir = path.parent
|
|
289
|
+
self.logger.info(f"Data directory (from file parent): {data_dir}")
|
|
286
290
|
# Try to load description from parent directory
|
|
287
291
|
desc_file = path.parent / "description.md"
|
|
288
292
|
if desc_file.exists():
|
|
@@ -291,6 +295,47 @@ class DataLoader:
|
|
|
291
295
|
self.logger.info(f"Loaded description from {desc_file}")
|
|
292
296
|
except Exception:
|
|
293
297
|
pass
|
|
298
|
+
else:
|
|
299
|
+
self.logger.warning(f"Path does not exist: {path}")
|
|
300
|
+
|
|
301
|
+
# Try to find the data in common locations
|
|
302
|
+
competition_id = path.name
|
|
303
|
+
|
|
304
|
+
# Common search locations for data
|
|
305
|
+
search_locations = [
|
|
306
|
+
# Current project: ./data/competitions/
|
|
307
|
+
Path.cwd() / "data" / "competitions" / competition_id,
|
|
308
|
+
# Parent dslighting: ../dslighting/data/competitions/
|
|
309
|
+
Path.cwd().parent / "dslighting" / "data" / "competitions" / competition_id,
|
|
310
|
+
# Parent data: ../data/competitions/
|
|
311
|
+
Path.cwd().parent / "data" / "competitions" / competition_id,
|
|
312
|
+
# From package location: ../../data/competitions/
|
|
313
|
+
Path(__file__).parent.parent.parent / "data" / "competitions" / competition_id,
|
|
314
|
+
# Absolute path fallback
|
|
315
|
+
Path("/Users/liufan/Applications/Github/dslighting/data/competitions") / competition_id,
|
|
316
|
+
]
|
|
317
|
+
|
|
318
|
+
for location in search_locations:
|
|
319
|
+
self.logger.info(f" Trying: {location}")
|
|
320
|
+
if location.exists() and location.is_dir():
|
|
321
|
+
data_dir = location
|
|
322
|
+
self.logger.info(f" ✓ Found data at: {data_dir}")
|
|
323
|
+
break
|
|
324
|
+
|
|
325
|
+
if data_dir is None:
|
|
326
|
+
# Last resort: use the original resolved path
|
|
327
|
+
self.logger.warning(f" Could not find data, using original path: {path}")
|
|
328
|
+
data_dir = path
|
|
329
|
+
|
|
330
|
+
# Try to load description (if data_dir was found)
|
|
331
|
+
if data_dir and data_dir.exists():
|
|
332
|
+
desc_file = data_dir / "description.md"
|
|
333
|
+
if desc_file.exists():
|
|
334
|
+
try:
|
|
335
|
+
description = desc_file.read_text(encoding='utf-8')
|
|
336
|
+
self.logger.info(f"Loaded description from {desc_file}")
|
|
337
|
+
except Exception:
|
|
338
|
+
pass
|
|
294
339
|
|
|
295
340
|
# Create MLE-style detection
|
|
296
341
|
from dslighting.utils.defaults import WORKFLOW_RECOMMENDATIONS
|
|
@@ -105,7 +105,7 @@ DEFAULT_CONFIG: Dict[str, Any] = {
|
|
|
105
105
|
"params": {}
|
|
106
106
|
},
|
|
107
107
|
"run": {
|
|
108
|
-
"name": "
|
|
108
|
+
"name": "dsat_run", # Use "dsat_run" to let DSATRunner auto-generate: dsat_run_{task_id}_{uid}
|
|
109
109
|
"total_steps": DEFAULT_MAX_ITERATIONS,
|
|
110
110
|
"keep_all_workspaces": DEFAULT_KEEP_ALL_WORKSPACES,
|
|
111
111
|
"keep_workspace_on_failure": DEFAULT_KEEP_WORKSPACE_ON_FAILURE,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|