diff --git a/hypha/http.py b/hypha/http.py
index a03f5ec5..b69d7e63 100644
--- a/hypha/http.py
+++ b/hypha/http.py
@@ -16,78 +16,6 @@
from hypha.utils import GzipRoute
from hypha import __version__ as VERSION
-SERVICES_OPENAPI_SCHEMA = {
- "openapi": "3.1.0",
- "info": {
- "title": "Hypha Services",
- "description": "Providing access to services in Hypha",
- "version": f"v{VERSION}",
- },
- "paths": {
- "/call": {
- "post": {
- "description": "Call a service function",
- "operationId": "CallServiceFunction",
- "requestBody": {
- "required": True,
- "description": "The request body type depends on each service function schema",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ServiceFunctionSchema"
- }
- }
- },
- },
- "deprecated": False,
- }
- },
- "/list": {
- "get": {
- "description": "List services under a workspace",
- "operationId": "ListServices",
- "parameters": [
- {
- "name": "workspace",
- "in": "query",
- "description": "Workspace name",
- "required": True,
- "schema": {"type": "string"},
- }
- ],
- "deprecated": False,
- }
- },
- },
- "components": {
- "schemas": {
- "ServiceFunctionSchema": {
- "type": "object",
- "properties": {
- "workspace": {
- "description": "Workspace name, optional if the service_id contains workspace name",
- "type": "string",
- },
- "service_id": {
- "description": "Service ID, format: workspace/client_id:service_id",
- "type": "string",
- },
- "function_key": {
- "description": "Function key, can contain dot to refer to deeper object",
- "type": "string",
- },
- "function_kwargs": {
- "description": "Function keyword arguments, must be set according to the function schema",
- "type": "object",
- },
- },
- "required": ["service_id", "function_key"],
- }
- }
- },
-}
-
-
class MsgpackResponse(Response):
"""Response class for msgpack encoding."""
@@ -283,7 +211,7 @@ async def list_all_workspaces(
content={"success": False, "detail": str(exp)},
)
- @router.get("/{workspace}/info")
+ @router.get("/workspaces/{workspace}")
async def get_workspace_info(
workspace: str,
user_info: login_optional = Depends(login_optional),
@@ -325,35 +253,7 @@ async def list_services(
"""List services under a workspace."""
return await get_workspace_services(workspace, user_info)
- @router.get("/services/{service_id:path}")
- async def get_service_info(
- service_id: str,
- user_info: login_optional = Depends(login_optional),
- ):
- """Get service info."""
- assert "/" in service_id, "service_id should contain workspace name"
- assert ":" in service_id, "service_id should contain client_id"
- workspace, service_id = service_id.split("/")
- return await get_service_info(workspace, service_id, user_info)
-
- @router.post("/services/{workspace}/{service_id}/{function_key}")
- async def call_service_function_post(
- workspace: str,
- service_id: str,
- function_key: str,
- request: Request,
- user_info: login_optional = Depends(login_optional),
- ):
- """Call a service function by keys."""
- function_kwargs = await extracted_kwargs(request, use_function_kwargs=False)
- response_type = detected_response_type(request)
- return await service_function(
- (workspace, service_id, function_key),
- function_kwargs,
- response_type,
- user_info,
- )
-
+ @router.get("/services//{workspace}")
@router.get("/{workspace}/services")
async def get_workspace_services(
workspace: str,
@@ -379,6 +279,7 @@ async def get_workspace_services(
content={"success": False, "detail": str(exp)},
)
+ @router.get("/services/{workspace}/{service_id}")
@router.get("/{workspace}/services/{service_id}")
async def get_service_info(
workspace: str,
@@ -412,32 +313,11 @@ async def _call_service_function(func, kwargs):
results = _rpc.encode(results)
return results
+ @router.get("/services/{workspace}/{service_id}/{function_key}")
+ @router.post("/services/{workspace}/{service_id}/{function_key}")
@router.get("/{workspace}/services/{service_id}/{function_key}")
- async def service_function_get(
- workspace: str,
- service_id: str,
- function_key: str,
- request: Request,
- user_info: login_optional = Depends(login_optional),
- ):
- """Run service function by keys."""
- function_kwargs = await extracted_kwargs(request, use_function_kwargs=False)
- response_type = detected_response_type(request)
- try:
- return await service_function(
- (workspace, service_id, function_key),
- function_kwargs,
- response_type,
- user_info,
- )
- except Exception:
- return JSONResponse(
- status_code=400,
- content={"success": False, "detail": traceback.format_exc()},
- )
-
@router.post("/{workspace}/services/{service_id}/{function_key}")
- async def service_function_post(
+ async def call_service_function(
workspace: str,
service_id: str,
function_key: str,
diff --git a/hypha/runner/__init__.py b/hypha/runner/__init__.py
index 1dddf2a0..d08232b8 100644
--- a/hypha/runner/__init__.py
+++ b/hypha/runner/__init__.py
@@ -15,21 +15,21 @@
logging.basicConfig(stream=sys.stdout)
-logger = logging.getLogger("browser-runner")
+logger = logging.getLogger("app-runner")
logger.setLevel(logging.INFO)
-async def export_service(plugin_api, config, hypha_rpc):
+async def export_service(app_api, config, hypha_rpc):
try:
wm = await connect_to_server(config)
hypha_rpc.api.update(wm) # make the api available to the app
rpc = wm.rpc
- if not isinstance(plugin_api, dict) and inspect.isclass(type(plugin_api)):
- plugin_api = {a: getattr(plugin_api, a) for a in dir(plugin_api)}
+ if not isinstance(app_api, dict) and inspect.isclass(type(app_api)):
+ app_api = {a: getattr(app_api, a) for a in dir(app_api)}
# Copy the app name as the default name
- plugin_api["id"] = "default"
- plugin_api["name"] = config.get("name", "default")
- svc = await rpc.register_service(plugin_api, overwrite=True, notify=True)
+ app_api["id"] = "default"
+ app_api["name"] = config.get("name", "default")
+ svc = await rpc.register_service(app_api, overwrite=True, notify=True)
svc = await rpc.get_remote_service(svc["id"])
if svc.setup:
await svc.setup()
@@ -53,26 +53,26 @@ def export(api, config=None):
return hypha_rpc
-async def run_plugin(plugin_file, default_config, quit_on_ready=False):
+async def run_app(app_file, default_config, quit_on_ready=False):
"""Load app file."""
loop = asyncio.get_event_loop()
- if os.path.isfile(plugin_file):
- async with aiofiles.open(plugin_file, "r", encoding="utf-8") as fil:
+ if os.path.isfile(app_file):
+ async with aiofiles.open(app_file, "r", encoding="utf-8") as fil:
content = await fil.read()
- elif plugin_file.startswith("http"):
- with urllib.request.urlopen(plugin_file) as response:
+ elif app_file.startswith("http"):
+ with urllib.request.urlopen(app_file) as response:
content = response.read().decode("utf-8")
# remove query string
- plugin_file = plugin_file.split("?")[0]
+ app_file = app_file.split("?")[0]
else:
- raise Exception(f"Invalid input app file path: {plugin_file}")
+ raise Exception(f"Invalid input app file path: {app_file}")
- if plugin_file.endswith(".py"):
- filename, _ = os.path.splitext(os.path.basename(plugin_file))
+ if app_file.endswith(".py"):
+ filename, _ = os.path.splitext(os.path.basename(app_file))
default_config["name"] = filename[:32]
hypha_rpc = await patch_hypha_rpc(default_config)
exec(content, globals()) # pylint: disable=exec-used
- logger.info("Plugin executed")
+ logger.info("app executed")
if quit_on_ready:
@@ -84,28 +84,28 @@ def done_callback(fut):
hypha_rpc.ready.add_done_callback(done_callback)
- elif plugin_file.endswith(".imjoy.html"):
+ elif app_file.endswith(".imjoy.html"):
# load config
found = re.findall("\n(.*)", content, re.DOTALL)[0]
if "json" in found[0]:
- plugin_config = json.loads(found[1])
+ app_config = json.loads(found[1])
elif "yaml" in found[0]:
- plugin_config = yaml.safe_load(found[1])
- default_config.update(plugin_config)
+ app_config = yaml.safe_load(found[1])
+ default_config.update(app_config)
hypha_rpc = await patch_hypha_rpc(default_config)
# load script
found = re.findall("", content, re.DOTALL)[0]
if "python" in found[0]:
exec(found[1], globals()) # pylint: disable=exec-used
- logger.info("Plugin executed")
+ logger.info("app executed")
if quit_on_ready:
hypha_rpc.ready.add_done_callback(lambda fut: loop.stop())
else:
raise RuntimeError(
- f"Invalid script type ({found[0]}) in file {plugin_file}"
+ f"Invalid script type ({found[0]}) in file {app_file}"
)
else:
- raise RuntimeError(f"Invalid script file type ({plugin_file})")
+ raise RuntimeError(f"Invalid script file type ({app_file})")
async def start(args):
@@ -116,7 +116,7 @@ async def start(args):
"workspace": args.workspace,
"token": args.token,
}
- await run_plugin(args.file, default_config, quit_on_ready=args.quit_on_ready)
+ await run_app(args.file, default_config, quit_on_ready=args.quit_on_ready)
except Exception: # pylint: disable=broad-except
logger.exception("Failed to run app, exiting.")
loop = asyncio.get_event_loop()
diff --git a/tests/example_service_script.py b/tests/example_service_script.py
index 8b30e46b..1bc6b0e1 100644
--- a/tests/example_service_script.py
+++ b/tests/example_service_script.py
@@ -35,7 +35,7 @@ async def start_service(server_url, service_id, workspace=None, token=None):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Test services")
parser.add_argument(
- "--server-url", type=str, default="https://ai.imjoy.io/", help="The server url"
+ "--server-url", type=str, default="https://ai.imjoy.io", help="The server url"
)
parser.add_argument(
"--service-id", type=str, default="test-service", help="The service id"
diff --git a/tests/test_http.py b/tests/test_http.py
index 69b34465..39cfae0c 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -130,7 +130,7 @@ async def test_http_proxy(minio_server, fastapi_server, test_user_token):
assert workspace in response
response = requests.get(
- f"{SERVER_URL}/{workspace}/info", headers={"Authorization": f"Bearer {token}"}
+ f"{SERVER_URL}/workspaces/{workspace}", headers={"Authorization": f"Bearer {token}"}
)
assert response.ok, response.json()["detail"]
response = response.json()