Skip to content

Commit

Permalink
Fix applications and daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
oeway committed Dec 6, 2024
1 parent f3d3b7e commit 1b7319b
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 12 deletions.
10 changes: 7 additions & 3 deletions hypha/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ async def list_apps(self, context: Optional[dict] = None):
)
return [app["manifest"] for app in apps]
except KeyError:
return []
raise KeyError(f"Applications collection not found: {ws}")
except Exception as exp:
raise Exception(f"Failed to list apps: {exp}") from exp

Expand All @@ -611,12 +611,16 @@ async def shutdown(self) -> None:

async def prepare_workspace(self, workspace_info: WorkspaceInfo):
"""Prepare the workspace."""
apps = await self.list_apps({"ws": workspace_info.id})
context = {
"ws": workspace_info.id,
"user": self.store.get_root_user().model_dump(),
}
apps = await self.list_apps(context=context)
# start daemon apps
for app in apps:
if app.get("daemon"):
try:
await self.start(app["id"], context={"ws": workspace_info.id})
await self.start(app["id"], context=context)
except Exception as exp:
logger.error(
f"Failed to start daemon app: {app['id']}, error: {exp}"
Expand Down
6 changes: 6 additions & 0 deletions hypha/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,12 @@ def model_validate(cls, data):
class ApplicationManifest(Artifact):
"""Represent application artifact."""

passive: Optional[bool] = False
dependencies: Optional[List[str]] = []
requirements: Optional[List[str]] = []
api_version: Optional[str] = "0.1.0"
icon: Optional[str] = None
env: Optional[Union[str, Dict[str, Any]]] = None
type: Optional[str] = "application"
entry_point: Optional[str] = None # entry point for the application
daemon: Optional[bool] = False # whether the application is a daemon
Expand Down
12 changes: 9 additions & 3 deletions hypha/plugin_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,17 @@ def convert_config_to_artifact(plugin_config, plugin_id, source_url=None):
"id": plugin_id,
}
)
if source_url:
artifact["source"] = source_url
for field in ApplicationManifest.model_fields.keys():

fields = [
field
for field in ApplicationManifest.model_fields.keys()
if field not in ["id", "type"]
]
for field in fields:
if field in plugin_config:
artifact[field] = plugin_config[field]
if source_url:
artifact["source"] = source_url
tags = plugin_config.get("labels", []) + plugin_config.get("flags", [])
artifact["tags"] = tags

Expand Down
2 changes: 1 addition & 1 deletion tests/testASGIWebPythonPlugin.imjoy.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"flags": [],
"icon": "extension",
"api_version": "0.1.7",
"env": "",
"env": {},
"requirements": ["fastapi==0.112.1"],
"dependencies": []
}
Expand Down
2 changes: 1 addition & 1 deletion tests/testFunctionsPlugin.imjoy.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"inputs": null,
"outputs": null,
"api_version": "0.1.7",
"env": "",
"env": {},
"requirements": [],
"dependencies": []
}
Expand Down
2 changes: 1 addition & 1 deletion tests/testUnreliablePlugin.imjoy.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"icon": "extension",
"inputs": null,
"outputs": null,
"env": "",
"env": {},
"requirements": [],
"dependencies": [],
"defaults": {"w": 7, "h": 7}
Expand Down
2 changes: 1 addition & 1 deletion tests/testWebPythonPlugin.imjoy.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"flags": [],
"icon": "extension",
"api_version": "0.1.7",
"env": "",
"env": {},
"requirements": [],
"dependencies": []
}
Expand Down
2 changes: 1 addition & 1 deletion tests/testWebWorkerPlugin.imjoy.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"inputs": null,
"outputs": null,
"api_version": "0.1.7",
"env": "",
"env": {},
"requirements": [],
"dependencies": []
}
Expand Down
2 changes: 1 addition & 1 deletion tests/testWindowPlugin1.imjoy.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"icon": "extension",
"inputs": null,
"outputs": null,
"env": "",
"env": {},
"requirements": [],
"dependencies": [],
"defaults": {"w": 7, "h": 7}
Expand Down
55 changes: 55 additions & 0 deletions tests/test_server_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,61 @@ async def test_singleton_apps(fastapi_server, test_user_token):
assert not any(app["id"] == config1.id for app in running_apps)


async def test_daemon_apps(fastapi_server, test_user_token, root_user_token):
"""Test the daemon apps."""
async with connect_to_server(
{
"name": "test client",
"server_url": WS_SERVER_URL,
"method_timeout": 30,
"token": test_user_token,
}
) as api:
controller = await api.get_service("public/server-apps")

# Launch a daemon app
config = await controller.launch(
source=TEST_APP_CODE,
config={"type": "window", "daemon": True},
overwrite=True,
wait_for_service="default",
)
assert "id" in config.keys()

# Verify the daemon app is running
running_apps = await controller.list_running()
assert any(app["id"] == config.id for app in running_apps)
await controller.stop(config.id)

apps = await controller.list_apps()
assert find_item(apps, "id", config.app_id)

await asyncio.sleep(0.5)

async with connect_to_server(
{"server_url": WS_SERVER_URL, "client_id": "admin", "token": root_user_token}
) as root:
admin = await root.get_service("admin-utils")
workspaces = await admin.list_workspaces()
assert not find_item(workspaces, "id", api.config["workspace"])

async with connect_to_server(
{
"name": "test client",
"server_url": WS_SERVER_URL,
"method_timeout": 30,
"token": test_user_token,
}
) as api:
controller = await api.get_service("public/server-apps")
apps = await controller.list_apps()
await api.wait_until_ready()
controller = await api.get_service("public/server-apps")
running_apps = await controller.list_running()
# The daemon app should be running
assert any(app["app_id"] == config.app_id for app in running_apps)


async def test_web_python_apps(fastapi_server, test_user_token):
"""Test webpython app."""
api = await connect_to_server(
Expand Down

0 comments on commit 1b7319b

Please sign in to comment.