1515import tarfile
1616import tempfile
1717from pathlib import Path
18- from typing import Dict , List , Optional
18+ from typing import Any , Dict , List , Optional
1919from urllib .parse import urlparse
2020
2121import requests
2424from fastapi .middleware .cors import CORSMiddleware
2525from pydantic import BaseModel
2626
27+ from lightning_app .core import constants
28+ from lightning_app .plugin .actions import _Action
2729from lightning_app .utilities .app_helpers import Logger
2830from lightning_app .utilities .component import _set_flow_context
2931from lightning_app .utilities .enum import AppStage
@@ -41,16 +43,20 @@ def __init__(self) -> None:
4143 self .cloudspace_id = ""
4244 self .cluster_id = ""
4345
44- def run (self , * args : str , ** kwargs : str ) -> None :
46+ def run (self , * args : str , ** kwargs : str ) -> Optional [ List [ _Action ]] :
4547 """Override with the logic to execute on the cloudspace."""
48+ raise NotImplementedError
4649
47- def run_job (self , name : str , app_entrypoint : str , env_vars : Optional [Dict [str , str ]] = None ) -> None :
50+ def run_job (self , name : str , app_entrypoint : str , env_vars : Optional [Dict [str , str ]] = None ) -> str :
4851 """Run a job in the cloudspace associated with this plugin.
4952
5053 Args:
5154 name: The name of the job.
5255 app_entrypoint: The path of the file containing the app to run.
5356 env_vars: Additional env vars to set when running the app.
57+
58+ Returns:
59+ The relative URL of the created job.
5460 """
5561 from lightning_app .runners .cloud import CloudRuntime
5662
@@ -74,12 +80,14 @@ def run_job(self, name: str, app_entrypoint: str, env_vars: Optional[Dict[str, s
7480 # Used to indicate Lightning has been dispatched
7581 os .environ ["LIGHTNING_DISPATCHED" ] = "1"
7682
77- runtime .cloudspace_dispatch (
83+ url = runtime .cloudspace_dispatch (
7884 project_id = self .project_id ,
7985 cloudspace_id = self .cloudspace_id ,
8086 name = name ,
8187 cluster_id = self .cluster_id ,
8288 )
89+ # Return a relative URL so it can be used with the NavigateTo action.
90+ return url .replace (constants .get_lightning_cloud_url (), "" )
8391
8492 def _setup (
8593 self ,
@@ -101,7 +109,7 @@ class _Run(BaseModel):
101109 plugin_arguments : Dict [str , str ]
102110
103111
104- def _run_plugin (run : _Run ) -> List :
112+ def _run_plugin (run : _Run ) -> Dict [ str , Any ] :
105113 """Create a run with the given name and entrypoint under the cloudspace with the given ID."""
106114 with tempfile .TemporaryDirectory () as tmpdir :
107115 download_path = os .path .join (tmpdir , "source.tar.gz" )
@@ -115,6 +123,9 @@ def _run_plugin(run: _Run) -> List:
115123
116124 response = requests .get (source_code_url )
117125
126+ # TODO: Backoff retry a few times in case the URL is flaky
127+ response .raise_for_status ()
128+
118129 with open (download_path , "wb" ) as f :
119130 f .write (response .content )
120131 except Exception as e :
@@ -152,17 +163,15 @@ def _run_plugin(run: _Run) -> List:
152163 cloudspace_id = run .cloudspace_id ,
153164 cluster_id = run .cluster_id ,
154165 )
155- plugin .run (** run .plugin_arguments )
166+ actions = plugin .run (** run .plugin_arguments ) or []
167+ return {"actions" : [action .to_spec ().to_dict () for action in actions ]}
156168 except Exception as e :
157169 raise HTTPException (
158170 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR , detail = f"Error running plugin: { str (e )} ."
159171 )
160172 finally :
161173 os .chdir (cwd )
162174
163- # TODO: Return actions from the plugin here
164- return []
165-
166175
167176def _start_plugin_server (host : str , port : int ) -> None :
168177 """Start the plugin server which can be used to dispatch apps or run plugins."""
0 commit comments