flock-core 0.5.3__py3-none-any.whl → 0.5.5__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/agent.py +20 -1
- flock/artifact_collector.py +2 -3
- flock/batch_accumulator.py +4 -4
- flock/components.py +32 -0
- flock/correlation_engine.py +9 -4
- flock/dashboard/collector.py +4 -0
- flock/dashboard/events.py +74 -0
- flock/dashboard/graph_builder.py +272 -0
- flock/dashboard/models/graph.py +3 -1
- flock/dashboard/service.py +363 -14
- flock/dashboard/static_v2/assets/index-DFRnI_mt.js +1 -1
- flock/dashboard/static_v2/index.html +3 -3
- flock/engines/dspy_engine.py +41 -3
- flock/engines/examples/__init__.py +6 -0
- flock/engines/examples/simple_batch_engine.py +61 -0
- flock/frontend/README.md +4 -4
- flock/frontend/docs/DESIGN_SYSTEM.md +1 -1
- flock/frontend/package-lock.json +2 -2
- flock/frontend/package.json +2 -2
- flock/frontend/src/components/controls/PublishControl.test.tsx +11 -11
- flock/frontend/src/components/controls/PublishControl.tsx +1 -1
- flock/frontend/src/components/graph/AgentNode.tsx +4 -0
- flock/frontend/src/components/graph/GraphCanvas.tsx +4 -0
- flock/frontend/src/components/graph/LogicOperationsDisplay.tsx +463 -0
- flock/frontend/src/components/graph/PendingBatchEdge.tsx +141 -0
- flock/frontend/src/components/graph/PendingJoinEdge.tsx +144 -0
- flock/frontend/src/components/settings/SettingsPanel.css +1 -1
- flock/frontend/src/components/settings/ThemeSelector.tsx +2 -2
- flock/frontend/src/services/graphService.ts +3 -1
- flock/frontend/src/services/indexeddb.ts +1 -1
- flock/frontend/src/services/websocket.ts +99 -1
- flock/frontend/src/store/graphStore.test.ts +2 -1
- flock/frontend/src/store/graphStore.ts +36 -5
- flock/frontend/src/styles/variables.css +1 -1
- flock/frontend/src/types/graph.ts +86 -0
- flock/orchestrator.py +268 -13
- flock/patches/__init__.py +1 -0
- flock/patches/dspy_streaming_patch.py +1 -0
- flock/runtime.py +3 -0
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/METADATA +11 -1
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/RECORD +44 -39
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/WHEEL +0 -0
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/entry_points.txt +0 -0
- {flock_core-0.5.3.dist-info → flock_core-0.5.5.dist-info}/licenses/LICENSE +0 -0
flock/dashboard/service.py
CHANGED
|
@@ -8,6 +8,7 @@ Provides real-time dashboard capabilities by:
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import os
|
|
11
|
+
from datetime import datetime, timedelta, timezone
|
|
11
12
|
from importlib.metadata import PackageNotFoundError, version
|
|
12
13
|
from pathlib import Path
|
|
13
14
|
from typing import Any
|
|
@@ -201,7 +202,10 @@ class DashboardHTTPService(BlackboardHTTPService):
|
|
|
201
202
|
|
|
202
203
|
@app.get("/api/agents")
|
|
203
204
|
async def get_agents() -> dict[str, Any]:
|
|
204
|
-
"""Get all registered agents.
|
|
205
|
+
"""Get all registered agents with logic operations state.
|
|
206
|
+
|
|
207
|
+
Phase 1.2 Enhancement: Now includes logic_operations configuration
|
|
208
|
+
and waiting state for agents using JoinSpec or BatchSpec.
|
|
205
209
|
|
|
206
210
|
Returns:
|
|
207
211
|
{
|
|
@@ -209,9 +213,18 @@ class DashboardHTTPService(BlackboardHTTPService):
|
|
|
209
213
|
{
|
|
210
214
|
"name": "agent_name",
|
|
211
215
|
"description": "...",
|
|
212
|
-
"status": "ready",
|
|
216
|
+
"status": "ready" | "waiting" | "active",
|
|
213
217
|
"subscriptions": ["TypeA", "TypeB"],
|
|
214
|
-
"output_types": ["TypeC", "TypeD"]
|
|
218
|
+
"output_types": ["TypeC", "TypeD"],
|
|
219
|
+
"logic_operations": [ # NEW: Phase 1.2
|
|
220
|
+
{
|
|
221
|
+
"subscription_index": 0,
|
|
222
|
+
"subscription_types": ["TypeA", "TypeB"],
|
|
223
|
+
"join": {...}, # JoinSpec config
|
|
224
|
+
"batch": {...}, # BatchSpec config
|
|
225
|
+
"waiting_state": {...} # Current state
|
|
226
|
+
}
|
|
227
|
+
]
|
|
215
228
|
},
|
|
216
229
|
...
|
|
217
230
|
]
|
|
@@ -228,15 +241,25 @@ class DashboardHTTPService(BlackboardHTTPService):
|
|
|
228
241
|
# Extract produced types from agent outputs
|
|
229
242
|
produced_types = [output.spec.type_name for output in agent.outputs]
|
|
230
243
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
244
|
+
# NEW Phase 1.2: Logic operations configuration
|
|
245
|
+
logic_operations = []
|
|
246
|
+
for idx, subscription in enumerate(agent.subscriptions):
|
|
247
|
+
logic_config = _build_logic_config(agent, subscription, idx, orchestrator)
|
|
248
|
+
if logic_config: # Only include if has join/batch
|
|
249
|
+
logic_operations.append(logic_config)
|
|
250
|
+
|
|
251
|
+
agent_data = {
|
|
252
|
+
"name": agent.name,
|
|
253
|
+
"description": agent.description or "",
|
|
254
|
+
"status": _compute_agent_status(agent, orchestrator), # NEW: Dynamic status
|
|
255
|
+
"subscriptions": consumed_types,
|
|
256
|
+
"output_types": produced_types,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if logic_operations:
|
|
260
|
+
agent_data["logic_operations"] = logic_operations
|
|
261
|
+
|
|
262
|
+
agents.append(agent_data)
|
|
240
263
|
|
|
241
264
|
return {"agents": agents}
|
|
242
265
|
|
|
@@ -693,10 +716,10 @@ class DashboardHTTPService(BlackboardHTTPService):
|
|
|
693
716
|
if time_range and time_range[0]:
|
|
694
717
|
# Convert nanoseconds to datetime
|
|
695
718
|
oldest_trace = datetime.fromtimestamp(
|
|
696
|
-
time_range[0] / 1_000_000_000
|
|
719
|
+
time_range[0] / 1_000_000_000, tz=timezone.utc
|
|
697
720
|
).isoformat()
|
|
698
721
|
newest_trace = datetime.fromtimestamp(
|
|
699
|
-
time_range[1] / 1_000_000_000
|
|
722
|
+
time_range[1] / 1_000_000_000, tz=timezone.utc
|
|
700
723
|
).isoformat()
|
|
701
724
|
|
|
702
725
|
# Get file size
|
|
@@ -988,4 +1011,330 @@ class DashboardHTTPService(BlackboardHTTPService):
|
|
|
988
1011
|
return self.app
|
|
989
1012
|
|
|
990
1013
|
|
|
1014
|
+
def _get_correlation_groups(
|
|
1015
|
+
engine: "CorrelationEngine", # noqa: F821
|
|
1016
|
+
agent_name: str,
|
|
1017
|
+
subscription_index: int,
|
|
1018
|
+
) -> list[dict[str, Any]]:
|
|
1019
|
+
"""Extract correlation group state from CorrelationEngine.
|
|
1020
|
+
|
|
1021
|
+
Returns waiting state for all correlation groups for the given agent subscription.
|
|
1022
|
+
Used by enhanced /api/agents endpoint to expose JoinSpec waiting state.
|
|
1023
|
+
|
|
1024
|
+
Args:
|
|
1025
|
+
engine: CorrelationEngine instance from orchestrator
|
|
1026
|
+
agent_name: Name of the agent
|
|
1027
|
+
subscription_index: Index of the subscription (for agents with multiple subscriptions)
|
|
1028
|
+
|
|
1029
|
+
Returns:
|
|
1030
|
+
List of correlation group states with progress metrics:
|
|
1031
|
+
[
|
|
1032
|
+
{
|
|
1033
|
+
"correlation_key": "patient_123",
|
|
1034
|
+
"created_at": "2025-10-13T14:30:00Z",
|
|
1035
|
+
"elapsed_seconds": 45.2,
|
|
1036
|
+
"expires_in_seconds": 254.8, # For time windows
|
|
1037
|
+
"expires_in_artifacts": 7, # For count windows
|
|
1038
|
+
"collected_types": {"XRayImage": 1, "LabResults": 0},
|
|
1039
|
+
"required_types": {"XRayImage": 1, "LabResults": 1},
|
|
1040
|
+
"waiting_for": ["LabResults"],
|
|
1041
|
+
"is_complete": False,
|
|
1042
|
+
"is_expired": False
|
|
1043
|
+
},
|
|
1044
|
+
...
|
|
1045
|
+
]
|
|
1046
|
+
"""
|
|
1047
|
+
|
|
1048
|
+
pool_key = (agent_name, subscription_index)
|
|
1049
|
+
groups = engine.correlation_groups.get(pool_key, {})
|
|
1050
|
+
|
|
1051
|
+
if not groups:
|
|
1052
|
+
return []
|
|
1053
|
+
|
|
1054
|
+
now = datetime.now(timezone.utc)
|
|
1055
|
+
result = []
|
|
1056
|
+
|
|
1057
|
+
for corr_key, group in groups.items():
|
|
1058
|
+
# Calculate elapsed time
|
|
1059
|
+
if group.created_at_time:
|
|
1060
|
+
created_at_time = group.created_at_time
|
|
1061
|
+
if created_at_time.tzinfo is None:
|
|
1062
|
+
created_at_time = created_at_time.replace(tzinfo=timezone.utc)
|
|
1063
|
+
elapsed = (now - created_at_time).total_seconds()
|
|
1064
|
+
else:
|
|
1065
|
+
elapsed = 0
|
|
1066
|
+
|
|
1067
|
+
# Calculate time remaining (for time windows)
|
|
1068
|
+
expires_in_seconds = None
|
|
1069
|
+
if isinstance(group.window_spec, timedelta):
|
|
1070
|
+
window_seconds = group.window_spec.total_seconds()
|
|
1071
|
+
expires_in_seconds = max(0, window_seconds - elapsed)
|
|
1072
|
+
|
|
1073
|
+
# Calculate artifact count remaining (for count windows)
|
|
1074
|
+
expires_in_artifacts = None
|
|
1075
|
+
if isinstance(group.window_spec, int):
|
|
1076
|
+
artifacts_passed = engine.global_sequence - group.created_at_sequence
|
|
1077
|
+
expires_in_artifacts = max(0, group.window_spec - artifacts_passed)
|
|
1078
|
+
|
|
1079
|
+
# Determine what we're waiting for
|
|
1080
|
+
collected_types = {
|
|
1081
|
+
type_name: len(group.waiting_artifacts.get(type_name, []))
|
|
1082
|
+
for type_name in group.required_types
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
waiting_for = [
|
|
1086
|
+
type_name
|
|
1087
|
+
for type_name, required_count in group.type_counts.items()
|
|
1088
|
+
if collected_types.get(type_name, 0) < required_count
|
|
1089
|
+
]
|
|
1090
|
+
|
|
1091
|
+
result.append(
|
|
1092
|
+
{
|
|
1093
|
+
"correlation_key": str(corr_key),
|
|
1094
|
+
"created_at": group.created_at_time.isoformat() if group.created_at_time else None,
|
|
1095
|
+
"elapsed_seconds": round(elapsed, 1),
|
|
1096
|
+
"expires_in_seconds": round(expires_in_seconds, 1)
|
|
1097
|
+
if expires_in_seconds is not None
|
|
1098
|
+
else None,
|
|
1099
|
+
"expires_in_artifacts": expires_in_artifacts,
|
|
1100
|
+
"collected_types": collected_types,
|
|
1101
|
+
"required_types": dict(group.type_counts),
|
|
1102
|
+
"waiting_for": waiting_for,
|
|
1103
|
+
"is_complete": group.is_complete(),
|
|
1104
|
+
"is_expired": group.is_expired(engine.global_sequence),
|
|
1105
|
+
}
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
return result
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
def _get_batch_state(
|
|
1112
|
+
engine: "BatchEngine", # noqa: F821
|
|
1113
|
+
agent_name: str,
|
|
1114
|
+
subscription_index: int,
|
|
1115
|
+
batch_spec: "BatchSpec", # noqa: F821
|
|
1116
|
+
) -> dict[str, Any] | None:
|
|
1117
|
+
"""Extract batch state from BatchEngine.
|
|
1118
|
+
|
|
1119
|
+
Returns current batch accumulator state for the given agent subscription.
|
|
1120
|
+
Used by enhanced /api/agents endpoint to expose BatchSpec waiting state.
|
|
1121
|
+
|
|
1122
|
+
Args:
|
|
1123
|
+
engine: BatchEngine instance from orchestrator
|
|
1124
|
+
agent_name: Name of the agent
|
|
1125
|
+
subscription_index: Index of the subscription
|
|
1126
|
+
batch_spec: BatchSpec configuration (needed for metrics)
|
|
1127
|
+
|
|
1128
|
+
Returns:
|
|
1129
|
+
Batch state dict or None if no batch or batch is empty:
|
|
1130
|
+
{
|
|
1131
|
+
"created_at": "2025-10-13T14:30:00Z",
|
|
1132
|
+
"elapsed_seconds": 12.5,
|
|
1133
|
+
"items_collected": 18,
|
|
1134
|
+
"items_target": 25,
|
|
1135
|
+
"items_remaining": 7,
|
|
1136
|
+
"timeout_seconds": 30,
|
|
1137
|
+
"timeout_remaining_seconds": 17.5,
|
|
1138
|
+
"will_flush": "on_size" | "on_timeout" | "unknown"
|
|
1139
|
+
}
|
|
1140
|
+
"""
|
|
1141
|
+
|
|
1142
|
+
batch_key = (agent_name, subscription_index)
|
|
1143
|
+
accumulator = engine.batches.get(batch_key)
|
|
1144
|
+
|
|
1145
|
+
# Return None if no batch or batch is empty
|
|
1146
|
+
if not accumulator or not accumulator.artifacts:
|
|
1147
|
+
return None
|
|
1148
|
+
|
|
1149
|
+
now = datetime.now(timezone.utc)
|
|
1150
|
+
# Ensure accumulator.created_at is timezone-aware
|
|
1151
|
+
created_at = accumulator.created_at
|
|
1152
|
+
if created_at.tzinfo is None:
|
|
1153
|
+
created_at = created_at.replace(tzinfo=timezone.utc)
|
|
1154
|
+
elapsed = (now - created_at).total_seconds()
|
|
1155
|
+
|
|
1156
|
+
# Calculate items collected (needed for all batch types)
|
|
1157
|
+
items_collected = len(accumulator.artifacts)
|
|
1158
|
+
# For group batching, use _group_count if available
|
|
1159
|
+
if hasattr(accumulator, "_group_count"):
|
|
1160
|
+
items_collected = accumulator._group_count
|
|
1161
|
+
|
|
1162
|
+
result = {
|
|
1163
|
+
"created_at": accumulator.created_at.isoformat(),
|
|
1164
|
+
"elapsed_seconds": round(elapsed, 1),
|
|
1165
|
+
"items_collected": items_collected, # Always include for all batch types
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
# Size-based metrics (only if size threshold configured)
|
|
1169
|
+
if batch_spec.size:
|
|
1170
|
+
result["items_target"] = batch_spec.size
|
|
1171
|
+
result["items_remaining"] = max(0, batch_spec.size - items_collected)
|
|
1172
|
+
else:
|
|
1173
|
+
# Timeout-only batches: no target
|
|
1174
|
+
result["items_target"] = None
|
|
1175
|
+
result["items_remaining"] = None
|
|
1176
|
+
|
|
1177
|
+
# Timeout-based metrics
|
|
1178
|
+
if batch_spec.timeout:
|
|
1179
|
+
timeout_seconds = batch_spec.timeout.total_seconds()
|
|
1180
|
+
timeout_remaining = max(0, timeout_seconds - elapsed)
|
|
1181
|
+
|
|
1182
|
+
result["timeout_seconds"] = int(timeout_seconds)
|
|
1183
|
+
result["timeout_remaining_seconds"] = round(timeout_remaining, 1)
|
|
1184
|
+
|
|
1185
|
+
# Determine what will trigger flush
|
|
1186
|
+
if batch_spec.size and batch_spec.timeout:
|
|
1187
|
+
# Hybrid: predict which will fire first based on progress percentages
|
|
1188
|
+
items_collected = result["items_collected"]
|
|
1189
|
+
items_target = result.get("items_target", 1)
|
|
1190
|
+
timeout_remaining = result.get("timeout_remaining_seconds", 0)
|
|
1191
|
+
|
|
1192
|
+
# Calculate progress toward each threshold
|
|
1193
|
+
size_progress = items_collected / items_target if items_target > 0 else 0
|
|
1194
|
+
timeout_elapsed = elapsed
|
|
1195
|
+
timeout_total = batch_spec.timeout.total_seconds()
|
|
1196
|
+
time_progress = timeout_elapsed / timeout_total if timeout_total > 0 else 0
|
|
1197
|
+
|
|
1198
|
+
# Predict based on which threshold we're progressing toward faster
|
|
1199
|
+
# If we're closer to size threshold (percentage-wise), predict size
|
|
1200
|
+
# Otherwise predict timeout
|
|
1201
|
+
if size_progress > time_progress:
|
|
1202
|
+
result["will_flush"] = "on_size"
|
|
1203
|
+
else:
|
|
1204
|
+
result["will_flush"] = "on_timeout"
|
|
1205
|
+
elif batch_spec.size:
|
|
1206
|
+
result["will_flush"] = "on_size"
|
|
1207
|
+
elif batch_spec.timeout:
|
|
1208
|
+
result["will_flush"] = "on_timeout"
|
|
1209
|
+
|
|
1210
|
+
return result
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
def _compute_agent_status(agent: "Agent", orchestrator: "Flock") -> str: # noqa: F821
|
|
1214
|
+
"""Determine agent status based on waiting state.
|
|
1215
|
+
|
|
1216
|
+
Checks if agent is waiting for correlation or batch completion.
|
|
1217
|
+
Used by enhanced /api/agents endpoint to show agent status.
|
|
1218
|
+
|
|
1219
|
+
Args:
|
|
1220
|
+
agent: Agent instance
|
|
1221
|
+
orchestrator: Flock orchestrator instance
|
|
1222
|
+
|
|
1223
|
+
Returns:
|
|
1224
|
+
"ready" - Agent not waiting for anything
|
|
1225
|
+
"waiting" - Agent has correlation groups or batches accumulating
|
|
1226
|
+
"active" - Agent currently executing (future enhancement)
|
|
1227
|
+
"""
|
|
1228
|
+
# Check if any subscription is waiting for correlation or batching
|
|
1229
|
+
for idx, subscription in enumerate(agent.subscriptions):
|
|
1230
|
+
if subscription.join:
|
|
1231
|
+
pool_key = (agent.name, idx)
|
|
1232
|
+
if pool_key in orchestrator._correlation_engine.correlation_groups:
|
|
1233
|
+
groups = orchestrator._correlation_engine.correlation_groups[pool_key]
|
|
1234
|
+
if groups: # Has waiting correlation groups
|
|
1235
|
+
return "waiting"
|
|
1236
|
+
|
|
1237
|
+
if subscription.batch:
|
|
1238
|
+
batch_key = (agent.name, idx)
|
|
1239
|
+
if batch_key in orchestrator._batch_engine.batches:
|
|
1240
|
+
accumulator = orchestrator._batch_engine.batches[batch_key]
|
|
1241
|
+
if accumulator and accumulator.artifacts:
|
|
1242
|
+
return "waiting"
|
|
1243
|
+
|
|
1244
|
+
return "ready"
|
|
1245
|
+
|
|
1246
|
+
|
|
1247
|
+
def _build_logic_config( # noqa: F821
|
|
1248
|
+
agent: "Agent", # noqa: F821
|
|
1249
|
+
subscription: "Subscription", # noqa: F821
|
|
1250
|
+
idx: int,
|
|
1251
|
+
orchestrator: "Flock",
|
|
1252
|
+
) -> dict[str, Any] | None:
|
|
1253
|
+
"""Build logic operations configuration for a subscription.
|
|
1254
|
+
|
|
1255
|
+
Phase 1.2: Extracts JoinSpec and BatchSpec configuration plus current
|
|
1256
|
+
waiting state for agents using logic operations.
|
|
1257
|
+
|
|
1258
|
+
Args:
|
|
1259
|
+
agent: Agent instance
|
|
1260
|
+
subscription: Subscription to analyze
|
|
1261
|
+
idx: Subscription index (for agents with multiple subscriptions)
|
|
1262
|
+
orchestrator: Flock orchestrator instance
|
|
1263
|
+
|
|
1264
|
+
Returns:
|
|
1265
|
+
Logic operations config dict or None if no join/batch:
|
|
1266
|
+
{
|
|
1267
|
+
"subscription_index": 0,
|
|
1268
|
+
"subscription_types": ["XRayImage", "LabResults"],
|
|
1269
|
+
"join": {...}, # JoinSpec config (if present)
|
|
1270
|
+
"batch": {...}, # BatchSpec config (if present)
|
|
1271
|
+
"waiting_state": {...} # Current state (if waiting)
|
|
1272
|
+
}
|
|
1273
|
+
"""
|
|
1274
|
+
if not subscription.join and not subscription.batch:
|
|
1275
|
+
return None
|
|
1276
|
+
|
|
1277
|
+
config = {
|
|
1278
|
+
"subscription_index": idx,
|
|
1279
|
+
"subscription_types": list(subscription.type_names),
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
# JoinSpec configuration
|
|
1283
|
+
if subscription.join:
|
|
1284
|
+
join_spec = subscription.join
|
|
1285
|
+
window_type = "time" if isinstance(join_spec.within, timedelta) else "count"
|
|
1286
|
+
window_value = (
|
|
1287
|
+
int(join_spec.within.total_seconds())
|
|
1288
|
+
if isinstance(join_spec.within, timedelta)
|
|
1289
|
+
else join_spec.within
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
config["join"] = {
|
|
1293
|
+
"correlation_strategy": "by_key",
|
|
1294
|
+
"window_type": window_type,
|
|
1295
|
+
"window_value": window_value,
|
|
1296
|
+
"window_unit": "seconds" if window_type == "time" else "artifacts",
|
|
1297
|
+
"required_types": list(subscription.type_names),
|
|
1298
|
+
"type_counts": dict(subscription.type_counts),
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
# Get waiting state from CorrelationEngine
|
|
1302
|
+
correlation_groups = _get_correlation_groups(
|
|
1303
|
+
orchestrator._correlation_engine, agent.name, idx
|
|
1304
|
+
)
|
|
1305
|
+
if correlation_groups:
|
|
1306
|
+
config["waiting_state"] = {
|
|
1307
|
+
"is_waiting": True,
|
|
1308
|
+
"correlation_groups": correlation_groups,
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
# BatchSpec configuration
|
|
1312
|
+
if subscription.batch:
|
|
1313
|
+
batch_spec = subscription.batch
|
|
1314
|
+
strategy = (
|
|
1315
|
+
"hybrid"
|
|
1316
|
+
if batch_spec.size and batch_spec.timeout
|
|
1317
|
+
else "size"
|
|
1318
|
+
if batch_spec.size
|
|
1319
|
+
else "timeout"
|
|
1320
|
+
)
|
|
1321
|
+
|
|
1322
|
+
config["batch"] = {
|
|
1323
|
+
"strategy": strategy,
|
|
1324
|
+
}
|
|
1325
|
+
if batch_spec.size:
|
|
1326
|
+
config["batch"]["size"] = batch_spec.size
|
|
1327
|
+
if batch_spec.timeout:
|
|
1328
|
+
config["batch"]["timeout_seconds"] = int(batch_spec.timeout.total_seconds())
|
|
1329
|
+
|
|
1330
|
+
# Get waiting state from BatchEngine
|
|
1331
|
+
batch_state = _get_batch_state(orchestrator._batch_engine, agent.name, idx, batch_spec)
|
|
1332
|
+
if batch_state:
|
|
1333
|
+
if "waiting_state" not in config:
|
|
1334
|
+
config["waiting_state"] = {"is_waiting": True}
|
|
1335
|
+
config["waiting_state"]["batch_state"] = batch_state
|
|
1336
|
+
|
|
1337
|
+
return config
|
|
1338
|
+
|
|
1339
|
+
|
|
991
1340
|
__all__ = ["DashboardHTTPService"]
|