xpander-sdk 2.0.332__py3-none-any.whl → 2.0.334__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.
- xpander_sdk/__init__.py +1 -1
- xpander_sdk/core/context_optimizer/context_optimizer.py +87 -2
- xpander_sdk/models/context_status.py +31 -0
- xpander_sdk/models/events.py +3 -0
- xpander_sdk/modules/backend/frameworks/agno.py +1 -1
- {xpander_sdk-2.0.332.dist-info → xpander_sdk-2.0.334.dist-info}/METADATA +1 -1
- {xpander_sdk-2.0.332.dist-info → xpander_sdk-2.0.334.dist-info}/RECORD +10 -9
- {xpander_sdk-2.0.332.dist-info → xpander_sdk-2.0.334.dist-info}/WHEEL +0 -0
- {xpander_sdk-2.0.332.dist-info → xpander_sdk-2.0.334.dist-info}/licenses/LICENSE +0 -0
- {xpander_sdk-2.0.332.dist-info → xpander_sdk-2.0.334.dist-info}/top_level.txt +0 -0
xpander_sdk/__init__.py
CHANGED
|
@@ -259,6 +259,11 @@ class XPanderContextOptimizer(MapReduceMixin, CompressionManager):
|
|
|
259
259
|
default_factory=set, init=False, repr=False
|
|
260
260
|
)
|
|
261
261
|
|
|
262
|
+
_last_emitted_status_pct: Optional[int] = field(
|
|
263
|
+
default=None, init=False, repr=False
|
|
264
|
+
)
|
|
265
|
+
_compacting: bool = field(default=False, init=False, repr=False)
|
|
266
|
+
|
|
262
267
|
# In-memory cache + write queue for Layer 1 offloaded blobs. The optimizer
|
|
263
268
|
# writes encrypted bytes here synchronously and the cache spawns the
|
|
264
269
|
# actual workspace POST in the background; the agno tool hook short-
|
|
@@ -544,6 +549,8 @@ class XPanderContextOptimizer(MapReduceMixin, CompressionManager):
|
|
|
544
549
|
# Layer 1: always runs — offload large tool results to workspace
|
|
545
550
|
await self.layer_1_microcompact(messages)
|
|
546
551
|
|
|
552
|
+
await self._publish_context_status(messages)
|
|
553
|
+
|
|
547
554
|
# Layer 3: agent-requested manual compaction (takes priority)
|
|
548
555
|
if self.compact_requested:
|
|
549
556
|
focus = self.compact_focus
|
|
@@ -1046,6 +1053,78 @@ class XPanderContextOptimizer(MapReduceMixin, CompressionManager):
|
|
|
1046
1053
|
),
|
|
1047
1054
|
)
|
|
1048
1055
|
|
|
1056
|
+
async def apublish_context_status(
|
|
1057
|
+
self, messages: List[Message]
|
|
1058
|
+
) -> "Optional[Any]":
|
|
1059
|
+
"""Force-emit a context_status snapshot and return the payload; for mid-loop hooks (tool-call completions, sub-execution boundaries) where the caller wants to refresh the indicator without changing the compacting flag."""
|
|
1060
|
+
if not messages:
|
|
1061
|
+
return None
|
|
1062
|
+
return await self._publish_context_status(messages, force=True)
|
|
1063
|
+
|
|
1064
|
+
async def apublish_final_context_status(
|
|
1065
|
+
self, messages: List[Message]
|
|
1066
|
+
) -> "Optional[Any]":
|
|
1067
|
+
"""Force-emit a final context_status with compacting cleared; called at agno run end (RunCompleted/Cancelled/Error) so the indicator settles on post-final-message state."""
|
|
1068
|
+
if not messages:
|
|
1069
|
+
return None
|
|
1070
|
+
self._compacting = False
|
|
1071
|
+
return await self._publish_context_status(messages, force=True)
|
|
1072
|
+
|
|
1073
|
+
async def _publish_context_status(
|
|
1074
|
+
self,
|
|
1075
|
+
messages: List[Message],
|
|
1076
|
+
*,
|
|
1077
|
+
force: bool = False,
|
|
1078
|
+
) -> "Optional[Any]":
|
|
1079
|
+
"""Emit a context_status snapshot for the chat UI indicator; returns the payload (or None if unconfigured / estimate failed)."""
|
|
1080
|
+
if not self.agent or not self.task:
|
|
1081
|
+
return None
|
|
1082
|
+
|
|
1083
|
+
from xpander_sdk.models.context_status import ContextStatus
|
|
1084
|
+
from xpander_sdk.models.events import TaskUpdateEventType
|
|
1085
|
+
|
|
1086
|
+
try:
|
|
1087
|
+
estimated = self._estimate_tokens(messages)
|
|
1088
|
+
except Exception as exc:
|
|
1089
|
+
logger.debug(f"[context-optimizer] context_status estimate failed: {exc}")
|
|
1090
|
+
return None
|
|
1091
|
+
|
|
1092
|
+
window = max(1, int(self.context_window))
|
|
1093
|
+
pct = max(0.0, min(100.0, (estimated / window) * 100.0))
|
|
1094
|
+
self._last_emitted_status_pct = int(pct)
|
|
1095
|
+
|
|
1096
|
+
payload = ContextStatus(
|
|
1097
|
+
estimated_tokens=estimated,
|
|
1098
|
+
context_window=window,
|
|
1099
|
+
percent=round(pct, 1),
|
|
1100
|
+
auto_compact_threshold=self._auto_compact_threshold,
|
|
1101
|
+
emergency_threshold=self._emergency_compact_threshold,
|
|
1102
|
+
compacting=self._compacting,
|
|
1103
|
+
)
|
|
1104
|
+
|
|
1105
|
+
coro = self._push_activity_event(
|
|
1106
|
+
event_type=TaskUpdateEventType.ContextStatus,
|
|
1107
|
+
data=payload,
|
|
1108
|
+
)
|
|
1109
|
+
try:
|
|
1110
|
+
task = asyncio.create_task(coro)
|
|
1111
|
+
except RuntimeError as exc:
|
|
1112
|
+
logger.debug(f"[context-optimizer] context_status schedule failed: {exc}")
|
|
1113
|
+
coro.close()
|
|
1114
|
+
return payload
|
|
1115
|
+
|
|
1116
|
+
def _log_status_task_exception(t: "asyncio.Task[Any]") -> None:
|
|
1117
|
+
if t.cancelled():
|
|
1118
|
+
return
|
|
1119
|
+
exc = t.exception()
|
|
1120
|
+
if exc is not None:
|
|
1121
|
+
logger.debug(
|
|
1122
|
+
f"[context-optimizer] context_status publish failed: {exc}"
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
task.add_done_callback(_log_status_task_exception)
|
|
1126
|
+
return payload
|
|
1127
|
+
|
|
1049
1128
|
# Progress-emission rate-limiter state. Reset at the start of every
|
|
1050
1129
|
# ``layer_2_auto_compact`` call by ``_reset_progress_state``.
|
|
1051
1130
|
_progress_last_percent: float = field(default=-1.0, init=False, repr=False)
|
|
@@ -1379,6 +1458,9 @@ class XPanderContextOptimizer(MapReduceMixin, CompressionManager):
|
|
|
1379
1458
|
f"threshold={self._auto_compact_threshold:,})"
|
|
1380
1459
|
)
|
|
1381
1460
|
|
|
1461
|
+
self._compacting = True
|
|
1462
|
+
await self._publish_context_status(messages, force=True)
|
|
1463
|
+
|
|
1382
1464
|
# Publish start event (fire-and-forget)
|
|
1383
1465
|
await self._publish_compaction_start(
|
|
1384
1466
|
trigger=trigger,
|
|
@@ -1574,6 +1656,9 @@ class XPanderContextOptimizer(MapReduceMixin, CompressionManager):
|
|
|
1574
1656
|
reduce_phase_seconds=telemetry.get("reduce_phase_seconds"),
|
|
1575
1657
|
)
|
|
1576
1658
|
|
|
1659
|
+
self._compacting = False
|
|
1660
|
+
await self._publish_context_status(messages, force=True)
|
|
1661
|
+
|
|
1577
1662
|
ratio = (
|
|
1578
1663
|
f"{(1 - post_tokens / pre_tokens) * 100:.1f}%"
|
|
1579
1664
|
if pre_tokens > 0
|
|
@@ -1665,9 +1750,9 @@ class XPanderContextOptimizer(MapReduceMixin, CompressionManager):
|
|
|
1665
1750
|
f"(trigger={trigger}, consecutive_failures={self._auto_compact_consecutive_failures}/"
|
|
1666
1751
|
f"{MAX_CONSECUTIVE_COMPACT_FAILURES}): {exc}"
|
|
1667
1752
|
)
|
|
1668
|
-
# Publish error event (fire-and-forget)
|
|
1669
1753
|
await self._publish_compaction_error(trigger=trigger, error=str(exc))
|
|
1670
|
-
|
|
1754
|
+
self._compacting = False
|
|
1755
|
+
await self._publish_context_status(messages, force=True)
|
|
1671
1756
|
|
|
1672
1757
|
# ================================================================== #
|
|
1673
1758
|
# Pre-retry helpers
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from xpander_sdk.models.shared import XPanderSharedModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ContextStatus(XPanderSharedModel):
|
|
7
|
+
"""Per-turn snapshot of the LLM context window for the chat UI indicator."""
|
|
8
|
+
|
|
9
|
+
estimated_tokens: int = Field(
|
|
10
|
+
...,
|
|
11
|
+
description="Rough token count of the current message list (post-Layer-1 microcompaction).",
|
|
12
|
+
)
|
|
13
|
+
context_window: int = Field(
|
|
14
|
+
..., description="Model context window size in tokens."
|
|
15
|
+
)
|
|
16
|
+
percent: float = Field(
|
|
17
|
+
...,
|
|
18
|
+
description="estimated_tokens / context_window * 100, clamped to [0, 100].",
|
|
19
|
+
)
|
|
20
|
+
auto_compact_threshold: int = Field(
|
|
21
|
+
...,
|
|
22
|
+
description="Token level at which Layer 2 auto-compaction fires.",
|
|
23
|
+
)
|
|
24
|
+
emergency_threshold: int = Field(
|
|
25
|
+
...,
|
|
26
|
+
description="Token level at which the emergency safety net (88%) fires.",
|
|
27
|
+
)
|
|
28
|
+
compacting: bool = Field(
|
|
29
|
+
default=False,
|
|
30
|
+
description="True while a Layer 2 compaction is in flight on the current turn.",
|
|
31
|
+
)
|
xpander_sdk/models/events.py
CHANGED
|
@@ -91,6 +91,9 @@ class TaskUpdateEventType(str, Enum):
|
|
|
91
91
|
# task compaction
|
|
92
92
|
TaskCompactization = "task_compactization"
|
|
93
93
|
|
|
94
|
+
# context window status (per-turn snapshot, delta-gated)
|
|
95
|
+
ContextStatus = "context_status"
|
|
96
|
+
|
|
94
97
|
# agent gateway
|
|
95
98
|
AgentGatewayDecision = "agent_gateway_decision"
|
|
96
99
|
|
|
@@ -604,7 +604,7 @@ async def build_agent_args(
|
|
|
604
604
|
{
|
|
605
605
|
"name": xpander_agent.name,
|
|
606
606
|
"model": model,
|
|
607
|
-
"description": xpander_agent.instructions.description,
|
|
607
|
+
"description": xpander_agent.description if xpander_agent.description and xpander_agent.description is not None and len(xpander_agent.description) != 0 else xpander_agent.instructions.description,
|
|
608
608
|
"instructions": (
|
|
609
609
|
task.instructions_override
|
|
610
610
|
if task.instructions_override
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
xpander_sdk/__init__.py,sha256=
|
|
1
|
+
xpander_sdk/__init__.py,sha256=jk-W2v05q-llpoU37bZkiv-XHMf7OLVUTNj3zOPOMx4,3262
|
|
2
2
|
xpander_sdk/consts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
xpander_sdk/consts/api_routes.py,sha256=n6kC105wVvycmSWfPYTAmUreHSJhf8v2rY7QvB-yhc4,2997
|
|
4
4
|
xpander_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -10,7 +10,7 @@ xpander_sdk/core/context_optimizer/action_ledger.py,sha256=tEMZUHWFmqXlFv1DqwnHY
|
|
|
10
10
|
xpander_sdk/core/context_optimizer/compact_retry_result.py,sha256=2vVvZ0NTiQS2HHobzlAqOicGbk6CYKnoSX23zrqkk_Q,553
|
|
11
11
|
xpander_sdk/core/context_optimizer/completion_evidence.py,sha256=V4J0CZP7HeVlseBqaHryt5IO2UGpywPKGJGIe8elmJk,7844
|
|
12
12
|
xpander_sdk/core/context_optimizer/constants.py,sha256=szWwUHbq2ol27xDFNOEza5qb1fVASkYLvrAJlAmB7p4,8579
|
|
13
|
-
xpander_sdk/core/context_optimizer/context_optimizer.py,sha256=
|
|
13
|
+
xpander_sdk/core/context_optimizer/context_optimizer.py,sha256=enncSRm0DlEMf6iU9qsuthiLxjU7BTfCgNEcklzaR3U,97493
|
|
14
14
|
xpander_sdk/core/context_optimizer/encryption.py,sha256=CHIHGi_jjhFykZg5U84-tCqPDpmv4oQ1CHtfa_Gwa88,1914
|
|
15
15
|
xpander_sdk/core/context_optimizer/error_patterns.py,sha256=BCp17HbZKgc-RvaUfLWX0npAeewgBX2RqSAmwpKcSlc,2679
|
|
16
16
|
xpander_sdk/core/context_optimizer/finalize_mode.py,sha256=IztEicji7GJ6xRZjktisSnYOd8uOXz1TXajV86C1fuw,11595
|
|
@@ -31,8 +31,9 @@ xpander_sdk/models/action_ledger.py,sha256=qPnPj6_LzUN6Q5vVhWrjbGXDDOhQndKvwCH0p
|
|
|
31
31
|
xpander_sdk/models/activity.py,sha256=pRqvJFHLLPxSA2x7M-S3h2evE0rt5Y2ueB7Q-zWrQZg,4646
|
|
32
32
|
xpander_sdk/models/compactization.py,sha256=JmlAv_0uvPUqRKVuN63icKLVofZWjCp3hQvqMyofFtc,8002
|
|
33
33
|
xpander_sdk/models/configuration.py,sha256=5vOgkioyj4IGFf7HRdalnkP65r6ey3r8rBUt3iFhU2U,3266
|
|
34
|
+
xpander_sdk/models/context_status.py,sha256=qwgAwy0PefVTsZmdI2HzY1rxH-twenWYp72aO3bk1hw,1022
|
|
34
35
|
xpander_sdk/models/deep_planning.py,sha256=opB30Akv9LZ2QLyPbkCG37569eL5_QyZpQlGiGduHkQ,558
|
|
35
|
-
xpander_sdk/models/events.py,sha256=
|
|
36
|
+
xpander_sdk/models/events.py,sha256=8f-0rG_JJFRwkQYecedTH6udrpl61v2nvl9OtPJw7uU,3046
|
|
36
37
|
xpander_sdk/models/frameworks.py,sha256=pmo05YINaYiQoDiUYao48pttEGrY8HUmwAl46O05PTM,2864
|
|
37
38
|
xpander_sdk/models/generic.py,sha256=E4Ud94vitMCc_xfULOJ4y8b7pFHerFS24HANzzitwjo,772
|
|
38
39
|
xpander_sdk/models/notifications.py,sha256=JX8wlUhWgZb-aUfsiVXINNZAK9rCn5DAwesLkYr5YAE,4135
|
|
@@ -56,7 +57,7 @@ xpander_sdk/modules/backend/events_registry.py,sha256=arlbPOwTj7DEONkLQbp2MMb8qE
|
|
|
56
57
|
xpander_sdk/modules/backend/decorators/__init__.py,sha256=ub9c8G0Ll6AuCvfcFB6rqR8iamMJxtcW7QjWw3WSkPU,106
|
|
57
58
|
xpander_sdk/modules/backend/decorators/on_auth_event.py,sha256=FwZIYD8TVqRFxex12zH5W4GOzXZjQGf5kp4XFbA7KC8,4673
|
|
58
59
|
xpander_sdk/modules/backend/frameworks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
xpander_sdk/modules/backend/frameworks/agno.py,sha256=
|
|
60
|
+
xpander_sdk/modules/backend/frameworks/agno.py,sha256=VE7AEIblMpChVN5vCUtuOnJ5iwOL3Yngs7eSkv_lLik,126385
|
|
60
61
|
xpander_sdk/modules/backend/frameworks/dispatch.py,sha256=eC35T4krqzk_rRNIGFc68TBs7URcRdKdnwUZ1nNYe4o,1957
|
|
61
62
|
xpander_sdk/modules/backend/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
63
|
xpander_sdk/modules/backend/utils/extra_headers.py,sha256=eYXIsHAMhBYw7RMrA_wzdxL5qAosEe-wUUkAuor0i-0,1634
|
|
@@ -113,8 +114,8 @@ xpander_sdk/utils/event_loop.py,sha256=CDiybs1SCguuynlwlYOA38-68CCqy6T3xIfjPuhzO
|
|
|
113
114
|
xpander_sdk/utils/generic.py,sha256=OSlcWDUjRtyYYBWeg6S4uxkcINKP0_z4pt9D8efcZW8,128
|
|
114
115
|
xpander_sdk/utils/tools.py,sha256=XZN5239Kid6Q1LeT7rYzFjrdDsNy6K_uhi5vYfNUUyE,1299
|
|
115
116
|
xpander_sdk/utils/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
|
-
xpander_sdk-2.0.
|
|
117
|
-
xpander_sdk-2.0.
|
|
118
|
-
xpander_sdk-2.0.
|
|
119
|
-
xpander_sdk-2.0.
|
|
120
|
-
xpander_sdk-2.0.
|
|
117
|
+
xpander_sdk-2.0.334.dist-info/licenses/LICENSE,sha256=XfXDOGoyJbLakLShycg1ZvSvTn1yisMoGVYBkeBYhw0,1262
|
|
118
|
+
xpander_sdk-2.0.334.dist-info/METADATA,sha256=ZCF5fqUYncShVZ7sc6KDcmHHckiBsOEh_h9EeQPCuqA,22146
|
|
119
|
+
xpander_sdk-2.0.334.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
120
|
+
xpander_sdk-2.0.334.dist-info/top_level.txt,sha256=UCjnxQpsMy5Zoe7lmRuVDO6DI2V_6PgRFfm4oizRbVs,12
|
|
121
|
+
xpander_sdk-2.0.334.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|