hud-python 0.3.5__py3-none-any.whl → 0.4.1__py3-none-any.whl
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.
Potentially problematic release.
This version of hud-python might be problematic. Click here for more details.
- hud/__init__.py +22 -89
- hud/agents/__init__.py +15 -0
- hud/agents/art.py +101 -0
- hud/agents/base.py +599 -0
- hud/{mcp → agents}/claude.py +373 -321
- hud/{mcp → agents}/langchain.py +250 -250
- hud/agents/misc/__init__.py +7 -0
- hud/{agent → agents}/misc/response_agent.py +80 -80
- hud/{mcp → agents}/openai.py +352 -334
- hud/agents/openai_chat_generic.py +154 -0
- hud/{mcp → agents}/tests/__init__.py +1 -1
- hud/agents/tests/test_base.py +742 -0
- hud/agents/tests/test_claude.py +324 -0
- hud/{mcp → agents}/tests/test_client.py +363 -324
- hud/{mcp → agents}/tests/test_openai.py +237 -238
- hud/cli/__init__.py +617 -0
- hud/cli/__main__.py +8 -0
- hud/cli/analyze.py +371 -0
- hud/cli/analyze_metadata.py +230 -0
- hud/cli/build.py +427 -0
- hud/cli/clone.py +185 -0
- hud/cli/cursor.py +92 -0
- hud/cli/debug.py +392 -0
- hud/cli/docker_utils.py +83 -0
- hud/cli/init.py +281 -0
- hud/cli/interactive.py +353 -0
- hud/cli/mcp_server.py +756 -0
- hud/cli/pull.py +336 -0
- hud/cli/push.py +370 -0
- hud/cli/remote_runner.py +311 -0
- hud/cli/runner.py +160 -0
- hud/cli/tests/__init__.py +3 -0
- hud/cli/tests/test_analyze.py +284 -0
- hud/cli/tests/test_cli_init.py +265 -0
- hud/cli/tests/test_cli_main.py +27 -0
- hud/cli/tests/test_clone.py +142 -0
- hud/cli/tests/test_cursor.py +253 -0
- hud/cli/tests/test_debug.py +453 -0
- hud/cli/tests/test_mcp_server.py +139 -0
- hud/cli/tests/test_utils.py +388 -0
- hud/cli/utils.py +263 -0
- hud/clients/README.md +143 -0
- hud/clients/__init__.py +16 -0
- hud/clients/base.py +379 -0
- hud/clients/fastmcp.py +222 -0
- hud/clients/mcp_use.py +278 -0
- hud/clients/tests/__init__.py +1 -0
- hud/clients/tests/test_client_integration.py +111 -0
- hud/clients/tests/test_fastmcp.py +342 -0
- hud/clients/tests/test_protocol.py +188 -0
- hud/clients/utils/__init__.py +1 -0
- hud/clients/utils/retry_transport.py +160 -0
- hud/datasets.py +322 -192
- hud/misc/__init__.py +1 -0
- hud/{agent → misc}/claude_plays_pokemon.py +292 -283
- hud/otel/__init__.py +35 -0
- hud/otel/collector.py +142 -0
- hud/otel/config.py +164 -0
- hud/otel/context.py +536 -0
- hud/otel/exporters.py +366 -0
- hud/otel/instrumentation.py +97 -0
- hud/otel/processors.py +118 -0
- hud/otel/tests/__init__.py +1 -0
- hud/otel/tests/test_processors.py +197 -0
- hud/server/__init__.py +5 -5
- hud/server/context.py +114 -0
- hud/server/helper/__init__.py +5 -0
- hud/server/low_level.py +132 -0
- hud/server/server.py +166 -0
- hud/server/tests/__init__.py +3 -0
- hud/settings.py +73 -79
- hud/shared/__init__.py +5 -0
- hud/{exceptions.py → shared/exceptions.py} +180 -180
- hud/{server → shared}/requests.py +264 -264
- hud/shared/tests/test_exceptions.py +157 -0
- hud/{server → shared}/tests/test_requests.py +275 -275
- hud/telemetry/__init__.py +25 -30
- hud/telemetry/instrument.py +379 -0
- hud/telemetry/job.py +309 -141
- hud/telemetry/replay.py +74 -0
- hud/telemetry/trace.py +83 -0
- hud/tools/__init__.py +33 -34
- hud/tools/base.py +365 -65
- hud/tools/bash.py +161 -137
- hud/tools/computer/__init__.py +15 -13
- hud/tools/computer/anthropic.py +437 -420
- hud/tools/computer/hud.py +376 -334
- hud/tools/computer/openai.py +295 -292
- hud/tools/computer/settings.py +82 -0
- hud/tools/edit.py +314 -290
- hud/tools/executors/__init__.py +30 -30
- hud/tools/executors/base.py +539 -532
- hud/tools/executors/pyautogui.py +621 -619
- hud/tools/executors/tests/__init__.py +1 -1
- hud/tools/executors/tests/test_base_executor.py +338 -338
- hud/tools/executors/tests/test_pyautogui_executor.py +165 -165
- hud/tools/executors/xdo.py +511 -503
- hud/tools/{playwright_tool.py → playwright.py} +412 -379
- hud/tools/tests/__init__.py +3 -3
- hud/tools/tests/test_base.py +282 -0
- hud/tools/tests/test_bash.py +158 -152
- hud/tools/tests/test_bash_extended.py +197 -0
- hud/tools/tests/test_computer.py +425 -52
- hud/tools/tests/test_computer_actions.py +34 -34
- hud/tools/tests/test_edit.py +259 -240
- hud/tools/tests/test_init.py +27 -27
- hud/tools/tests/test_playwright_tool.py +183 -183
- hud/tools/tests/test_tools.py +145 -157
- hud/tools/tests/test_utils.py +156 -156
- hud/tools/types.py +72 -0
- hud/tools/utils.py +50 -50
- hud/types.py +136 -89
- hud/utils/__init__.py +10 -16
- hud/utils/async_utils.py +65 -0
- hud/utils/design.py +168 -0
- hud/utils/mcp.py +55 -0
- hud/utils/progress.py +149 -149
- hud/utils/telemetry.py +66 -66
- hud/utils/tests/test_async_utils.py +173 -0
- hud/utils/tests/test_init.py +17 -21
- hud/utils/tests/test_progress.py +261 -225
- hud/utils/tests/test_telemetry.py +82 -37
- hud/utils/tests/test_version.py +8 -8
- hud/version.py +7 -7
- hud_python-0.4.1.dist-info/METADATA +476 -0
- hud_python-0.4.1.dist-info/RECORD +132 -0
- hud_python-0.4.1.dist-info/entry_points.txt +3 -0
- {hud_python-0.3.5.dist-info → hud_python-0.4.1.dist-info}/licenses/LICENSE +21 -21
- hud/adapters/__init__.py +0 -8
- hud/adapters/claude/__init__.py +0 -5
- hud/adapters/claude/adapter.py +0 -180
- hud/adapters/claude/tests/__init__.py +0 -1
- hud/adapters/claude/tests/test_adapter.py +0 -519
- hud/adapters/common/__init__.py +0 -6
- hud/adapters/common/adapter.py +0 -178
- hud/adapters/common/tests/test_adapter.py +0 -289
- hud/adapters/common/types.py +0 -446
- hud/adapters/operator/__init__.py +0 -5
- hud/adapters/operator/adapter.py +0 -108
- hud/adapters/operator/tests/__init__.py +0 -1
- hud/adapters/operator/tests/test_adapter.py +0 -370
- hud/agent/__init__.py +0 -19
- hud/agent/base.py +0 -126
- hud/agent/claude.py +0 -271
- hud/agent/langchain.py +0 -215
- hud/agent/misc/__init__.py +0 -3
- hud/agent/operator.py +0 -268
- hud/agent/tests/__init__.py +0 -1
- hud/agent/tests/test_base.py +0 -202
- hud/env/__init__.py +0 -11
- hud/env/client.py +0 -35
- hud/env/docker_client.py +0 -349
- hud/env/environment.py +0 -446
- hud/env/local_docker_client.py +0 -358
- hud/env/remote_client.py +0 -212
- hud/env/remote_docker_client.py +0 -292
- hud/gym.py +0 -130
- hud/job.py +0 -773
- hud/mcp/__init__.py +0 -17
- hud/mcp/base.py +0 -631
- hud/mcp/client.py +0 -312
- hud/mcp/tests/test_base.py +0 -512
- hud/mcp/tests/test_claude.py +0 -294
- hud/task.py +0 -149
- hud/taskset.py +0 -237
- hud/telemetry/_trace.py +0 -347
- hud/telemetry/context.py +0 -230
- hud/telemetry/exporter.py +0 -575
- hud/telemetry/instrumentation/__init__.py +0 -3
- hud/telemetry/instrumentation/mcp.py +0 -259
- hud/telemetry/instrumentation/registry.py +0 -59
- hud/telemetry/mcp_models.py +0 -270
- hud/telemetry/tests/__init__.py +0 -1
- hud/telemetry/tests/test_context.py +0 -210
- hud/telemetry/tests/test_trace.py +0 -312
- hud/tools/helper/README.md +0 -56
- hud/tools/helper/__init__.py +0 -9
- hud/tools/helper/mcp_server.py +0 -78
- hud/tools/helper/server_initialization.py +0 -115
- hud/tools/helper/utils.py +0 -58
- hud/trajectory.py +0 -94
- hud/utils/agent.py +0 -37
- hud/utils/common.py +0 -256
- hud/utils/config.py +0 -120
- hud/utils/deprecation.py +0 -115
- hud/utils/misc.py +0 -53
- hud/utils/tests/test_common.py +0 -277
- hud/utils/tests/test_config.py +0 -129
- hud_python-0.3.5.dist-info/METADATA +0 -284
- hud_python-0.3.5.dist-info/RECORD +0 -120
- /hud/{adapters/common → shared}/tests/__init__.py +0 -0
- {hud_python-0.3.5.dist-info → hud_python-0.4.1.dist-info}/WHEEL +0 -0
hud/env/environment.py
DELETED
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
"""Base classes for environment implementations."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
-
|
|
8
|
-
from pydantic import BaseModel
|
|
9
|
-
|
|
10
|
-
from hud.env.client import Client
|
|
11
|
-
from hud.env.remote_client import RemoteClient, SetupRequest
|
|
12
|
-
from hud.task import Task
|
|
13
|
-
from hud.utils.agent import format_agent_prompt
|
|
14
|
-
from hud.utils.common import FunctionConfig, FunctionConfigs, Observation
|
|
15
|
-
from hud.utils.config import (
|
|
16
|
-
LOCAL_EVALUATORS,
|
|
17
|
-
REMOTE_EVALUATE,
|
|
18
|
-
REMOTE_FUNCTION_PREFIX,
|
|
19
|
-
expand_config,
|
|
20
|
-
)
|
|
21
|
-
from hud.utils.telemetry import stream
|
|
22
|
-
|
|
23
|
-
logger = logging.getLogger("hud.environment")
|
|
24
|
-
|
|
25
|
-
if TYPE_CHECKING:
|
|
26
|
-
from hud.adapters.common import CLA
|
|
27
|
-
from hud.agent import Agent
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class Environment(BaseModel):
|
|
31
|
-
"""
|
|
32
|
-
Environment base class that provides common functionality for all environment implementations.
|
|
33
|
-
This class uses the primitives provided by EnvClient to implement core environment operations.
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
metadata: dict[str, Any]
|
|
37
|
-
client: Client
|
|
38
|
-
url: str | None = None
|
|
39
|
-
live_url: str | None = None
|
|
40
|
-
# The task id to use for the environment reset
|
|
41
|
-
task: Task | None = None
|
|
42
|
-
build_data: dict[str, Any]
|
|
43
|
-
|
|
44
|
-
# The task run id
|
|
45
|
-
task_run_id: str | None = None
|
|
46
|
-
|
|
47
|
-
# final response
|
|
48
|
-
final_response: str | None = None
|
|
49
|
-
|
|
50
|
-
# environment prompt information
|
|
51
|
-
environment_prompt: str | None = None
|
|
52
|
-
|
|
53
|
-
async def _invoke_all(self, configs: FunctionConfigs) -> list[Any]:
|
|
54
|
-
# Execute each config and collect results
|
|
55
|
-
configs_all = [configs] if not isinstance(configs, list) else configs
|
|
56
|
-
results = []
|
|
57
|
-
for config in configs_all:
|
|
58
|
-
for expanded_config in expand_config(config):
|
|
59
|
-
result, stdout, stderr = await self.client.invoke(expanded_config)
|
|
60
|
-
results.append(result)
|
|
61
|
-
if stdout:
|
|
62
|
-
logger.info(
|
|
63
|
-
"%s produced stdout:\n%s",
|
|
64
|
-
expanded_config.function,
|
|
65
|
-
stdout.decode(),
|
|
66
|
-
)
|
|
67
|
-
if stderr:
|
|
68
|
-
logger.warning(
|
|
69
|
-
"%s produced stderr:\n%s",
|
|
70
|
-
expanded_config.function,
|
|
71
|
-
stderr.decode(),
|
|
72
|
-
)
|
|
73
|
-
return results
|
|
74
|
-
|
|
75
|
-
async def _setup(self, config: FunctionConfigs | None = None) -> None:
|
|
76
|
-
"""
|
|
77
|
-
Setup the environment.
|
|
78
|
-
No-op if no config or task is provided.
|
|
79
|
-
|
|
80
|
-
Args:
|
|
81
|
-
config: The configuration to use for the setup
|
|
82
|
-
"""
|
|
83
|
-
if isinstance(self.client, RemoteClient):
|
|
84
|
-
await self.get_urls()
|
|
85
|
-
|
|
86
|
-
setup_request = SetupRequest()
|
|
87
|
-
|
|
88
|
-
if self.task:
|
|
89
|
-
setup_request.task_id = self.task.id
|
|
90
|
-
setup_request.config = self.task.config
|
|
91
|
-
setup_request.metadata = _format_task_metadata(self.task)
|
|
92
|
-
if self.task.setup:
|
|
93
|
-
setup_request.setup = expand_config(self.task.setup)[0]
|
|
94
|
-
elif config:
|
|
95
|
-
setup_request.setup = expand_config(config)[0]
|
|
96
|
-
else:
|
|
97
|
-
raise ValueError("No task or config provided for remote environment")
|
|
98
|
-
|
|
99
|
-
result = await self.client.setup(setup_request)
|
|
100
|
-
|
|
101
|
-
if result and result.get("id"):
|
|
102
|
-
self.task_run_id = result.get("id")
|
|
103
|
-
logger.info("View the live trace at https://app.hud.so/trace/%s", self.task_run_id)
|
|
104
|
-
else:
|
|
105
|
-
logger.warning("No task run id found in the result")
|
|
106
|
-
else:
|
|
107
|
-
if config is not None:
|
|
108
|
-
await self._invoke_all(config)
|
|
109
|
-
elif self.task and self.task.setup is not None:
|
|
110
|
-
await self._invoke_all(self.task.setup)
|
|
111
|
-
|
|
112
|
-
async def evaluate(
|
|
113
|
-
self,
|
|
114
|
-
config: FunctionConfigs | None = None,
|
|
115
|
-
metadata: dict[str, Any] | None = None,
|
|
116
|
-
) -> Any:
|
|
117
|
-
"""
|
|
118
|
-
Evaluate the environment.
|
|
119
|
-
|
|
120
|
-
Args:
|
|
121
|
-
config: The configuration to use for the evaluation
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
Any: Result of the evaluation
|
|
125
|
-
"""
|
|
126
|
-
if isinstance(self.client, RemoteClient):
|
|
127
|
-
results = await self._invoke_all(
|
|
128
|
-
create_remote_config(self, config, REMOTE_EVALUATE, metadata)
|
|
129
|
-
)
|
|
130
|
-
else:
|
|
131
|
-
if config is not None:
|
|
132
|
-
results = await self._invoke_all(config)
|
|
133
|
-
elif self.task and self.task.evaluate is not None:
|
|
134
|
-
results = await self._invoke_all(self.task.evaluate)
|
|
135
|
-
else:
|
|
136
|
-
raise ValueError("No config or task provided for local environment")
|
|
137
|
-
if len(results) == 1:
|
|
138
|
-
return results[0]
|
|
139
|
-
else:
|
|
140
|
-
return results
|
|
141
|
-
|
|
142
|
-
async def reset(self) -> tuple[Observation, dict[str, Any]]:
|
|
143
|
-
"""
|
|
144
|
-
Reset the environment and return the first observation with the agent prompt.
|
|
145
|
-
|
|
146
|
-
Args:
|
|
147
|
-
None
|
|
148
|
-
|
|
149
|
-
Returns:
|
|
150
|
-
Observation: The first observation from the environment with the agent prompt
|
|
151
|
-
info: Dictionary of information about the environment
|
|
152
|
-
"""
|
|
153
|
-
# await self._setup(configs)
|
|
154
|
-
obs, _, _, info = await self.step()
|
|
155
|
-
|
|
156
|
-
if self.build_data.get("environment_prompt"):
|
|
157
|
-
self.environment_prompt = self.build_data["environment_prompt"]
|
|
158
|
-
|
|
159
|
-
# Format the agent prompt with the environment prompt and the task prompt
|
|
160
|
-
obs.text = format_agent_prompt(self.environment_prompt, self.task)
|
|
161
|
-
|
|
162
|
-
return obs, info
|
|
163
|
-
|
|
164
|
-
async def step(
|
|
165
|
-
self,
|
|
166
|
-
actions: CLA | list[CLA] | None = None,
|
|
167
|
-
verbose: bool = False,
|
|
168
|
-
) -> tuple[Observation, float, bool, dict[str, Any]]:
|
|
169
|
-
"""Execute a step in the environment.
|
|
170
|
-
|
|
171
|
-
Args:
|
|
172
|
-
action: The action to execute
|
|
173
|
-
|
|
174
|
-
Returns:
|
|
175
|
-
Any: Result of the step execution
|
|
176
|
-
"""
|
|
177
|
-
if not isinstance(actions, list) and actions is not None:
|
|
178
|
-
actions = [actions]
|
|
179
|
-
if actions is None or len(actions) == 0:
|
|
180
|
-
actions = []
|
|
181
|
-
args = [[action.model_dump() for action in actions]]
|
|
182
|
-
|
|
183
|
-
# TODO: Move this into the server side
|
|
184
|
-
self._maybe_store_response(actions)
|
|
185
|
-
|
|
186
|
-
result, stdout, stderr = await self.client.invoke(
|
|
187
|
-
FunctionConfig(function="step", args=args)
|
|
188
|
-
)
|
|
189
|
-
if verbose:
|
|
190
|
-
if stdout:
|
|
191
|
-
logger.info("Step produced stdout: %s", stdout.decode())
|
|
192
|
-
if stderr:
|
|
193
|
-
logger.warning("Step produced stderr: %s", stderr.decode())
|
|
194
|
-
|
|
195
|
-
observation = Observation.model_validate(result["observation"], strict=True)
|
|
196
|
-
|
|
197
|
-
info = result.get("info", {})
|
|
198
|
-
|
|
199
|
-
return observation, 0, False, info
|
|
200
|
-
|
|
201
|
-
def _maybe_store_response(self, actions: list[CLA]) -> bool:
|
|
202
|
-
"""Store the final response into the environment.
|
|
203
|
-
|
|
204
|
-
Args:
|
|
205
|
-
actions: The action(s) to check
|
|
206
|
-
|
|
207
|
-
Returns:
|
|
208
|
-
bool: True if the response was submitted, False otherwise
|
|
209
|
-
"""
|
|
210
|
-
if len(actions) > 0 and actions[-1].type == "response":
|
|
211
|
-
self.final_response = actions[-1].text
|
|
212
|
-
return True
|
|
213
|
-
return False
|
|
214
|
-
|
|
215
|
-
async def get_urls(self) -> dict[str, Any]:
|
|
216
|
-
"""Get URLs for the environment.
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
dict: Dictionary of URLs for accessing the environment
|
|
220
|
-
"""
|
|
221
|
-
data, _, _ = await self.client.invoke(FunctionConfig(function="get_urls", args=[]))
|
|
222
|
-
|
|
223
|
-
self.url = data.get("url")
|
|
224
|
-
self.live_url = data.get("live_url")
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
"url": self.url,
|
|
228
|
-
"live_url": self.live_url,
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async def close(self) -> None:
|
|
232
|
-
"""Close the environment.
|
|
233
|
-
|
|
234
|
-
This should release any resources and clean up the environment.
|
|
235
|
-
"""
|
|
236
|
-
await self.client.close()
|
|
237
|
-
|
|
238
|
-
async def stream(self) -> str | None:
|
|
239
|
-
if not self.live_url:
|
|
240
|
-
await self.get_urls()
|
|
241
|
-
if self.live_url is None:
|
|
242
|
-
logger.warning("No live URL found")
|
|
243
|
-
return None
|
|
244
|
-
return stream(self.live_url)
|
|
245
|
-
|
|
246
|
-
async def run(self, agent: Agent, max_steps: int = 27, verbose: bool = True) -> Any:
|
|
247
|
-
"""Run an agent in the environment.
|
|
248
|
-
|
|
249
|
-
Args:
|
|
250
|
-
agent: The agent to run
|
|
251
|
-
"""
|
|
252
|
-
if verbose:
|
|
253
|
-
logger.info("Running agent in environment...")
|
|
254
|
-
obs, _ = await self.reset()
|
|
255
|
-
for i in range(max_steps):
|
|
256
|
-
action, done = await agent.predict(obs, verbose=verbose)
|
|
257
|
-
if verbose:
|
|
258
|
-
logger.info(
|
|
259
|
-
"Step %d: Action: %s",
|
|
260
|
-
i,
|
|
261
|
-
[str(a) for a in action] if len(action) > 1 else str(action[0]),
|
|
262
|
-
)
|
|
263
|
-
obs, reward, terminated, info = await self.step(action)
|
|
264
|
-
if verbose:
|
|
265
|
-
logger.info("Step %d: Observation: %s", i, obs)
|
|
266
|
-
if done or terminated:
|
|
267
|
-
break
|
|
268
|
-
result = await self.evaluate()
|
|
269
|
-
if verbose:
|
|
270
|
-
logger.info("Evaluation result: %s", result)
|
|
271
|
-
return result
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def _format_task_metadata(task: Task) -> dict[str, Any]:
|
|
275
|
-
metadata = {}
|
|
276
|
-
if task.metadata:
|
|
277
|
-
for key, value in task.metadata.items():
|
|
278
|
-
metadata[str(key)] = value
|
|
279
|
-
if task.sensitive_data:
|
|
280
|
-
metadata["sensitive_data"] = task.sensitive_data
|
|
281
|
-
return metadata
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
def create_remote_config(
|
|
285
|
-
env: Environment | None = None,
|
|
286
|
-
config: FunctionConfigs | None = None,
|
|
287
|
-
function: str | None = None,
|
|
288
|
-
metadata: dict[str, Any] | None = None,
|
|
289
|
-
) -> list[FunctionConfig]:
|
|
290
|
-
"""
|
|
291
|
-
Create a remote configuration for setup or evaluate, determining the final
|
|
292
|
-
function call structure based on the provided task or explicit config.
|
|
293
|
-
|
|
294
|
-
This function orchestrates how setup and evaluate steps defined in a Task
|
|
295
|
-
or passed directly are prepared for remote execution via `env._invoke_all`.
|
|
296
|
-
|
|
297
|
-
Args:
|
|
298
|
-
env: Environment object, potentially containing a task definition.
|
|
299
|
-
Used to access `env.task` and `env.final_response`.
|
|
300
|
-
config: Direct configuration override (e.g., passed to `env.evaluate(config=...)`).
|
|
301
|
-
Can be in various FunctionConfigs formats.
|
|
302
|
-
function: The top-level function context, typically "setup" or "evaluate".
|
|
303
|
-
|
|
304
|
-
Returns:
|
|
305
|
-
list[FunctionConfig]: A list containing a single FunctionConfig object
|
|
306
|
-
ready for remote invocation via `client.invoke`.
|
|
307
|
-
The specific function/arguments are chosen based on this priority:
|
|
308
|
-
1. Explicit `config` parameter (if provided).
|
|
309
|
-
2. Specific `task` attribute (e.g., `task.evaluate`).
|
|
310
|
-
3. General `task.config` dictionary.
|
|
311
|
-
4. Default private function using `task.id`
|
|
312
|
-
(e.g., `private_evaluate(task.id)`).
|
|
313
|
-
5. Base `function` name with minimal/default arguments.
|
|
314
|
-
|
|
315
|
-
Logic & Examples (Assuming `function="evaluate"` for examples):
|
|
316
|
-
|
|
317
|
-
1) Explicit `config` provided: The `config` is expanded and becomes the `args`
|
|
318
|
-
for the top-level `function` call. If the environment has a final_response,
|
|
319
|
-
it's appended to these args.
|
|
320
|
-
- Example Input:
|
|
321
|
-
`env` (with `final_response="Paris"`)
|
|
322
|
-
`config=("contains_text", "Paris")`
|
|
323
|
-
`function="evaluate"`
|
|
324
|
-
- Example Output:
|
|
325
|
-
`[FunctionConfig(function='evaluate', args=[
|
|
326
|
-
FunctionConfig(function='contains_text', args=['Paris', 'Paris'])
|
|
327
|
-
])]`
|
|
328
|
-
|
|
329
|
-
2) No explicit `config`, Task has the attribute (e.g., `task.evaluate`):
|
|
330
|
-
The Task's attribute value (e.g., `task.evaluate`) is expanded and becomes the `args`
|
|
331
|
-
for the top-level `function` call. Task ID is added if present. `final_response` is
|
|
332
|
-
appended if present.
|
|
333
|
-
- Example Input:
|
|
334
|
-
`env` (`task=Task(id="t1", evaluate=("check_answer",), ...)`, `final_response="42"`)
|
|
335
|
-
`config=None`
|
|
336
|
-
`function="evaluate"`
|
|
337
|
-
- Example Output:
|
|
338
|
-
`[FunctionConfig(function='evaluate', args=[FunctionConfig(function='check_answer',
|
|
339
|
-
args=['42'], id='t1')])]`
|
|
340
|
-
|
|
341
|
-
3) No explicit `config`, no specific Task attribute, Task has `task.config`:
|
|
342
|
-
The `task.config` dictionary becomes the single argument for the top-level
|
|
343
|
-
`function` call. Task ID is added to the config dict if present. `final_response` is
|
|
344
|
-
appended if present.
|
|
345
|
-
- Example Input:
|
|
346
|
-
`env` (with `task=Task(id="t2", config={"expected": "val"}, ...)`)
|
|
347
|
-
`config=None`
|
|
348
|
-
`function="evaluate"`
|
|
349
|
-
- Example Output:
|
|
350
|
-
`[FunctionConfig(function='evaluate', args=[{"expected": "val", "id": "t2"}])]`
|
|
351
|
-
|
|
352
|
-
4) No explicit `config`, no specific Task attribute, no `task.config`, Task has `task.id`:
|
|
353
|
-
Calls a private function (`private_<function>`) on the remote end, passing
|
|
354
|
-
the `task.id` as the only argument.
|
|
355
|
-
- Example Input:
|
|
356
|
-
`env` (with `task=Task(id="t3", ...)`)
|
|
357
|
-
`config=None`
|
|
358
|
-
`function="evaluate"`
|
|
359
|
-
- Example Output:
|
|
360
|
-
`[FunctionConfig(function='private_evaluate', args=['t3'])]`
|
|
361
|
-
|
|
362
|
-
5) No explicit `config` and no relevant Task info:
|
|
363
|
-
Calls the top-level `function` with empty args.
|
|
364
|
-
- Example Input:
|
|
365
|
-
`env` (with `task=Task(...)`)
|
|
366
|
-
`config=None`
|
|
367
|
-
`function="evaluate"`
|
|
368
|
-
- Example Output:
|
|
369
|
-
`[FunctionConfig(function='evaluate', args=[])]`
|
|
370
|
-
"""
|
|
371
|
-
# If no function provided, just expand the config and return it directly
|
|
372
|
-
if metadata is None:
|
|
373
|
-
metadata = {}
|
|
374
|
-
if function is None:
|
|
375
|
-
if config:
|
|
376
|
-
return expand_config(config)
|
|
377
|
-
raise ValueError("Either function or config must be provided")
|
|
378
|
-
|
|
379
|
-
# Case 1: Explicit config provided
|
|
380
|
-
if config:
|
|
381
|
-
if not isinstance(config, dict):
|
|
382
|
-
expanded_configs = expand_config(config)
|
|
383
|
-
if env and env.final_response and expanded_configs[0].args[0] in LOCAL_EVALUATORS:
|
|
384
|
-
# Ensure args is a list before appending
|
|
385
|
-
if not isinstance(expanded_configs[0].args, list):
|
|
386
|
-
expanded_configs[0].args = [expanded_configs[0].args]
|
|
387
|
-
expanded_configs[0].args.append(env.final_response) # for remote responses
|
|
388
|
-
return [FunctionConfig(function=function, args=expanded_configs, metadata=metadata)]
|
|
389
|
-
else:
|
|
390
|
-
return [FunctionConfig(function=function, args=[config], metadata=metadata)]
|
|
391
|
-
|
|
392
|
-
# Otherwise, use the environment's task
|
|
393
|
-
task = env.task if env else None
|
|
394
|
-
|
|
395
|
-
# Must have a task for the remaining cases
|
|
396
|
-
if task is None:
|
|
397
|
-
raise ValueError("Either task or config must be provided")
|
|
398
|
-
|
|
399
|
-
metadata = _format_task_metadata(task)
|
|
400
|
-
|
|
401
|
-
# Case 2: Task has the specified function attribute
|
|
402
|
-
task_config = getattr(task, function, None)
|
|
403
|
-
if task_config:
|
|
404
|
-
expanded_configs = expand_config(task_config)
|
|
405
|
-
if task.id:
|
|
406
|
-
expanded_configs[0].id = task.id # for remote IDs
|
|
407
|
-
if env and env.final_response and expanded_configs[0].function in LOCAL_EVALUATORS:
|
|
408
|
-
# Ensure args is a list before appending
|
|
409
|
-
if not isinstance(expanded_configs[0].args, list):
|
|
410
|
-
expanded_configs[0].args = [expanded_configs[0].args]
|
|
411
|
-
expanded_configs[0].args.append(env.final_response) # for remote responses
|
|
412
|
-
return [FunctionConfig(function=function, args=expanded_configs, metadata=metadata)]
|
|
413
|
-
|
|
414
|
-
# Case 3: Check for task.config
|
|
415
|
-
if hasattr(task, "config") and task.config:
|
|
416
|
-
# Ensure task.config is a dictionary before adding id
|
|
417
|
-
final_args = task.config.copy() if isinstance(task.config, dict) else {}
|
|
418
|
-
if task.id:
|
|
419
|
-
final_args["id"] = task.id # for remote IDs
|
|
420
|
-
if env and env.final_response:
|
|
421
|
-
# Append response, ensuring args exists and is a list
|
|
422
|
-
if "args" not in final_args:
|
|
423
|
-
final_args["args"] = []
|
|
424
|
-
if not isinstance(final_args["args"], list):
|
|
425
|
-
final_args["args"] = [final_args["args"]]
|
|
426
|
-
final_args["args"].append(env.final_response)
|
|
427
|
-
return [FunctionConfig(function=function, args=[final_args], metadata=metadata)]
|
|
428
|
-
|
|
429
|
-
# Case 4: Use task.id
|
|
430
|
-
if task.id:
|
|
431
|
-
args_list = [task.id]
|
|
432
|
-
if env and env.final_response:
|
|
433
|
-
args_list.append(env.final_response) # Append final response
|
|
434
|
-
return [
|
|
435
|
-
FunctionConfig(
|
|
436
|
-
function=f"{REMOTE_FUNCTION_PREFIX}{function}",
|
|
437
|
-
args=args_list,
|
|
438
|
-
metadata=metadata,
|
|
439
|
-
)
|
|
440
|
-
]
|
|
441
|
-
|
|
442
|
-
# Case 5: No valid configuration found
|
|
443
|
-
args_list = []
|
|
444
|
-
if env and env.final_response:
|
|
445
|
-
args_list.append(env.final_response)
|
|
446
|
-
return [FunctionConfig(function=function, args=args_list, metadata=metadata)]
|