llama-deploy-appserver 0.2.7a1__py3-none-any.whl → 0.3.0a1__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 (29) hide show
  1. llama_deploy/appserver/__main__.py +0 -4
  2. llama_deploy/appserver/app.py +105 -25
  3. llama_deploy/appserver/bootstrap.py +76 -24
  4. llama_deploy/appserver/deployment.py +7 -421
  5. llama_deploy/appserver/deployment_config_parser.py +35 -59
  6. llama_deploy/appserver/routers/__init__.py +4 -3
  7. llama_deploy/appserver/routers/deployments.py +162 -385
  8. llama_deploy/appserver/routers/status.py +4 -31
  9. llama_deploy/appserver/routers/ui_proxy.py +213 -0
  10. llama_deploy/appserver/settings.py +57 -55
  11. llama_deploy/appserver/types.py +0 -3
  12. llama_deploy/appserver/workflow_loader.py +383 -0
  13. {llama_deploy_appserver-0.2.7a1.dist-info → llama_deploy_appserver-0.3.0a1.dist-info}/METADATA +3 -6
  14. llama_deploy_appserver-0.3.0a1.dist-info/RECORD +17 -0
  15. {llama_deploy_appserver-0.2.7a1.dist-info → llama_deploy_appserver-0.3.0a1.dist-info}/WHEEL +1 -1
  16. llama_deploy/appserver/client/__init__.py +0 -3
  17. llama_deploy/appserver/client/base.py +0 -30
  18. llama_deploy/appserver/client/client.py +0 -49
  19. llama_deploy/appserver/client/models/__init__.py +0 -4
  20. llama_deploy/appserver/client/models/apiserver.py +0 -356
  21. llama_deploy/appserver/client/models/model.py +0 -82
  22. llama_deploy/appserver/run_autodeploy.py +0 -141
  23. llama_deploy/appserver/server.py +0 -60
  24. llama_deploy/appserver/source_managers/__init__.py +0 -5
  25. llama_deploy/appserver/source_managers/base.py +0 -33
  26. llama_deploy/appserver/source_managers/git.py +0 -48
  27. llama_deploy/appserver/source_managers/local.py +0 -51
  28. llama_deploy/appserver/tracing.py +0 -237
  29. llama_deploy_appserver-0.2.7a1.dist-info/RECORD +0 -28
@@ -1,12 +1,8 @@
1
1
  import uvicorn
2
- from prometheus_client import start_http_server
3
2
 
4
3
  from .settings import settings
5
4
 
6
5
  if __name__ == "__main__":
7
- if settings.prometheus_enabled:
8
- start_http_server(settings.prometheus_port)
9
-
10
6
  uvicorn.run(
11
7
  "llama_deploy.appserver.app:app",
12
8
  host=settings.host,
@@ -1,24 +1,75 @@
1
1
  import logging
2
2
  import os
3
+ from pathlib import Path
4
+ import threading
5
+ import time
6
+ from llama_deploy.appserver.deployment_config_parser import (
7
+ get_deployment_config,
8
+ )
9
+ from llama_deploy.appserver.settings import configure_settings, settings
3
10
 
4
11
  from fastapi import FastAPI
5
12
  from fastapi.middleware.cors import CORSMiddleware
6
- from fastapi.requests import Request
7
- from fastapi.responses import JSONResponse, RedirectResponse
13
+ from llama_deploy.appserver.workflow_loader import (
14
+ build_ui,
15
+ do_install,
16
+ start_dev_ui_process,
17
+ )
18
+ import uvicorn
8
19
 
9
- from .routers import deployments_router, status_router
10
- from .server import lifespan, manager
11
- from .settings import settings
12
- from .tracing import configure_tracing
20
+ from .routers import health_router
21
+ from prometheus_fastapi_instrumentator import Instrumentator
22
+ from contextlib import asynccontextmanager
23
+ from typing import Any, AsyncGenerator
13
24
 
25
+ from llama_deploy.appserver.routers.deployments import (
26
+ create_base_router,
27
+ create_deployments_router,
28
+ )
29
+ from llama_deploy.appserver.routers.ui_proxy import (
30
+ create_ui_proxy_router,
31
+ mount_static_files,
32
+ )
33
+ from llama_deploy.appserver.workflow_loader import (
34
+ load_workflows,
35
+ )
36
+
37
+ from .deployment import Deployment
38
+ from .stats import apiserver_state
39
+ import webbrowser
14
40
 
15
41
  logger = logging.getLogger("uvicorn.info")
16
42
 
17
43
 
18
- app = FastAPI(lifespan=lifespan)
44
+ @asynccontextmanager
45
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, Any]:
46
+ apiserver_state.state("starting")
47
+
48
+ config = get_deployment_config()
49
+
50
+ workflows = load_workflows(config)
51
+ deployment = Deployment(workflows)
52
+ base_router = create_base_router(config.name)
53
+ deploy_router = create_deployments_router(config.name, deployment)
54
+ app.include_router(base_router)
55
+ app.include_router(deploy_router)
56
+ # proxy UI in dev mode
57
+ if config.ui is not None:
58
+ if settings.proxy_ui:
59
+ ui_router = create_ui_proxy_router(config.name, config.ui.port)
60
+ app.include_router(ui_router)
61
+ else:
62
+ # otherwise serve the pre-built if available
63
+ mount_static_files(app, config, settings)
64
+
65
+ apiserver_state.state("running")
66
+ yield
67
+
68
+ apiserver_state.state("stopped")
69
+
19
70
 
20
- # Setup tracing
21
- configure_tracing(settings)
71
+ app = FastAPI(lifespan=lifespan)
72
+ Instrumentator().instrument(app).expose(app)
22
73
 
23
74
  # Configure CORS middleware if the environment variable is set
24
75
  if not os.environ.get("DISABLE_CORS", False):
@@ -30,20 +81,49 @@ if not os.environ.get("DISABLE_CORS", False):
30
81
  allow_headers=["Content-Type", "Authorization"],
31
82
  )
32
83
 
33
- app.include_router(deployments_router)
34
- app.include_router(status_router)
35
-
36
-
37
- @app.get("/", response_model=None)
38
- async def root(request: Request) -> JSONResponse | RedirectResponse:
39
- # for local dev, just redirect to the one UI if we have one
40
- if len(manager.deployment_names) == 1:
41
- deployment = manager.get_deployment(manager.deployment_names[0])
42
- if deployment is not None and deployment._ui_server_process is not None:
43
- return RedirectResponse(f"deployments/{deployment.name}/ui")
44
- return JSONResponse(
45
- {
46
- "swagger_docs": f"{request.base_url}docs",
47
- "status": f"{request.base_url}status",
48
- }
84
+ app.include_router(health_router)
85
+
86
+
87
+ def start_server(
88
+ proxy_ui: bool = False,
89
+ reload: bool = False,
90
+ cwd: Path | None = None,
91
+ deployment_file: Path | None = None,
92
+ install: bool = False,
93
+ build: bool = False,
94
+ open_browser: bool = False,
95
+ ) -> None:
96
+ # Configure via environment so uvicorn reload workers inherit the values
97
+ configure_settings(
98
+ proxy_ui=proxy_ui, app_root=cwd, deployment_file_path=deployment_file
49
99
  )
100
+ if install:
101
+ do_install()
102
+ if build:
103
+ build_ui(settings.config_parent, get_deployment_config())
104
+
105
+ ui_process = None
106
+ if proxy_ui:
107
+ ui_process = start_dev_ui_process(
108
+ settings.config_parent, settings.port, get_deployment_config()
109
+ )
110
+ try:
111
+ if open_browser:
112
+
113
+ def open_with_delay():
114
+ time.sleep(1)
115
+ webbrowser.open(f"http://{settings.host}:{settings.port}")
116
+
117
+ threading.Thread(
118
+ target=open_with_delay,
119
+ ).start()
120
+
121
+ uvicorn.run(
122
+ "llama_deploy.appserver.app:app",
123
+ host=settings.host,
124
+ port=settings.port,
125
+ reload=reload,
126
+ )
127
+ finally:
128
+ if ui_process is not None:
129
+ ui_process.terminate()
@@ -4,40 +4,92 @@ Bootstraps an application from a remote github repository given environment vari
4
4
  This just sets up the files from the repository. It's more of a build process, does not start an application.
5
5
  """
6
6
 
7
- import asyncio
7
+ import os
8
+ from pathlib import Path
9
+ from llama_deploy.appserver.settings import settings
10
+ from llama_deploy.appserver.deployment_config_parser import get_deployment_config
11
+ from llama_deploy.appserver.workflow_loader import build_ui, do_install
8
12
  from llama_deploy.core.git.git_util import (
9
13
  clone_repo,
10
14
  )
11
- from pydantic import Field
12
- from pydantic_settings import BaseSettings, SettingsConfigDict
15
+ from llama_deploy.appserver.app import start_server
16
+ from llama_deploy.appserver.settings import BootstrapSettings, configure_settings
13
17
 
18
+ import argparse
14
19
 
15
- class BootstrapSettings(BaseSettings):
16
- model_config = SettingsConfigDict(env_prefix="LLAMA_DEPLOY_")
17
- git_url: str = Field(..., description="The URL of the git repository to clone")
18
- git_token: str | None = Field(
19
- default=None, description="The token to use to clone the git repository"
20
- )
21
- git_ref: str | None = Field(
22
- default=None, description="The git reference to checkout"
23
- )
24
- git_sha: str | None = Field(default=None, description="The git SHA to checkout")
25
- deployment_file_path: str = Field(
26
- default="llama_deploy.yaml", description="The path to the deployment file"
27
- )
28
- deployment_name: str | None = Field(
29
- default=None, description="The name of the deployment"
30
- )
31
20
 
32
-
33
- async def main():
34
- settings = BootstrapSettings()
21
+ def bootstrap_app_from_repo(
22
+ clone: bool = False,
23
+ build: bool = False,
24
+ serve: bool = False,
25
+ target_dir: str = "/opt/app/",
26
+ ):
27
+ bootstrap_settings = BootstrapSettings()
35
28
  # Needs the github url+auth, and the deployment file path
36
29
  # clones the repo to a standard directory
37
30
  # (eventually) runs the UI build process and moves that to a standard directory for a file server
38
- clone_repo(settings.git_url, "/app/", settings.git_token)
31
+ if clone:
32
+ repo_url = bootstrap_settings.repo_url
33
+ if repo_url is None:
34
+ raise ValueError("repo_url is required to bootstrap")
35
+ clone_repo(
36
+ repository_url=repo_url,
37
+ git_ref=bootstrap_settings.git_sha or bootstrap_settings.git_ref,
38
+ basic_auth=bootstrap_settings.auth_token,
39
+ dest_dir=target_dir,
40
+ )
41
+ # Ensure target_dir exists locally when running tests outside a container
42
+ os.makedirs(target_dir, exist_ok=True)
43
+ os.chdir(target_dir)
44
+ configure_settings(
45
+ app_root=Path(target_dir),
46
+ deployment_file_path=Path(bootstrap_settings.deployment_file_path),
47
+ )
48
+
49
+ built = True
50
+ if build:
51
+ do_install()
52
+ built = build_ui(settings.config_parent, get_deployment_config())
53
+
54
+ if serve:
55
+ start_server(
56
+ proxy_ui=not built,
57
+ )
39
58
  pass
40
59
 
41
60
 
42
61
  if __name__ == "__main__":
43
- asyncio.run(main())
62
+ parser = argparse.ArgumentParser()
63
+ parser.add_argument(
64
+ "--clone",
65
+ action=argparse.BooleanOptionalAction,
66
+ default=False,
67
+ help="Clone the repository before bootstrapping (use --no-clone to disable)",
68
+ )
69
+ parser.add_argument(
70
+ "--build",
71
+ action=argparse.BooleanOptionalAction,
72
+ default=False,
73
+ help="Build the UI/assets (use --no-build to disable)",
74
+ )
75
+ parser.add_argument(
76
+ "--serve",
77
+ action=argparse.BooleanOptionalAction,
78
+ default=False,
79
+ help="Start the API server after bootstrap (use --no-serve to disable)",
80
+ )
81
+ args = parser.parse_args()
82
+ try:
83
+ bootstrap_app_from_repo(
84
+ clone=args.clone,
85
+ build=args.build,
86
+ serve=args.serve,
87
+ )
88
+ except Exception as e:
89
+ import logging
90
+
91
+ logging.exception("Error during bootstrap. Pausing for debugging.")
92
+ import time
93
+
94
+ time.sleep(1000000)
95
+ raise e