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.
Files changed (69) hide show
  1. cudag/__init__.py +334 -0
  2. cudag/annotation/__init__.py +77 -0
  3. cudag/annotation/codegen.py +648 -0
  4. cudag/annotation/config.py +545 -0
  5. cudag/annotation/loader.py +342 -0
  6. cudag/annotation/scaffold.py +121 -0
  7. cudag/annotation/transcription.py +296 -0
  8. cudag/cli/__init__.py +5 -0
  9. cudag/cli/main.py +315 -0
  10. cudag/cli/new.py +873 -0
  11. cudag/core/__init__.py +364 -0
  12. cudag/core/button.py +137 -0
  13. cudag/core/canvas.py +222 -0
  14. cudag/core/config.py +70 -0
  15. cudag/core/coords.py +233 -0
  16. cudag/core/data_grid.py +804 -0
  17. cudag/core/dataset.py +678 -0
  18. cudag/core/distribution.py +136 -0
  19. cudag/core/drawing.py +75 -0
  20. cudag/core/fonts.py +156 -0
  21. cudag/core/generator.py +163 -0
  22. cudag/core/grid.py +367 -0
  23. cudag/core/grounding_task.py +247 -0
  24. cudag/core/icon.py +207 -0
  25. cudag/core/iconlist_task.py +301 -0
  26. cudag/core/models.py +1251 -0
  27. cudag/core/random.py +130 -0
  28. cudag/core/renderer.py +190 -0
  29. cudag/core/screen.py +402 -0
  30. cudag/core/scroll_task.py +254 -0
  31. cudag/core/scrollable_grid.py +447 -0
  32. cudag/core/state.py +110 -0
  33. cudag/core/task.py +293 -0
  34. cudag/core/taskbar.py +350 -0
  35. cudag/core/text.py +212 -0
  36. cudag/core/utils.py +82 -0
  37. cudag/data/surnames.txt +5000 -0
  38. cudag/modal_apps/__init__.py +4 -0
  39. cudag/modal_apps/archive.py +103 -0
  40. cudag/modal_apps/extract.py +138 -0
  41. cudag/modal_apps/preprocess.py +529 -0
  42. cudag/modal_apps/upload.py +317 -0
  43. cudag/prompts/SYSTEM_PROMPT.txt +104 -0
  44. cudag/prompts/__init__.py +33 -0
  45. cudag/prompts/system.py +43 -0
  46. cudag/prompts/tools.py +382 -0
  47. cudag/py.typed +0 -0
  48. cudag/schemas/filesystem.json +90 -0
  49. cudag/schemas/test_record.schema.json +113 -0
  50. cudag/schemas/train_record.schema.json +90 -0
  51. cudag/server/__init__.py +21 -0
  52. cudag/server/app.py +232 -0
  53. cudag/server/services/__init__.py +9 -0
  54. cudag/server/services/generator.py +128 -0
  55. cudag/templates/scripts/archive.sh +35 -0
  56. cudag/templates/scripts/build.sh +13 -0
  57. cudag/templates/scripts/extract.sh +54 -0
  58. cudag/templates/scripts/generate.sh +116 -0
  59. cudag/templates/scripts/pre-commit.sh +44 -0
  60. cudag/templates/scripts/preprocess.sh +46 -0
  61. cudag/templates/scripts/upload.sh +63 -0
  62. cudag/templates/scripts/verify.py +428 -0
  63. cudag/validation/__init__.py +35 -0
  64. cudag/validation/validate.py +508 -0
  65. cudag-0.3.10.dist-info/METADATA +570 -0
  66. cudag-0.3.10.dist-info/RECORD +69 -0
  67. cudag-0.3.10.dist-info/WHEEL +4 -0
  68. cudag-0.3.10.dist-info/entry_points.txt +2 -0
  69. 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
+ }