lyceum-cli 1.0.25__py3-none-any.whl → 1.0.27__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 (34) hide show
  1. lyceum/external/auth/login.py +18 -18
  2. lyceum/external/compute/execution/docker.py +4 -2
  3. lyceum/external/compute/execution/docker_compose.py +263 -0
  4. lyceum/external/compute/execution/notebook.py +0 -2
  5. lyceum/external/compute/execution/python.py +2 -1
  6. lyceum/external/compute/inference/batch.py +8 -10
  7. lyceum/external/vms/instances.py +301 -0
  8. lyceum/external/vms/management.py +383 -0
  9. lyceum/main.py +3 -0
  10. lyceum/shared/config.py +19 -24
  11. lyceum/shared/display.py +12 -31
  12. lyceum/shared/streaming.py +17 -45
  13. {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/METADATA +1 -1
  14. lyceum_cli-1.0.27.dist-info/RECORD +34 -0
  15. {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/WHEEL +1 -1
  16. {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/top_level.txt +0 -1
  17. lyceum/external/compute/execution/docker_config.py +0 -123
  18. lyceum/external/storage/files.py +0 -273
  19. lyceum_cli-1.0.25.dist-info/RECORD +0 -46
  20. tests/__init__.py +0 -1
  21. tests/conftest.py +0 -200
  22. tests/unit/__init__.py +0 -1
  23. tests/unit/external/__init__.py +0 -1
  24. tests/unit/external/compute/__init__.py +0 -1
  25. tests/unit/external/compute/execution/__init__.py +0 -1
  26. tests/unit/external/compute/execution/test_data.py +0 -33
  27. tests/unit/external/compute/execution/test_dependency_resolver.py +0 -257
  28. tests/unit/external/compute/execution/test_python_helpers.py +0 -406
  29. tests/unit/external/compute/execution/test_python_run.py +0 -289
  30. tests/unit/shared/__init__.py +0 -1
  31. tests/unit/shared/test_config.py +0 -341
  32. tests/unit/shared/test_streaming.py +0 -259
  33. /lyceum/external/{storage → vms}/__init__.py +0 -0
  34. {lyceum_cli-1.0.25.dist-info → lyceum_cli-1.0.27.dist-info}/entry_points.txt +0 -0
@@ -1,259 +0,0 @@
1
- """Tests for streaming utilities."""
2
-
3
- import json
4
- from unittest.mock import MagicMock, patch
5
-
6
- import pytest
7
-
8
- from lyceum.shared.streaming import (
9
- StatusLine,
10
- check_execution_status,
11
- normalize_newlines,
12
- stream_execution_output,
13
- strip_ansi_codes,
14
- )
15
-
16
-
17
- class TestStripAnsiCodes:
18
- """Tests for strip_ansi_codes function."""
19
-
20
- def test_removes_color_codes(self):
21
- text = "\x1b[31mRed text\x1b[0m"
22
- assert strip_ansi_codes(text) == "Red text"
23
-
24
- def test_removes_multiple_color_codes(self):
25
- text = "\x1b[1m\x1b[31mBold Red\x1b[0m Normal"
26
- assert strip_ansi_codes(text) == "Bold Red Normal"
27
-
28
- def test_preserves_plain_text(self):
29
- text = "Plain text without codes"
30
- assert strip_ansi_codes(text) == text
31
-
32
- def test_handles_empty_string(self):
33
- assert strip_ansi_codes("") == ""
34
-
35
- def test_handles_nested_codes(self):
36
- text = "\x1b[38;5;196mExtended color\x1b[0m"
37
- assert strip_ansi_codes(text) == "Extended color"
38
-
39
-
40
- class TestNormalizeNewlines:
41
- """Tests for normalize_newlines function."""
42
-
43
- def test_collapses_double_newlines(self):
44
- assert normalize_newlines("a\n\nb") == "a\nb"
45
-
46
- def test_collapses_triple_newlines(self):
47
- assert normalize_newlines("a\n\n\nb") == "a\nb"
48
-
49
- def test_preserves_single_newlines(self):
50
- assert normalize_newlines("a\nb\nc") == "a\nb\nc"
51
-
52
- def test_handles_mixed_newlines(self):
53
- text = "a\nb\n\n\nc\nd"
54
- assert normalize_newlines(text) == "a\nb\nc\nd"
55
-
56
- def test_handles_empty_string(self):
57
- assert normalize_newlines("") == ""
58
-
59
- def test_handles_only_newlines(self):
60
- assert normalize_newlines("\n\n\n") == "\n"
61
-
62
-
63
- class TestStatusLine:
64
- """Tests for StatusLine class."""
65
-
66
- def test_start_stop_lifecycle(self):
67
- status = StatusLine()
68
- status.start()
69
- assert status._live is not None
70
- status.stop()
71
- assert status._live is None
72
-
73
- def test_multiple_stops_no_error(self):
74
- status = StatusLine()
75
- status.start()
76
- status.stop()
77
- status.stop() # Should not raise
78
-
79
- def test_context_manager(self):
80
- with StatusLine() as status:
81
- assert status._live is not None
82
- assert status._live is None
83
-
84
- def test_update_message(self):
85
- status = StatusLine()
86
- status.start()
87
- status.update("Loading...") # Should not raise
88
- assert status._current_status == "Loading..."
89
- status.stop()
90
-
91
- def test_update_without_start(self):
92
- status = StatusLine()
93
- status.update("Test") # Should not raise even without start
94
- assert status._current_status == "Test"
95
-
96
- def test_stop_without_start(self):
97
- status = StatusLine()
98
- status.stop() # Should not raise
99
-
100
-
101
- class TestStreamExecutionOutput:
102
- """Tests for stream_execution_output function."""
103
-
104
- @patch("lyceum.shared.streaming.httpx.stream")
105
- @patch("lyceum.shared.streaming.config")
106
- def test_success_with_output(
107
- self,
108
- mock_config,
109
- mock_httpx_stream,
110
- setup_httpx_stream,
111
- mock_execution_id,
112
- ):
113
- from tests.unit.external.compute.execution.test_data import (
114
- SAMPLE_SSE_JOB_FINISHED,
115
- SAMPLE_SSE_OUTPUT_EVENT,
116
- )
117
-
118
- mock_config.api_key = "test-key"
119
- mock_config.base_url = "https://api.lyceum.dev"
120
- mock_httpx_stream.return_value = setup_httpx_stream(
121
- events=[SAMPLE_SSE_OUTPUT_EVENT, SAMPLE_SSE_JOB_FINISHED]
122
- )
123
-
124
- result = stream_execution_output(mock_execution_id, "http://stream.url")
125
- assert result is True
126
-
127
- @patch("lyceum.shared.streaming.httpx.stream")
128
- @patch("lyceum.shared.streaming.config")
129
- def test_failure_with_nonzero_exit(
130
- self,
131
- mock_config,
132
- mock_httpx_stream,
133
- setup_httpx_stream,
134
- mock_execution_id,
135
- ):
136
- from tests.unit.external.compute.execution.test_data import SAMPLE_SSE_JOB_FAILED
137
-
138
- mock_config.api_key = "test-key"
139
- mock_config.base_url = "https://api.lyceum.dev"
140
- mock_httpx_stream.return_value = setup_httpx_stream(events=[SAMPLE_SSE_JOB_FAILED])
141
-
142
- result = stream_execution_output(mock_execution_id, "http://stream.url")
143
- assert result is False
144
-
145
- @patch("lyceum.shared.streaming.httpx.stream")
146
- @patch("lyceum.shared.streaming.config")
147
- def test_http_error_response(
148
- self,
149
- mock_config,
150
- mock_httpx_stream,
151
- mock_execution_id,
152
- ):
153
- mock_config.api_key = "test-key"
154
- mock_config.base_url = "https://api.lyceum.dev"
155
-
156
- mock_response = MagicMock()
157
- mock_response.status_code = 500
158
- mock_context = MagicMock()
159
- mock_context.__enter__.return_value = mock_response
160
- mock_context.__exit__.return_value = None
161
- mock_httpx_stream.return_value = mock_context
162
-
163
- result = stream_execution_output(mock_execution_id, "http://stream.url")
164
- assert result is False
165
-
166
- @patch("lyceum.shared.streaming.httpx.stream")
167
- @patch("lyceum.shared.streaming.config")
168
- def test_uses_default_url_when_not_provided(
169
- self,
170
- mock_config,
171
- mock_httpx_stream,
172
- setup_httpx_stream,
173
- mock_execution_id,
174
- ):
175
- from tests.unit.external.compute.execution.test_data import SAMPLE_SSE_JOB_FINISHED
176
-
177
- mock_config.api_key = "test-key"
178
- mock_config.base_url = "https://api.lyceum.dev"
179
- mock_httpx_stream.return_value = setup_httpx_stream(events=[SAMPLE_SSE_JOB_FINISHED])
180
-
181
- stream_execution_output(mock_execution_id)
182
-
183
- # Check that the default URL was constructed
184
- call_args = mock_httpx_stream.call_args
185
- assert mock_execution_id in call_args[0][1]
186
-
187
-
188
- class TestCheckExecutionStatus:
189
- """Tests for check_execution_status function."""
190
-
191
- @patch("lyceum.shared.streaming.httpx.get")
192
- @patch("lyceum.shared.streaming.config")
193
- def test_completed_status(self, mock_config, mock_httpx_get, mock_execution_id):
194
- mock_config.api_key = "test-key"
195
- mock_config.base_url = "https://api.lyceum.dev"
196
-
197
- mock_response = MagicMock()
198
- mock_response.status_code = 200
199
- mock_response.json.return_value = {"status": "completed"}
200
- mock_httpx_get.return_value = mock_response
201
-
202
- result = check_execution_status(mock_execution_id)
203
- assert result is True
204
-
205
- @patch("lyceum.shared.streaming.httpx.get")
206
- @patch("lyceum.shared.streaming.config")
207
- def test_failed_user_status(self, mock_config, mock_httpx_get, mock_execution_id):
208
- mock_config.api_key = "test-key"
209
- mock_config.base_url = "https://api.lyceum.dev"
210
-
211
- mock_response = MagicMock()
212
- mock_response.status_code = 200
213
- mock_response.json.return_value = {"status": "failed_user", "errors": "Error msg"}
214
- mock_httpx_get.return_value = mock_response
215
-
216
- result = check_execution_status(mock_execution_id)
217
- assert result is False
218
-
219
- @patch("lyceum.shared.streaming.httpx.get")
220
- @patch("lyceum.shared.streaming.config")
221
- def test_failed_system_status(self, mock_config, mock_httpx_get, mock_execution_id):
222
- mock_config.api_key = "test-key"
223
- mock_config.base_url = "https://api.lyceum.dev"
224
-
225
- mock_response = MagicMock()
226
- mock_response.status_code = 200
227
- mock_response.json.return_value = {"status": "failed_system"}
228
- mock_httpx_get.return_value = mock_response
229
-
230
- result = check_execution_status(mock_execution_id)
231
- assert result is False
232
-
233
- @patch("lyceum.shared.streaming.httpx.get")
234
- @patch("lyceum.shared.streaming.config")
235
- def test_timeout_status(self, mock_config, mock_httpx_get, mock_execution_id):
236
- mock_config.api_key = "test-key"
237
- mock_config.base_url = "https://api.lyceum.dev"
238
-
239
- mock_response = MagicMock()
240
- mock_response.status_code = 200
241
- mock_response.json.return_value = {"status": "timeout"}
242
- mock_httpx_get.return_value = mock_response
243
-
244
- result = check_execution_status(mock_execution_id)
245
- assert result is False
246
-
247
- @patch("lyceum.shared.streaming.httpx.get")
248
- @patch("lyceum.shared.streaming.config")
249
- def test_cancelled_status(self, mock_config, mock_httpx_get, mock_execution_id):
250
- mock_config.api_key = "test-key"
251
- mock_config.base_url = "https://api.lyceum.dev"
252
-
253
- mock_response = MagicMock()
254
- mock_response.status_code = 200
255
- mock_response.json.return_value = {"status": "cancelled"}
256
- mock_httpx_get.return_value = mock_response
257
-
258
- result = check_execution_status(mock_execution_id)
259
- assert result is False
File without changes