1+ import asyncio
12from datetime import UTC , datetime
23from typing import Annotated , Any , cast
34from uuid import uuid4
3839# these schedules within the shared Temporal namespace and keeps the id stable
3940# and small (the row id is the only thing the workflow needs).
4041RUN_SCHEDULE_TEMPORAL_ID_PREFIX = "agent-run-schedule"
42+ MAX_LIVE_ENRICHMENT_CONCURRENCY = 10
4143
4244# Registered (class) name of the workflow each fire starts. Referenced by name so
4345# the API/service layer doesn't import the Temporal workflow definition.
@@ -160,6 +162,7 @@ async def list_schedules(
160162 agent_id : str ,
161163 authorized_schedule_ids : list [str ] | None = None ,
162164 limit : int = 100 ,
165+ include_live : bool = False ,
163166 ) -> AgentRunScheduleListResponse :
164167 # Fetch without a DB limit so the authorization filter below runs against
165168 # the full set, then truncate to ``limit`` after filtering. Applying the
@@ -178,23 +181,39 @@ async def list_schedules(
178181 else None
179182 )
180183 agent = await self .agent_repository .get (id = agent_id )
181- items : list [AgentRunScheduleResponse ] = []
184+ visible_rows : list [AgentRunScheduleEntity ] = []
182185 for row in rows :
183- if len (items ) >= limit :
186+ if len (visible_rows ) >= limit :
184187 break
185188 selector = build_run_schedule_authz_selector (agent_id , row .id )
186189 if authorized is not None and selector not in authorized :
187190 continue
188- temporal_id = build_run_schedule_temporal_id (row .id )
189- # Serve the list from Postgres only — no per-row Temporal describe.
190- # Fanning out one RPC per row (up to the route's limit of 1000) makes
191- # list latency scale with Temporal round-trips; live fields are
192- # available on the single-schedule GET instead.
193- items .append (
191+ visible_rows .append (row )
192+
193+ if not include_live :
194+ items = [
194195 await self ._to_response (
195- row , agent = agent , temporal_id = temporal_id , include_live = False
196+ row ,
197+ agent = agent ,
198+ temporal_id = build_run_schedule_temporal_id (row .id ),
199+ include_live = False ,
196200 )
197- )
201+ for row in visible_rows
202+ ]
203+ return AgentRunScheduleListResponse (run_schedules = items , total = len (items ))
204+
205+ semaphore = asyncio .Semaphore (MAX_LIVE_ENRICHMENT_CONCURRENCY )
206+
207+ async def enrich (row : AgentRunScheduleEntity ) -> AgentRunScheduleResponse :
208+ async with semaphore :
209+ return await self ._to_response (
210+ row ,
211+ agent = agent ,
212+ temporal_id = build_run_schedule_temporal_id (row .id ),
213+ include_live = True ,
214+ )
215+
216+ items = await asyncio .gather (* (enrich (row ) for row in visible_rows ))
198217 return AgentRunScheduleListResponse (run_schedules = items , total = len (items ))
199218
200219 async def get_schedule (
0 commit comments