lybic-guiagents 0.1.0__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.

Files changed (85) hide show
  1. desktop_env/__init__.py +1 -0
  2. desktop_env/actions.py +203 -0
  3. desktop_env/controllers/__init__.py +0 -0
  4. desktop_env/controllers/python.py +471 -0
  5. desktop_env/controllers/setup.py +882 -0
  6. desktop_env/desktop_env.py +509 -0
  7. desktop_env/evaluators/__init__.py +5 -0
  8. desktop_env/evaluators/getters/__init__.py +41 -0
  9. desktop_env/evaluators/getters/calc.py +15 -0
  10. desktop_env/evaluators/getters/chrome.py +1774 -0
  11. desktop_env/evaluators/getters/file.py +154 -0
  12. desktop_env/evaluators/getters/general.py +42 -0
  13. desktop_env/evaluators/getters/gimp.py +38 -0
  14. desktop_env/evaluators/getters/impress.py +126 -0
  15. desktop_env/evaluators/getters/info.py +24 -0
  16. desktop_env/evaluators/getters/misc.py +406 -0
  17. desktop_env/evaluators/getters/replay.py +20 -0
  18. desktop_env/evaluators/getters/vlc.py +86 -0
  19. desktop_env/evaluators/getters/vscode.py +35 -0
  20. desktop_env/evaluators/metrics/__init__.py +160 -0
  21. desktop_env/evaluators/metrics/basic_os.py +68 -0
  22. desktop_env/evaluators/metrics/chrome.py +493 -0
  23. desktop_env/evaluators/metrics/docs.py +1011 -0
  24. desktop_env/evaluators/metrics/general.py +665 -0
  25. desktop_env/evaluators/metrics/gimp.py +637 -0
  26. desktop_env/evaluators/metrics/libreoffice.py +28 -0
  27. desktop_env/evaluators/metrics/others.py +92 -0
  28. desktop_env/evaluators/metrics/pdf.py +31 -0
  29. desktop_env/evaluators/metrics/slides.py +957 -0
  30. desktop_env/evaluators/metrics/table.py +585 -0
  31. desktop_env/evaluators/metrics/thunderbird.py +176 -0
  32. desktop_env/evaluators/metrics/utils.py +719 -0
  33. desktop_env/evaluators/metrics/vlc.py +524 -0
  34. desktop_env/evaluators/metrics/vscode.py +283 -0
  35. desktop_env/providers/__init__.py +35 -0
  36. desktop_env/providers/aws/__init__.py +0 -0
  37. desktop_env/providers/aws/manager.py +278 -0
  38. desktop_env/providers/aws/provider.py +186 -0
  39. desktop_env/providers/aws/provider_with_proxy.py +315 -0
  40. desktop_env/providers/aws/proxy_pool.py +193 -0
  41. desktop_env/providers/azure/__init__.py +0 -0
  42. desktop_env/providers/azure/manager.py +87 -0
  43. desktop_env/providers/azure/provider.py +207 -0
  44. desktop_env/providers/base.py +97 -0
  45. desktop_env/providers/gcp/__init__.py +0 -0
  46. desktop_env/providers/gcp/manager.py +0 -0
  47. desktop_env/providers/gcp/provider.py +0 -0
  48. desktop_env/providers/virtualbox/__init__.py +0 -0
  49. desktop_env/providers/virtualbox/manager.py +463 -0
  50. desktop_env/providers/virtualbox/provider.py +124 -0
  51. desktop_env/providers/vmware/__init__.py +0 -0
  52. desktop_env/providers/vmware/manager.py +455 -0
  53. desktop_env/providers/vmware/provider.py +105 -0
  54. gui_agents/__init__.py +0 -0
  55. gui_agents/agents/Action.py +209 -0
  56. gui_agents/agents/__init__.py +0 -0
  57. gui_agents/agents/agent_s.py +832 -0
  58. gui_agents/agents/global_state.py +610 -0
  59. gui_agents/agents/grounding.py +651 -0
  60. gui_agents/agents/hardware_interface.py +129 -0
  61. gui_agents/agents/manager.py +568 -0
  62. gui_agents/agents/translator.py +132 -0
  63. gui_agents/agents/worker.py +355 -0
  64. gui_agents/cli_app.py +560 -0
  65. gui_agents/core/__init__.py +0 -0
  66. gui_agents/core/engine.py +1496 -0
  67. gui_agents/core/knowledge.py +449 -0
  68. gui_agents/core/mllm.py +555 -0
  69. gui_agents/tools/__init__.py +0 -0
  70. gui_agents/tools/tools.py +727 -0
  71. gui_agents/unit_test/__init__.py +0 -0
  72. gui_agents/unit_test/run_tests.py +65 -0
  73. gui_agents/unit_test/test_manager.py +330 -0
  74. gui_agents/unit_test/test_worker.py +269 -0
  75. gui_agents/utils/__init__.py +0 -0
  76. gui_agents/utils/analyze_display.py +301 -0
  77. gui_agents/utils/common_utils.py +263 -0
  78. gui_agents/utils/display_viewer.py +281 -0
  79. gui_agents/utils/embedding_manager.py +53 -0
  80. gui_agents/utils/image_axis_utils.py +27 -0
  81. lybic_guiagents-0.1.0.dist-info/METADATA +416 -0
  82. lybic_guiagents-0.1.0.dist-info/RECORD +85 -0
  83. lybic_guiagents-0.1.0.dist-info/WHEEL +5 -0
  84. lybic_guiagents-0.1.0.dist-info/licenses/LICENSE +201 -0
  85. lybic_guiagents-0.1.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,154 @@
1
+ import os
2
+ import logging
3
+ from typing import Dict, List, Set
4
+ from typing import Optional, Any, Union
5
+ from datetime import datetime
6
+ import requests
7
+ import pandas as pd
8
+
9
+ logger = logging.getLogger("desktopenv.getter.file")
10
+
11
+
12
+ def get_content_from_vm_file(env, config: Dict[str, Any]) -> Any:
13
+ """
14
+ Config:
15
+ path (str): absolute path on the VM to fetch
16
+ """
17
+
18
+ path = config["path"]
19
+ file_path = get_vm_file(env, {"path": path, "dest": os.path.basename(path)})
20
+ file_type, file_content = config['file_type'], config['file_content']
21
+ if file_type == 'xlsx':
22
+ if file_content == 'last_row':
23
+ df = pd.read_excel(file_path)
24
+ last_row = df.iloc[-1]
25
+ last_row_as_list = last_row.astype(str).tolist()
26
+ return last_row_as_list
27
+ else:
28
+ raise NotImplementedError(f"File type {file_type} not supported")
29
+
30
+
31
+ def get_cloud_file(env, config: Dict[str, Any]) -> Union[str, List[str]]:
32
+ """
33
+ Config:
34
+ path (str|List[str]): the url to download from
35
+ dest (str|List[str])): file name of the downloaded file
36
+ multi (bool) : optional. if path and dest are lists providing
37
+ information of multiple files. defaults to False
38
+ gives (List[int]): optional. defaults to [0]. which files are directly
39
+ returned to the metric. if len==1, str is returned; else, list is
40
+ returned.
41
+ """
42
+
43
+ if not config.get("multi", False):
44
+ paths: List[str] = [config["path"]]
45
+ dests: List[str] = [config["dest"]]
46
+ else:
47
+ paths: List[str] = config["path"]
48
+ dests: List[str] = config["dest"]
49
+ cache_paths: List[str] = []
50
+
51
+ gives: Set[int] = set(config.get("gives", [0]))
52
+
53
+ for i, (p, d) in enumerate(zip(paths, dests)):
54
+ _path = os.path.join(env.cache_dir, d)
55
+ if i in gives:
56
+ cache_paths.append(_path)
57
+
58
+ if os.path.exists(_path):
59
+ #return _path
60
+ continue
61
+
62
+ url = p
63
+ response = requests.get(url, stream=True)
64
+ response.raise_for_status()
65
+
66
+ with open(_path, 'wb') as f:
67
+ for chunk in response.iter_content(chunk_size=8192):
68
+ if chunk:
69
+ f.write(chunk)
70
+
71
+ return cache_paths[0] if len(cache_paths)==1 else cache_paths
72
+
73
+
74
+ def get_vm_file(env, config: Dict[str, Any]) -> Union[Optional[str], List[Optional[str]]]:
75
+ """
76
+ Config:
77
+ path (str): absolute path on the VM to fetch
78
+ dest (str): file name of the downloaded file
79
+ multi (bool) : optional. if path and dest are lists providing
80
+ information of multiple files. defaults to False
81
+ gives (List[int]): optional. defaults to [0]. which files are directly
82
+ returned to the metric. if len==1, str is returned; else, list is
83
+ returned.
84
+ only support for single file now:
85
+ time_suffix(bool): optional. defaults to False. if True, append the current time in required format.
86
+ time_format(str): optional. defaults to "%Y%m%d_%H%M%S". format of the time suffix.
87
+ """
88
+ time_format = "%Y%m%d_%H%M%S"
89
+ if not config.get("multi", False):
90
+ paths: List[str] = [config["path"]]
91
+ dests: List[str] = [config["dest"]]
92
+ if config.get("time_suffix", False):
93
+ time_format = config.get("time_format", time_format)
94
+ # Insert time before file extension.
95
+ dests = [f"{os.path.splitext(d)[0]}_{datetime.now().strftime(time_format)}{os.path.splitext(d)[1]}" for d in dests]
96
+ else:
97
+ paths: List[str] = config["path"]
98
+ dests: List[str] = config["dest"]
99
+
100
+
101
+ cache_paths: List[str] = []
102
+
103
+ gives: Set[int] = set(config.get("gives", [0]))
104
+
105
+ for i, (p, d) in enumerate(zip(paths, dests)):
106
+ _path = os.path.join(env.cache_dir, d)
107
+
108
+ try:
109
+ # Try to get file from VM
110
+ file = env.controller.get_file(p)
111
+ if file is None:
112
+ logger.warning(f"Failed to get file from VM: {p}")
113
+ if i in gives:
114
+ cache_paths.append(None)
115
+ continue
116
+
117
+ if i in gives:
118
+ cache_paths.append(_path)
119
+
120
+ # Write file with robust error handling
121
+ try:
122
+ # Ensure cache directory exists
123
+ os.makedirs(env.cache_dir, exist_ok=True)
124
+
125
+ with open(_path, "wb") as f:
126
+ f.write(file)
127
+ logger.info(f"Successfully saved file: {_path} ({len(file)} bytes)")
128
+
129
+ except IOError as e:
130
+ logger.error(f"IO error writing file {_path}: {e}")
131
+ if i in gives:
132
+ cache_paths[-1] = None # Replace the path we just added with None
133
+ except Exception as e:
134
+ logger.error(f"Unexpected error writing file {_path}: {e}")
135
+ if i in gives:
136
+ cache_paths[-1] = None
137
+
138
+ except Exception as e:
139
+ logger.error(f"Error processing file {p}: {e}")
140
+ if i in gives:
141
+ cache_paths.append(None)
142
+
143
+ return cache_paths[0] if len(cache_paths)==1 else cache_paths
144
+
145
+
146
+ def get_cache_file(env, config: Dict[str, str]) -> str:
147
+ """
148
+ Config:
149
+ path (str): relative path in cache dir
150
+ """
151
+
152
+ _path = os.path.join(env.cache_dir, config["path"])
153
+ assert os.path.exists(_path)
154
+ return _path
@@ -0,0 +1,42 @@
1
+ import logging
2
+ from typing import Dict
3
+ import requests
4
+
5
+ logger = logging.getLogger("desktopenv.getters.general")
6
+
7
+
8
+ def get_vm_command_line(env, config: Dict[str, str]):
9
+ vm_ip = env.vm_ip
10
+ port = env.server_port
11
+ command = config["command"]
12
+ shell = config.get("shell", False)
13
+
14
+ response = requests.post(f"http://{vm_ip}:{port}/execute", json={"command": command, "shell": shell})
15
+
16
+ print(response.json())
17
+
18
+ if response.status_code == 200:
19
+ return response.json()["output"]
20
+ else:
21
+ logger.error("Failed to get vm command line. Status code: %d", response.status_code)
22
+ return None
23
+
24
+ def get_vm_command_error(env, config: Dict[str, str]):
25
+ vm_ip = env.vm_ip
26
+ port = env.server_port
27
+ command = config["command"]
28
+ shell = config.get("shell", False)
29
+
30
+ response = requests.post(f"http://{vm_ip}:{port}/execute", json={"command": command, "shell": shell})
31
+
32
+ print(response.json())
33
+
34
+ if response.status_code == 200:
35
+ return response.json()["error"]
36
+ else:
37
+ logger.error("Failed to get vm command line error. Status code: %d", response.status_code)
38
+ return None
39
+
40
+
41
+ def get_vm_terminal_output(env, config: Dict[str, str]):
42
+ return env.controller.get_terminal_output()
@@ -0,0 +1,38 @@
1
+ import logging
2
+ import os
3
+ from typing import Dict
4
+
5
+ logger = logging.getLogger("desktopenv.getters.gimp")
6
+
7
+
8
+ def get_gimp_config_file(env, config: Dict[str, str]):
9
+ """
10
+ Gets the config setting of GIMP.
11
+ """
12
+
13
+ os_type = env.vm_platform
14
+ print(os_type)
15
+
16
+ if os_type == "Linux":
17
+ config_path = \
18
+ env.controller.execute_python_command(f"import os; print("
19
+ f"os"
20
+ f".path.expanduser("
21
+ f"'~/.config/GIMP/2.10/"
22
+ f"{config['file_name']}'))")[
23
+ 'output'].strip()
24
+ # TODO: Add support for macOS and Windows
25
+ else:
26
+ raise Exception("Unsupported operating system", os_type)
27
+
28
+ _path = os.path.join(env.cache_dir, config["dest"])
29
+ content = env.controller.get_file(config_path)
30
+
31
+ if not content:
32
+ logger.error("Failed to get GIMP config file.")
33
+ return None
34
+
35
+ with open(_path, "wb") as f:
36
+ f.write(content)
37
+
38
+ return _path
@@ -0,0 +1,126 @@
1
+ import os
2
+ import tempfile
3
+ import xml.etree.ElementTree as ET
4
+ import zipfile
5
+ from typing import Dict
6
+
7
+ from desktop_env.evaluators.getters.file import get_vm_file
8
+
9
+
10
+ def get_background_image_in_slide(env, config: Dict[str, str]):
11
+ ppt_file_path, slide_index, dest = config["ppt_file_path"], int(config["slide_index"]), config["dest"]
12
+ image_id, image_file_path = None, None
13
+
14
+ ppt_file_localhost_path = get_vm_file(env, {"path": ppt_file_path, "dest": os.path.split(ppt_file_path)[-1]})
15
+
16
+ with zipfile.ZipFile(ppt_file_localhost_path, 'r') as myzip:
17
+ slide1_xml_file = 'ppt/slides/slide{}.xml'.format(slide_index + 1)
18
+ # firstly, check whether the background image is used in the slide
19
+ if slide1_xml_file not in myzip.namelist(): return None
20
+ with myzip.open(slide1_xml_file) as f:
21
+ # Parse the XML tree from the relationships file
22
+ tree = ET.parse(f)
23
+ root = tree.getroot()
24
+ bg_tag = "{http://schemas.openxmlformats.org/presentationml/2006/main}bgPr"
25
+ image_tag = "{http://schemas.openxmlformats.org/drawingml/2006/main}blip"
26
+ attr_tag = "{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed"
27
+ for child in root.iter(bg_tag):
28
+ try:
29
+ for element in child.iter(image_tag):
30
+ image_id = element.attrib[attr_tag]
31
+ break
32
+ except: pass
33
+ if image_id is not None: break
34
+ else: return None
35
+
36
+ # next, extract the background image from the slide
37
+ slide1_rels_file = 'ppt/slides/_rels/slide{}.xml.rels'.format(slide_index + 1)
38
+ if slide1_rels_file in myzip.namelist():
39
+ with myzip.open(slide1_rels_file) as f:
40
+ # Parse the XML tree from the relationships file
41
+ tree = ET.parse(f)
42
+ root = tree.getroot()
43
+ # Define the namespace used in the relationships file
44
+ namespaces = {'r': 'http://schemas.openxmlformats.org/package/2006/relationships'}
45
+ # Look for all relationship elements that have a type attribute for image
46
+ for rel in root.findall('r:Relationship', namespaces):
47
+ # Check if the relationship is for an image file
48
+ if 'image' in rel.attrib['Type'] and rel.attrib['Id'] == image_id:
49
+ target = rel.attrib['Target']
50
+ if target.startswith('..'):
51
+ # Resolve the relative path to get the correct path within the zip file
52
+ image_file_path = os.path.normpath(os.path.join('ppt/slides', target))
53
+ # Replace backslashes with forward slashes for ZIP compatibility
54
+ image_file_path = image_file_path.replace('\\', '/')
55
+ tmpdirname = os.path.dirname(ppt_file_localhost_path)
56
+ myzip.extract(image_file_path, tmpdirname)
57
+ image_file_path = os.path.join(tmpdirname, image_file_path)
58
+ return image_file_path
59
+ else: # absolute path
60
+ assert target.startswith("file://"), target
61
+ image_file_path = target[7:]
62
+ break
63
+ if image_file_path is None:
64
+ return None
65
+
66
+ else:
67
+ # Get the audio file from vm and return the file path in the host
68
+ return get_vm_file(env, {"path": image_file_path, "dest": dest})
69
+
70
+
71
+ def get_audio_in_slide(env, config: Dict[str, str]):
72
+ ppt_file_path, slide_index, dest = config["ppt_file_path"], int(config["slide_index"]), config["dest"]
73
+
74
+ # Open the .pptx file as a zip file, fixme: now we assume there is only one audio file in the slides
75
+ audio_file_path = None
76
+
77
+ ppt_file_localhost_path = get_vm_file(env, {"path": ppt_file_path, "dest": os.path.split(ppt_file_path)[-1]})
78
+
79
+ with zipfile.ZipFile(ppt_file_localhost_path, 'r') as myzip:
80
+ # Find the relationships XML file for the first slide
81
+ slide1_rels_file = 'ppt/slides/_rels/slide{}.xml.rels'.format(slide_index + 1)
82
+ if slide1_rels_file in myzip.namelist():
83
+ with myzip.open(slide1_rels_file) as f:
84
+ # Parse the XML tree from the relationships file
85
+ tree = ET.parse(f)
86
+ root = tree.getroot()
87
+ # Define the namespace used in the relationships file
88
+ namespaces = {'r': 'http://schemas.openxmlformats.org/package/2006/relationships'}
89
+ # Look for all relationship elements that have a type attribute for audio
90
+ for rel in root.findall('r:Relationship', namespaces):
91
+ # Check if the relationship is for an audio file
92
+ if 'audio' in rel.attrib['Type']:
93
+ # The audio can be embedded inside the file or linked to an external file
94
+ # Get the target attribute which contains the audio file path
95
+ target = rel.attrib['Target']
96
+
97
+ if target.startswith('..'):
98
+ # Resolve the relative path to get the correct path within the zip file
99
+ audio_file_path = os.path.normpath(os.path.join('ppt/slides', target))
100
+ # Replace backslashes with forward slashes for ZIP compatibility
101
+ audio_file_path = audio_file_path.replace('\\', '/')
102
+
103
+ # Create a temporary directory to extract the audio file
104
+ tmpdirname = os.path.dirname(ppt_file_localhost_path)
105
+ myzip.extract(audio_file_path, tmpdirname)
106
+ audio_file_path = os.path.join(tmpdirname, audio_file_path)
107
+ return audio_file_path
108
+ # with tempfile.TemporaryDirectory() as tmpdirname:
109
+ # # Extract the audio file
110
+ # myzip.extract(audio_file_path, tmpdirname)
111
+ # # Get the full path of the extracted audio file
112
+ # extracted_audio_path = os.path.join(tmpdirname, audio_file_path)
113
+ # # Return the extracted audio file path
114
+ # audio_file_path = extracted_audio_path
115
+ else:
116
+ # the audio file is external to the .pptx file
117
+ # Return the audio file path
118
+ assert target.startswith("file://"), target
119
+ audio_file_path = target[7:]
120
+ break
121
+ if audio_file_path is None:
122
+ return None
123
+
124
+ else:
125
+ # Get the audio file from vm and return the file path in the host
126
+ return get_vm_file(env, {"path": audio_file_path, "dest": dest})
@@ -0,0 +1,24 @@
1
+ import os
2
+ from typing import Union
3
+
4
+
5
+ def get_vm_screen_size(env, config: dict) -> dict:
6
+ return env.controller.get_vm_screen_size()
7
+
8
+
9
+ def get_vm_window_size(env, config: dict) -> dict:
10
+ return env.controller.get_vm_window_size(app_class_name=config["app_class_name"])
11
+
12
+
13
+ def get_vm_wallpaper(env, config: dict) -> Union[str, bytes]:
14
+ _path = os.path.join(env.cache_dir, config["dest"])
15
+
16
+ content = env.controller.get_vm_wallpaper()
17
+ with open(_path, "wb") as f:
18
+ f.write(content)
19
+
20
+ return _path
21
+
22
+
23
+ def get_list_directory(env, config: dict) -> dict:
24
+ return env.controller.get_vm_directory_tree(config["path"])