droidrun 0.3.10.dev3__py3-none-any.whl → 0.3.10.dev5__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.
- droidrun/agent/codeact/__init__.py +1 -4
- droidrun/agent/codeact/codeact_agent.py +95 -86
- droidrun/agent/codeact/events.py +1 -2
- droidrun/agent/context/__init__.py +5 -9
- droidrun/agent/context/episodic_memory.py +1 -3
- droidrun/agent/context/task_manager.py +8 -2
- droidrun/agent/droid/droid_agent.py +102 -141
- droidrun/agent/droid/events.py +45 -14
- droidrun/agent/executor/__init__.py +6 -4
- droidrun/agent/executor/events.py +29 -9
- droidrun/agent/executor/executor_agent.py +86 -28
- droidrun/agent/executor/prompts.py +8 -2
- droidrun/agent/manager/__init__.py +6 -7
- droidrun/agent/manager/events.py +16 -4
- droidrun/agent/manager/manager_agent.py +130 -69
- droidrun/agent/manager/prompts.py +1 -159
- droidrun/agent/utils/chat_utils.py +64 -2
- droidrun/agent/utils/device_state_formatter.py +54 -26
- droidrun/agent/utils/executer.py +66 -80
- droidrun/agent/utils/inference.py +11 -10
- droidrun/agent/utils/tools.py +58 -6
- droidrun/agent/utils/trajectory.py +18 -12
- droidrun/cli/logs.py +118 -56
- droidrun/cli/main.py +154 -136
- droidrun/config_manager/__init__.py +9 -7
- droidrun/config_manager/app_card_loader.py +148 -0
- droidrun/config_manager/config_manager.py +200 -102
- droidrun/config_manager/path_resolver.py +104 -0
- droidrun/config_manager/prompt_loader.py +75 -0
- droidrun/macro/__init__.py +1 -1
- droidrun/macro/cli.py +23 -18
- droidrun/telemetry/__init__.py +2 -2
- droidrun/telemetry/events.py +3 -3
- droidrun/telemetry/tracker.py +1 -1
- droidrun/tools/adb.py +1 -1
- droidrun/tools/ios.py +3 -2
- {droidrun-0.3.10.dev3.dist-info → droidrun-0.3.10.dev5.dist-info}/METADATA +10 -4
- droidrun-0.3.10.dev5.dist-info/RECORD +61 -0
- droidrun/agent/codeact/prompts.py +0 -26
- droidrun/agent/context/agent_persona.py +0 -16
- droidrun/agent/context/context_injection_manager.py +0 -66
- droidrun/agent/context/personas/__init__.py +0 -11
- droidrun/agent/context/personas/app_starter.py +0 -44
- droidrun/agent/context/personas/big_agent.py +0 -96
- droidrun/agent/context/personas/default.py +0 -95
- droidrun/agent/context/personas/ui_expert.py +0 -108
- droidrun/agent/planner/__init__.py +0 -13
- droidrun/agent/planner/events.py +0 -21
- droidrun/agent/planner/planner_agent.py +0 -311
- droidrun/agent/planner/prompts.py +0 -124
- droidrun-0.3.10.dev3.dist-info/RECORD +0 -70
- {droidrun-0.3.10.dev3.dist-info → droidrun-0.3.10.dev5.dist-info}/WHEEL +0 -0
- {droidrun-0.3.10.dev3.dist-info → droidrun-0.3.10.dev5.dist-info}/entry_points.txt +0 -0
- {droidrun-0.3.10.dev3.dist-info → droidrun-0.3.10.dev5.dist-info}/licenses/LICENSE +0 -0
@@ -2,10 +2,14 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import os
|
4
4
|
import threading
|
5
|
-
import
|
5
|
+
from dataclasses import asdict, dataclass, field
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Any, Callable,
|
8
|
-
|
7
|
+
from typing import Any, Callable, Dict, List, Optional
|
8
|
+
|
9
|
+
import yaml
|
10
|
+
|
11
|
+
from droidrun.config_manager.path_resolver import PathResolver
|
12
|
+
|
9
13
|
|
10
14
|
# ---------- Helpers / defaults ----------
|
11
15
|
def _default_config_text() -> str:
|
@@ -17,13 +21,44 @@ def _default_config_text() -> str:
|
|
17
21
|
agent:
|
18
22
|
# Maximum number of steps per task
|
19
23
|
max_steps: 15
|
20
|
-
# Enable vision capabilities per agent (screenshots)
|
21
|
-
vision:
|
22
|
-
manager: false
|
23
|
-
executor: false
|
24
|
-
codeact: false
|
25
24
|
# Enable planning with reasoning mode
|
26
25
|
reasoning: false
|
26
|
+
# Sleep duration after each action, waits for ui state to be updated (seconds)
|
27
|
+
after_sleep_action: 1.0
|
28
|
+
# Wait duration for UI to stabilize (seconds)
|
29
|
+
wait_for_stable_ui: 0.3
|
30
|
+
# Base directory for prompt templates
|
31
|
+
prompts_dir: config/prompts
|
32
|
+
|
33
|
+
# CodeAct Agent Configuration
|
34
|
+
codeact:
|
35
|
+
# Enable vision capabilities (screenshots)
|
36
|
+
vision: false
|
37
|
+
# System prompt filename (located in prompts_dir/codeact/)
|
38
|
+
system_prompt: system.md
|
39
|
+
# User prompt filename (located in prompts_dir/codeact/)
|
40
|
+
user_prompt: user.md
|
41
|
+
|
42
|
+
# Manager Agent Configuration
|
43
|
+
manager:
|
44
|
+
# Enable vision capabilities (screenshots)
|
45
|
+
vision: false
|
46
|
+
# System prompt filename (located in prompts_dir/manager/)
|
47
|
+
system_prompt: system.md
|
48
|
+
|
49
|
+
# Executor Agent Configuration
|
50
|
+
executor:
|
51
|
+
# Enable vision capabilities (screenshots)
|
52
|
+
vision: false
|
53
|
+
# System prompt filename (located in prompts_dir/executor/)
|
54
|
+
system_prompt: system.md
|
55
|
+
|
56
|
+
# App Cards Configuration
|
57
|
+
app_cards:
|
58
|
+
# Enable app-specific instruction cards
|
59
|
+
enabled: true
|
60
|
+
# Directory containing app card files
|
61
|
+
app_cards_dir: config/app_cards
|
27
62
|
|
28
63
|
# === LLM Profiles ===
|
29
64
|
# Define LLM configurations for each agent type
|
@@ -35,7 +70,7 @@ llm_profiles:
|
|
35
70
|
temperature: 0.2
|
36
71
|
kwargs:
|
37
72
|
max_tokens: 8192
|
38
|
-
|
73
|
+
|
39
74
|
# Executor: Selects and executes atomic actions
|
40
75
|
executor:
|
41
76
|
provider: GoogleGenAI
|
@@ -43,7 +78,7 @@ llm_profiles:
|
|
43
78
|
temperature: 0.1
|
44
79
|
kwargs:
|
45
80
|
max_tokens: 4096
|
46
|
-
|
81
|
+
|
47
82
|
# CodeAct: Generates and executes code actions
|
48
83
|
codeact:
|
49
84
|
provider: GoogleGenAI
|
@@ -51,7 +86,7 @@ llm_profiles:
|
|
51
86
|
temperature: 0.2
|
52
87
|
kwargs:
|
53
88
|
max_tokens: 8192
|
54
|
-
|
89
|
+
|
55
90
|
# Text Manipulator: Edits text in input fields
|
56
91
|
text_manipulator:
|
57
92
|
provider: GoogleGenAI
|
@@ -59,7 +94,7 @@ llm_profiles:
|
|
59
94
|
temperature: 0.3
|
60
95
|
kwargs:
|
61
96
|
max_tokens: 4096
|
62
|
-
|
97
|
+
|
63
98
|
# App Opener: Opens apps by name/description
|
64
99
|
app_opener:
|
65
100
|
provider: OpenAI
|
@@ -77,13 +112,11 @@ device:
|
|
77
112
|
serial: null
|
78
113
|
# Use TCP communication instead of content provider
|
79
114
|
use_tcp: false
|
80
|
-
# Sleep duration after each action (seconds)
|
81
|
-
after_sleep_action: 1.0
|
82
115
|
|
83
116
|
# === Telemetry Settings ===
|
84
117
|
telemetry:
|
85
118
|
# Enable anonymous telemetry
|
86
|
-
enabled:
|
119
|
+
enabled: false
|
87
120
|
|
88
121
|
# === Tracing Settings ===
|
89
122
|
tracing:
|
@@ -96,18 +129,14 @@ logging:
|
|
96
129
|
debug: false
|
97
130
|
# Trajectory saving level (none, step, action)
|
98
131
|
save_trajectory: none
|
99
|
-
|
132
|
+
rich_text: false
|
100
133
|
# === Tool Settings ===
|
101
134
|
tools:
|
102
135
|
# Enable drag tool
|
103
136
|
allow_drag: false
|
104
137
|
"""
|
105
138
|
|
106
|
-
|
107
|
-
"""
|
108
|
-
Use module-relative resolution: two parents above this file -> project root.
|
109
|
-
"""
|
110
|
-
return Path(__file__).resolve().parents[2] / "config.yaml"
|
139
|
+
# Removed: _default_project_config_path() - now using PathResolver
|
111
140
|
|
112
141
|
|
113
142
|
# ---------- Config Schema ----------
|
@@ -120,7 +149,7 @@ class LLMProfile:
|
|
120
149
|
base_url: Optional[str] = None
|
121
150
|
api_base: Optional[str] = None
|
122
151
|
kwargs: Dict[str, Any] = field(default_factory=dict)
|
123
|
-
|
152
|
+
|
124
153
|
def to_load_llm_kwargs(self) -> Dict[str, Any]:
|
125
154
|
"""Convert profile to kwargs for load_llm function."""
|
126
155
|
result = {
|
@@ -138,36 +167,63 @@ class LLMProfile:
|
|
138
167
|
|
139
168
|
|
140
169
|
@dataclass
|
141
|
-
class
|
142
|
-
"""
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
170
|
+
class CodeActConfig:
|
171
|
+
"""CodeAct agent configuration."""
|
172
|
+
vision: bool = False
|
173
|
+
system_prompt: str = "system.md"
|
174
|
+
user_prompt: str = "user.md"
|
175
|
+
|
176
|
+
|
177
|
+
@dataclass
|
178
|
+
class ManagerConfig:
|
179
|
+
"""Manager agent configuration."""
|
180
|
+
vision: bool = False
|
181
|
+
system_prompt: str = "system.md"
|
182
|
+
|
183
|
+
|
184
|
+
@dataclass
|
185
|
+
class ExecutorConfig:
|
186
|
+
"""Executor agent configuration."""
|
187
|
+
vision: bool = False
|
188
|
+
system_prompt: str = "system.md"
|
189
|
+
|
190
|
+
|
191
|
+
@dataclass
|
192
|
+
class AppCardConfig:
|
193
|
+
"""App card configuration."""
|
194
|
+
enabled: bool = True
|
195
|
+
app_cards_dir: str = "config/app_cards"
|
161
196
|
|
162
197
|
|
163
198
|
@dataclass
|
164
199
|
class AgentConfig:
|
165
200
|
"""Agent-related configuration."""
|
166
201
|
max_steps: int = 15
|
167
|
-
vision: VisionConfig = field(default_factory=VisionConfig)
|
168
202
|
reasoning: bool = False
|
169
203
|
after_sleep_action: float = 1.0
|
170
204
|
wait_for_stable_ui: float = 0.3
|
205
|
+
prompts_dir: str = "config/prompts"
|
206
|
+
|
207
|
+
codeact: CodeActConfig = field(default_factory=CodeActConfig)
|
208
|
+
manager: ManagerConfig = field(default_factory=ManagerConfig)
|
209
|
+
executor: ExecutorConfig = field(default_factory=ExecutorConfig)
|
210
|
+
app_cards: AppCardConfig = field(default_factory=AppCardConfig)
|
211
|
+
|
212
|
+
def get_codeact_system_prompt_path(self) -> str:
|
213
|
+
"""Get full path to CodeAct system prompt."""
|
214
|
+
return f"{self.prompts_dir}/codeact/{self.codeact.system_prompt}"
|
215
|
+
|
216
|
+
def get_codeact_user_prompt_path(self) -> str:
|
217
|
+
"""Get full path to CodeAct user prompt."""
|
218
|
+
return f"{self.prompts_dir}/codeact/{self.codeact.user_prompt}"
|
219
|
+
|
220
|
+
def get_manager_system_prompt_path(self) -> str:
|
221
|
+
"""Get full path to Manager system prompt."""
|
222
|
+
return f"{self.prompts_dir}/manager/{self.manager.system_prompt}"
|
223
|
+
|
224
|
+
def get_executor_system_prompt_path(self) -> str:
|
225
|
+
"""Get full path to Executor system prompt."""
|
226
|
+
return f"{self.prompts_dir}/executor/{self.executor.system_prompt}"
|
171
227
|
|
172
228
|
|
173
229
|
@dataclass
|
@@ -180,7 +236,7 @@ class DeviceConfig:
|
|
180
236
|
@dataclass
|
181
237
|
class TelemetryConfig:
|
182
238
|
"""Telemetry configuration."""
|
183
|
-
enabled: bool =
|
239
|
+
enabled: bool = False
|
184
240
|
|
185
241
|
|
186
242
|
@dataclass
|
@@ -194,7 +250,7 @@ class LoggingConfig:
|
|
194
250
|
"""Logging configuration."""
|
195
251
|
debug: bool = False
|
196
252
|
save_trajectory: str = "none"
|
197
|
-
|
253
|
+
rich_text: bool = False
|
198
254
|
|
199
255
|
@dataclass
|
200
256
|
class ToolsConfig:
|
@@ -217,7 +273,7 @@ class DroidRunConfig:
|
|
217
273
|
"""Ensure default profiles exist."""
|
218
274
|
if not self.llm_profiles:
|
219
275
|
self.llm_profiles = self._default_profiles()
|
220
|
-
|
276
|
+
|
221
277
|
@staticmethod
|
222
278
|
def _default_profiles() -> Dict[str, LLMProfile]:
|
223
279
|
"""Get default agent specific LLM profiles."""
|
@@ -247,7 +303,7 @@ class DroidRunConfig:
|
|
247
303
|
kwargs={}
|
248
304
|
),
|
249
305
|
"app_opener": LLMProfile(
|
250
|
-
provider="
|
306
|
+
provider="GoogleGenAI",
|
251
307
|
model="models/gemini-2.5-pro",
|
252
308
|
temperature=0.0,
|
253
309
|
kwargs={}
|
@@ -261,11 +317,6 @@ class DroidRunConfig:
|
|
261
317
|
result["llm_profiles"] = {
|
262
318
|
name: asdict(profile) for name, profile in self.llm_profiles.items()
|
263
319
|
}
|
264
|
-
# Convert VisionConfig to dict
|
265
|
-
if isinstance(result["agent"]["vision"], dict):
|
266
|
-
pass # Already a dict from asdict
|
267
|
-
else:
|
268
|
-
result["agent"]["vision"] = self.agent.vision.to_dict()
|
269
320
|
return result
|
270
321
|
|
271
322
|
@classmethod
|
@@ -275,18 +326,34 @@ class DroidRunConfig:
|
|
275
326
|
llm_profiles = {}
|
276
327
|
for name, profile_data in data.get("llm_profiles", {}).items():
|
277
328
|
llm_profiles[name] = LLMProfile(**profile_data)
|
278
|
-
|
279
|
-
# Parse agent config with
|
329
|
+
|
330
|
+
# Parse agent config with sub-configs
|
280
331
|
agent_data = data.get("agent", {})
|
281
|
-
|
282
|
-
|
283
|
-
|
332
|
+
|
333
|
+
codeact_data = agent_data.get("codeact", {})
|
334
|
+
codeact_config = CodeActConfig(**codeact_data) if codeact_data else CodeActConfig()
|
335
|
+
|
336
|
+
manager_data = agent_data.get("manager", {})
|
337
|
+
manager_config = ManagerConfig(**manager_data) if manager_data else ManagerConfig()
|
338
|
+
|
339
|
+
executor_data = agent_data.get("executor", {})
|
340
|
+
executor_config = ExecutorConfig(**executor_data) if executor_data else ExecutorConfig()
|
341
|
+
|
342
|
+
app_cards_data = agent_data.get("app_cards", {})
|
343
|
+
app_cards_config = AppCardConfig(**app_cards_data) if app_cards_data else AppCardConfig()
|
344
|
+
|
284
345
|
agent_config = AgentConfig(
|
285
346
|
max_steps=agent_data.get("max_steps", 15),
|
286
|
-
vision=vision_config,
|
287
347
|
reasoning=agent_data.get("reasoning", False),
|
348
|
+
after_sleep_action=agent_data.get("after_sleep_action", 1.0),
|
349
|
+
wait_for_stable_ui=agent_data.get("wait_for_stable_ui", 0.3),
|
350
|
+
prompts_dir=agent_data.get("prompts_dir", "config/prompts"),
|
351
|
+
codeact=codeact_config,
|
352
|
+
manager=manager_config,
|
353
|
+
executor=executor_config,
|
354
|
+
app_cards=app_cards_config,
|
288
355
|
)
|
289
|
-
|
356
|
+
|
290
357
|
return cls(
|
291
358
|
agent=agent_config,
|
292
359
|
llm_profiles=llm_profiles,
|
@@ -304,17 +371,20 @@ class ConfigManager:
|
|
304
371
|
Thread-safe singleton ConfigManager with typed configuration schema.
|
305
372
|
|
306
373
|
Usage:
|
307
|
-
from droidrun.config_manager import
|
308
|
-
|
374
|
+
from droidrun.config_manager.config_manager import ConfigManager
|
375
|
+
|
376
|
+
# Create config instance (singleton pattern)
|
377
|
+
config = ConfigManager()
|
378
|
+
|
309
379
|
# Access typed config objects
|
310
380
|
print(config.agent.max_steps)
|
311
|
-
|
312
|
-
# Load all
|
381
|
+
|
382
|
+
# Load all LLMs
|
313
383
|
llms = config.load_all_llms()
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
384
|
+
manager_llm = llms['manager']
|
385
|
+
executor_llm = llms['executor']
|
386
|
+
codeact_llm = llms['codeact']
|
387
|
+
|
318
388
|
# Modify and save
|
319
389
|
config.save()
|
320
390
|
"""
|
@@ -335,18 +405,19 @@ class ConfigManager:
|
|
335
405
|
|
336
406
|
self._lock = threading.RLock()
|
337
407
|
|
338
|
-
#
|
339
|
-
# 1)
|
408
|
+
# Resolution order:
|
409
|
+
# 1) Explicit path arg
|
340
410
|
# 2) DROIDRUN_CONFIG env var
|
341
|
-
# 3)
|
411
|
+
# 3) Default "config.yaml" (checks working dir, then project dir)
|
342
412
|
if path:
|
343
|
-
self.path =
|
413
|
+
self.path = PathResolver.resolve(path)
|
344
414
|
else:
|
345
415
|
env = os.environ.get("DROIDRUN_CONFIG")
|
346
416
|
if env:
|
347
|
-
self.path =
|
417
|
+
self.path = PathResolver.resolve(env)
|
348
418
|
else:
|
349
|
-
|
419
|
+
# Default: checks CWD first, then project dir
|
420
|
+
self.path = PathResolver.resolve("config.yaml")
|
350
421
|
|
351
422
|
# Initialize with default config
|
352
423
|
self._config = DroidRunConfig()
|
@@ -393,24 +464,24 @@ class ConfigManager:
|
|
393
464
|
"""Access tools configuration."""
|
394
465
|
with self._lock:
|
395
466
|
return self._config.tools
|
396
|
-
|
467
|
+
|
397
468
|
@property
|
398
469
|
def llm_profiles(self) -> Dict[str, LLMProfile]:
|
399
470
|
"""Access LLM profiles."""
|
400
471
|
with self._lock:
|
401
472
|
return self._config.llm_profiles
|
402
|
-
|
473
|
+
|
403
474
|
# ---------------- LLM Profile Helpers ----------------
|
404
475
|
def get_llm_profile(self, profile_name: str) -> LLMProfile:
|
405
476
|
"""
|
406
477
|
Get an LLM profile by name.
|
407
|
-
|
478
|
+
|
408
479
|
Args:
|
409
480
|
profile_name: Name of the profile (fast, mid, smart, custom, etc.)
|
410
|
-
|
481
|
+
|
411
482
|
Returns:
|
412
483
|
LLMProfile object
|
413
|
-
|
484
|
+
|
414
485
|
Raises:
|
415
486
|
KeyError: If profile_name doesn't exist
|
416
487
|
"""
|
@@ -420,62 +491,62 @@ class ConfigManager:
|
|
420
491
|
f"LLM profile '{profile_name}' not found. "
|
421
492
|
f"Available profiles: {list(self._config.llm_profiles.keys())}"
|
422
493
|
)
|
423
|
-
|
494
|
+
|
424
495
|
return self._config.llm_profiles[profile_name]
|
425
|
-
|
496
|
+
|
426
497
|
def load_llm_from_profile(self, profile_name: str, **override_kwargs):
|
427
498
|
"""
|
428
499
|
Load an LLM using a profile configuration.
|
429
|
-
|
500
|
+
|
430
501
|
Args:
|
431
502
|
profile_name: Name of the profile to use (fast, mid, smart, custom)
|
432
503
|
**override_kwargs: Additional kwargs to override profile settings
|
433
|
-
|
504
|
+
|
434
505
|
Returns:
|
435
506
|
Initialized LLM instance
|
436
|
-
|
507
|
+
|
437
508
|
Example:
|
438
509
|
# Use specific profile
|
439
510
|
llm = config.load_llm_from_profile("smart")
|
440
|
-
|
511
|
+
|
441
512
|
# Override specific settings
|
442
513
|
llm = config.load_llm_from_profile("fast", temperature=0.5)
|
443
514
|
"""
|
444
515
|
from droidrun.agent.utils.llm_picker import load_llm
|
445
|
-
|
516
|
+
|
446
517
|
profile = self.get_llm_profile(profile_name)
|
447
|
-
|
518
|
+
|
448
519
|
# Get kwargs from profile
|
449
520
|
kwargs = profile.to_load_llm_kwargs()
|
450
|
-
|
521
|
+
|
451
522
|
# Override with any provided kwargs
|
452
523
|
kwargs.update(override_kwargs)
|
453
|
-
|
524
|
+
|
454
525
|
# Load the LLM
|
455
526
|
return load_llm(provider_name=profile.provider, **kwargs)
|
456
|
-
|
527
|
+
|
457
528
|
def load_all_llms(self, profile_names: Optional[list[str]] = None, **override_kwargs_per_profile):
|
458
529
|
"""
|
459
530
|
Load multiple LLMs from profiles for different use cases.
|
460
|
-
|
531
|
+
|
461
532
|
Args:
|
462
533
|
profile_names: List of profile names to load. If None, loads agent-specific profiles
|
463
534
|
**override_kwargs_per_profile: Dict of profile-specific overrides
|
464
535
|
Example: manager={'temperature': 0.1}, executor={'max_tokens': 8000}
|
465
|
-
|
536
|
+
|
466
537
|
Returns:
|
467
538
|
Dict mapping profile names to initialized LLM instances
|
468
|
-
|
539
|
+
|
469
540
|
Example:
|
470
541
|
# Load all agent-specific profiles
|
471
542
|
llms = config.load_all_llms()
|
472
543
|
manager_llm = llms['manager']
|
473
544
|
executor_llm = llms['executor']
|
474
545
|
codeact_llm = llms['codeact']
|
475
|
-
|
546
|
+
|
476
547
|
# Load specific profiles
|
477
548
|
llms = config.load_all_llms(['manager', 'executor'])
|
478
|
-
|
549
|
+
|
479
550
|
# Load with overrides
|
480
551
|
llms = config.load_all_llms(
|
481
552
|
manager={'temperature': 0.1},
|
@@ -483,24 +554,24 @@ class ConfigManager:
|
|
483
554
|
)
|
484
555
|
"""
|
485
556
|
from droidrun.agent.utils.llm_picker import load_llm
|
486
|
-
|
557
|
+
|
487
558
|
if profile_names is None:
|
488
559
|
profile_names = ["manager", "executor", "codeact", "text_manipulator", "app_opener"]
|
489
|
-
|
560
|
+
|
490
561
|
llms = {}
|
491
562
|
for profile_name in profile_names:
|
492
563
|
profile = self.get_llm_profile(profile_name)
|
493
|
-
|
564
|
+
|
494
565
|
# Get kwargs from profile
|
495
566
|
kwargs = profile.to_load_llm_kwargs()
|
496
|
-
|
567
|
+
|
497
568
|
# Apply profile-specific overrides if provided
|
498
569
|
if profile_name in override_kwargs_per_profile:
|
499
570
|
kwargs.update(override_kwargs_per_profile[profile_name])
|
500
|
-
|
571
|
+
|
501
572
|
# Load the LLM
|
502
573
|
llms[profile_name] = load_llm(provider_name=profile.provider, **kwargs)
|
503
|
-
|
574
|
+
|
504
575
|
return llms
|
505
576
|
|
506
577
|
# ---------------- I/O ----------------
|
@@ -569,6 +640,35 @@ class ConfigManager:
|
|
569
640
|
import copy
|
570
641
|
return copy.deepcopy(self._config.to_dict())
|
571
642
|
|
643
|
+
# Implemented for for config webiu so we can have dropdown prompt selection. but canceled webui plan.
|
644
|
+
def list_available_prompts(self, agent_type: str) -> List[str]:
|
645
|
+
"""
|
646
|
+
List all available prompt files for a given agent type.
|
647
|
+
|
648
|
+
Args:
|
649
|
+
agent_type: One of "codeact", "manager", "executor"
|
650
|
+
|
651
|
+
Returns:
|
652
|
+
List of prompt filenames available in the agent's prompts directory
|
653
|
+
|
654
|
+
Example:
|
655
|
+
>>> config.list_available_prompts("manager")
|
656
|
+
['system.md', 'experimental.md', 'minimal.md']
|
657
|
+
"""
|
658
|
+
agent_type = agent_type.lower()
|
659
|
+
if agent_type not in ["codeact", "manager", "executor"]:
|
660
|
+
raise ValueError(f"Invalid agent_type: {agent_type}. Must be one of: codeact, manager, executor")
|
661
|
+
|
662
|
+
# Resolve prompts directory
|
663
|
+
prompts_path = f"{self.agent.prompts_dir}/{agent_type}"
|
664
|
+
prompts_dir = PathResolver.resolve(prompts_path)
|
665
|
+
|
666
|
+
if not prompts_dir.exists():
|
667
|
+
return []
|
668
|
+
|
669
|
+
# List all .md files in the directory
|
670
|
+
return sorted([f.name for f in prompts_dir.glob("*.md")])
|
671
|
+
|
572
672
|
# useful for tests to reset singleton state
|
573
673
|
@classmethod
|
574
674
|
def _reset_instance_for_testing(cls) -> None:
|
@@ -579,5 +679,3 @@ class ConfigManager:
|
|
579
679
|
return f"<ConfigManager path={self.path!s}>"
|
580
680
|
|
581
681
|
|
582
|
-
# ---------- global singleton ----------
|
583
|
-
config = ConfigManager()
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""
|
2
|
+
Unified path resolution for DroidRun.
|
3
|
+
|
4
|
+
This module provides a single path resolver that handles all file path resolution
|
5
|
+
with consistent priority: working directory first, then project directory.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from pathlib import Path
|
9
|
+
from typing import Union
|
10
|
+
|
11
|
+
|
12
|
+
class PathResolver:
|
13
|
+
"""
|
14
|
+
Unified path resolver for all DroidRun file operations.
|
15
|
+
|
16
|
+
Resolution order:
|
17
|
+
1. Absolute paths → use as-is
|
18
|
+
2. Relative paths → check working dir first, then project dir
|
19
|
+
3. For creation → prefer working dir
|
20
|
+
"""
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def get_project_root() -> Path:
|
24
|
+
"""
|
25
|
+
Get the project root directory (where config.yaml lives).
|
26
|
+
|
27
|
+
This is 2 parents up from this file's location:
|
28
|
+
droidrun/config_manager/path_resolver.py -> droidrun/ (project root)
|
29
|
+
"""
|
30
|
+
return Path(__file__).resolve().parents[2]
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def resolve(
|
34
|
+
path: Union[str, Path],
|
35
|
+
create_if_missing: bool = False,
|
36
|
+
must_exist: bool = False
|
37
|
+
) -> Path:
|
38
|
+
"""
|
39
|
+
Universal path resolver for all file operations.
|
40
|
+
|
41
|
+
Resolution order:
|
42
|
+
1. Absolute path → use as-is
|
43
|
+
2. Relative path:
|
44
|
+
- If creating: prefer working directory
|
45
|
+
- If reading: check working dir first, then project dir
|
46
|
+
3. If must_exist and not found → raise FileNotFoundError
|
47
|
+
|
48
|
+
Args:
|
49
|
+
path: Path to resolve (str or Path object)
|
50
|
+
create_if_missing: If True, prefer working dir for relative paths (output mode)
|
51
|
+
must_exist: If True, raise FileNotFoundError if path doesn't exist
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
Resolved Path object
|
55
|
+
|
56
|
+
Raises:
|
57
|
+
FileNotFoundError: If must_exist=True and path not found in any location
|
58
|
+
|
59
|
+
Examples:
|
60
|
+
# Reading config (checks CWD first, then project dir)
|
61
|
+
config_path = PathResolver.resolve("config.yaml")
|
62
|
+
|
63
|
+
# Creating output (creates in CWD)
|
64
|
+
output_dir = PathResolver.resolve("trajectories", create_if_missing=True)
|
65
|
+
|
66
|
+
# Loading prompts (must exist, checks both locations)
|
67
|
+
prompt = PathResolver.resolve("config/prompts/system.md", must_exist=True)
|
68
|
+
|
69
|
+
# Absolute path (used as-is)
|
70
|
+
abs_path = PathResolver.resolve("/tmp/output")
|
71
|
+
"""
|
72
|
+
# Convert to Path and expand user home directory (~/)
|
73
|
+
path = Path(path).expanduser()
|
74
|
+
|
75
|
+
# Absolute paths: use as-is
|
76
|
+
if path.is_absolute():
|
77
|
+
if must_exist and not path.exists():
|
78
|
+
raise FileNotFoundError(f"Path not found: {path}")
|
79
|
+
return path
|
80
|
+
|
81
|
+
# Relative paths: check working dir and project dir
|
82
|
+
cwd_path = Path.cwd() / path
|
83
|
+
project_path = PathResolver.get_project_root() / path
|
84
|
+
|
85
|
+
# For creation, always prefer working directory (user's context)
|
86
|
+
if create_if_missing:
|
87
|
+
return cwd_path
|
88
|
+
|
89
|
+
# For reading, check both locations (working dir first)
|
90
|
+
if cwd_path.exists():
|
91
|
+
return cwd_path
|
92
|
+
if project_path.exists():
|
93
|
+
return project_path
|
94
|
+
|
95
|
+
# Not found in either location
|
96
|
+
if must_exist:
|
97
|
+
raise FileNotFoundError(
|
98
|
+
f"Path not found in:\n"
|
99
|
+
f" - Working dir: {cwd_path}\n"
|
100
|
+
f" - Project dir: {project_path}"
|
101
|
+
)
|
102
|
+
|
103
|
+
# Default to working dir (user's context)
|
104
|
+
return cwd_path
|