lyceum-cli 1.0.13__tar.gz → 1.0.18__tar.gz
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.
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/PKG-INFO +1 -1
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/external/compute/execution/python.py +33 -30
- lyceum_cli-1.0.18/lyceum/external/compute/inference/__init__.py +2 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/main.py +0 -8
- lyceum_cli-1.0.18/lyceum/shared/__init__.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/shared/config.py +7 -8
- lyceum_cli-1.0.18/lyceum/shared/display.py +195 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/shared/streaming.py +57 -7
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum_cli.egg-info/PKG-INFO +1 -1
- lyceum_cli-1.0.18/lyceum_cli.egg-info/SOURCES.txt +27 -0
- lyceum_cli-1.0.18/lyceum_cloud_execution_api_client/__init__.py +0 -0
- lyceum_cli-1.0.18/lyceum_cloud_execution_api_client/api/__init__.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/setup.py +1 -1
- lyceum_cli-1.0.13/lyceum/__init__.py +0 -3
- lyceum_cli-1.0.13/lyceum/external/auth/api_keys.py +0 -58
- lyceum_cli-1.0.13/lyceum/external/compute/execution/docker.py +0 -134
- lyceum_cli-1.0.13/lyceum/external/compute/execution/workloads.py +0 -129
- lyceum_cli-1.0.13/lyceum/external/compute/inference/__init__.py +0 -5
- lyceum_cli-1.0.13/lyceum/external/general/resources.py +0 -51
- lyceum_cli-1.0.13/lyceum/shared/display.py +0 -32
- lyceum_cli-1.0.13/lyceum_cli.egg-info/SOURCES.txt +0 -130
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/__init__.py +0 -8
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/api_keys/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/api_keys/create_api_key_api_v1_api_keys_post.py +0 -172
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/api_keys/list_api_keys_api_v1_api_keys_get.py +0 -139
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/api_keys/revoke_api_key_api_v1_api_keys_api_key_id_delete.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/api_keys/toggle_api_key_api_v1_api_keys_api_key_id_toggle_patch.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/authentication/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/authentication/get_current_user_info_api_v1_auth_me_get.py +0 -83
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/authentication/login_api_v2_external_auth_login_post.py +0 -176
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/authentication/validate_token_api_v1_auth_validate_post.py +0 -85
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/billing_credits/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/billing_credits/create_checkout_session_api_v2_external_billing_checkout_post.py +0 -172
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/billing_credits/get_execution_history_api_v2_external_billing_history_get.py +0 -181
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/billing_credits/get_user_credits_api_v2_external_billing_credits_get.py +0 -134
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/billing_credits/get_user_credits_details_api_v2_external_billing_credits_details_get.py +0 -130
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/cloud_storage/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/cloud_storage/connect_cloud_storage_api_v1_api_cloud_storage_connect_post.py +0 -170
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/cloud_storage/disconnect_cloud_storage_api_v1_api_cloud_storage_provider_delete.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/cloud_storage/get_cloud_storage_credentials_api_v1_api_cloud_storage_credentials_provider_get.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/cloud_storage/get_cloud_storage_status_api_v1_api_cloud_storage_status_get.py +0 -134
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/code_execution/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/code_execution/run_code_api_v1_execution_run_post.py +0 -172
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/code_execution/start_execution_api_v1_execution_start_post.py +0 -188
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/code_execution/start_execution_api_v2_external_compute_execution_run_post.py +0 -206
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/code_execution/start_prebuilt_api_v1_execution_start_prebuilt_post.py +0 -164
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/code_execution/stream_execution_api_v1_execution_stream_execution_id_get.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/default/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/default/create_checkout_session_api_v1_create_checkout_session_post.py +0 -162
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/default/delete_user_api_v1_delete_user_post.py +0 -162
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/default/root_get.py +0 -79
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/default/stripe_webhook_api_v1_webhook_post.py +0 -79
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/default/upload_file_api_v1_upload_file_post.py +0 -162
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/docker_execution/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/docker_execution/start_docker_execution_api_v2_external_compute_execution_docker_post.py +0 -172
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/docker_execution/stream_docker_execution_api_v2_external_compute_execution_stream_execution_id_get.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/execution_history/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/execution_history/get_execution_detail_api_v1_history_execution_id_get.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/execution_history/get_execution_history_api_v1_history_get.py +0 -184
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/machine_types/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/machine_types/get_machine_types_api_v2_external_compute_machine_types_get.py +0 -134
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_credentials/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_credentials/get_storage_credentials_api_v2_external_storage_credentials_post.py +0 -134
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/delete_file_api_v2_external_storage_delete_file_key_delete.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/delete_folder_api_v2_external_storage_delete_folder_folder_prefix_delete.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/download_file_api_v2_external_storage_download_file_key_get.py +0 -161
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/list_files_api_v2_external_storage_list_files_get.py +0 -191
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/upload_bulk_files_api_v2_external_storage_upload_bulk_post.py +0 -172
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/storage_files/upload_file_api_v2_external_storage_upload_post.py +0 -195
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/workload_management/__init__.py +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/workload_management/abort_execution_api_v1_workloads_abort_execution_id_post.py +0 -163
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/api/workload_management/list_non_complete_executions_api_v1_workloads_list_get.py +0 -139
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/client.py +0 -268
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/errors.py +0 -16
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/abort_response.py +0 -76
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/api_key_create.py +0 -93
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/api_key_create_response.py +0 -119
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/api_key_response.py +0 -143
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/body_upload_bulk_files_api_v2_external_storage_upload_bulk_post.py +0 -109
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/body_upload_file_api_v1_upload_file_post.py +0 -78
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/body_upload_file_api_v2_external_storage_upload_post.py +0 -73
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/bulk_upload_response.py +0 -105
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/bulk_upload_result.py +0 -105
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/checkout_session_request.py +0 -101
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/checkout_session_response.py +0 -67
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/cloud_storage_status.py +0 -85
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/cloud_storage_status_aws_s3.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/cloud_storage_status_azure_blob.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/cloud_storage_status_gcp_storage.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/code_execution.py +0 -221
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/connect_request.py +0 -73
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/connect_request_credentials.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/create_checkout_session_request.py +0 -79
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/credits_balance.py +0 -83
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/delete_user_request.py +0 -59
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/docker_execution.py +0 -228
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/docker_execution_docker_env_type_0.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/docker_execution_response.py +0 -83
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/execution_response.py +0 -211
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/execution_response_result_files_type_0.py +0 -63
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/execution_response_result_files_type_0_additional_property.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/execution_summary.py +0 -114
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/file_info.py +0 -85
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/http_validation_error.py +0 -75
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/login_request.py +0 -67
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/login_response.py +0 -83
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/machine_type.py +0 -67
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/machine_types_response.py +0 -81
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/start_execution_api_v1_execution_start_post_response_start_execution_api_v1_execution_start_post.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/start_execution_api_v2_external_compute_execution_run_post_response_start_execution_api_v2_external_compute_execution_run_post.py +0 -47
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/start_prebuilt_execution_aws_credentials.py +0 -59
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/start_prebuilt_execution_request.py +0 -209
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/start_prebuilt_execution_request_docker_run_env_type_0.py +0 -44
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/start_prebuilt_execution_response.py +0 -59
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/storage_credentials.py +0 -113
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/upload_response.py +0 -83
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/user_credits.py +0 -131
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/models/validation_error.py +0 -88
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/py.typed +0 -1
- lyceum_cli-1.0.13/lyceum_cloud_execution_api_client/types.py +0 -54
- {lyceum_cli-1.0.13/lyceum/external → lyceum_cli-1.0.18/lyceum}/__init__.py +0 -0
- {lyceum_cli-1.0.13/lyceum/external/auth → lyceum_cli-1.0.18/lyceum/external}/__init__.py +0 -0
- {lyceum_cli-1.0.13/lyceum/external/compute → lyceum_cli-1.0.18/lyceum/external/auth}/__init__.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/external/auth/login.py +0 -0
- {lyceum_cli-1.0.13/lyceum/external/compute/execution → lyceum_cli-1.0.18/lyceum/external/compute}/__init__.py +0 -0
- {lyceum_cli-1.0.13/lyceum/external/general → lyceum_cli-1.0.18/lyceum/external/compute/execution}/__init__.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/external/compute/inference/batch.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/external/compute/inference/chat.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum/external/compute/inference/models.py +0 -0
- {lyceum_cli-1.0.13/lyceum/shared → lyceum_cli-1.0.18/lyceum/external/general}/__init__.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum_cli.egg-info/dependency_links.txt +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum_cli.egg-info/entry_points.txt +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum_cli.egg-info/requires.txt +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum_cli.egg-info/top_level.txt +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/lyceum_cloud_execution_api_client/models/__init__.py +0 -0
- {lyceum_cli-1.0.13 → lyceum_cli-1.0.18}/setup.cfg +0 -0
|
@@ -6,16 +6,13 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
import typer
|
|
8
8
|
from rich.console import Console
|
|
9
|
+
import httpx
|
|
9
10
|
|
|
10
11
|
from ....shared.config import config
|
|
11
12
|
from ....shared.streaming import stream_execution_output
|
|
12
13
|
|
|
13
14
|
console = Console()
|
|
14
15
|
|
|
15
|
-
# Import generated client modules
|
|
16
|
-
from lyceum_cloud_execution_api_client.api.code_execution import start_execution_api_v2_external_compute_execution_run_post
|
|
17
|
-
from lyceum_cloud_execution_api_client.models import CodeExecution
|
|
18
|
-
|
|
19
16
|
python_app = typer.Typer(name="python", help="Python execution commands")
|
|
20
17
|
|
|
21
18
|
|
|
@@ -29,8 +26,6 @@ def run_python(
|
|
|
29
26
|
imports: Optional[list[str]] = typer.Option(None, "--import", help="Pre-import modules (can be used multiple times)"),
|
|
30
27
|
):
|
|
31
28
|
"""Execute Python code or file on Lyceum Cloud"""
|
|
32
|
-
client = config.get_client()
|
|
33
|
-
|
|
34
29
|
try:
|
|
35
30
|
# Check if it's a file path
|
|
36
31
|
code_to_execute = code_or_file
|
|
@@ -41,7 +36,7 @@ def run_python(
|
|
|
41
36
|
# Use filename as execution name if not provided
|
|
42
37
|
if not file_name:
|
|
43
38
|
file_name = Path(code_or_file).name
|
|
44
|
-
|
|
39
|
+
|
|
45
40
|
# Handle requirements
|
|
46
41
|
requirements_content = None
|
|
47
42
|
if requirements:
|
|
@@ -53,39 +48,47 @@ def run_python(
|
|
|
53
48
|
else:
|
|
54
49
|
# Treat as direct pip requirements string
|
|
55
50
|
requirements_content = requirements
|
|
56
|
-
|
|
57
|
-
# Create execution request
|
|
58
|
-
|
|
59
|
-
code
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
|
|
52
|
+
# Create execution request payload
|
|
53
|
+
payload = {
|
|
54
|
+
"code": code_to_execute,
|
|
55
|
+
"nbcode": 0,
|
|
56
|
+
"execution_type": machine_type,
|
|
57
|
+
"timeout": timeout,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if file_name:
|
|
61
|
+
payload["file_name"] = file_name
|
|
62
|
+
if requirements_content:
|
|
63
|
+
payload["requirements_content"] = requirements_content
|
|
64
|
+
if imports:
|
|
65
|
+
payload["prior_imports"] = imports
|
|
66
|
+
|
|
67
|
+
# Make API request
|
|
68
|
+
response = httpx.post(
|
|
69
|
+
f"{config.base_url}/api/v2/external/execution/streaming/start",
|
|
70
|
+
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
71
|
+
json=payload,
|
|
72
|
+
timeout=30.0
|
|
70
73
|
)
|
|
71
|
-
|
|
74
|
+
|
|
72
75
|
if response.status_code != 200:
|
|
73
76
|
console.print(f"[red]Error: HTTP {response.status_code}[/red]")
|
|
74
|
-
|
|
75
|
-
console.print(f"[red]{response.content}[/red]")
|
|
77
|
+
console.print(f"[red]{response.content.decode()}[/red]")
|
|
76
78
|
raise typer.Exit(1)
|
|
77
|
-
|
|
78
|
-
data = response.
|
|
79
|
+
|
|
80
|
+
data = response.json()
|
|
79
81
|
execution_id = data['execution_id']
|
|
80
|
-
|
|
82
|
+
streaming_url = data.get('streaming_url')
|
|
83
|
+
|
|
81
84
|
console.print(f"[green]✅ Execution started![/green]")
|
|
82
85
|
console.print(f"[dim]Execution ID: {execution_id}[/dim]")
|
|
83
|
-
|
|
86
|
+
|
|
84
87
|
if 'pythia_decision' in data:
|
|
85
88
|
console.print(f"[dim]Pythia recommendation: {data['pythia_decision']}[/dim]")
|
|
86
|
-
|
|
89
|
+
|
|
87
90
|
# Stream the execution output
|
|
88
|
-
success = stream_execution_output(execution_id,
|
|
91
|
+
success = stream_execution_output(execution_id, streaming_url)
|
|
89
92
|
|
|
90
93
|
if not success:
|
|
91
94
|
console.print(f"[yellow]💡 You can check the execution later with: lyceum status[/yellow]")
|
|
@@ -9,14 +9,10 @@ from rich.console import Console
|
|
|
9
9
|
|
|
10
10
|
# Import all command modules
|
|
11
11
|
from .external.auth.login import auth_app
|
|
12
|
-
from .external.auth.api_keys import api_keys_app
|
|
13
12
|
from .external.compute.execution.python import python_app
|
|
14
|
-
from .external.compute.execution.docker import docker_app
|
|
15
|
-
from .external.compute.execution.workloads import workloads_app
|
|
16
13
|
from .external.compute.inference.batch import batch_app
|
|
17
14
|
from .external.compute.inference.chat import chat_app
|
|
18
15
|
from .external.compute.inference.models import models_app
|
|
19
|
-
from .external.general.resources import resources_app
|
|
20
16
|
|
|
21
17
|
app = typer.Typer(
|
|
22
18
|
name="lyceum",
|
|
@@ -28,14 +24,10 @@ console = Console()
|
|
|
28
24
|
|
|
29
25
|
# Add all command groups
|
|
30
26
|
app.add_typer(auth_app, name="auth")
|
|
31
|
-
app.add_typer(api_keys_app, name="api-keys")
|
|
32
27
|
app.add_typer(python_app, name="python")
|
|
33
|
-
app.add_typer(docker_app, name="docker")
|
|
34
|
-
app.add_typer(workloads_app, name="workloads")
|
|
35
28
|
app.add_typer(batch_app, name="batch")
|
|
36
29
|
app.add_typer(chat_app, name="chat")
|
|
37
30
|
app.add_typer(models_app, name="models")
|
|
38
|
-
app.add_typer(resources_app, name="resources")
|
|
39
31
|
|
|
40
32
|
# Legacy aliases for backward compatibility
|
|
41
33
|
|
|
File without changes
|
|
@@ -17,7 +17,8 @@ from supabase import create_client
|
|
|
17
17
|
import sys
|
|
18
18
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "lyceum-cloud-execution-api-client"))
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# Commented out - using httpx directly instead
|
|
21
|
+
# from lyceum_cloud_execution_api_client.client import AuthenticatedClient
|
|
21
22
|
|
|
22
23
|
console = Console()
|
|
23
24
|
|
|
@@ -122,23 +123,21 @@ class Config:
|
|
|
122
123
|
console.print(f"[yellow]⚠️ Token refresh error: {e}[/yellow]")
|
|
123
124
|
return False
|
|
124
125
|
|
|
125
|
-
def get_client(self)
|
|
126
|
+
def get_client(self):
|
|
126
127
|
"""Get authenticated API client with automatic token refresh"""
|
|
127
128
|
if not self.api_key:
|
|
128
129
|
console.print("[red]Error: Not authenticated. Run 'lyceum login' first.[/red]")
|
|
129
130
|
raise typer.Exit(1)
|
|
130
|
-
|
|
131
|
+
|
|
131
132
|
# Check if token is expired and try to refresh
|
|
132
133
|
if self.is_token_expired():
|
|
133
134
|
console.print("[dim]🔄 Token expired, attempting refresh...[/dim]")
|
|
134
135
|
if not self.refresh_access_token():
|
|
135
136
|
console.print("[red]❌ Token refresh failed. Please run 'lyceum login' again.[/red]")
|
|
136
137
|
raise typer.Exit(1)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
token=self.api_key
|
|
141
|
-
)
|
|
138
|
+
|
|
139
|
+
# Return config instance - commands use httpx directly
|
|
140
|
+
return self
|
|
142
141
|
|
|
143
142
|
|
|
144
143
|
# Global config instance
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Display utilities for CLI output formatting
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_table(title: str, columns: list[tuple[str, str]]) -> Table:
|
|
11
|
+
"""
|
|
12
|
+
Create a Rich table with the given title and columns.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
title: Table title
|
|
16
|
+
columns: List of (column_name, style) tuples
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Rich Table instance
|
|
20
|
+
"""
|
|
21
|
+
table = Table(title=title)
|
|
22
|
+
for col_name, style in columns:
|
|
23
|
+
table.add_column(col_name, style=style)
|
|
24
|
+
return table
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def format_timestamp(timestamp: str | int | datetime, relative: bool = False) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Format a timestamp for display.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
timestamp: Unix timestamp (int/float), ISO string, or datetime object
|
|
33
|
+
relative: If True, show relative time (e.g., "2 hours ago")
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Formatted timestamp string
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
# Convert to datetime if needed
|
|
40
|
+
if isinstance(timestamp, (int, float)):
|
|
41
|
+
dt = datetime.fromtimestamp(timestamp)
|
|
42
|
+
elif isinstance(timestamp, str):
|
|
43
|
+
# Try parsing ISO format
|
|
44
|
+
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
|
45
|
+
elif isinstance(timestamp, datetime):
|
|
46
|
+
dt = timestamp
|
|
47
|
+
else:
|
|
48
|
+
return str(timestamp)
|
|
49
|
+
|
|
50
|
+
if relative:
|
|
51
|
+
# Calculate relative time
|
|
52
|
+
now = datetime.now(dt.tzinfo) if dt.tzinfo else datetime.now()
|
|
53
|
+
diff = now - dt
|
|
54
|
+
|
|
55
|
+
seconds = diff.total_seconds()
|
|
56
|
+
if seconds < 60:
|
|
57
|
+
return f"{int(seconds)}s ago"
|
|
58
|
+
elif seconds < 3600:
|
|
59
|
+
return f"{int(seconds / 60)}m ago"
|
|
60
|
+
elif seconds < 86400:
|
|
61
|
+
return f"{int(seconds / 3600)}h ago"
|
|
62
|
+
elif seconds < 604800:
|
|
63
|
+
return f"{int(seconds / 86400)}d ago"
|
|
64
|
+
else:
|
|
65
|
+
return dt.strftime("%Y-%m-%d")
|
|
66
|
+
else:
|
|
67
|
+
# Return absolute time
|
|
68
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
69
|
+
|
|
70
|
+
except Exception:
|
|
71
|
+
return str(timestamp)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def truncate_id(id_str: str, length: int = 8) -> str:
|
|
75
|
+
"""
|
|
76
|
+
Truncate an ID string for display.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
id_str: The ID string to truncate
|
|
80
|
+
length: Number of characters to keep from the start
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Truncated ID string
|
|
84
|
+
"""
|
|
85
|
+
if not id_str:
|
|
86
|
+
return ""
|
|
87
|
+
|
|
88
|
+
if len(id_str) <= length:
|
|
89
|
+
return id_str
|
|
90
|
+
|
|
91
|
+
return f"{id_str[:length]}..."
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def format_file_size(bytes: int) -> str:
|
|
95
|
+
"""
|
|
96
|
+
Format file size in human-readable format.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
bytes: Size in bytes
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Formatted size string (e.g., "1.5 MB")
|
|
103
|
+
"""
|
|
104
|
+
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
|
105
|
+
if bytes < 1024.0:
|
|
106
|
+
return f"{bytes:.1f} {unit}"
|
|
107
|
+
bytes /= 1024.0
|
|
108
|
+
return f"{bytes:.1f} PB"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def format_duration(seconds: float) -> str:
|
|
112
|
+
"""
|
|
113
|
+
Format duration in human-readable format.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
seconds: Duration in seconds
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Formatted duration string (e.g., "2h 15m 30s")
|
|
120
|
+
"""
|
|
121
|
+
if seconds < 60:
|
|
122
|
+
return f"{seconds:.1f}s"
|
|
123
|
+
|
|
124
|
+
minutes = int(seconds / 60)
|
|
125
|
+
secs = int(seconds % 60)
|
|
126
|
+
|
|
127
|
+
if minutes < 60:
|
|
128
|
+
return f"{minutes}m {secs}s"
|
|
129
|
+
|
|
130
|
+
hours = int(minutes / 60)
|
|
131
|
+
mins = int(minutes % 60)
|
|
132
|
+
|
|
133
|
+
if hours < 24:
|
|
134
|
+
return f"{hours}h {mins}m"
|
|
135
|
+
|
|
136
|
+
days = int(hours / 24)
|
|
137
|
+
hrs = int(hours % 24)
|
|
138
|
+
|
|
139
|
+
return f"{days}d {hrs}h"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def status_color(status: str) -> str:
|
|
143
|
+
"""
|
|
144
|
+
Get color for status display.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
status: Status string
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Color name for Rich formatting
|
|
151
|
+
"""
|
|
152
|
+
status_lower = status.lower()
|
|
153
|
+
|
|
154
|
+
if status_lower in ['completed', 'success', 'running']:
|
|
155
|
+
return 'green'
|
|
156
|
+
elif status_lower in ['failed', 'error', 'failed_user', 'failed_system']:
|
|
157
|
+
return 'red'
|
|
158
|
+
elif status_lower in ['pending', 'queued', 'in_progress', 'validating', 'finalizing']:
|
|
159
|
+
return 'yellow'
|
|
160
|
+
elif status_lower in ['cancelled', 'expired', 'timeout']:
|
|
161
|
+
return 'dim'
|
|
162
|
+
else:
|
|
163
|
+
return 'white'
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def status_emoji(status: str) -> str:
|
|
167
|
+
"""
|
|
168
|
+
Get emoji for status display.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
status: Status string
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Emoji character
|
|
175
|
+
"""
|
|
176
|
+
status_lower = status.lower()
|
|
177
|
+
|
|
178
|
+
if status_lower in ['completed', 'success']:
|
|
179
|
+
return '✅'
|
|
180
|
+
elif status_lower in ['failed', 'error', 'failed_user', 'failed_system']:
|
|
181
|
+
return '❌'
|
|
182
|
+
elif status_lower in ['running', 'in_progress']:
|
|
183
|
+
return '🔄'
|
|
184
|
+
elif status_lower in ['pending', 'queued']:
|
|
185
|
+
return '⏳'
|
|
186
|
+
elif status_lower in ['validating']:
|
|
187
|
+
return '🔍'
|
|
188
|
+
elif status_lower in ['finalizing']:
|
|
189
|
+
return '📦'
|
|
190
|
+
elif status_lower in ['cancelled']:
|
|
191
|
+
return '🛑'
|
|
192
|
+
elif status_lower in ['expired', 'timeout']:
|
|
193
|
+
return '⏰'
|
|
194
|
+
else:
|
|
195
|
+
return '⚪'
|
|
@@ -18,13 +18,18 @@ def strip_ansi_codes(text: str) -> str:
|
|
|
18
18
|
return ansi_escape.sub('', text)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def stream_execution_output(execution_id: str,
|
|
21
|
+
def stream_execution_output(execution_id: str, streaming_url: str = None) -> bool:
|
|
22
22
|
"""Stream execution output in real-time. Returns True if successful, False if failed."""
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if not streaming_url:
|
|
24
|
+
# Fallback to old endpoint if no streaming URL provided
|
|
25
|
+
stream_url = f"{config.base_url}/api/v2/external/execution/streaming/{execution_id}"
|
|
26
|
+
else:
|
|
27
|
+
stream_url = streaming_url
|
|
28
|
+
|
|
25
29
|
try:
|
|
26
30
|
console.print(f"[dim]🔗 Connecting to execution stream...[/dim]")
|
|
27
|
-
|
|
31
|
+
console.print(f"[dim]Stream URL: {stream_url}[/dim]")
|
|
32
|
+
|
|
28
33
|
with httpx.stream("GET", stream_url, headers={"Authorization": f"Bearer {config.api_key}"}, timeout=600.0) as response:
|
|
29
34
|
if response.status_code != 200:
|
|
30
35
|
console.print(f"[red]❌ Stream failed: HTTP {response.status_code}[/red]")
|
|
@@ -77,8 +82,53 @@ def stream_execution_output(execution_id: str, client) -> bool:
|
|
|
77
82
|
continue
|
|
78
83
|
|
|
79
84
|
console.print(f"\n[yellow]⚠️ Stream ended without completion signal[/yellow]")
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
# Fallback: poll execution status
|
|
86
|
+
return check_execution_status(execution_id)
|
|
87
|
+
|
|
82
88
|
except Exception as e:
|
|
83
89
|
console.print(f"\n[red]❌ Streaming error: {e}[/red]")
|
|
84
|
-
|
|
90
|
+
# Fallback: poll execution status
|
|
91
|
+
return check_execution_status(execution_id)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def check_execution_status(execution_id: str) -> bool:
|
|
95
|
+
"""Check execution status as fallback when streaming fails."""
|
|
96
|
+
import time
|
|
97
|
+
|
|
98
|
+
console.print("[dim]⏳ Checking execution status...[/dim]")
|
|
99
|
+
|
|
100
|
+
for _ in range(30): # Poll for up to 30 seconds
|
|
101
|
+
try:
|
|
102
|
+
response = httpx.get(
|
|
103
|
+
f"{config.base_url}/api/v2/external/execution/streaming/{execution_id}/status",
|
|
104
|
+
headers={"Authorization": f"Bearer {config.api_key}"},
|
|
105
|
+
timeout=10.0
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if response.status_code == 200:
|
|
109
|
+
data = response.json()
|
|
110
|
+
status = data.get('status', 'unknown')
|
|
111
|
+
|
|
112
|
+
if status == 'completed':
|
|
113
|
+
console.print(f"[green]✅ Execution completed successfully![/green]")
|
|
114
|
+
return True
|
|
115
|
+
elif status in ['failed_user', 'failed_system', 'failed']:
|
|
116
|
+
console.print(f"[red]❌ Execution failed: {status}[/red]")
|
|
117
|
+
errors = data.get('errors')
|
|
118
|
+
if errors:
|
|
119
|
+
console.print(f"[red]Error: {errors}[/red]")
|
|
120
|
+
return False
|
|
121
|
+
elif status in ['timeout', 'cancelled']:
|
|
122
|
+
console.print(f"[yellow]⚠️ Execution {status}[/yellow]")
|
|
123
|
+
return False
|
|
124
|
+
elif status in ['running', 'pending', 'queued']:
|
|
125
|
+
# Still running, continue polling
|
|
126
|
+
time.sleep(1)
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
console.print(f"[red]Error checking status: {e}[/red]")
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
console.print("[yellow]⚠️ Status check timed out[/yellow]")
|
|
134
|
+
return False
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
setup.py
|
|
2
|
+
lyceum/__init__.py
|
|
3
|
+
lyceum/main.py
|
|
4
|
+
lyceum/external/__init__.py
|
|
5
|
+
lyceum/external/auth/__init__.py
|
|
6
|
+
lyceum/external/auth/login.py
|
|
7
|
+
lyceum/external/compute/__init__.py
|
|
8
|
+
lyceum/external/compute/execution/__init__.py
|
|
9
|
+
lyceum/external/compute/execution/python.py
|
|
10
|
+
lyceum/external/compute/inference/__init__.py
|
|
11
|
+
lyceum/external/compute/inference/batch.py
|
|
12
|
+
lyceum/external/compute/inference/chat.py
|
|
13
|
+
lyceum/external/compute/inference/models.py
|
|
14
|
+
lyceum/external/general/__init__.py
|
|
15
|
+
lyceum/shared/__init__.py
|
|
16
|
+
lyceum/shared/config.py
|
|
17
|
+
lyceum/shared/display.py
|
|
18
|
+
lyceum/shared/streaming.py
|
|
19
|
+
lyceum_cli.egg-info/PKG-INFO
|
|
20
|
+
lyceum_cli.egg-info/SOURCES.txt
|
|
21
|
+
lyceum_cli.egg-info/dependency_links.txt
|
|
22
|
+
lyceum_cli.egg-info/entry_points.txt
|
|
23
|
+
lyceum_cli.egg-info/requires.txt
|
|
24
|
+
lyceum_cli.egg-info/top_level.txt
|
|
25
|
+
lyceum_cloud_execution_api_client/__init__.py
|
|
26
|
+
lyceum_cloud_execution_api_client/api/__init__.py
|
|
27
|
+
lyceum_cloud_execution_api_client/models/__init__.py
|
|
File without changes
|
|
File without changes
|
|
@@ -15,7 +15,7 @@ if readme_file.exists():
|
|
|
15
15
|
|
|
16
16
|
setup(
|
|
17
17
|
name="lyceum-cli",
|
|
18
|
-
version="1.0.
|
|
18
|
+
version="1.0.18",
|
|
19
19
|
description="Command-line interface for Lyceum Cloud Execution API",
|
|
20
20
|
long_description=long_description,
|
|
21
21
|
long_description_content_type="text/markdown",
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
API key management commands
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import typer
|
|
6
|
-
from rich.console import Console
|
|
7
|
-
|
|
8
|
-
from ...shared.config import config
|
|
9
|
-
from ...shared.display import create_table, format_timestamp, truncate_id
|
|
10
|
-
|
|
11
|
-
console = Console()
|
|
12
|
-
|
|
13
|
-
# Import generated client modules
|
|
14
|
-
from lyceum_cloud_execution_api_client.api.api_keys import list_api_keys_api_v1_api_keys_get
|
|
15
|
-
|
|
16
|
-
api_keys_app = typer.Typer(name="api-keys", help="API key management")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@api_keys_app.command("list")
|
|
20
|
-
def list_api_keys():
|
|
21
|
-
"""List your API keys"""
|
|
22
|
-
client = config.get_client()
|
|
23
|
-
|
|
24
|
-
try:
|
|
25
|
-
response = list_api_keys_api_v1_api_keys_get.sync_detailed(client=client)
|
|
26
|
-
|
|
27
|
-
if response.status_code != 200:
|
|
28
|
-
console.print(f"[red]Error: HTTP {response.status_code}[/red]")
|
|
29
|
-
raise typer.Exit(1)
|
|
30
|
-
|
|
31
|
-
api_keys = response.parsed if response.parsed else []
|
|
32
|
-
|
|
33
|
-
if not api_keys:
|
|
34
|
-
console.print("[dim]No API keys found[/dim]")
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
columns = [
|
|
38
|
-
{"header": "ID", "style": "cyan", "no_wrap": True, "max_width": 12},
|
|
39
|
-
{"header": "Name", "style": "yellow"},
|
|
40
|
-
{"header": "Active", "style": "green"},
|
|
41
|
-
{"header": "Created", "style": "dim"}
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
table = create_table("API Keys", columns)
|
|
45
|
-
|
|
46
|
-
for key in api_keys:
|
|
47
|
-
table.add_row(
|
|
48
|
-
truncate_id(key.id, 8),
|
|
49
|
-
key.key_name,
|
|
50
|
-
"✅" if key.is_active else "❌",
|
|
51
|
-
format_timestamp(getattr(key, 'created_at', None))
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
console.print(table)
|
|
55
|
-
|
|
56
|
-
except Exception as e:
|
|
57
|
-
console.print(f"[red]Error: {e}[/red]")
|
|
58
|
-
raise typer.Exit(1)
|