dayhoff-tools 1.1.10__py3-none-any.whl → 1.13.12__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 (41) hide show
  1. dayhoff_tools/__init__.py +10 -0
  2. dayhoff_tools/cli/cloud_commands.py +179 -43
  3. dayhoff_tools/cli/engine1/__init__.py +323 -0
  4. dayhoff_tools/cli/engine1/engine_core.py +703 -0
  5. dayhoff_tools/cli/engine1/engine_lifecycle.py +136 -0
  6. dayhoff_tools/cli/engine1/engine_maintenance.py +431 -0
  7. dayhoff_tools/cli/engine1/engine_management.py +505 -0
  8. dayhoff_tools/cli/engine1/shared.py +501 -0
  9. dayhoff_tools/cli/engine1/studio_commands.py +825 -0
  10. dayhoff_tools/cli/engines_studios/__init__.py +6 -0
  11. dayhoff_tools/cli/engines_studios/api_client.py +351 -0
  12. dayhoff_tools/cli/engines_studios/auth.py +144 -0
  13. dayhoff_tools/cli/engines_studios/engine-studio-cli.md +1230 -0
  14. dayhoff_tools/cli/engines_studios/engine_commands.py +1151 -0
  15. dayhoff_tools/cli/engines_studios/progress.py +260 -0
  16. dayhoff_tools/cli/engines_studios/simulators/cli-simulators.md +151 -0
  17. dayhoff_tools/cli/engines_studios/simulators/demo.sh +75 -0
  18. dayhoff_tools/cli/engines_studios/simulators/engine_list_simulator.py +319 -0
  19. dayhoff_tools/cli/engines_studios/simulators/engine_status_simulator.py +369 -0
  20. dayhoff_tools/cli/engines_studios/simulators/idle_status_simulator.py +476 -0
  21. dayhoff_tools/cli/engines_studios/simulators/simulator_utils.py +180 -0
  22. dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py +374 -0
  23. dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py +164 -0
  24. dayhoff_tools/cli/engines_studios/studio_commands.py +755 -0
  25. dayhoff_tools/cli/main.py +106 -7
  26. dayhoff_tools/cli/utility_commands.py +896 -179
  27. dayhoff_tools/deployment/base.py +70 -6
  28. dayhoff_tools/deployment/deploy_aws.py +165 -25
  29. dayhoff_tools/deployment/deploy_gcp.py +78 -5
  30. dayhoff_tools/deployment/deploy_utils.py +20 -7
  31. dayhoff_tools/deployment/job_runner.py +9 -4
  32. dayhoff_tools/deployment/processors.py +230 -418
  33. dayhoff_tools/deployment/swarm.py +47 -12
  34. dayhoff_tools/embedders.py +28 -26
  35. dayhoff_tools/fasta.py +181 -64
  36. dayhoff_tools/warehouse.py +268 -1
  37. {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/METADATA +20 -5
  38. dayhoff_tools-1.13.12.dist-info/RECORD +54 -0
  39. {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/WHEEL +1 -1
  40. dayhoff_tools-1.1.10.dist-info/RECORD +0 -32
  41. {dayhoff_tools-1.1.10.dist-info → dayhoff_tools-1.13.12.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,374 @@
1
+ #!/usr/bin/env python3
2
+ """Simulator for studio list output - iterate on design locally without AWS.
3
+
4
+ This lets you quickly see how the list command output looks with different
5
+ studio states and configurations.
6
+
7
+ Usage:
8
+ python dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py # Show all scenarios
9
+ python dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py --scenario few # Show specific scenario
10
+ python dayhoff_tools/cli/engines_studios/simulators/studio_list_simulator.py --env prod # Simulate different environment
11
+ """
12
+
13
+ import argparse
14
+ import sys
15
+ from typing import Any
16
+
17
+
18
+ def colorize(text: str, color_code: str) -> str:
19
+ """Apply ANSI color code to text."""
20
+ return f"\033[{color_code}m{text}\033[0m"
21
+
22
+
23
+ def format_list_output(
24
+ studios: list[dict[str, Any]], engines_map: dict[str, str], env: str = "dev"
25
+ ) -> None:
26
+ """Format and print studio list output matching the actual CLI."""
27
+
28
+ # Header with blue account name
29
+ print(f"\nStudios for AWS Account {colorize(env, '34')}")
30
+
31
+ if not studios:
32
+ print("No studios found\n")
33
+ return
34
+
35
+ # Calculate dynamic width for User column (longest user + 2 for padding)
36
+ max_user_len = max(
37
+ (len(studio.get("user", "unknown")) for studio in studios), default=4
38
+ )
39
+ user_width = max(max_user_len + 2, len("User") + 2)
40
+
41
+ # Calculate dynamic width for Attached To column
42
+ max_attached_len = 0
43
+ for studio in studios:
44
+ if studio.get("attached_to"):
45
+ instance_id = studio["attached_to"]
46
+ engine_name = engines_map.get(instance_id, "unknown")
47
+ max_attached_len = max(max_attached_len, len(engine_name))
48
+ attached_width = max(
49
+ max_attached_len + 2, len("Attached To") + 2, 3
50
+ ) # At least 3 for "-"
51
+
52
+ # Fixed widths for other columns
53
+ status_width = 12
54
+ size_width = 10
55
+ id_width = 25
56
+
57
+ # Table top border
58
+ print(
59
+ "╭"
60
+ + "─" * (user_width + 1)
61
+ + "┬"
62
+ + "─" * (status_width + 1)
63
+ + "┬"
64
+ + "─" * (attached_width + 1)
65
+ + "┬"
66
+ + "─" * (size_width + 1)
67
+ + "┬"
68
+ + "─" * (id_width + 1)
69
+ + "╮"
70
+ )
71
+
72
+ # Table header
73
+ print(
74
+ f"│ {'User':<{user_width}}│ {'Status':<{status_width}}│ {'Attached To':<{attached_width}}│ {'Size':<{size_width}}│ {'Studio ID':<{id_width}}│"
75
+ )
76
+
77
+ # Header separator
78
+ print(
79
+ "├"
80
+ + "─" * (user_width + 1)
81
+ + "┼"
82
+ + "─" * (status_width + 1)
83
+ + "┼"
84
+ + "─" * (attached_width + 1)
85
+ + "┼"
86
+ + "─" * (size_width + 1)
87
+ + "┼"
88
+ + "─" * (id_width + 1)
89
+ + "┤"
90
+ )
91
+
92
+ # Table rows
93
+ for studio in studios:
94
+ user = studio.get("user", "unknown")
95
+ status = studio.get("status", "unknown")
96
+ size = f"{studio.get('size_gb', 0)}GB"
97
+ studio_id = studio.get("studio_id", "unknown")
98
+ attached_to = studio.get("attached_to")
99
+
100
+ # Truncate if needed
101
+ if len(user) > user_width - 1:
102
+ user = user[: user_width - 1]
103
+
104
+ # Color the user (blue)
105
+ user_display = colorize(f"{user:<{user_width}}", "34")
106
+
107
+ # Format status - display "in-use" as "attached" in purple
108
+ if status == "in-use":
109
+ display_status = "attached"
110
+ status_display = colorize(
111
+ f"{display_status:<{status_width}}", "35"
112
+ ) # Purple
113
+ elif status == "available":
114
+ status_display = colorize(f"{status:<{status_width}}", "32") # Green
115
+ elif status in ["attaching", "detaching"]:
116
+ status_display = colorize(f"{status:<{status_width}}", "33") # Yellow
117
+ elif status == "attached":
118
+ status_display = colorize(f"{status:<{status_width}}", "35") # Purple
119
+ elif status == "error":
120
+ status_display = colorize(f"{status:<{status_width}}", "31") # Red
121
+ else:
122
+ status_display = f"{status:<{status_width}}" # No color for other states
123
+
124
+ # Format Attached To column
125
+ if attached_to:
126
+ instance_id = attached_to
127
+ engine_name = engines_map.get(instance_id, "unknown")
128
+ # Engine name in white (no color)
129
+ attached_display = f"{engine_name:<{attached_width}}"
130
+ else:
131
+ attached_display = f"{'-':<{attached_width}}"
132
+
133
+ # Color the studio ID (grey)
134
+ studio_id_display = colorize(f"{studio_id:<{id_width}}", "90")
135
+
136
+ print(
137
+ f"│ {user_display}│ {status_display}│ {attached_display}│ {size:<{size_width}}│ {studio_id_display}│"
138
+ )
139
+
140
+ # Table bottom border
141
+ print(
142
+ "╰"
143
+ + "─" * (user_width + 1)
144
+ + "┴"
145
+ + "─" * (status_width + 1)
146
+ + "┴"
147
+ + "─" * (attached_width + 1)
148
+ + "┴"
149
+ + "─" * (size_width + 1)
150
+ + "┴"
151
+ + "─" * (id_width + 1)
152
+ + "╯"
153
+ )
154
+
155
+ print(f"Total: {len(studios)}\n")
156
+
157
+
158
+ def generate_scenarios() -> dict[str, dict[str, Any]]:
159
+ """Generate various test scenarios for studio list output."""
160
+
161
+ scenarios = {}
162
+
163
+ # Create a consistent engines map for all scenarios
164
+ engines_map = {
165
+ "i-0123456789abcdef0": "alice-gpu",
166
+ "i-1234567890abcdef1": "bob-cpu",
167
+ "i-2345678901abcdef2": "charlie-work",
168
+ "i-3456789012abcdef3": "diana-dev",
169
+ }
170
+
171
+ # Scenario 1: Single available studio
172
+ scenarios["single"] = {
173
+ "name": "Single Available Studio",
174
+ "studios": [
175
+ {
176
+ "user": "alice",
177
+ "status": "available",
178
+ "size_gb": 100,
179
+ "studio_id": "vol-0abc123def456789a",
180
+ "attached_to": None,
181
+ }
182
+ ],
183
+ "engines_map": engines_map,
184
+ "env": "dev",
185
+ }
186
+
187
+ # Scenario 2: Few studios with various states
188
+ scenarios["few"] = {
189
+ "name": "Few Studios - Mixed States",
190
+ "studios": [
191
+ {
192
+ "user": "alice",
193
+ "status": "in-use", # Will be displayed as "attached" in purple
194
+ "size_gb": 100,
195
+ "studio_id": "vol-0abc123def456789a",
196
+ "attached_to": "i-0123456789abcdef0",
197
+ },
198
+ {
199
+ "user": "bob",
200
+ "status": "available",
201
+ "size_gb": 200,
202
+ "studio_id": "vol-0abc123def456789b",
203
+ "attached_to": None,
204
+ },
205
+ {
206
+ "user": "charlie",
207
+ "status": "attaching",
208
+ "size_gb": 150,
209
+ "studio_id": "vol-0abc123def456789c",
210
+ "attached_to": "i-2345678901abcdef2",
211
+ },
212
+ ],
213
+ "engines_map": engines_map,
214
+ "env": "sand",
215
+ }
216
+
217
+ # Scenario 3: Many studios (production-like)
218
+ scenarios["many"] = {
219
+ "name": "Many Studios - Production",
220
+ "studios": [
221
+ {
222
+ "user": "alice",
223
+ "status": "attached",
224
+ "size_gb": 100,
225
+ "studio_id": "vol-0abc123def456789a",
226
+ "attached_to": "i-0123456789abcdef0",
227
+ },
228
+ {
229
+ "user": "bob",
230
+ "status": "attached",
231
+ "size_gb": 200,
232
+ "studio_id": "vol-0abc123def456789b",
233
+ "attached_to": "i-1234567890abcdef1",
234
+ },
235
+ {
236
+ "user": "charlie",
237
+ "status": "available",
238
+ "size_gb": 150,
239
+ "studio_id": "vol-0abc123def456789c",
240
+ "attached_to": None,
241
+ },
242
+ {
243
+ "user": "diana",
244
+ "status": "attached",
245
+ "size_gb": 250,
246
+ "studio_id": "vol-0abc123def456789d",
247
+ "attached_to": "i-3456789012abcdef3",
248
+ },
249
+ {
250
+ "user": "eve",
251
+ "status": "available",
252
+ "size_gb": 100,
253
+ "studio_id": "vol-0abc123def456789e",
254
+ "attached_to": None,
255
+ },
256
+ {
257
+ "user": "frank",
258
+ "status": "detaching",
259
+ "size_gb": 300,
260
+ "studio_id": "vol-0abc123def456789f",
261
+ "attached_to": None,
262
+ },
263
+ ],
264
+ "engines_map": engines_map,
265
+ "env": "prod",
266
+ }
267
+
268
+ # Scenario 4: Empty list
269
+ scenarios["empty"] = {
270
+ "name": "No Studios",
271
+ "studios": [],
272
+ "engines_map": engines_map,
273
+ "env": "dev",
274
+ }
275
+
276
+ # Scenario 5: All transitional states
277
+ scenarios["transitions"] = {
278
+ "name": "Transitional States",
279
+ "studios": [
280
+ {
281
+ "user": "alice",
282
+ "status": "attaching",
283
+ "size_gb": 100,
284
+ "studio_id": "vol-0abc123def456789a",
285
+ "attached_to": "i-0123456789abcdef0",
286
+ },
287
+ {
288
+ "user": "bob",
289
+ "status": "detaching",
290
+ "size_gb": 200,
291
+ "studio_id": "vol-0abc123def456789b",
292
+ "attached_to": None,
293
+ },
294
+ {
295
+ "user": "charlie",
296
+ "status": "error",
297
+ "size_gb": 150,
298
+ "studio_id": "vol-0abc123def456789c",
299
+ "attached_to": None,
300
+ },
301
+ ],
302
+ "engines_map": engines_map,
303
+ "env": "sand",
304
+ }
305
+
306
+ # Scenario 6: Long names
307
+ scenarios["long_names"] = {
308
+ "name": "Long User Names",
309
+ "studios": [
310
+ {
311
+ "user": "alice-with-very-long-username",
312
+ "status": "attached",
313
+ "size_gb": 100,
314
+ "studio_id": "vol-0abc123def456789a",
315
+ "attached_to": "i-0123456789abcdef0",
316
+ },
317
+ {
318
+ "user": "bob",
319
+ "status": "available",
320
+ "size_gb": 200,
321
+ "studio_id": "vol-0abc123def456789b",
322
+ "attached_to": None,
323
+ },
324
+ ],
325
+ "engines_map": engines_map,
326
+ "env": "dev",
327
+ }
328
+
329
+ return scenarios
330
+
331
+
332
+ def main():
333
+ parser = argparse.ArgumentParser(
334
+ description="Simulate studio list output for design iteration"
335
+ )
336
+ parser.add_argument(
337
+ "--scenario",
338
+ choices=["single", "few", "many", "empty", "transitions", "long_names", "all"],
339
+ default="all",
340
+ help="Which scenario to display (default: all)",
341
+ )
342
+ parser.add_argument(
343
+ "--env",
344
+ choices=["dev", "sand", "prod"],
345
+ help="Override environment for display",
346
+ )
347
+
348
+ args = parser.parse_args()
349
+
350
+ scenarios = generate_scenarios()
351
+
352
+ if args.scenario == "all":
353
+ # Show all scenarios
354
+ for _, scenario_data in scenarios.items():
355
+ print("\n" + "=" * 80)
356
+ print(f"SCENARIO: {scenario_data['name']}")
357
+ print("=" * 80 + "\n")
358
+
359
+ env = args.env if args.env else scenario_data["env"]
360
+ format_list_output(
361
+ scenario_data["studios"], scenario_data["engines_map"], env
362
+ )
363
+ print() # Extra newline between scenarios
364
+ else:
365
+ # Show specific scenario
366
+ scenario_data = scenarios[args.scenario]
367
+ print(f"\nSCENARIO: {scenario_data['name']}\n")
368
+
369
+ env = args.env if args.env else scenario_data["env"]
370
+ format_list_output(scenario_data["studios"], scenario_data["engines_map"], env)
371
+
372
+
373
+ if __name__ == "__main__":
374
+ main()
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python3
2
+ """Simulator for studio status output - iterate on design locally without AWS.
3
+
4
+ This lets you quickly see how the studio status command output looks for different
5
+ studio states and configurations.
6
+
7
+ Usage:
8
+ python dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py # Show all scenarios
9
+ python dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py --scenario attached # Show specific scenario
10
+ python dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py --env prod # Override environment
11
+ """
12
+
13
+ import argparse
14
+ import sys
15
+ from datetime import datetime, timedelta, timezone
16
+ from pathlib import Path
17
+
18
+ # Import standalone utilities
19
+ simulator_dir = Path(__file__).parent
20
+ sys.path.insert(0, str(simulator_dir))
21
+ from simulator_utils import format_time_ago
22
+
23
+
24
+ def colorize(text: str, color_code: str) -> str:
25
+ """Apply ANSI color code to text."""
26
+ return f"\033[{color_code}m{text}\033[0m"
27
+
28
+
29
+ def format_studio_status(studio_data: dict, env: str = "dev") -> None:
30
+ """Format and print studio status output matching the actual CLI."""
31
+
32
+ # Reordered output: User, Status, Attached to, Account, Size, Created, Studio ID
33
+ print(f"User: {studio_data['user']}")
34
+ # Status in blue
35
+ print(f"Status: {colorize(studio_data['status'], '34')}")
36
+ # Attached to in blue (if present)
37
+ if studio_data.get("attached_to"):
38
+ print(f"Attached to: {colorize(studio_data['attached_to'], '34')}")
39
+ print(f"Account: {env}")
40
+ print(f"Size: {studio_data['size_gb']}GB")
41
+ if studio_data.get("created_at"):
42
+ print(f"Created: {format_time_ago(studio_data['created_at'])}")
43
+ print(f"Studio ID: {studio_data['studio_id']}")
44
+
45
+
46
+ def generate_scenarios() -> dict:
47
+ """Generate various test scenarios for studio status output."""
48
+
49
+ scenarios = {}
50
+
51
+ # Scenario 1: Available studio (not attached)
52
+ scenarios["available"] = {
53
+ "name": "Available Studio - Not Attached",
54
+ "studio_data": {
55
+ "user": "alice",
56
+ "size_gb": 100,
57
+ "status": "available",
58
+ "created_at": (datetime.now(timezone.utc) - timedelta(days=5)).isoformat(),
59
+ "studio_id": "vol-0123456789abcdef0",
60
+ },
61
+ "env": "dev",
62
+ }
63
+
64
+ # Scenario 2: Studio attached to engine
65
+ scenarios["attached"] = {
66
+ "name": "Studio Attached to Engine",
67
+ "studio_data": {
68
+ "user": "bob",
69
+ "size_gb": 150,
70
+ "status": "attached",
71
+ "created_at": (
72
+ datetime.now(timezone.utc) - timedelta(days=21, hours=3)
73
+ ).isoformat(),
74
+ "studio_id": "vol-1234567890abcdef1",
75
+ "attached_to": "bob-main (i-1234567890abcdef1)",
76
+ },
77
+ "env": "sand",
78
+ }
79
+
80
+ # Scenario 3: Large studio in production
81
+ scenarios["large"] = {
82
+ "name": "Large Production Studio",
83
+ "studio_data": {
84
+ "user": "charlie",
85
+ "size_gb": 500,
86
+ "status": "available",
87
+ "created_at": (
88
+ datetime.now(timezone.utc) - timedelta(days=120)
89
+ ).isoformat(),
90
+ "studio_id": "vol-2345678901abcdef2",
91
+ },
92
+ "env": "prod",
93
+ }
94
+
95
+ # Scenario 4: Recently created studio
96
+ scenarios["new"] = {
97
+ "name": "Newly Created Studio",
98
+ "studio_data": {
99
+ "user": "diana",
100
+ "size_gb": 50,
101
+ "status": "available",
102
+ "created_at": (datetime.now(timezone.utc) - timedelta(hours=2)).isoformat(),
103
+ "studio_id": "vol-3456789012abcdef3",
104
+ },
105
+ "env": "dev",
106
+ }
107
+
108
+ # Scenario 5: Studio being modified
109
+ scenarios["modifying"] = {
110
+ "name": "Studio Being Modified",
111
+ "studio_data": {
112
+ "user": "eve",
113
+ "size_gb": 200,
114
+ "status": "modifying",
115
+ "created_at": (datetime.now(timezone.utc) - timedelta(days=30)).isoformat(),
116
+ "studio_id": "vol-4567890123abcdef4",
117
+ },
118
+ "env": "sand",
119
+ }
120
+
121
+ return scenarios
122
+
123
+
124
+ def main():
125
+ parser = argparse.ArgumentParser(
126
+ description="Simulate studio status output for design iteration"
127
+ )
128
+ parser.add_argument(
129
+ "--scenario",
130
+ choices=["available", "attached", "large", "new", "modifying", "all"],
131
+ default="all",
132
+ help="Which scenario to display (default: all)",
133
+ )
134
+ parser.add_argument(
135
+ "--env",
136
+ choices=["dev", "sand", "prod"],
137
+ help="Override environment for display",
138
+ )
139
+
140
+ args = parser.parse_args()
141
+
142
+ scenarios = generate_scenarios()
143
+
144
+ if args.scenario == "all":
145
+ # Show all scenarios
146
+ for scenario_key, scenario_data in scenarios.items():
147
+ print("\n" + "=" * 80)
148
+ print(f"SCENARIO: {scenario_data['name']}")
149
+ print("=" * 80 + "\n")
150
+
151
+ env = args.env if args.env else scenario_data["env"]
152
+ format_studio_status(scenario_data["studio_data"], env)
153
+ print() # Extra newline between scenarios
154
+ else:
155
+ # Show specific scenario
156
+ scenario_data = scenarios[args.scenario]
157
+ print(f"\nSCENARIO: {scenario_data['name']}\n")
158
+
159
+ env = args.env if args.env else scenario_data["env"]
160
+ format_studio_status(scenario_data["studio_data"], env)
161
+
162
+
163
+ if __name__ == "__main__":
164
+ main()