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
cudag/core/random.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
"""Random data generation utilities for CUDAG framework."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from datetime import datetime, timedelta
|
|
10
|
+
from random import Random
|
|
11
|
+
from typing import Any, Sequence, TypeVar
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def choose(rng: Random, values: Sequence[T]) -> T:
|
|
17
|
+
"""Choose a random item from a sequence.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
rng: Random number generator.
|
|
21
|
+
values: Non-empty sequence of values to choose from.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A randomly selected item.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If values is empty.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
>>> rng = Random(42)
|
|
31
|
+
>>> choose(rng, ["apple", "banana", "cherry"])
|
|
32
|
+
'cherry'
|
|
33
|
+
"""
|
|
34
|
+
if not values:
|
|
35
|
+
raise ValueError("Cannot choose from empty sequence")
|
|
36
|
+
return values[rng.randint(0, len(values) - 1)]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def date_in_range(
|
|
40
|
+
rng: Random,
|
|
41
|
+
start: str,
|
|
42
|
+
end: str,
|
|
43
|
+
fmt: str = "%m/%d/%Y",
|
|
44
|
+
input_fmt: str = "%Y-%m-%d",
|
|
45
|
+
) -> str:
|
|
46
|
+
"""Generate a random date in the given range.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
rng: Random number generator.
|
|
50
|
+
start: Start date string (input_fmt format, default YYYY-MM-DD).
|
|
51
|
+
end: End date string (input_fmt format, default YYYY-MM-DD).
|
|
52
|
+
fmt: Output format string (default MM/DD/YYYY).
|
|
53
|
+
input_fmt: Input format string (default YYYY-MM-DD).
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Formatted date string in the specified output format.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
>>> rng = Random(42)
|
|
60
|
+
>>> date_in_range(rng, "2024-01-01", "2024-12-31")
|
|
61
|
+
'11/01/2024'
|
|
62
|
+
"""
|
|
63
|
+
start_date = datetime.strptime(start, input_fmt).date()
|
|
64
|
+
end_date = datetime.strptime(end, input_fmt).date()
|
|
65
|
+
delta_days = (end_date - start_date).days
|
|
66
|
+
if delta_days <= 0:
|
|
67
|
+
return start_date.strftime(fmt)
|
|
68
|
+
target = start_date + timedelta(days=rng.randint(0, delta_days))
|
|
69
|
+
return target.strftime(fmt)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def amount(
|
|
73
|
+
rng: Random,
|
|
74
|
+
min_val: float,
|
|
75
|
+
max_val: float,
|
|
76
|
+
*,
|
|
77
|
+
allow_zero: bool = False,
|
|
78
|
+
zero_probability: float = 0.2,
|
|
79
|
+
decimal_places: int = 2,
|
|
80
|
+
) -> str:
|
|
81
|
+
"""Generate a random monetary amount.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
rng: Random number generator.
|
|
85
|
+
min_val: Minimum value (inclusive).
|
|
86
|
+
max_val: Maximum value (inclusive).
|
|
87
|
+
allow_zero: If True, sometimes return "0.00". Default False.
|
|
88
|
+
zero_probability: Probability of returning zero when allow_zero=True.
|
|
89
|
+
decimal_places: Number of decimal places. Default 2.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Formatted amount string.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> rng = Random(42)
|
|
96
|
+
>>> amount(rng, 10.0, 100.0)
|
|
97
|
+
'69.65'
|
|
98
|
+
"""
|
|
99
|
+
if allow_zero and rng.random() < zero_probability:
|
|
100
|
+
return f"{0:.{decimal_places}f}"
|
|
101
|
+
value = rng.uniform(min_val, max_val)
|
|
102
|
+
return f"{value:.{decimal_places}f}"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def weighted_choice(
|
|
106
|
+
rng: Random,
|
|
107
|
+
choices: dict[str, float],
|
|
108
|
+
) -> str:
|
|
109
|
+
"""Choose a random key based on weighted probabilities.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
rng: Random number generator.
|
|
113
|
+
choices: Dict mapping choice keys to their probabilities (should sum to ~1.0).
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Selected choice key.
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
>>> rng = Random(42)
|
|
120
|
+
>>> weighted_choice(rng, {"common": 0.8, "rare": 0.15, "legendary": 0.05})
|
|
121
|
+
'common'
|
|
122
|
+
"""
|
|
123
|
+
roll = rng.random()
|
|
124
|
+
cumulative = 0.0
|
|
125
|
+
for choice, prob in choices.items():
|
|
126
|
+
cumulative += prob
|
|
127
|
+
if roll < cumulative:
|
|
128
|
+
return choice
|
|
129
|
+
# Fallback to last choice
|
|
130
|
+
return list(choices.keys())[-1] if choices else ""
|
cudag/core/renderer.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
"""Base renderer class for image generation.
|
|
6
|
+
|
|
7
|
+
A Renderer generates images from Screen + State.
|
|
8
|
+
Like a View in MVC, it handles the visual presentation.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from abc import ABC, abstractmethod
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypeVar
|
|
16
|
+
|
|
17
|
+
from cudag.core.coords import normalize_coord, pixel_from_normalized
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from PIL import Image
|
|
21
|
+
|
|
22
|
+
from cudag.core.screen import ScreenBase
|
|
23
|
+
from cudag.core.state import BaseState
|
|
24
|
+
|
|
25
|
+
# Type variable for state
|
|
26
|
+
S = TypeVar("S", bound="BaseState")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class BaseRenderer(ABC, Generic[S]):
|
|
30
|
+
"""Abstract base class for screen renderers.
|
|
31
|
+
|
|
32
|
+
A renderer is responsible for:
|
|
33
|
+
1. Loading base images and assets (fonts, overlays)
|
|
34
|
+
2. Rendering screen state to images
|
|
35
|
+
3. Building metadata for the rendered images
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
class CalendarRenderer(BaseRenderer[CalendarState]):
|
|
39
|
+
screen_class = CalendarScreen
|
|
40
|
+
|
|
41
|
+
def load_assets(self) -> None:
|
|
42
|
+
self.font = ImageFont.truetype(self.asset_path("arial.ttf"), 12)
|
|
43
|
+
|
|
44
|
+
def render(self, state: CalendarState) -> tuple[Image, dict]:
|
|
45
|
+
image = self.load_base_image()
|
|
46
|
+
self.draw_calendar_grid(image, state)
|
|
47
|
+
return image, {"month": state.month, "year": state.year}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
screen_class: ClassVar[type[ScreenBase]]
|
|
51
|
+
"""The Screen class this renderer is for."""
|
|
52
|
+
|
|
53
|
+
def __init__(self, assets_dir: Path | str) -> None:
|
|
54
|
+
"""Initialize the renderer.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
assets_dir: Path to the assets directory
|
|
58
|
+
"""
|
|
59
|
+
self.assets_dir = Path(assets_dir)
|
|
60
|
+
self._base_image: Image.Image | None = None
|
|
61
|
+
self.load_assets()
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def load_assets(self) -> None:
|
|
65
|
+
"""Load fonts, base images, and other assets.
|
|
66
|
+
|
|
67
|
+
Called during initialization. Override to load your assets.
|
|
68
|
+
Store loaded assets as instance attributes.
|
|
69
|
+
"""
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def render(self, state: S) -> tuple[Image.Image, dict[str, Any]]:
|
|
74
|
+
"""Render the screen with given state.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
state: Screen state to render
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Tuple of (PIL Image, metadata dict)
|
|
81
|
+
"""
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
def asset_path(self, *parts: str) -> Path:
|
|
85
|
+
"""Get path to an asset file.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
*parts: Path components relative to assets_dir
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Full path to the asset
|
|
92
|
+
"""
|
|
93
|
+
return self.assets_dir.joinpath(*parts)
|
|
94
|
+
|
|
95
|
+
def load_base_image(self) -> Image.Image:
|
|
96
|
+
"""Load and return a copy of the base image.
|
|
97
|
+
|
|
98
|
+
Uses the base_image defined in the Screen's Meta class.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Copy of the base image (safe to modify)
|
|
102
|
+
"""
|
|
103
|
+
from PIL import Image
|
|
104
|
+
|
|
105
|
+
if self._base_image is None:
|
|
106
|
+
base_path = self.screen_class.meta().base_image
|
|
107
|
+
if isinstance(base_path, str):
|
|
108
|
+
base_path = self.asset_path(base_path)
|
|
109
|
+
self._base_image = Image.open(base_path).convert("RGB")
|
|
110
|
+
|
|
111
|
+
return self._base_image.copy()
|
|
112
|
+
|
|
113
|
+
def normalize(
|
|
114
|
+
self,
|
|
115
|
+
pixel: tuple[int, int],
|
|
116
|
+
image_size: tuple[int, int] | None = None,
|
|
117
|
+
) -> tuple[int, int]:
|
|
118
|
+
"""Normalize pixel coordinates to RU [0, 1000].
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
pixel: (x, y) pixel coordinates
|
|
122
|
+
image_size: Image dimensions, or use screen size from Meta
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Normalized (x, y) coordinates
|
|
126
|
+
"""
|
|
127
|
+
if image_size is None:
|
|
128
|
+
image_size = self.screen_class.meta().size
|
|
129
|
+
return normalize_coord(pixel, image_size)
|
|
130
|
+
|
|
131
|
+
def to_pixel(
|
|
132
|
+
self,
|
|
133
|
+
normalized: tuple[int, int],
|
|
134
|
+
image_size: tuple[int, int] | None = None,
|
|
135
|
+
) -> tuple[int, int]:
|
|
136
|
+
"""Convert normalized RU coordinates to pixels.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
normalized: (x, y) coordinates in [0, 1000]
|
|
140
|
+
image_size: Image dimensions, or use screen size from Meta
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Pixel (x, y) coordinates
|
|
144
|
+
"""
|
|
145
|
+
if image_size is None:
|
|
146
|
+
image_size = self.screen_class.meta().size
|
|
147
|
+
return pixel_from_normalized(normalized, image_size)
|
|
148
|
+
|
|
149
|
+
def get_region_center(self, region_name: str) -> tuple[int, int]:
|
|
150
|
+
"""Get the center pixel coordinates of a region.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
region_name: Name of the region on the screen
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
(x, y) pixel coordinates of region center
|
|
157
|
+
"""
|
|
158
|
+
region = self.screen_class.get_region(region_name)
|
|
159
|
+
return region.bounds.center
|
|
160
|
+
|
|
161
|
+
def get_action_point(self, region_name: str, target: Any = None) -> tuple[int, int]:
|
|
162
|
+
"""Get the pixel coordinates for an action on a region.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
region_name: Name of the region
|
|
166
|
+
target: Optional target (cell index, item, etc.)
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
(x, y) pixel coordinates for the action
|
|
170
|
+
"""
|
|
171
|
+
region = self.screen_class.get_region(region_name)
|
|
172
|
+
return region.get_action_point(target)
|
|
173
|
+
|
|
174
|
+
def build_metadata(self, state: S, **extra: Any) -> dict[str, Any]:
|
|
175
|
+
"""Build standard metadata dict for a rendered image.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
state: The state that was rendered
|
|
179
|
+
**extra: Additional metadata fields
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Metadata dictionary
|
|
183
|
+
"""
|
|
184
|
+
meta = self.screen_class.meta()
|
|
185
|
+
return {
|
|
186
|
+
"screen": meta.name,
|
|
187
|
+
"image_size": {"width": meta.size[0], "height": meta.size[1]},
|
|
188
|
+
"state": state.to_dict(),
|
|
189
|
+
**extra,
|
|
190
|
+
}
|