lybic-guiagents 0.1.0__py3-none-any.whl → 0.2.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 lybic-guiagents might be problematic. Click here for more details.
- gui_agents/__init__.py +63 -0
- gui_agents/agents/Action.py +3 -3
- gui_agents/agents/Backend/ADBBackend.py +62 -0
- gui_agents/agents/Backend/Backend.py +28 -0
- gui_agents/agents/Backend/LybicBackend.py +354 -0
- gui_agents/agents/Backend/PyAutoGUIBackend.py +183 -0
- gui_agents/agents/Backend/PyAutoGUIVMwareBackend.py +250 -0
- gui_agents/agents/Backend/__init__.py +0 -0
- gui_agents/agents/agent_s.py +0 -2
- gui_agents/agents/grounding.py +1 -6
- gui_agents/agents/hardware_interface.py +24 -7
- gui_agents/agents/manager.py +0 -3
- gui_agents/agents/translator.py +1 -1
- gui_agents/agents/worker.py +1 -2
- gui_agents/cli_app.py +143 -8
- gui_agents/core/engine.py +0 -2
- gui_agents/core/knowledge.py +0 -2
- gui_agents/lybic_client/__init__.py +0 -0
- gui_agents/lybic_client/lybic_client.py +88 -0
- gui_agents/prompts/__init__.py +0 -0
- gui_agents/prompts/prompts.py +869 -0
- gui_agents/service/__init__.py +19 -0
- gui_agents/service/agent_service.py +527 -0
- gui_agents/service/api_models.py +136 -0
- gui_agents/service/config.py +241 -0
- gui_agents/service/exceptions.py +35 -0
- gui_agents/store/__init__.py +0 -0
- gui_agents/store/registry.py +22 -0
- gui_agents/tools/tools.py +0 -4
- gui_agents/unit_test/test_manager.py +0 -2
- gui_agents/unit_test/test_worker.py +0 -2
- gui_agents/utils/analyze_display.py +1 -1
- gui_agents/utils/common_utils.py +0 -2
- {lybic_guiagents-0.1.0.dist-info → lybic_guiagents-0.2.1.dist-info}/METADATA +203 -75
- {lybic_guiagents-0.1.0.dist-info → lybic_guiagents-0.2.1.dist-info}/RECORD +38 -21
- {lybic_guiagents-0.1.0.dist-info → lybic_guiagents-0.2.1.dist-info}/WHEEL +0 -0
- {lybic_guiagents-0.1.0.dist-info → lybic_guiagents-0.2.1.dist-info}/licenses/LICENSE +0 -0
- {lybic_guiagents-0.1.0.dist-info → lybic_guiagents-0.2.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Configuration management for Agent Service"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import platform
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Dict, Any, Optional, Union
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from .exceptions import ConfigurationError, APIKeyError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class LLMConfig:
|
|
14
|
+
"""LLM provider configuration"""
|
|
15
|
+
provider: str # openai, anthropic, zhipu, etc.
|
|
16
|
+
model: str
|
|
17
|
+
api_key: Optional[str] = None
|
|
18
|
+
base_url: Optional[str] = None
|
|
19
|
+
api_version: Optional[str] = None
|
|
20
|
+
rate_limit: int = -1
|
|
21
|
+
extra_params: Dict[str, Any] = field(default_factory=dict)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ServiceConfig:
|
|
26
|
+
"""Comprehensive service configuration with multi-level API key support"""
|
|
27
|
+
|
|
28
|
+
# Agent settings
|
|
29
|
+
default_backend: str = 'lybic'
|
|
30
|
+
default_mode: str = 'normal'
|
|
31
|
+
default_max_steps: int = 50
|
|
32
|
+
default_platform: str = field(default_factory=lambda: platform.system().lower())
|
|
33
|
+
|
|
34
|
+
# Service settings
|
|
35
|
+
max_concurrent_tasks: int = 5
|
|
36
|
+
task_timeout: int = 3600 # 1 hour
|
|
37
|
+
enable_takeover: bool = False
|
|
38
|
+
enable_search: bool = True
|
|
39
|
+
|
|
40
|
+
# Logging settings
|
|
41
|
+
log_level: str = 'INFO'
|
|
42
|
+
log_dir: str = 'runtime'
|
|
43
|
+
|
|
44
|
+
# LLM configuration
|
|
45
|
+
llm_config: Optional[LLMConfig] = None
|
|
46
|
+
|
|
47
|
+
# API Keys - multiple providers support
|
|
48
|
+
api_keys: Dict[str, str] = field(default_factory=dict)
|
|
49
|
+
|
|
50
|
+
# Backend-specific configurations
|
|
51
|
+
backend_configs: Dict[str, Dict[str, Any]] = field(default_factory=dict)
|
|
52
|
+
|
|
53
|
+
# Environment variable mappings
|
|
54
|
+
env_mapping: Dict[str, str] = field(default_factory=lambda: {
|
|
55
|
+
'AGENT_BACKEND': 'default_backend',
|
|
56
|
+
'AGENT_MODE': 'default_mode',
|
|
57
|
+
'AGENT_MAX_STEPS': 'default_max_steps',
|
|
58
|
+
'AGENT_LOG_LEVEL': 'log_level',
|
|
59
|
+
'AGENT_LOG_DIR': 'log_dir',
|
|
60
|
+
'AGENT_TASK_TIMEOUT': 'task_timeout',
|
|
61
|
+
'AGENT_ENABLE_TAKEOVER': 'enable_takeover',
|
|
62
|
+
'AGENT_ENABLE_SEARCH': 'enable_search',
|
|
63
|
+
# Lybic specific
|
|
64
|
+
'LYBIC_PRECREATE_SID': 'lybic_sid',
|
|
65
|
+
'LYBIC_API_KEY': 'lybic_api_key',
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
# Known API key environment variables
|
|
69
|
+
api_key_env_vars: Dict[str, str] = field(default_factory=lambda: {
|
|
70
|
+
'openai': 'OPENAI_API_KEY',
|
|
71
|
+
'anthropic': 'ANTHROPIC_API_KEY',
|
|
72
|
+
'zhipu': 'ZHIPU_API_KEY',
|
|
73
|
+
'gemini': 'GEMINI_API_KEY',
|
|
74
|
+
'groq': 'GROQ_API_KEY',
|
|
75
|
+
'deepseek': 'DEEPSEEK_API_KEY',
|
|
76
|
+
'dashscope': 'DASHSCOPE_API_KEY',
|
|
77
|
+
'azure_openai': 'AZURE_OPENAI_API_KEY',
|
|
78
|
+
'lybic': 'LYBIC_API_KEY',
|
|
79
|
+
'huggingface': 'HF_TOKEN',
|
|
80
|
+
'siliconflow': 'SILICONFLOW_API_KEY',
|
|
81
|
+
'monica': 'MONICA_API_KEY',
|
|
82
|
+
'openrouter': 'OPENROUTER_API_KEY',
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_env(cls, config_file: Optional[Union[str, Path]] = None) -> 'ServiceConfig':
|
|
87
|
+
"""Create configuration from environment variables and optional config file
|
|
88
|
+
|
|
89
|
+
Priority order (highest to lowest):
|
|
90
|
+
1. Environment variables
|
|
91
|
+
2. Config file
|
|
92
|
+
3. Default values
|
|
93
|
+
"""
|
|
94
|
+
config = cls()
|
|
95
|
+
|
|
96
|
+
# Load from config file first (lower priority)
|
|
97
|
+
if config_file:
|
|
98
|
+
config = config.load_from_file(config_file)
|
|
99
|
+
|
|
100
|
+
# Override with environment variables (higher priority)
|
|
101
|
+
config._load_from_env()
|
|
102
|
+
|
|
103
|
+
return config
|
|
104
|
+
|
|
105
|
+
def _load_from_env(self):
|
|
106
|
+
"""Load configuration from environment variables"""
|
|
107
|
+
# Load general settings
|
|
108
|
+
for env_key, attr_name in self.env_mapping.items():
|
|
109
|
+
if env_key in os.environ:
|
|
110
|
+
value = os.environ[env_key]
|
|
111
|
+
# Type conversion based on attribute type
|
|
112
|
+
if hasattr(self, attr_name):
|
|
113
|
+
current_value = getattr(self, attr_name)
|
|
114
|
+
if isinstance(current_value, bool):
|
|
115
|
+
value = value.lower() in ('true', '1', 'yes', 'on')
|
|
116
|
+
elif isinstance(current_value, int):
|
|
117
|
+
try:
|
|
118
|
+
value = int(value)
|
|
119
|
+
except ValueError:
|
|
120
|
+
continue
|
|
121
|
+
setattr(self, attr_name, value)
|
|
122
|
+
|
|
123
|
+
# Load API keys from environment
|
|
124
|
+
for provider, env_var in self.api_key_env_vars.items():
|
|
125
|
+
if env_var in os.environ:
|
|
126
|
+
self.api_keys[provider] = os.environ[env_var]
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def load_from_file(cls, config_file: Union[str, Path]) -> 'ServiceConfig':
|
|
130
|
+
"""Load configuration from JSON file"""
|
|
131
|
+
config_path = Path(config_file)
|
|
132
|
+
|
|
133
|
+
if not config_path.exists():
|
|
134
|
+
raise ConfigurationError(f"Configuration file not found: {config_path}")
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
138
|
+
data = json.load(f)
|
|
139
|
+
|
|
140
|
+
# Create config instance
|
|
141
|
+
config = cls()
|
|
142
|
+
|
|
143
|
+
# Update with file data
|
|
144
|
+
for key, value in data.items():
|
|
145
|
+
if hasattr(config, key):
|
|
146
|
+
setattr(config, key, value)
|
|
147
|
+
|
|
148
|
+
return config
|
|
149
|
+
|
|
150
|
+
except json.JSONDecodeError as e:
|
|
151
|
+
raise ConfigurationError("Invalid JSON in config file") from e
|
|
152
|
+
except OSError as e:
|
|
153
|
+
raise ConfigurationError("Error loading config file") from e
|
|
154
|
+
|
|
155
|
+
def save_to_file(self, config_file: Union[str, Path]):
|
|
156
|
+
"""Save configuration to JSON file"""
|
|
157
|
+
config_path = Path(config_file)
|
|
158
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
159
|
+
|
|
160
|
+
# Convert to dict, excluding non-serializable fields
|
|
161
|
+
data = {}
|
|
162
|
+
for key, value in self.__dict__.items():
|
|
163
|
+
if not key.startswith('_') and key not in ['env_mapping', 'api_key_env_vars']:
|
|
164
|
+
data[key] = value
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
with open(config_path, 'w', encoding='utf-8') as f:
|
|
168
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
169
|
+
except Exception as e:
|
|
170
|
+
raise ConfigurationError(f"Error saving config file: {e}")
|
|
171
|
+
|
|
172
|
+
def get_api_key(self, provider: str, required: bool = True) -> Optional[str]:
|
|
173
|
+
"""Get API key for a provider with fallback chain
|
|
174
|
+
|
|
175
|
+
Priority:
|
|
176
|
+
1. Direct api_keys dict
|
|
177
|
+
2. Environment variable
|
|
178
|
+
3. None (if not required)
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
provider: Provider name (openai, anthropic, etc.)
|
|
182
|
+
required: Whether to raise error if not found
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
API key string or None
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
APIKeyError: If required but not found
|
|
189
|
+
"""
|
|
190
|
+
# Check direct api_keys dict first
|
|
191
|
+
if provider in self.api_keys:
|
|
192
|
+
return self.api_keys[provider]
|
|
193
|
+
|
|
194
|
+
# Check environment variable
|
|
195
|
+
if provider in self.api_key_env_vars:
|
|
196
|
+
env_var = self.api_key_env_vars[provider]
|
|
197
|
+
if env_var in os.environ:
|
|
198
|
+
api_key = os.environ[env_var]
|
|
199
|
+
# Cache it for future use
|
|
200
|
+
self.api_keys[provider] = api_key
|
|
201
|
+
return api_key
|
|
202
|
+
|
|
203
|
+
# Not found
|
|
204
|
+
if required:
|
|
205
|
+
env_var = self.api_key_env_vars.get(provider, f"{provider.upper()}_API_KEY")
|
|
206
|
+
raise APIKeyError(
|
|
207
|
+
f"API key for '{provider}' not found. "
|
|
208
|
+
f"Please provide it via api_keys parameter or {env_var} environment variable"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
def set_api_key(self, provider: str, api_key: str):
|
|
214
|
+
"""Set API key for a provider"""
|
|
215
|
+
self.api_keys[provider] = api_key
|
|
216
|
+
|
|
217
|
+
def get_backend_config(self, backend: str) -> Dict[str, Any]:
|
|
218
|
+
"""Get backend-specific configuration"""
|
|
219
|
+
return self.backend_configs.get(backend, {})
|
|
220
|
+
|
|
221
|
+
def set_backend_config(self, backend: str, config: Dict[str, Any]):
|
|
222
|
+
"""Set backend-specific configuration"""
|
|
223
|
+
self.backend_configs[backend] = config
|
|
224
|
+
|
|
225
|
+
def validate(self):
|
|
226
|
+
"""Validate configuration"""
|
|
227
|
+
if self.default_max_steps <= 0:
|
|
228
|
+
raise ConfigurationError("default_max_steps must be positive")
|
|
229
|
+
|
|
230
|
+
if self.task_timeout <= 0:
|
|
231
|
+
raise ConfigurationError("task_timeout must be positive")
|
|
232
|
+
|
|
233
|
+
if self.max_concurrent_tasks <= 0:
|
|
234
|
+
raise ConfigurationError("max_concurrent_tasks must be positive")
|
|
235
|
+
|
|
236
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
237
|
+
"""Convert configuration to dictionary"""
|
|
238
|
+
return {
|
|
239
|
+
key: value for key, value in self.__dict__.items()
|
|
240
|
+
if not key.startswith('_')
|
|
241
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Custom exceptions for the Agent Service"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AgentServiceError(Exception):
|
|
5
|
+
"""Base exception for Agent Service errors"""
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ConfigurationError(AgentServiceError):
|
|
10
|
+
"""Raised when there are configuration issues"""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TaskExecutionError(AgentServiceError):
|
|
15
|
+
"""Raised when task execution fails"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message: str, task_id: str | None = None, step: int | None = None):
|
|
18
|
+
super().__init__(message)
|
|
19
|
+
self.task_id = task_id
|
|
20
|
+
self.step = step
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TaskTimeoutError(TaskExecutionError):
|
|
24
|
+
"""Raised when task execution times out"""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BackendError(AgentServiceError):
|
|
29
|
+
"""Raised when backend operations fail"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class APIKeyError(ConfigurationError):
|
|
34
|
+
"""Raised when API key configuration is invalid"""
|
|
35
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# gui_agents/s2/store/registry.py
|
|
2
|
+
|
|
3
|
+
# Usage: in any file, get the object through Registry.get
|
|
4
|
+
# from gui_agents.store.registry import Registry
|
|
5
|
+
# GlobalStateStore = Registry.get("GlobalStateStore")
|
|
6
|
+
|
|
7
|
+
class Registry:
|
|
8
|
+
_services: dict[str, object] = {}
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def register(cls, name: str, obj: object):
|
|
12
|
+
cls._services[name] = obj
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def get(cls, name: str) -> object:
|
|
16
|
+
if name not in cls._services:
|
|
17
|
+
raise KeyError(f"{name!r} not registered in Registry")
|
|
18
|
+
return cls._services[name]
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def clear(cls):
|
|
22
|
+
cls._services.clear()
|
gui_agents/tools/tools.py
CHANGED
|
@@ -6,10 +6,6 @@ context fusion, subtask planning, trajectory reflection, memory retrieval, groun
|
|
|
6
6
|
evaluation, and action generation.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import os
|
|
10
|
-
import json
|
|
11
|
-
import base64
|
|
12
|
-
import requests
|
|
13
9
|
import time
|
|
14
10
|
from typing import Dict, Any, Optional, List, Union, Tuple
|
|
15
11
|
from abc import ABC, abstractmethod
|