harness-sdk-python 0.36.2__py3-none-any.whl → 0.36.4__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.
- harness_sdk/deepagents.py +31 -3
- harness_sdk/langgraph.py +78 -0
- harness_sdk/run_manager.py +160 -13
- {harness_sdk_python-0.36.2.dist-info → harness_sdk_python-0.36.4.dist-info}/METADATA +3 -3
- {harness_sdk_python-0.36.2.dist-info → harness_sdk_python-0.36.4.dist-info}/RECORD +6 -5
- {harness_sdk_python-0.36.2.dist-info → harness_sdk_python-0.36.4.dist-info}/WHEEL +0 -0
harness_sdk/deepagents.py
CHANGED
|
@@ -146,6 +146,8 @@ class DeepagentsMessages:
|
|
|
146
146
|
)
|
|
147
147
|
self._checkpointer = checkpointer
|
|
148
148
|
self._captured: dict[str, Any] | None = None
|
|
149
|
+
self._admission: Any | None = None
|
|
150
|
+
self._input_checkpoint_seen = False
|
|
149
151
|
self._ephemeral: dict[str, dict[str, Any]] = {}
|
|
150
152
|
self._write_lock = asyncio.Lock()
|
|
151
153
|
|
|
@@ -190,8 +192,7 @@ class DeepagentsMessages:
|
|
|
190
192
|
self, ctx: Any
|
|
191
193
|
) -> tuple[dict[str, Any] | None, dict[str, Any]]:
|
|
192
194
|
"""The dispatch's langgraph (payload, config): plans the resumption, restores a rewind's window, appends the batch to the wire state, and confirms it applied."""
|
|
193
|
-
self.
|
|
194
|
-
self._thread.abandon_plan()
|
|
195
|
+
self.abandon_turn()
|
|
195
196
|
config = self.config()
|
|
196
197
|
if ctx.resume:
|
|
197
198
|
return None, config
|
|
@@ -206,7 +207,7 @@ class DeepagentsMessages:
|
|
|
206
207
|
turn: list[Any] = [*plan.replay]
|
|
207
208
|
for message in ctx.messages:
|
|
208
209
|
turn.append(self._append_user(message))
|
|
209
|
-
ctx.applied()
|
|
210
|
+
self.admit(ctx.applied())
|
|
210
211
|
if plan.fork:
|
|
211
212
|
if plan.checkpoint_id is not None:
|
|
212
213
|
config["configurable"]["checkpoint_id"] = plan.checkpoint_id
|
|
@@ -217,11 +218,34 @@ class DeepagentsMessages:
|
|
|
217
218
|
turn = [RemoveMessage(id=REMOVE_ALL_MESSAGES), *turn]
|
|
218
219
|
return ({"messages": turn} if turn else None), config
|
|
219
220
|
|
|
221
|
+
def admit(self, admission: Any) -> None:
|
|
222
|
+
"""Retain a visibly applied batch until its root checkpoint is saved."""
|
|
223
|
+
if self._admission is not None:
|
|
224
|
+
raise RuntimeError("previous input admission is still pending")
|
|
225
|
+
self._admission = admission
|
|
226
|
+
|
|
227
|
+
def checkpoint_saved(self, config: Any, checkpoint: Any, metadata: Any) -> None:
|
|
228
|
+
"""Confirm this turn's input from a FrontierCheckpointer checkpoint callback."""
|
|
229
|
+
configurable = config["configurable"]
|
|
230
|
+
if (
|
|
231
|
+
configurable["thread_id"] == self._thread_id
|
|
232
|
+
and configurable.get("checkpoint_ns", "") == ""
|
|
233
|
+
and metadata["source"] in ("input", "loop")
|
|
234
|
+
):
|
|
235
|
+
self._confirm_admission()
|
|
236
|
+
|
|
237
|
+
def _confirm_admission(self) -> None:
|
|
238
|
+
if self._admission is not None:
|
|
239
|
+
self._admission.dur()
|
|
240
|
+
self._admission = None
|
|
241
|
+
|
|
220
242
|
def append_event(self, namespace: tuple, mode: str, event: Any) -> None:
|
|
221
243
|
"""Fold one ``astream`` event into the wire state; the root checkpoint stream is captured for the turn's ingest."""
|
|
222
244
|
if mode == "checkpoints":
|
|
223
245
|
if namespace == ():
|
|
224
246
|
self._captured = event
|
|
247
|
+
if event["metadata"]["source"] in ("input", "loop"):
|
|
248
|
+
self._input_checkpoint_seen = True
|
|
225
249
|
return
|
|
226
250
|
from statewire.langgraph import append_langgraph_event
|
|
227
251
|
|
|
@@ -231,11 +255,15 @@ class DeepagentsMessages:
|
|
|
231
255
|
"""Ingest the turn's captured checkpoint and restamp; ``refetch`` discards the capture and reads the checkpointer's latest instead."""
|
|
232
256
|
captured, self._captured = self._captured, None
|
|
233
257
|
await deepagents_ingest(self._thread, captured, refetch=refetch)
|
|
258
|
+
if self._input_checkpoint_seen:
|
|
259
|
+
self._confirm_admission()
|
|
234
260
|
self._restamp()
|
|
235
261
|
|
|
236
262
|
def abandon_turn(self) -> None:
|
|
237
263
|
"""Discard a stream capture and any fork planned for the abandoned turn."""
|
|
238
264
|
self._captured = None
|
|
265
|
+
self._admission = None
|
|
266
|
+
self._input_checkpoint_seen = False
|
|
239
267
|
self._thread.abandon_plan()
|
|
240
268
|
|
|
241
269
|
def append_users(self, messages: Any) -> list[dict[str, Any]]:
|
harness_sdk/langgraph.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from typing import Any, AsyncIterator, Awaitable, Callable
|
|
2
|
+
|
|
3
|
+
from langgraph.checkpoint.base import BaseCheckpointSaver
|
|
4
|
+
from pinned import Frontier
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FrontierCheckpointer(BaseCheckpointSaver):
|
|
8
|
+
"""Order a LangGraph saver's async writes with the owning instance's durable effects."""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
saver: BaseCheckpointSaver,
|
|
13
|
+
*,
|
|
14
|
+
frontier: Frontier,
|
|
15
|
+
on_checkpoint: Callable[[Any, Any, Any], None] | None = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
if isinstance(saver, FrontierCheckpointer):
|
|
18
|
+
raise ValueError("a checkpointer can participate in only one frontier")
|
|
19
|
+
super().__init__(serde=saver.serde)
|
|
20
|
+
self.saver = saver
|
|
21
|
+
self.frontier = frontier
|
|
22
|
+
self.on_checkpoint = on_checkpoint
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def config_specs(self):
|
|
26
|
+
return self.saver.config_specs
|
|
27
|
+
|
|
28
|
+
def get_next_version(self, current, channel):
|
|
29
|
+
return self.saver.get_next_version(current, channel)
|
|
30
|
+
|
|
31
|
+
async def _persist(self, effect: Callable[[], Awaitable[Any]]) -> Any:
|
|
32
|
+
result = None
|
|
33
|
+
|
|
34
|
+
async def write() -> None:
|
|
35
|
+
nonlocal result
|
|
36
|
+
result = await effect()
|
|
37
|
+
|
|
38
|
+
pending = write()
|
|
39
|
+
try:
|
|
40
|
+
position = self.frontier.enlist(pending)
|
|
41
|
+
except BaseException:
|
|
42
|
+
pending.close()
|
|
43
|
+
raise
|
|
44
|
+
await self.frontier.wait(position)
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
async def aget_tuple(self, config):
|
|
48
|
+
await self.frontier.receipt()
|
|
49
|
+
return await self.saver.aget_tuple(config)
|
|
50
|
+
|
|
51
|
+
async def aget_delta_channel_history(self, *, config, channels):
|
|
52
|
+
await self.frontier.receipt()
|
|
53
|
+
return await self.saver.aget_delta_channel_history(config=config, channels=channels)
|
|
54
|
+
|
|
55
|
+
async def alist(
|
|
56
|
+
self, config, *, filter=None, before=None, limit=None
|
|
57
|
+
) -> AsyncIterator[Any]:
|
|
58
|
+
await self.frontier.receipt()
|
|
59
|
+
async for checkpoint in self.saver.alist(
|
|
60
|
+
config, filter=filter, before=before, limit=limit
|
|
61
|
+
):
|
|
62
|
+
yield checkpoint
|
|
63
|
+
|
|
64
|
+
async def aput(self, config, checkpoint, metadata, new_versions):
|
|
65
|
+
result = await self._persist(
|
|
66
|
+
lambda: self.saver.aput(config, checkpoint, metadata, new_versions)
|
|
67
|
+
)
|
|
68
|
+
if self.on_checkpoint is not None:
|
|
69
|
+
self.on_checkpoint(config, checkpoint, metadata)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
async def aput_writes(self, config, writes, task_id, task_path="") -> None:
|
|
73
|
+
await self._persist(
|
|
74
|
+
lambda: self.saver.aput_writes(config, writes, task_id, task_path)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
async def adelete_thread(self, thread_id) -> None:
|
|
78
|
+
await self._persist(lambda: self.saver.adelete_thread(thread_id))
|
harness_sdk/run_manager.py
CHANGED
|
@@ -26,9 +26,10 @@ order, then takes exactly one action based on the derived status and net
|
|
|
26
26
|
effect (rewind > send-dispatch > continue > input-resume). Every initiator applies
|
|
27
27
|
and settles accepted with its state-visible application, and every verb's
|
|
28
28
|
verdict follows the admission's durable flush. ``entry["dispatching"]`` is
|
|
29
|
-
the
|
|
29
|
+
the visible batch record; ``ctx.applied()`` clears it at memory speed and
|
|
30
|
+
returns an admission whose ``dur()`` confirms consumption behind prior frontier effects,
|
|
30
31
|
``steering.take()`` may refill it via deferred durable pops — at most one
|
|
31
|
-
|
|
32
|
+
visibly unapplied batch exists. Admission handles retain unconfirmed batches. Settle consumes a confirmed batch; a batch
|
|
32
33
|
still unconfirmed at settle returns to the front of its lanes (after a crash,
|
|
33
34
|
unconfirmed taken entries re-present there on their own). A batch already
|
|
34
35
|
durably confirmed redelivers in resume mode instead (``ctx.resume``): the
|
|
@@ -68,6 +69,7 @@ from dataclasses import dataclass, field
|
|
|
68
69
|
from typing import Any, AsyncIterator, Callable, Coroutine, Iterable, Protocol
|
|
69
70
|
|
|
70
71
|
from pinned import EvictionRequested
|
|
72
|
+
from pinned.durability import FenceLost
|
|
71
73
|
from pinned.trays import Tray, TrayDescriptor, TrayCtx, TrayMapNamespace, inbox
|
|
72
74
|
from pinned.trays import persistent, persistent_outbox, persistent_queue
|
|
73
75
|
from statepatch import observable
|
|
@@ -396,6 +398,7 @@ class RunManager(Tray):
|
|
|
396
398
|
self._staged_rewind: _Rewind | None = None
|
|
397
399
|
self._batch: list[tuple[str, dict[str, Any]]] = []
|
|
398
400
|
self._pending_takes = 0
|
|
401
|
+
self._admissions: list[RunManager.Admission] = []
|
|
399
402
|
self._input_requests: list[dict[str, Any]] = []
|
|
400
403
|
self._input_answers: dict[str, Any] = {}
|
|
401
404
|
self._run_live: "RunManager.RunContext | None" = None
|
|
@@ -914,7 +917,9 @@ class RunManager(Tray):
|
|
|
914
917
|
"run must return a RunManager outcome, got "
|
|
915
918
|
f"{type(outcome).__name__}"
|
|
916
919
|
)
|
|
917
|
-
if isinstance(outcome, RunManager.Complete) and
|
|
920
|
+
if isinstance(outcome, RunManager.Complete) and (
|
|
921
|
+
self.dispatching.value is not None or self._admissions
|
|
922
|
+
):
|
|
918
923
|
raise RuntimeError("run returned Complete with an unconfirmed batch")
|
|
919
924
|
except EvictionRequested:
|
|
920
925
|
self._advance()
|
|
@@ -1028,11 +1033,33 @@ class RunManager(Tray):
|
|
|
1028
1033
|
def _settle(self, ctx: "RunManager.RunContext") -> None:
|
|
1029
1034
|
if self._run_live is ctx:
|
|
1030
1035
|
self._run_live = None
|
|
1036
|
+
takes = self._pending_takes + sum(a._takes for a in self._admissions)
|
|
1037
|
+
if takes:
|
|
1038
|
+
self.steerQueue.untake(takes)
|
|
1039
|
+
self._drop_dispatched(
|
|
1040
|
+
message["id"]
|
|
1041
|
+
for admission in self._admissions
|
|
1042
|
+
if admission._takes
|
|
1043
|
+
for _, message in admission._batch
|
|
1044
|
+
)
|
|
1045
|
+
if self._pending_takes:
|
|
1046
|
+
self._drop_dispatched(message["id"] for _, message in self._batch)
|
|
1047
|
+
self._batch = []
|
|
1048
|
+
self._pending_takes = 0
|
|
1049
|
+
self._admissions.clear()
|
|
1050
|
+
recorded = self.outbox.value
|
|
1051
|
+
if recorded is not None and not self.batchApplied.value:
|
|
1052
|
+
item = recorded["item"]
|
|
1053
|
+
self._batch = list(zip(item["lanes"], item["record"]["messages"]))
|
|
1031
1054
|
if self._batch:
|
|
1032
1055
|
self._drop_dispatched(item["id"] for _, item in self._batch)
|
|
1033
1056
|
self._unstage_batch()
|
|
1034
1057
|
else:
|
|
1035
|
-
record =
|
|
1058
|
+
record = (
|
|
1059
|
+
recorded["item"]["record"]
|
|
1060
|
+
if recorded is not None and not self.batchApplied.value
|
|
1061
|
+
else self.dispatching.value
|
|
1062
|
+
)
|
|
1036
1063
|
if (
|
|
1037
1064
|
record is not None
|
|
1038
1065
|
and record["trigger"] != "steer"
|
|
@@ -1068,16 +1095,15 @@ class RunManager(Tray):
|
|
|
1068
1095
|
self.dispatching.value = None
|
|
1069
1096
|
self._finish_settle()
|
|
1070
1097
|
|
|
1071
|
-
def _applied(self) ->
|
|
1098
|
+
def _applied(self, ctx: "RunManager.RunContext") -> "RunManager.Admission":
|
|
1072
1099
|
if self.dispatching.value is None:
|
|
1073
1100
|
raise RuntimeError("applied() with no unapplied batch")
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
else:
|
|
1078
|
-
self.batchApplied.value = True
|
|
1101
|
+
admission = RunManager.Admission(ctx, list(self._batch), self._pending_takes)
|
|
1102
|
+
self._admissions.append(admission)
|
|
1103
|
+
self._pending_takes = 0
|
|
1079
1104
|
self._batch = []
|
|
1080
1105
|
self.dispatching.value = None
|
|
1106
|
+
return admission
|
|
1081
1107
|
|
|
1082
1108
|
def _unstage_batch(self) -> None:
|
|
1083
1109
|
taken, self._batch = self._batch, []
|
|
@@ -1600,6 +1626,29 @@ class RunManager(Tray):
|
|
|
1600
1626
|
for item in items
|
|
1601
1627
|
)
|
|
1602
1628
|
|
|
1629
|
+
@dataclass(eq=False)
|
|
1630
|
+
class Admission:
|
|
1631
|
+
"""One visibly applied batch; ``dur()`` orders its consumption behind enlisted checkpoint writes."""
|
|
1632
|
+
|
|
1633
|
+
_ctx: "RunManager.RunContext"
|
|
1634
|
+
_batch: list[tuple[str, dict[str, Any]]]
|
|
1635
|
+
_takes: int
|
|
1636
|
+
_confirmed: bool = False
|
|
1637
|
+
|
|
1638
|
+
def dur(self) -> None:
|
|
1639
|
+
"""Enlist this batch's durable consumption; idempotent until the dispatch settles."""
|
|
1640
|
+
self._ctx._ensure_active()
|
|
1641
|
+
if self._confirmed:
|
|
1642
|
+
return
|
|
1643
|
+
self._confirmed = True
|
|
1644
|
+
manager = self._ctx._manager
|
|
1645
|
+
while manager._admissions and manager._admissions[0]._confirmed:
|
|
1646
|
+
admission = manager._admissions.pop(0)
|
|
1647
|
+
if admission._takes:
|
|
1648
|
+
manager.confirmedTakes.value += admission._takes
|
|
1649
|
+
else:
|
|
1650
|
+
manager.batchApplied.value = True
|
|
1651
|
+
|
|
1603
1652
|
@dataclass(frozen=True, eq=False)
|
|
1604
1653
|
class RunContext:
|
|
1605
1654
|
trigger: str
|
|
@@ -1691,10 +1740,10 @@ class RunManager(Tray):
|
|
|
1691
1740
|
if self._manager._run_live is not self:
|
|
1692
1741
|
raise RuntimeError("this run has already settled")
|
|
1693
1742
|
|
|
1694
|
-
def applied(self) ->
|
|
1695
|
-
"""
|
|
1743
|
+
def applied(self) -> "RunManager.Admission":
|
|
1744
|
+
"""Clear the visible dispatch immediately; call the returned admission's ``dur()`` after enlisting its checkpoint."""
|
|
1696
1745
|
self._ensure_active()
|
|
1697
|
-
self._manager._applied()
|
|
1746
|
+
return self._manager._applied(self)
|
|
1698
1747
|
|
|
1699
1748
|
|
|
1700
1749
|
class RunManagerNamespace(TrayMapNamespace):
|
|
@@ -1709,6 +1758,8 @@ class RunManagerNamespace(TrayMapNamespace):
|
|
|
1709
1758
|
"(RunManager.Thread) to host @run_manager"
|
|
1710
1759
|
)
|
|
1711
1760
|
self._mint: "asyncio.Task[Any] | None" = None
|
|
1761
|
+
self._loading: "asyncio.Task[None] | None" = None
|
|
1762
|
+
self._loaded = False
|
|
1712
1763
|
self._mounted: list[RunManager] = []
|
|
1713
1764
|
api.state["runs"] = []
|
|
1714
1765
|
api.state["status"] = "ready"
|
|
@@ -1725,7 +1776,103 @@ class RunManagerNamespace(TrayMapNamespace):
|
|
|
1725
1776
|
tray, _minted = await self._machine(run_id, principal)
|
|
1726
1777
|
return tray
|
|
1727
1778
|
|
|
1779
|
+
async def _load_existing(self) -> None:
|
|
1780
|
+
if self._loaded:
|
|
1781
|
+
return
|
|
1782
|
+
if self._loading is None:
|
|
1783
|
+
self._loading = self._api.create_task(self._restore_existing())
|
|
1784
|
+
await asyncio.shield(self._loading)
|
|
1785
|
+
|
|
1786
|
+
async def _restore_existing(self) -> None:
|
|
1787
|
+
durability = self._durability()
|
|
1788
|
+
try:
|
|
1789
|
+
rows = await durability.store.list(
|
|
1790
|
+
durability.ns, durability.id, durability.epoch, tray_ns=self._spec.name
|
|
1791
|
+
)
|
|
1792
|
+
except FenceLost:
|
|
1793
|
+
self._api.abort()
|
|
1794
|
+
raise
|
|
1795
|
+
for _, run_id, marked, doc in rows:
|
|
1796
|
+
await self._obtain(run_id, create=False, row=(marked, doc))
|
|
1797
|
+
self._loaded = True
|
|
1798
|
+
|
|
1799
|
+
async def maybe_sync(
|
|
1800
|
+
self,
|
|
1801
|
+
*,
|
|
1802
|
+
pending_inputs: Iterable[dict[str, Any]],
|
|
1803
|
+
has_pending_tasks: bool,
|
|
1804
|
+
principal: Any,
|
|
1805
|
+
meta: Any = None,
|
|
1806
|
+
) -> bool:
|
|
1807
|
+
"""Import external pending work into an inactive run; return whether its state changed."""
|
|
1808
|
+
if type(has_pending_tasks) is not bool:
|
|
1809
|
+
raise TypeError("has_pending_tasks must be a bool")
|
|
1810
|
+
requests = tuple(pending_inputs)
|
|
1811
|
+
if requests:
|
|
1812
|
+
RunManager.InputRequired(requests)
|
|
1813
|
+
await self._load_existing()
|
|
1814
|
+
if self._mint is not None:
|
|
1815
|
+
await asyncio.shield(self._mint)
|
|
1816
|
+
tray = self.live()
|
|
1817
|
+
if tray is None:
|
|
1818
|
+
if not requests and not has_pending_tasks:
|
|
1819
|
+
return False
|
|
1820
|
+
tray, _ = await self._machine(self._api.domain.token_hex(16), principal)
|
|
1821
|
+
tray._check_run_owner(principal)
|
|
1822
|
+
if (
|
|
1823
|
+
tray._in_flight()
|
|
1824
|
+
or tray._intake
|
|
1825
|
+
or tray._batch
|
|
1826
|
+
or tray.queue.value
|
|
1827
|
+
or tray.steerQueue.value
|
|
1828
|
+
or tray.nextDispatch.value is not None
|
|
1829
|
+
or tray._staged_rewind is not None
|
|
1830
|
+
or tray.outcome.value is not None
|
|
1831
|
+
):
|
|
1832
|
+
return False
|
|
1833
|
+
previous = {r["id"]: r for r in tray.inputRequests.value or ()}
|
|
1834
|
+
synchronized = []
|
|
1835
|
+
for request in requests:
|
|
1836
|
+
item = {k: v for k, v in request.items() if k not in ("response", "meta")}
|
|
1837
|
+
old = previous.get(request["id"], {})
|
|
1838
|
+
if "response" in old:
|
|
1839
|
+
item.update(response=old["response"], meta=old.get("meta"))
|
|
1840
|
+
synchronized.append(item)
|
|
1841
|
+
if requests:
|
|
1842
|
+
if (
|
|
1843
|
+
tray.inputRequests.value == synchronized
|
|
1844
|
+
and tray.halted.value is None
|
|
1845
|
+
and tray.error.value is None
|
|
1846
|
+
):
|
|
1847
|
+
return False
|
|
1848
|
+
tray.inputRequests.value = synchronized
|
|
1849
|
+
tray._input_requests = [
|
|
1850
|
+
{k: v for k, v in r.items() if k not in ("response", "meta")}
|
|
1851
|
+
for r in synchronized
|
|
1852
|
+
]
|
|
1853
|
+
tray._input_answers = {
|
|
1854
|
+
r["id"]: (r["response"], r.get("meta"))
|
|
1855
|
+
for r in synchronized if "response" in r
|
|
1856
|
+
}
|
|
1857
|
+
tray.halted.value = None
|
|
1858
|
+
tray.error.value = None
|
|
1859
|
+
tray._publish_status()
|
|
1860
|
+
tray._schedule(tray._advance)
|
|
1861
|
+
return True
|
|
1862
|
+
changed = bool(tray.inputRequests.value)
|
|
1863
|
+
tray._clear_input()
|
|
1864
|
+
if has_pending_tasks:
|
|
1865
|
+
tray._dispatch("stop-continue", [], root_meta=meta)
|
|
1866
|
+
tray.batchApplied.value = True
|
|
1867
|
+
tray.dispatching.value = None
|
|
1868
|
+
tray._publish_status()
|
|
1869
|
+
return True
|
|
1870
|
+
if changed:
|
|
1871
|
+
tray._publish_status()
|
|
1872
|
+
return changed
|
|
1873
|
+
|
|
1728
1874
|
async def _machine(self, run_id: str, principal: Any) -> tuple[RunManager, bool]:
|
|
1875
|
+
await self._load_existing()
|
|
1729
1876
|
# No await between the live() check and the mint's publication, and no
|
|
1730
1877
|
# lock held across storage I/O: same-tick callers await the shared mint
|
|
1731
1878
|
# task and merge into the machine it mounts, or raise its failure.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: harness-sdk-python
|
|
3
|
-
Version: 0.36.
|
|
3
|
+
Version: 0.36.4
|
|
4
4
|
Summary: RunManager: the harness-sdk runs subsystem for Python Statewire hosts
|
|
5
5
|
Project-URL: Repository, https://github.com/assistant-ui/harness-sdk
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Requires-Python: <4.0,>=3.12
|
|
8
|
-
Requires-Dist: pinned<0.16,>=0.15.
|
|
9
|
-
Requires-Dist: statewire<0.18,>=0.17.
|
|
8
|
+
Requires-Dist: pinned<0.16,>=0.15.2
|
|
9
|
+
Requires-Dist: statewire<0.18,>=0.17.4
|
|
10
10
|
Provides-Extra: deepagents
|
|
11
11
|
Requires-Dist: deepagents>=0.6.12; extra == 'deepagents'
|
|
12
12
|
Requires-Dist: langchain-core>=0.3; extra == 'deepagents'
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
harness_sdk/__init__.py,sha256=zoABjY3ixYzh5YyWtHQKs3MjphMJrwklzV0LOacDA5c,1740
|
|
2
2
|
harness_sdk/checkpoints.py,sha256=4Jv0eyjGoUv9lC6ZZSOpHmZ4j_vMFkFPSxFcrpJ-NTQ,28195
|
|
3
|
-
harness_sdk/deepagents.py,sha256=
|
|
3
|
+
harness_sdk/deepagents.py,sha256=ilrz32oEhvoiKkKGPGbmsTmjZvWomhg1roxPMMxSCXk,15745
|
|
4
4
|
harness_sdk/fenced_postgres.py,sha256=hbbrMR1WaqrHTNgd4wFrXfRrzQFGnyim-c8nZXgwWp4,13235
|
|
5
|
+
harness_sdk/langgraph.py,sha256=0yT3l7wWrxjG4SOMQQAjre-EK2JBndryAmBg-k8NESg,2680
|
|
5
6
|
harness_sdk/linear_thread.py,sha256=laDRXKLFsMlyWS4snqNAM6t38wO26gevLWXRNivhHSM,1810
|
|
6
7
|
harness_sdk/protocol.py,sha256=4diqaWeYIVEKRjyKkqzeMMHJb7zrOQCvPkjQzkeIku8,140
|
|
7
|
-
harness_sdk/run_manager.py,sha256=
|
|
8
|
+
harness_sdk/run_manager.py,sha256=wKhMHscBUtAT4qddHC6_mftGO3u9T3HivlUgwfmJCAk,88062
|
|
8
9
|
harness_sdk/voice.py,sha256=bRloaKSS1d0mFvfIM4Bhp50ADBK_7GpdarGvmDBubjg,32370
|
|
9
|
-
harness_sdk_python-0.36.
|
|
10
|
-
harness_sdk_python-0.36.
|
|
11
|
-
harness_sdk_python-0.36.
|
|
10
|
+
harness_sdk_python-0.36.4.dist-info/METADATA,sha256=s8ACi-pVJMePCNS9BK75tFL7umZJxu0YYo7B7sI3sCA,2488
|
|
11
|
+
harness_sdk_python-0.36.4.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
12
|
+
harness_sdk_python-0.36.4.dist-info/RECORD,,
|
|
File without changes
|