llama-deploy-appserver 0.3.27__tar.gz → 0.4.1__tar.gz

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 (25) hide show
  1. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/PKG-INFO +2 -3
  2. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/pyproject.toml +2 -3
  3. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/app.py +26 -3
  4. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/workflow_loader.py +20 -8
  5. llama_deploy_appserver-0.4.1/src/llama_deploy/appserver/workflow_store/agent_data_store.py +216 -0
  6. llama_deploy_appserver-0.3.27/src/llama_deploy/appserver/workflow_store/agent_data_store.py +0 -124
  7. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/README.md +0 -0
  8. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/__init__.py +0 -0
  9. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/bootstrap.py +0 -0
  10. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/configure_logging.py +0 -0
  11. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/correlation_id.py +0 -0
  12. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/deployment.py +0 -0
  13. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/deployment_config_parser.py +0 -0
  14. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/interrupts.py +0 -0
  15. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/process_utils.py +0 -0
  16. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/py.typed +0 -0
  17. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/routers/__init__.py +0 -0
  18. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/routers/deployments.py +0 -0
  19. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/routers/status.py +0 -0
  20. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/routers/ui_proxy.py +0 -0
  21. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/settings.py +0 -0
  22. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/stats.py +0 -0
  23. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/types.py +0 -0
  24. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/workflow_store/keyed_lock.py +0 -0
  25. {llama_deploy_appserver-0.3.27 → llama_deploy_appserver-0.4.1}/src/llama_deploy/appserver/workflow_store/lru_cache.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: llama-deploy-appserver
3
- Version: 0.3.27
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.3.27,<0.4.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'
@@ -14,7 +14,7 @@ dev = [
14
14
 
15
15
  [project]
16
16
  name = "llama-deploy-appserver"
17
- version = "0.3.27"
17
+ version = "0.4.1"
18
18
  description = "Application server components for LlamaDeploy"
19
19
  readme = "README.md"
20
20
  license = {text = "MIT"}
@@ -28,14 +28,13 @@ dependencies = [
28
28
  "pydantic-settings>=2.10.1",
29
29
  "fastapi>=0.100.0",
30
30
  "websockets>=12.0",
31
- "llama-deploy-core>=0.3.27,<0.4.0",
31
+ "llama-deploy-core>=0.4.1,<0.5.0",
32
32
  "httpx>=0.24.0,<1.0.0",
33
33
  "prometheus-fastapi-instrumentator>=7.1.0",
34
34
  "packaging>=25.0",
35
35
  "structlog>=25.4.0",
36
36
  "rich>=14.1.0",
37
37
  "pyyaml>=6.0.2",
38
- "llama-cloud-services>=0.6.60",
39
38
  "watchfiles>=1.1.0",
40
39
  "uvicorn>=0.35.0",
41
40
  "typing-extensions>=4.15.0 ; python_full_version < '3.12'"
@@ -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(cwd=Path.cwd(), deployment_file=args.deployment_file)
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(config: DeploymentConfig) -> None:
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
- missing_list = ", ".join(sorted(missing))
89
- raise RuntimeError(
90
- (
91
- "Missing required environment variables defined in required_env_vars: "
92
- f"{missing_list}. Provide them via your environment, .env files, or the deployment secrets."
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(
@@ -0,0 +1,216 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import sys
5
+ from typing import Any, List
6
+
7
+ import httpx
8
+ from llama_deploy.appserver.settings import ApiserverSettings
9
+ from llama_deploy.core.client.ssl_util import get_httpx_verify_param
10
+ from llama_deploy.core.deployment_config import DeploymentConfig
11
+ from workflows.server import AbstractWorkflowStore, HandlerQuery, PersistentHandler
12
+
13
+ from .keyed_lock import AsyncKeyedLock
14
+ from .lru_cache import LRUCache
15
+
16
+ if sys.version_info <= (3, 11):
17
+ from typing_extensions import override
18
+ else:
19
+ from typing import override
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ DEFAULT_BASE_URL = "https://api.cloud.llamaindex.ai"
24
+
25
+
26
+ class AgentDataStore(AbstractWorkflowStore):
27
+ """Workflow store backed by LlamaCloud Agent Data API using raw httpx."""
28
+
29
+ def __init__(
30
+ self, settings: DeploymentConfig, server_settings: ApiserverSettings
31
+ ) -> None:
32
+ agent_url_id: str | None = server_settings.cloud_persistence_name
33
+ collection = "workflow_contexts"
34
+ if agent_url_id is not None:
35
+ parts = agent_url_id.split(":")
36
+ if len(parts) > 1:
37
+ collection = parts[1]
38
+ agent_url_id = parts[0]
39
+ else:
40
+ agent_url_id = settings.name
41
+
42
+ self.settings = settings
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
+
50
+ self.lock = AsyncKeyedLock()
51
+ # workflow id -> agent data id
52
+ self.cache = LRUCache[str, str](maxsize=1024)
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
+
72
+ @override
73
+ async def query(self, query: HandlerQuery) -> List[PersistentHandler]:
74
+ filters = self._build_filters(query)
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]
91
+
92
+ @override
93
+ async def update(self, handler: PersistentHandler) -> None:
94
+ async with self.lock.acquire(handler.handler_id):
95
+ id = await self._get_item_id(handler)
96
+ if id is None:
97
+ item = await self._create_item(handler)
98
+ item_id = item.get("id")
99
+ if item_id is None:
100
+ raise ValueError(f"Failed to create handler {handler.handler_id}")
101
+ self.cache.set(handler.handler_id, item_id)
102
+ else:
103
+ await self._update_item(id, handler)
104
+
105
+ @override
106
+ async def delete(self, query: HandlerQuery) -> int:
107
+ filters = self._build_filters(query)
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", [])
123
+ await asyncio.gather(
124
+ *[self._delete_item(item["id"]) for item in items if item.get("id")]
125
+ )
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()
162
+
163
+ async def _get_item_id(self, handler: PersistentHandler) -> str | None:
164
+ cached_id = self.cache.get(handler.handler_id)
165
+ if cached_id is not None:
166
+ return cached_id
167
+ search_filter = {"handler_id": {"eq": handler.handler_id}}
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:
184
+ return None
185
+ id = items[0].get("id")
186
+ if id is None:
187
+ return None
188
+ self.cache.set(handler.handler_id, id)
189
+ return id
190
+
191
+ def _build_filters(self, query: HandlerQuery) -> dict[str, Any]:
192
+ filters: dict[str, Any] = {}
193
+ if query.handler_id_in is not None:
194
+ filters["handler_id"] = {
195
+ "includes": query.handler_id_in,
196
+ }
197
+ if query.workflow_name_in is not None:
198
+ filters["workflow_name"] = {
199
+ "includes": query.workflow_name_in,
200
+ }
201
+ if query.status_in is not None:
202
+ filters["status"] = {
203
+ "includes": query.status_in,
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
+ }
216
+ return filters
@@ -1,124 +0,0 @@
1
- import asyncio
2
- import logging
3
- import os
4
- import sys
5
- from typing import Any, List, cast
6
-
7
- from llama_cloud.client import AsyncLlamaCloud, httpx
8
- from llama_cloud_services.beta.agent_data import AsyncAgentDataClient
9
- from llama_deploy.appserver.settings import ApiserverSettings
10
- from llama_deploy.core.client.ssl_util import get_httpx_verify_param
11
- from llama_deploy.core.deployment_config import DeploymentConfig
12
- from workflows.server import AbstractWorkflowStore, HandlerQuery, PersistentHandler
13
-
14
- from .keyed_lock import AsyncKeyedLock
15
- from .lru_cache import LRUCache
16
-
17
- if sys.version_info <= (3, 11):
18
- from typing_extensions import override
19
- else:
20
- from typing import override
21
-
22
- logger = logging.getLogger(__name__)
23
-
24
-
25
- class AgentDataStore(AbstractWorkflowStore):
26
- def __init__(
27
- self, settings: DeploymentConfig, server_settings: ApiserverSettings
28
- ) -> None:
29
- agent_url_id: str | None = server_settings.cloud_persistence_name
30
- collection = "workflow_contexts"
31
- if agent_url_id is not None:
32
- parts = agent_url_id.split(":")
33
- if len(parts) > 1:
34
- collection = parts[1]
35
- agent_url_id = parts[0]
36
- else:
37
- agent_url_id = settings.name
38
-
39
- self.settings = settings
40
- project_id = os.getenv("LLAMA_DEPLOY_PROJECT_ID")
41
- self.client = AsyncAgentDataClient(
42
- type=PersistentHandler,
43
- collection=collection,
44
- agent_url_id=agent_url_id,
45
- client=AsyncLlamaCloud(
46
- base_url=os.getenv("LLAMA_CLOUD_BASE_URL"),
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
- )
54
- self.lock = AsyncKeyedLock()
55
- # workflow id -> agent data id
56
- self.cache = LRUCache[str, str](maxsize=1024)
57
-
58
- @override
59
- async def query(self, query: HandlerQuery) -> List[PersistentHandler]:
60
- filters = self._build_filters(query)
61
- results = await self.client.search(
62
- filter=filters,
63
- page_size=1000,
64
- )
65
- return [x.data for x in results.items]
66
-
67
- @override
68
- async def update(self, handler: PersistentHandler) -> None:
69
- async with self.lock.acquire(handler.handler_id):
70
- id = await self._get_item_id(handler)
71
- if id is None:
72
- item = await self.client.create_item(
73
- data=handler,
74
- )
75
- if item.id is None:
76
- raise ValueError(f"Failed to create handler {handler.handler_id}")
77
- self.cache.set(handler.handler_id, item.id)
78
- else:
79
- await self.client.update_item(
80
- item_id=id,
81
- data=handler,
82
- )
83
-
84
- @override
85
- async def delete(self, query: HandlerQuery) -> int:
86
- filters = self._build_filters(query)
87
- results = await self.client.search(filter=filters, page_size=1000)
88
- await asyncio.gather(
89
- *[self.client.delete_item(item_id=x.id) for x in results.items if x.id]
90
- )
91
- return len(results.items)
92
-
93
- async def _get_item_id(self, handler: PersistentHandler) -> str | None:
94
- cached_id = self.cache.get(handler.handler_id)
95
- if cached_id is not None:
96
- return cached_id
97
- search_filter = {"handler_id": {"eq": handler.handler_id}}
98
- results = await self.client.search(
99
- filter=cast(Any, search_filter),
100
- page_size=1,
101
- )
102
- if not results.items:
103
- return None
104
- id = results.items[0].id
105
- if id is None:
106
- return None
107
- self.cache.set(handler.handler_id, id)
108
- return id
109
-
110
- def _build_filters(self, query: HandlerQuery) -> dict[str, Any]:
111
- filters: dict[str, Any] = {}
112
- if query.handler_id_in is not None:
113
- filters["handler_id"] = {
114
- "includes": query.handler_id_in,
115
- }
116
- if query.workflow_name_in is not None:
117
- filters["workflow_name"] = {
118
- "includes": query.workflow_name_in,
119
- }
120
- if query.status_in is not None:
121
- filters["status"] = {
122
- "includes": query.status_in,
123
- }
124
- return filters