cudag 0.3.10__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.
- cudag/__init__.py +334 -0
- cudag/annotation/__init__.py +77 -0
- cudag/annotation/codegen.py +648 -0
- cudag/annotation/config.py +545 -0
- cudag/annotation/loader.py +342 -0
- cudag/annotation/scaffold.py +121 -0
- cudag/annotation/transcription.py +296 -0
- cudag/cli/__init__.py +5 -0
- cudag/cli/main.py +315 -0
- cudag/cli/new.py +873 -0
- cudag/core/__init__.py +364 -0
- cudag/core/button.py +137 -0
- cudag/core/canvas.py +222 -0
- cudag/core/config.py +70 -0
- cudag/core/coords.py +233 -0
- cudag/core/data_grid.py +804 -0
- cudag/core/dataset.py +678 -0
- cudag/core/distribution.py +136 -0
- cudag/core/drawing.py +75 -0
- cudag/core/fonts.py +156 -0
- cudag/core/generator.py +163 -0
- cudag/core/grid.py +367 -0
- cudag/core/grounding_task.py +247 -0
- cudag/core/icon.py +207 -0
- cudag/core/iconlist_task.py +301 -0
- cudag/core/models.py +1251 -0
- cudag/core/random.py +130 -0
- cudag/core/renderer.py +190 -0
- cudag/core/screen.py +402 -0
- cudag/core/scroll_task.py +254 -0
- cudag/core/scrollable_grid.py +447 -0
- cudag/core/state.py +110 -0
- cudag/core/task.py +293 -0
- cudag/core/taskbar.py +350 -0
- cudag/core/text.py +212 -0
- cudag/core/utils.py +82 -0
- cudag/data/surnames.txt +5000 -0
- cudag/modal_apps/__init__.py +4 -0
- cudag/modal_apps/archive.py +103 -0
- cudag/modal_apps/extract.py +138 -0
- cudag/modal_apps/preprocess.py +529 -0
- cudag/modal_apps/upload.py +317 -0
- cudag/prompts/SYSTEM_PROMPT.txt +104 -0
- cudag/prompts/__init__.py +33 -0
- cudag/prompts/system.py +43 -0
- cudag/prompts/tools.py +382 -0
- cudag/py.typed +0 -0
- cudag/schemas/filesystem.json +90 -0
- cudag/schemas/test_record.schema.json +113 -0
- cudag/schemas/train_record.schema.json +90 -0
- cudag/server/__init__.py +21 -0
- cudag/server/app.py +232 -0
- cudag/server/services/__init__.py +9 -0
- cudag/server/services/generator.py +128 -0
- cudag/templates/scripts/archive.sh +35 -0
- cudag/templates/scripts/build.sh +13 -0
- cudag/templates/scripts/extract.sh +54 -0
- cudag/templates/scripts/generate.sh +116 -0
- cudag/templates/scripts/pre-commit.sh +44 -0
- cudag/templates/scripts/preprocess.sh +46 -0
- cudag/templates/scripts/upload.sh +63 -0
- cudag/templates/scripts/verify.py +428 -0
- cudag/validation/__init__.py +35 -0
- cudag/validation/validate.py +508 -0
- cudag-0.3.10.dist-info/METADATA +570 -0
- cudag-0.3.10.dist-info/RECORD +69 -0
- cudag-0.3.10.dist-info/WHEEL +4 -0
- cudag-0.3.10.dist-info/entry_points.txt +2 -0
- cudag-0.3.10.dist-info/licenses/LICENSE +66 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2025 Tylt LLC. All rights reserved.
|
|
3
|
+
# CONFIDENTIAL AND PROPRIETARY. Unauthorized use, copying, or distribution
|
|
4
|
+
# is strictly prohibited. For licensing inquiries: hello@claimhawk.app
|
|
5
|
+
|
|
6
|
+
"""Compress a dataset run into chunks and upload to Modal with resume support.
|
|
7
|
+
|
|
8
|
+
Pipeline: upload -> extract -> preprocess
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
uv run python -m modal_apps.upload # Upload and auto-preprocess
|
|
12
|
+
uv run python -m modal_apps.upload --dry # Upload only, no preprocess
|
|
13
|
+
uv run python -m modal_apps.upload --no-resume # Force re-upload all chunks
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
20
|
+
import math
|
|
21
|
+
import shutil
|
|
22
|
+
import subprocess
|
|
23
|
+
import tarfile
|
|
24
|
+
import tempfile
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
# =============================================================================
|
|
28
|
+
# CENTRALIZED CONFIGURATION
|
|
29
|
+
# =============================================================================
|
|
30
|
+
# Volume names are loaded from config/adapters.yaml via the SDK.
|
|
31
|
+
# Users can customize these by editing the YAML file.
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
from sdk.modal_compat import get_volume_name
|
|
35
|
+
DEFAULT_VOLUME = get_volume_name("lora_training")
|
|
36
|
+
except ImportError:
|
|
37
|
+
# Fallback when SDK not available
|
|
38
|
+
DEFAULT_VOLUME = "claimhawk-lora-training"
|
|
39
|
+
DATASETS_ROOT = Path("datasets")
|
|
40
|
+
CHUNK_SIZE_MB = 500 # Size of each chunk in MB
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_args() -> argparse.Namespace:
|
|
44
|
+
"""Parse command-line arguments."""
|
|
45
|
+
parser = argparse.ArgumentParser(description="Archive and upload a dataset run to Modal.")
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"run_dir",
|
|
48
|
+
type=Path,
|
|
49
|
+
nargs="?",
|
|
50
|
+
help="Dataset subdirectory under ./datasets to upload (defaults to newest run).",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--chunk-size",
|
|
54
|
+
type=int,
|
|
55
|
+
default=CHUNK_SIZE_MB,
|
|
56
|
+
help=f"Chunk size in MB (default: {CHUNK_SIZE_MB})",
|
|
57
|
+
)
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--resume",
|
|
60
|
+
action="store_true",
|
|
61
|
+
default=True,
|
|
62
|
+
help="Resume a previously interrupted upload (default: True)",
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"--no-resume",
|
|
66
|
+
action="store_false",
|
|
67
|
+
dest="resume",
|
|
68
|
+
help="Disable resume and re-upload all chunks",
|
|
69
|
+
)
|
|
70
|
+
return parser.parse_args()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def pick_latest_run() -> Path:
|
|
74
|
+
"""Find the most recently modified dataset run directory."""
|
|
75
|
+
if not DATASETS_ROOT.exists():
|
|
76
|
+
raise SystemExit("datasets/ directory not found")
|
|
77
|
+
runs = [p for p in DATASETS_ROOT.iterdir() if p.is_dir()]
|
|
78
|
+
if not runs:
|
|
79
|
+
raise SystemExit("No dataset runs found under ./datasets")
|
|
80
|
+
return max(runs, key=lambda p: p.stat().st_mtime)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def file_md5(path: Path) -> str:
|
|
84
|
+
"""Calculate MD5 hash of a file."""
|
|
85
|
+
hasher = hashlib.md5()
|
|
86
|
+
with open(path, "rb") as f:
|
|
87
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
88
|
+
hasher.update(chunk)
|
|
89
|
+
return hasher.hexdigest()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def create_archive(run_path: Path, temp_dir: Path) -> Path:
|
|
93
|
+
"""Create a gzipped tarball of the dataset run."""
|
|
94
|
+
archive_path = temp_dir / f"{run_path.name}.tar.gz"
|
|
95
|
+
print(f"Creating archive {archive_path.name}...")
|
|
96
|
+
with tarfile.open(archive_path, "w:gz") as archive:
|
|
97
|
+
archive.add(run_path, arcname=run_path.name)
|
|
98
|
+
return archive_path
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def split_archive(archive_path: Path, chunk_size_mb: int) -> list[Path]:
|
|
102
|
+
"""Split archive into chunks of specified size."""
|
|
103
|
+
chunk_size = chunk_size_mb * 1024 * 1024
|
|
104
|
+
archive_size = archive_path.stat().st_size
|
|
105
|
+
num_chunks = math.ceil(archive_size / chunk_size)
|
|
106
|
+
|
|
107
|
+
if num_chunks == 1:
|
|
108
|
+
return [archive_path]
|
|
109
|
+
|
|
110
|
+
print(f"Splitting into {num_chunks} chunks of {chunk_size_mb}MB each...")
|
|
111
|
+
chunks = []
|
|
112
|
+
with open(archive_path, "rb") as f:
|
|
113
|
+
for i in range(num_chunks):
|
|
114
|
+
chunk_path = archive_path.parent / f"{archive_path.stem}.part{i:03d}"
|
|
115
|
+
with open(chunk_path, "wb") as chunk_file:
|
|
116
|
+
chunk_file.write(f.read(chunk_size))
|
|
117
|
+
chunks.append(chunk_path)
|
|
118
|
+
print(f" Created {chunk_path.name}")
|
|
119
|
+
|
|
120
|
+
return chunks
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def create_manifest(
|
|
124
|
+
ds_name: str, chunks: list[Path], temp_dir: Path
|
|
125
|
+
) -> tuple[Path, dict[str, str]]:
|
|
126
|
+
"""Create a manifest file with chunk info and checksums."""
|
|
127
|
+
chunks_dict: dict[str, dict[str, object]] = {}
|
|
128
|
+
checksums: dict[str, str] = {}
|
|
129
|
+
|
|
130
|
+
for chunk in chunks:
|
|
131
|
+
checksum = file_md5(chunk)
|
|
132
|
+
chunks_dict[chunk.name] = {
|
|
133
|
+
"size": chunk.stat().st_size,
|
|
134
|
+
"md5": checksum,
|
|
135
|
+
}
|
|
136
|
+
checksums[chunk.name] = checksum
|
|
137
|
+
|
|
138
|
+
manifest: dict[str, object] = {
|
|
139
|
+
"ds_name": ds_name,
|
|
140
|
+
"num_chunks": len(chunks),
|
|
141
|
+
"chunks": chunks_dict,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
manifest_path = temp_dir / f"{ds_name}.manifest.json"
|
|
145
|
+
with open(manifest_path, "w") as f:
|
|
146
|
+
json.dump(manifest, f, indent=2)
|
|
147
|
+
|
|
148
|
+
return manifest_path, checksums
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_uploaded_chunks(ds_name: str) -> dict[str, str]:
|
|
152
|
+
"""Get list of already uploaded chunks from Modal volume."""
|
|
153
|
+
try:
|
|
154
|
+
result = subprocess.run(
|
|
155
|
+
["uvx", "modal", "volume", "ls", DEFAULT_VOLUME, f"/datasets/{ds_name}_chunks/"],
|
|
156
|
+
capture_output=True,
|
|
157
|
+
text=True,
|
|
158
|
+
check=False,
|
|
159
|
+
)
|
|
160
|
+
if result.returncode != 0:
|
|
161
|
+
return {}
|
|
162
|
+
|
|
163
|
+
# Parse the ls output to get filenames
|
|
164
|
+
uploaded = {}
|
|
165
|
+
for line in result.stdout.strip().split("\n"):
|
|
166
|
+
if line and not line.startswith("Directory"):
|
|
167
|
+
# Extract filename from ls output
|
|
168
|
+
parts = line.split()
|
|
169
|
+
if parts:
|
|
170
|
+
filename = parts[-1]
|
|
171
|
+
if filename.endswith(".md5"):
|
|
172
|
+
continue
|
|
173
|
+
# Try to get the checksum file
|
|
174
|
+
md5_result = subprocess.run(
|
|
175
|
+
[
|
|
176
|
+
"uvx",
|
|
177
|
+
"modal",
|
|
178
|
+
"volume",
|
|
179
|
+
"get",
|
|
180
|
+
DEFAULT_VOLUME,
|
|
181
|
+
f"/datasets/{ds_name}_chunks/{filename}.md5",
|
|
182
|
+
],
|
|
183
|
+
capture_output=True,
|
|
184
|
+
text=True,
|
|
185
|
+
check=False,
|
|
186
|
+
)
|
|
187
|
+
if md5_result.returncode == 0:
|
|
188
|
+
uploaded[filename] = md5_result.stdout.strip()
|
|
189
|
+
return uploaded
|
|
190
|
+
except Exception:
|
|
191
|
+
return {}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def ensure_volume() -> None:
|
|
195
|
+
"""Create the Modal volume if it doesn't exist."""
|
|
196
|
+
subprocess.run(["uvx", "modal", "volume", "create", DEFAULT_VOLUME], check=False)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def upload_chunk(chunk_path: Path, ds_name: str, checksum: str) -> None:
|
|
200
|
+
"""Upload a single chunk to the Modal volume."""
|
|
201
|
+
chunk_name = chunk_path.name
|
|
202
|
+
remote_path = f"/datasets/{ds_name}_chunks/{chunk_name}"
|
|
203
|
+
|
|
204
|
+
# Upload the chunk
|
|
205
|
+
subprocess.run(
|
|
206
|
+
[
|
|
207
|
+
"uvx",
|
|
208
|
+
"modal",
|
|
209
|
+
"volume",
|
|
210
|
+
"put",
|
|
211
|
+
"-f",
|
|
212
|
+
DEFAULT_VOLUME,
|
|
213
|
+
str(chunk_path),
|
|
214
|
+
remote_path,
|
|
215
|
+
],
|
|
216
|
+
check=True,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Upload checksum file for resume verification
|
|
220
|
+
checksum_path = chunk_path.parent / f"{chunk_name}.md5"
|
|
221
|
+
with open(checksum_path, "w") as f:
|
|
222
|
+
f.write(checksum)
|
|
223
|
+
|
|
224
|
+
subprocess.run(
|
|
225
|
+
[
|
|
226
|
+
"uvx",
|
|
227
|
+
"modal",
|
|
228
|
+
"volume",
|
|
229
|
+
"put",
|
|
230
|
+
"-f",
|
|
231
|
+
DEFAULT_VOLUME,
|
|
232
|
+
str(checksum_path),
|
|
233
|
+
f"{remote_path}.md5",
|
|
234
|
+
],
|
|
235
|
+
check=True,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def upload_manifest(manifest_path: Path, ds_name: str) -> None:
|
|
240
|
+
"""Upload the manifest file."""
|
|
241
|
+
subprocess.run(
|
|
242
|
+
[
|
|
243
|
+
"uvx",
|
|
244
|
+
"modal",
|
|
245
|
+
"volume",
|
|
246
|
+
"put",
|
|
247
|
+
"-f",
|
|
248
|
+
DEFAULT_VOLUME,
|
|
249
|
+
str(manifest_path),
|
|
250
|
+
f"/datasets/{ds_name}_chunks/{manifest_path.name}",
|
|
251
|
+
],
|
|
252
|
+
check=True,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def main() -> None:
|
|
257
|
+
"""Archive and upload a dataset run to Modal with chunking and resume support."""
|
|
258
|
+
args = parse_args()
|
|
259
|
+
run_path = args.run_dir if args.run_dir else pick_latest_run()
|
|
260
|
+
run_path = run_path.resolve()
|
|
261
|
+
if not run_path.exists():
|
|
262
|
+
raise SystemExit(f"Run path {run_path} does not exist")
|
|
263
|
+
|
|
264
|
+
ds_name = run_path.name
|
|
265
|
+
temp_dir = Path(tempfile.mkdtemp(prefix="dataset_archive_"))
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
ensure_volume()
|
|
269
|
+
|
|
270
|
+
# Check for existing uploads if resuming
|
|
271
|
+
uploaded_chunks: dict[str, str] = {}
|
|
272
|
+
if args.resume:
|
|
273
|
+
print("Checking for previously uploaded chunks...")
|
|
274
|
+
uploaded_chunks = get_uploaded_chunks(ds_name)
|
|
275
|
+
if uploaded_chunks:
|
|
276
|
+
print(f"Found {len(uploaded_chunks)} previously uploaded chunks")
|
|
277
|
+
|
|
278
|
+
# Create archive
|
|
279
|
+
archive_path = create_archive(run_path, temp_dir)
|
|
280
|
+
total_size = archive_path.stat().st_size
|
|
281
|
+
print(f"Archive size: {total_size / (1024 * 1024):.1f}MB")
|
|
282
|
+
|
|
283
|
+
# Split into chunks
|
|
284
|
+
chunks = split_archive(archive_path, args.chunk_size)
|
|
285
|
+
|
|
286
|
+
# Create manifest with checksums
|
|
287
|
+
manifest_path, checksums = create_manifest(ds_name, chunks, temp_dir)
|
|
288
|
+
|
|
289
|
+
# Upload chunks (skip already uploaded ones with matching checksums)
|
|
290
|
+
for chunk in chunks:
|
|
291
|
+
chunk_name = chunk.name
|
|
292
|
+
expected_checksum = checksums[chunk_name]
|
|
293
|
+
|
|
294
|
+
if chunk_name in uploaded_chunks:
|
|
295
|
+
if uploaded_chunks[chunk_name] == expected_checksum:
|
|
296
|
+
print(f"Skipping {chunk_name} (already uploaded, checksum matches)")
|
|
297
|
+
continue
|
|
298
|
+
else:
|
|
299
|
+
print(f"Re-uploading {chunk_name} (checksum mismatch)")
|
|
300
|
+
|
|
301
|
+
print(f"Uploading {chunk_name}...")
|
|
302
|
+
upload_chunk(chunk, ds_name, expected_checksum)
|
|
303
|
+
|
|
304
|
+
# Upload manifest
|
|
305
|
+
print("Uploading manifest...")
|
|
306
|
+
upload_manifest(manifest_path, ds_name)
|
|
307
|
+
|
|
308
|
+
finally:
|
|
309
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
310
|
+
|
|
311
|
+
print(f"\nUploaded {ds_name} to Modal volume '{DEFAULT_VOLUME}' ({len(chunks)} chunks)")
|
|
312
|
+
# Output dataset name for shell script to use
|
|
313
|
+
print(f"DATASET_NAME={ds_name}")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
if __name__ == "__main__":
|
|
317
|
+
main()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Use a mouse and keyboard to interact with a computer, and take screenshots.
|
|
2
|
+
* This is an interface to a desktop GUI. You do not have access to a terminal or applications menu. You must click on desktop icons to start applications.
|
|
3
|
+
* Some applications may take time to start or process actions, so you may need to wait and take successive screenshots to see the results of your actions. E.g. if you click on Firefox and a window doesn't open, try wait and taking another screenshot.
|
|
4
|
+
* The screen's resolution is 1000x1000.
|
|
5
|
+
* Whenever you intend to move the cursor to click on an element like an icon, you should consult a screenshot to determine the coordinates of the element before moving the cursor.
|
|
6
|
+
* If you tried clicking on a program or link but it failed to load even after waiting, try adjusting your cursor position so that the tip of the cursor visually falls on the element that you want to click.
|
|
7
|
+
* Make sure to click any buttons, links, icons, etc with the cursor tip in the center of the element. Don't click boxes on their edges unless asked.
|
|
8
|
+
|
|
9
|
+
# Tools
|
|
10
|
+
|
|
11
|
+
You may call one or more functions to assist with the user query.
|
|
12
|
+
|
|
13
|
+
You are provided with function signatures within <tools></tools> XML tags:
|
|
14
|
+
<tools>
|
|
15
|
+
{
|
|
16
|
+
"type": "function",
|
|
17
|
+
"function": {
|
|
18
|
+
"name_for_human": "computer_use",
|
|
19
|
+
"name": "computer_use",
|
|
20
|
+
"description": "Perform computer actions",
|
|
21
|
+
"parameters": {
|
|
22
|
+
"properties": {
|
|
23
|
+
"action": {
|
|
24
|
+
"description": "* `key`: Performs key down presses on the arguments passed in order, then performs key releases in reverse order.\n* `type`: Type a string of text on the keyboard.\n* `mouse_move`: Move the cursor to a specified (x, y) pixel coordinate on the screen.\n* `left_click`: Left click at a specified (x, y) pixel coordinate on the screen.\n* `left_click_drag`: Click and drag the cursor to a specified (x, y) pixel coordinate on the screen.\n* `right_click`: Right click at a specified (x, y) pixel coordinate on the screen.\n* `middle_click`: Middle click at a specified (x, y) pixel coordinate on the screen.\n* `double_click`: Double-click the left mouse button at a specified (x, y) pixel coordinate on the screen.\n* `triple_click`: Triple-click the left mouse button at a specified (x, y) pixel coordinate on the screen.\n* `scroll`: Performs a scroll of the mouse scroll wheel.\n* `hscroll`: Performs a horizontal scroll.\n* `wait`: Wait specified seconds for the change to happen.\n* `terminate`: Terminate the current task and report its completion status.\n* `answer`: Answer a question.",
|
|
25
|
+
"enum": ["key", "type", "mouse_move", "left_click", "left_click_drag", "right_click", "middle_click", "double_click", "triple_click", "scroll", "hscroll", "wait", "terminate", "answer"],
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"keys": {
|
|
29
|
+
"description": "Required only by `action=key`.",
|
|
30
|
+
"type": "array"
|
|
31
|
+
},
|
|
32
|
+
"text": {
|
|
33
|
+
"description": "Required only by `action=type`.",
|
|
34
|
+
"type": "string"
|
|
35
|
+
},
|
|
36
|
+
"coordinate": {
|
|
37
|
+
"description": "The x,y coordinates for mouse actions.",
|
|
38
|
+
"type": "array"
|
|
39
|
+
},
|
|
40
|
+
"pixels": {
|
|
41
|
+
"description": "The amount of scrolling.",
|
|
42
|
+
"type": "number"
|
|
43
|
+
},
|
|
44
|
+
"time": {
|
|
45
|
+
"description": "The seconds to wait.",
|
|
46
|
+
"type": "number"
|
|
47
|
+
},
|
|
48
|
+
"status": {
|
|
49
|
+
"description": "The status of the task.",
|
|
50
|
+
"type": "string",
|
|
51
|
+
"enum": ["success", "failure"]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"required": ["action"],
|
|
55
|
+
"type": "object"
|
|
56
|
+
},
|
|
57
|
+
"args_format": "Format the arguments as a JSON object."
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
{
|
|
61
|
+
"type": "function",
|
|
62
|
+
"function": {
|
|
63
|
+
"name_for_human": "get_bbox",
|
|
64
|
+
"name": "get_bbox",
|
|
65
|
+
"description": "Return the bounding box for a UI element",
|
|
66
|
+
"parameters": {
|
|
67
|
+
"properties": {
|
|
68
|
+
"bbox_2d": {
|
|
69
|
+
"description": "The bounding box coordinates [x1, y1, x2, y2] in resolution units (0-1000).",
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": {"type": "number"},
|
|
72
|
+
"minItems": 4,
|
|
73
|
+
"maxItems": 4
|
|
74
|
+
},
|
|
75
|
+
"label": {
|
|
76
|
+
"description": "The text label of the UI element.",
|
|
77
|
+
"type": "string"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": ["bbox_2d"],
|
|
81
|
+
"type": "object"
|
|
82
|
+
},
|
|
83
|
+
"args_format": "Format the arguments as a JSON object."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</tools>
|
|
87
|
+
|
|
88
|
+
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
|
|
89
|
+
<tool_call>
|
|
90
|
+
{"name": <function-name>, "arguments": <args-json-object>}
|
|
91
|
+
</tool_call>
|
|
92
|
+
|
|
93
|
+
# Response format
|
|
94
|
+
|
|
95
|
+
Response format for every step:
|
|
96
|
+
1) Action: a short imperative describing what to do in the UI.
|
|
97
|
+
2) One or more <tool_call>...</tool_call> blocks, one per line, each containing only the JSON.
|
|
98
|
+
|
|
99
|
+
Rules:
|
|
100
|
+
- Output exactly in the order: Action, <tool_call>(s).
|
|
101
|
+
- Be brief: one sentence for Action.
|
|
102
|
+
- Multiple tool calls can be output, one per line.
|
|
103
|
+
- Do not output anything else outside those parts.
|
|
104
|
+
- If finishing, use action=terminate in the tool call.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) 2025 Tylt LLC. All rights reserved.
|
|
2
|
+
# CONFIDENTIAL AND PROPRIETARY. Unauthorized use, copying, or distribution
|
|
3
|
+
# is strictly prohibited. For licensing inquiries: hello@claimhawk.app
|
|
4
|
+
|
|
5
|
+
"""System prompts and tool definitions for computer use training."""
|
|
6
|
+
|
|
7
|
+
from cudag.prompts.system import (
|
|
8
|
+
CUA_SYSTEM_PROMPT,
|
|
9
|
+
SYSTEM_PROMPT,
|
|
10
|
+
get_system_prompt,
|
|
11
|
+
)
|
|
12
|
+
from cudag.prompts.tools import (
|
|
13
|
+
COMPUTER_USE_TOOL,
|
|
14
|
+
TOOL_ACTIONS,
|
|
15
|
+
BboxCall,
|
|
16
|
+
ToolCall,
|
|
17
|
+
format_tool_call,
|
|
18
|
+
parse_tool_call,
|
|
19
|
+
validate_tool_call,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"COMPUTER_USE_TOOL",
|
|
24
|
+
"TOOL_ACTIONS",
|
|
25
|
+
"BboxCall",
|
|
26
|
+
"ToolCall",
|
|
27
|
+
"format_tool_call",
|
|
28
|
+
"parse_tool_call",
|
|
29
|
+
"validate_tool_call",
|
|
30
|
+
"CUA_SYSTEM_PROMPT",
|
|
31
|
+
"SYSTEM_PROMPT",
|
|
32
|
+
"get_system_prompt",
|
|
33
|
+
]
|
cudag/prompts/system.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Copyright (c) 2025 Tylt LLC. All rights reserved.
|
|
2
|
+
# CONFIDENTIAL AND PROPRIETARY. Unauthorized use, copying, or distribution
|
|
3
|
+
# is strictly prohibited. For licensing inquiries: hello@claimhawk.app
|
|
4
|
+
|
|
5
|
+
"""System prompt for VLM training datasets.
|
|
6
|
+
|
|
7
|
+
IMPORTANT: The system prompt is managed by the system-prompt project.
|
|
8
|
+
Run `system-prompt/scripts/sync.sh` to update from the canonical source.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Load prompt from text file (managed by system-prompt project)
|
|
16
|
+
_PROMPTS_DIR = Path(__file__).parent
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_prompt() -> str:
|
|
20
|
+
"""Load the system prompt from text file."""
|
|
21
|
+
filepath = _PROMPTS_DIR / "SYSTEM_PROMPT.txt"
|
|
22
|
+
if not filepath.exists():
|
|
23
|
+
raise FileNotFoundError(
|
|
24
|
+
f"System prompt file not found: {filepath}\n"
|
|
25
|
+
"Run system-prompt/scripts/sync.sh to generate prompt files."
|
|
26
|
+
)
|
|
27
|
+
return filepath.read_text().strip()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# The canonical system prompt
|
|
31
|
+
SYSTEM_PROMPT = _load_prompt()
|
|
32
|
+
|
|
33
|
+
# Aliases for backward compatibility
|
|
34
|
+
CUA_SYSTEM_PROMPT = SYSTEM_PROMPT
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_system_prompt() -> str:
|
|
38
|
+
"""Get the system prompt.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
System prompt string
|
|
42
|
+
"""
|
|
43
|
+
return SYSTEM_PROMPT
|