llama-deploy-appserver 0.3.27__py3-none-any.whl → 0.4.1__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.
- llama_deploy/appserver/app.py +26 -3
- llama_deploy/appserver/workflow_loader.py +20 -8
- llama_deploy/appserver/workflow_store/agent_data_store.py +132 -40
- {llama_deploy_appserver-0.3.27.dist-info → llama_deploy_appserver-0.4.1.dist-info}/METADATA +2 -3
- {llama_deploy_appserver-0.3.27.dist-info → llama_deploy_appserver-0.4.1.dist-info}/RECORD +6 -6
- {llama_deploy_appserver-0.3.27.dist-info → llama_deploy_appserver-0.4.1.dist-info}/WHEEL +0 -0
llama_deploy/appserver/app.py
CHANGED
|
@@ -173,13 +173,14 @@ def prepare_server(
|
|
|
173
173
|
install: bool = False,
|
|
174
174
|
build: bool = False,
|
|
175
175
|
install_ui_deps: bool = True,
|
|
176
|
+
skip_env_validation: bool = False,
|
|
176
177
|
) -> None:
|
|
177
178
|
configure_settings(
|
|
178
179
|
deployment_file_path=deployment_file or Path(DEFAULT_DEPLOYMENT_FILE_PATH)
|
|
179
180
|
)
|
|
180
181
|
cfg = get_deployment_config()
|
|
181
182
|
load_environment_variables(cfg, settings.resolved_config_parent)
|
|
182
|
-
validate_required_env_vars(cfg)
|
|
183
|
+
validate_required_env_vars(cfg, fill_missing=skip_env_validation)
|
|
183
184
|
if install:
|
|
184
185
|
config = get_deployment_config()
|
|
185
186
|
inject_appserver_into_target(config, settings.resolved_config_parent)
|
|
@@ -298,10 +299,16 @@ def start_server_in_target_venv(
|
|
|
298
299
|
def start_preflight_in_target_venv(
|
|
299
300
|
cwd: Path | None = None,
|
|
300
301
|
deployment_file: Path | None = None,
|
|
302
|
+
skip_env_validation: bool = False,
|
|
301
303
|
) -> None:
|
|
302
304
|
"""
|
|
303
305
|
Run preflight validation inside the target project's virtual environment using uv.
|
|
304
306
|
Mirrors the venv targeting and invocation strategy used by start_server_in_target_venv.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
cwd: Working directory for the validation.
|
|
310
|
+
deployment_file: Path to the deployment configuration file.
|
|
311
|
+
skip_env_validation: If True, skip validation of required environment variables.
|
|
305
312
|
"""
|
|
306
313
|
configure_settings(
|
|
307
314
|
app_root=cwd,
|
|
@@ -320,6 +327,8 @@ def start_preflight_in_target_venv(
|
|
|
320
327
|
]
|
|
321
328
|
if deployment_file:
|
|
322
329
|
args.extend(["--deployment-file", str(deployment_file)])
|
|
330
|
+
if skip_env_validation:
|
|
331
|
+
args.append("--skip-env-validation")
|
|
323
332
|
|
|
324
333
|
run_process(
|
|
325
334
|
args,
|
|
@@ -386,10 +395,19 @@ def preflight_validate(
|
|
|
386
395
|
cwd: Path | None = None,
|
|
387
396
|
deployment_file: Path | None = None,
|
|
388
397
|
configure_logging: bool = False,
|
|
398
|
+
skip_env_validation: bool = False,
|
|
389
399
|
) -> None:
|
|
390
400
|
"""
|
|
391
401
|
Perform the same initialization path as starting the server, without serving.
|
|
392
402
|
This catches import errors and runs workflow validations.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
cwd: Working directory for the validation.
|
|
406
|
+
deployment_file: Path to the deployment configuration file.
|
|
407
|
+
configure_logging: Whether to configure logging.
|
|
408
|
+
skip_env_validation: If True, fill missing required env vars with placeholder
|
|
409
|
+
values instead of raising an error. Useful when validating workflow structure
|
|
410
|
+
without actual environment variable values.
|
|
393
411
|
"""
|
|
394
412
|
configure_settings(
|
|
395
413
|
app_root=cwd,
|
|
@@ -397,7 +415,7 @@ def preflight_validate(
|
|
|
397
415
|
)
|
|
398
416
|
cfg = get_deployment_config()
|
|
399
417
|
load_environment_variables(cfg, settings.resolved_config_parent)
|
|
400
|
-
validate_required_env_vars(cfg)
|
|
418
|
+
validate_required_env_vars(cfg, fill_missing=skip_env_validation)
|
|
401
419
|
|
|
402
420
|
workflows = load_workflows(cfg)
|
|
403
421
|
# Instantiate Deployment to ensure server wiring doesn't raise
|
|
@@ -454,12 +472,17 @@ if __name__ == "__main__":
|
|
|
454
472
|
parser.add_argument("--deployment-file", type=Path)
|
|
455
473
|
parser.add_argument("--open-browser", action="store_true")
|
|
456
474
|
parser.add_argument("--preflight", action="store_true")
|
|
475
|
+
parser.add_argument("--skip-env-validation", action="store_true")
|
|
457
476
|
parser.add_argument("--export-json-graph", action="store_true")
|
|
458
477
|
parser.add_argument("--export-output", type=Path)
|
|
459
478
|
|
|
460
479
|
args = parser.parse_args()
|
|
461
480
|
if args.preflight:
|
|
462
|
-
preflight_validate(
|
|
481
|
+
preflight_validate(
|
|
482
|
+
cwd=Path.cwd(),
|
|
483
|
+
deployment_file=args.deployment_file,
|
|
484
|
+
skip_env_validation=args.skip_env_validation,
|
|
485
|
+
)
|
|
463
486
|
elif args.export_json_graph:
|
|
464
487
|
export_json_graph(
|
|
465
488
|
cwd=Path.cwd(),
|
|
@@ -73,25 +73,37 @@ def load_environment_variables(config: DeploymentConfig, source_root: Path) -> N
|
|
|
73
73
|
os.environ[key] = value
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def validate_required_env_vars(
|
|
76
|
+
def validate_required_env_vars(
|
|
77
|
+
config: DeploymentConfig, *, fill_missing: bool = False
|
|
78
|
+
) -> None:
|
|
77
79
|
"""
|
|
78
80
|
Validate that all required environment variables are present and non-empty.
|
|
79
81
|
|
|
82
|
+
Args:
|
|
83
|
+
config: The deployment configuration containing required_env_vars.
|
|
84
|
+
fill_missing: If True, fill missing env vars with placeholder values instead
|
|
85
|
+
of raising an error. This is useful for validation where env vars may be
|
|
86
|
+
read statically during import time.
|
|
87
|
+
|
|
80
88
|
Raises:
|
|
81
|
-
RuntimeError: If any required env vars are missing or empty.
|
|
89
|
+
RuntimeError: If any required env vars are missing or empty and fill_missing is False.
|
|
82
90
|
"""
|
|
83
91
|
required = config.required_env_vars
|
|
84
92
|
if not required:
|
|
85
93
|
return
|
|
86
94
|
missing = [name for name in required if not os.environ.get(name)]
|
|
87
95
|
if missing:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
if fill_missing:
|
|
97
|
+
for name in missing:
|
|
98
|
+
os.environ[name] = f"__PLACEHOLDER_{name}__"
|
|
99
|
+
else:
|
|
100
|
+
missing_list = ", ".join(sorted(missing))
|
|
101
|
+
raise RuntimeError(
|
|
102
|
+
(
|
|
103
|
+
"Missing required environment variables defined in required_env_vars: "
|
|
104
|
+
f"{missing_list}. Provide them via your environment, .env files, or the deployment secrets."
|
|
105
|
+
)
|
|
93
106
|
)
|
|
94
|
-
)
|
|
95
107
|
|
|
96
108
|
|
|
97
109
|
def parse_environment_variables(
|
|
@@ -2,10 +2,9 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
-
from typing import Any, List
|
|
5
|
+
from typing import Any, List
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
from llama_cloud_services.beta.agent_data import AsyncAgentDataClient
|
|
7
|
+
import httpx
|
|
9
8
|
from llama_deploy.appserver.settings import ApiserverSettings
|
|
10
9
|
from llama_deploy.core.client.ssl_util import get_httpx_verify_param
|
|
11
10
|
from llama_deploy.core.deployment_config import DeploymentConfig
|
|
@@ -21,8 +20,12 @@ else:
|
|
|
21
20
|
|
|
22
21
|
logger = logging.getLogger(__name__)
|
|
23
22
|
|
|
23
|
+
DEFAULT_BASE_URL = "https://api.cloud.llamaindex.ai"
|
|
24
|
+
|
|
24
25
|
|
|
25
26
|
class AgentDataStore(AbstractWorkflowStore):
|
|
27
|
+
"""Workflow store backed by LlamaCloud Agent Data API using raw httpx."""
|
|
28
|
+
|
|
26
29
|
def __init__(
|
|
27
30
|
self, settings: DeploymentConfig, server_settings: ApiserverSettings
|
|
28
31
|
) -> None:
|
|
@@ -37,71 +40,149 @@ class AgentDataStore(AbstractWorkflowStore):
|
|
|
37
40
|
agent_url_id = settings.name
|
|
38
41
|
|
|
39
42
|
self.settings = settings
|
|
40
|
-
|
|
41
|
-
self.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
token=os.getenv("LLAMA_CLOUD_API_KEY"),
|
|
48
|
-
httpx_client=httpx.AsyncClient(
|
|
49
|
-
headers={"Project-Id": project_id} if project_id else None,
|
|
50
|
-
verify=get_httpx_verify_param(),
|
|
51
|
-
),
|
|
52
|
-
),
|
|
53
|
-
)
|
|
43
|
+
self.collection = collection
|
|
44
|
+
self.deployment_name = agent_url_id
|
|
45
|
+
|
|
46
|
+
self.base_url = os.getenv("LLAMA_CLOUD_BASE_URL", DEFAULT_BASE_URL).rstrip("/")
|
|
47
|
+
self.api_key = os.getenv("LLAMA_CLOUD_API_KEY")
|
|
48
|
+
self.project_id = os.getenv("LLAMA_DEPLOY_PROJECT_ID")
|
|
49
|
+
|
|
54
50
|
self.lock = AsyncKeyedLock()
|
|
55
51
|
# workflow id -> agent data id
|
|
56
52
|
self.cache = LRUCache[str, str](maxsize=1024)
|
|
57
53
|
|
|
54
|
+
def _get_headers(self) -> dict[str, str]:
|
|
55
|
+
"""Build HTTP headers for API requests."""
|
|
56
|
+
headers = {
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
}
|
|
59
|
+
if self.api_key:
|
|
60
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
61
|
+
if self.project_id:
|
|
62
|
+
headers["Project-Id"] = self.project_id
|
|
63
|
+
return headers
|
|
64
|
+
|
|
65
|
+
def _get_client(self) -> httpx.AsyncClient:
|
|
66
|
+
"""Create a new httpx client."""
|
|
67
|
+
return httpx.AsyncClient(
|
|
68
|
+
headers=self._get_headers(),
|
|
69
|
+
verify=get_httpx_verify_param(),
|
|
70
|
+
)
|
|
71
|
+
|
|
58
72
|
@override
|
|
59
73
|
async def query(self, query: HandlerQuery) -> List[PersistentHandler]:
|
|
60
74
|
filters = self._build_filters(query)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
search_request = {
|
|
76
|
+
"deployment_name": self.deployment_name,
|
|
77
|
+
"collection": self.collection,
|
|
78
|
+
"filter": filters,
|
|
79
|
+
"page_size": 1000,
|
|
80
|
+
}
|
|
81
|
+
async with self._get_client() as client:
|
|
82
|
+
response = await client.post(
|
|
83
|
+
f"{self.base_url}/api/v1/beta/agent-data/:search",
|
|
84
|
+
json=search_request,
|
|
85
|
+
)
|
|
86
|
+
response.raise_for_status()
|
|
87
|
+
data = response.json()
|
|
88
|
+
|
|
89
|
+
items = data.get("items", [])
|
|
90
|
+
return [PersistentHandler(**item["data"]) for item in items]
|
|
66
91
|
|
|
67
92
|
@override
|
|
68
93
|
async def update(self, handler: PersistentHandler) -> None:
|
|
69
94
|
async with self.lock.acquire(handler.handler_id):
|
|
70
95
|
id = await self._get_item_id(handler)
|
|
71
96
|
if id is None:
|
|
72
|
-
item = await self.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if item.id is None:
|
|
97
|
+
item = await self._create_item(handler)
|
|
98
|
+
item_id = item.get("id")
|
|
99
|
+
if item_id is None:
|
|
76
100
|
raise ValueError(f"Failed to create handler {handler.handler_id}")
|
|
77
|
-
self.cache.set(handler.handler_id,
|
|
101
|
+
self.cache.set(handler.handler_id, item_id)
|
|
78
102
|
else:
|
|
79
|
-
await self.
|
|
80
|
-
item_id=id,
|
|
81
|
-
data=handler,
|
|
82
|
-
)
|
|
103
|
+
await self._update_item(id, handler)
|
|
83
104
|
|
|
84
105
|
@override
|
|
85
106
|
async def delete(self, query: HandlerQuery) -> int:
|
|
86
107
|
filters = self._build_filters(query)
|
|
87
|
-
|
|
108
|
+
search_request = {
|
|
109
|
+
"deployment_name": self.deployment_name,
|
|
110
|
+
"collection": self.collection,
|
|
111
|
+
"filter": filters,
|
|
112
|
+
"page_size": 1000,
|
|
113
|
+
}
|
|
114
|
+
async with self._get_client() as client:
|
|
115
|
+
response = await client.post(
|
|
116
|
+
f"{self.base_url}/api/v1/beta/agent-data/:search",
|
|
117
|
+
json=search_request,
|
|
118
|
+
)
|
|
119
|
+
response.raise_for_status()
|
|
120
|
+
data = response.json()
|
|
121
|
+
|
|
122
|
+
items = data.get("items", [])
|
|
88
123
|
await asyncio.gather(
|
|
89
|
-
*[self.
|
|
124
|
+
*[self._delete_item(item["id"]) for item in items if item.get("id")]
|
|
90
125
|
)
|
|
91
|
-
return len(
|
|
126
|
+
return len(items)
|
|
127
|
+
|
|
128
|
+
async def _create_item(self, handler: PersistentHandler) -> dict[str, Any]:
|
|
129
|
+
"""Create a new agent data item."""
|
|
130
|
+
create_request = {
|
|
131
|
+
"deployment_name": self.deployment_name,
|
|
132
|
+
"collection": self.collection,
|
|
133
|
+
"data": handler.model_dump(mode="json"),
|
|
134
|
+
}
|
|
135
|
+
async with self._get_client() as client:
|
|
136
|
+
response = await client.post(
|
|
137
|
+
f"{self.base_url}/api/v1/beta/agent-data",
|
|
138
|
+
json=create_request,
|
|
139
|
+
)
|
|
140
|
+
response.raise_for_status()
|
|
141
|
+
return response.json()
|
|
142
|
+
|
|
143
|
+
async def _update_item(self, item_id: str, handler: PersistentHandler) -> None:
|
|
144
|
+
"""Update an existing agent data item."""
|
|
145
|
+
update_request = {
|
|
146
|
+
"data": handler.model_dump(mode="json"),
|
|
147
|
+
}
|
|
148
|
+
async with self._get_client() as client:
|
|
149
|
+
response = await client.put(
|
|
150
|
+
f"{self.base_url}/api/v1/beta/agent-data/{item_id}",
|
|
151
|
+
json=update_request,
|
|
152
|
+
)
|
|
153
|
+
response.raise_for_status()
|
|
154
|
+
|
|
155
|
+
async def _delete_item(self, item_id: str) -> None:
|
|
156
|
+
"""Delete an agent data item."""
|
|
157
|
+
async with self._get_client() as client:
|
|
158
|
+
response = await client.delete(
|
|
159
|
+
f"{self.base_url}/api/v1/beta/agent-data/{item_id}",
|
|
160
|
+
)
|
|
161
|
+
response.raise_for_status()
|
|
92
162
|
|
|
93
163
|
async def _get_item_id(self, handler: PersistentHandler) -> str | None:
|
|
94
164
|
cached_id = self.cache.get(handler.handler_id)
|
|
95
165
|
if cached_id is not None:
|
|
96
166
|
return cached_id
|
|
97
167
|
search_filter = {"handler_id": {"eq": handler.handler_id}}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
168
|
+
search_request = {
|
|
169
|
+
"deployment_name": self.deployment_name,
|
|
170
|
+
"collection": self.collection,
|
|
171
|
+
"filter": search_filter,
|
|
172
|
+
"page_size": 1,
|
|
173
|
+
}
|
|
174
|
+
async with self._get_client() as client:
|
|
175
|
+
response = await client.post(
|
|
176
|
+
f"{self.base_url}/api/v1/beta/agent-data/:search",
|
|
177
|
+
json=search_request,
|
|
178
|
+
)
|
|
179
|
+
response.raise_for_status()
|
|
180
|
+
data = response.json()
|
|
181
|
+
|
|
182
|
+
items = data.get("items", [])
|
|
183
|
+
if not items:
|
|
103
184
|
return None
|
|
104
|
-
id =
|
|
185
|
+
id = items[0].get("id")
|
|
105
186
|
if id is None:
|
|
106
187
|
return None
|
|
107
188
|
self.cache.set(handler.handler_id, id)
|
|
@@ -121,4 +202,15 @@ class AgentDataStore(AbstractWorkflowStore):
|
|
|
121
202
|
filters["status"] = {
|
|
122
203
|
"includes": query.status_in,
|
|
123
204
|
}
|
|
205
|
+
if query.is_idle is not None:
|
|
206
|
+
if query.is_idle:
|
|
207
|
+
# Filter for handlers where idle_since is set (any valid datetime)
|
|
208
|
+
filters["idle_since"] = {
|
|
209
|
+
"gte": "1970-01-01T00:00:00Z",
|
|
210
|
+
}
|
|
211
|
+
else:
|
|
212
|
+
# Filter for handlers where idle_since is not set (null)
|
|
213
|
+
filters["idle_since"] = {
|
|
214
|
+
"eq": None,
|
|
215
|
+
}
|
|
124
216
|
return filters
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: llama-deploy-appserver
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Application server components for LlamaDeploy
|
|
5
5
|
Author: Massimiliano Pippi, Adrian Lyjak
|
|
6
6
|
Author-email: Massimiliano Pippi <mpippi@gmail.com>, Adrian Lyjak <adrianlyjak@gmail.com>
|
|
@@ -9,14 +9,13 @@ Requires-Dist: llama-index-workflows[server]>=2.12.0,<2.13.0
|
|
|
9
9
|
Requires-Dist: pydantic-settings>=2.10.1
|
|
10
10
|
Requires-Dist: fastapi>=0.100.0
|
|
11
11
|
Requires-Dist: websockets>=12.0
|
|
12
|
-
Requires-Dist: llama-deploy-core>=0.
|
|
12
|
+
Requires-Dist: llama-deploy-core>=0.4.1,<0.5.0
|
|
13
13
|
Requires-Dist: httpx>=0.24.0,<1.0.0
|
|
14
14
|
Requires-Dist: prometheus-fastapi-instrumentator>=7.1.0
|
|
15
15
|
Requires-Dist: packaging>=25.0
|
|
16
16
|
Requires-Dist: structlog>=25.4.0
|
|
17
17
|
Requires-Dist: rich>=14.1.0
|
|
18
18
|
Requires-Dist: pyyaml>=6.0.2
|
|
19
|
-
Requires-Dist: llama-cloud-services>=0.6.60
|
|
20
19
|
Requires-Dist: watchfiles>=1.1.0
|
|
21
20
|
Requires-Dist: uvicorn>=0.35.0
|
|
22
21
|
Requires-Dist: typing-extensions>=4.15.0 ; python_full_version < '3.12'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
llama_deploy/appserver/__init__.py,sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855,0
|
|
2
|
-
llama_deploy/appserver/app.py,sha256=
|
|
2
|
+
llama_deploy/appserver/app.py,sha256=0049778979421d9f35134098122dfb485113342f43e83365d0403b66b4f1c0dc,16305
|
|
3
3
|
llama_deploy/appserver/bootstrap.py,sha256=34724cf3056b653b71d1bedab64dbde221d7c47443a2a41032e767d50168a24b,2581
|
|
4
4
|
llama_deploy/appserver/configure_logging.py,sha256=2431fda77c47d0aef783992b0f01978a3a643768a2e82f10f3c263538f23d800,6361
|
|
5
5
|
llama_deploy/appserver/correlation_id.py,sha256=8ac5bc6160c707b93a9fb818b64dd369a4ef7a53f9f91a6b3d90c4cf446f7327,572
|
|
@@ -15,10 +15,10 @@ llama_deploy/appserver/routers/ui_proxy.py,sha256=38f00793a5644c937b1f09af862a63
|
|
|
15
15
|
llama_deploy/appserver/settings.py,sha256=aa4512d2f1f28b8ee7d3fedc8c61f77bce9f807f85857c0f6282054320c9da23,5124
|
|
16
16
|
llama_deploy/appserver/stats.py,sha256=1f3989f6705a6de3e4d61ee8cdd189fbe04a2c53ec5e720b2e5168acc331427f,691
|
|
17
17
|
llama_deploy/appserver/types.py,sha256=4edc991aafb6b8497f068d12387455df292da3ff8440223637641ab1632553ec,2133
|
|
18
|
-
llama_deploy/appserver/workflow_loader.py,sha256=
|
|
19
|
-
llama_deploy/appserver/workflow_store/agent_data_store.py,sha256=
|
|
18
|
+
llama_deploy/appserver/workflow_loader.py,sha256=869c0ed658afc73a3f502ea41154e232ac9ce4482b9899d20ad60df4a79bee3c,16241
|
|
19
|
+
llama_deploy/appserver/workflow_store/agent_data_store.py,sha256=816710c9957c37792ce38e97a2c57cd8079953d55489b445675646ff74b4e13e,7710
|
|
20
20
|
llama_deploy/appserver/workflow_store/keyed_lock.py,sha256=72bcfafbce56d5b36d53aff764b573c2dca2b3f5bc59f2d8baa80be0e4db6e34,1037
|
|
21
21
|
llama_deploy/appserver/workflow_store/lru_cache.py,sha256=10b7d69e4be7d929d9dac009b59635b20fbed4603fb004bc35cbdc3ce538af8b,1454
|
|
22
|
-
llama_deploy_appserver-0.
|
|
23
|
-
llama_deploy_appserver-0.
|
|
24
|
-
llama_deploy_appserver-0.
|
|
22
|
+
llama_deploy_appserver-0.4.1.dist-info/WHEEL,sha256=66530aef82d5020ef5af27ae0123c71abb9261377c5bc519376c671346b12918,79
|
|
23
|
+
llama_deploy_appserver-0.4.1.dist-info/METADATA,sha256=5ac33cfcd59421dc86f5d0ae96feea42a301134c3e7ad677a78b35c820bcc9f4,1117
|
|
24
|
+
llama_deploy_appserver-0.4.1.dist-info/RECORD,,
|
|
File without changes
|