harness-sdk-python 0.31.2__py3-none-any.whl → 0.32.1__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/run_manager.py +15 -3
- harness_sdk/voice.py +25 -20
- {harness_sdk_python-0.31.2.dist-info → harness_sdk_python-0.32.1.dist-info}/METADATA +1 -1
- {harness_sdk_python-0.31.2.dist-info → harness_sdk_python-0.32.1.dist-info}/RECORD +5 -5
- {harness_sdk_python-0.31.2.dist-info → harness_sdk_python-0.32.1.dist-info}/WHEEL +0 -0
harness_sdk/run_manager.py
CHANGED
|
@@ -342,7 +342,6 @@ class RunManager(Tray):
|
|
|
342
342
|
"dispatching": dispatching,
|
|
343
343
|
"stopping": stopping,
|
|
344
344
|
"nextDispatch": nextDispatch,
|
|
345
|
-
"inputRequests": inputRequests,
|
|
346
345
|
"error": error,
|
|
347
346
|
"stopReason": stopReason,
|
|
348
347
|
}
|
|
@@ -370,9 +369,14 @@ class RunManager(Tray):
|
|
|
370
369
|
self.state["runId"] = self.id
|
|
371
370
|
self._hydrate_machine()
|
|
372
371
|
self._mirror_steer()
|
|
373
|
-
|
|
372
|
+
self._mirror_input()
|
|
373
|
+
unsubscribes = [
|
|
374
|
+
self.steerQueue.subscribe(lambda _op: self._mirror_steer()),
|
|
375
|
+
self.inputRequests.subscribe(lambda _op: self._mirror_input()),
|
|
376
|
+
]
|
|
374
377
|
yield
|
|
375
|
-
unsubscribe
|
|
378
|
+
for unsubscribe in unsubscribes:
|
|
379
|
+
unsubscribe()
|
|
376
380
|
|
|
377
381
|
class Thread(Protocol):
|
|
378
382
|
"""The thread projection RunManager reads; the host owns the tree."""
|
|
@@ -500,6 +504,14 @@ class RunManager(Tray):
|
|
|
500
504
|
def _mirror_steer(self) -> None:
|
|
501
505
|
self.steerView.value = [_public(item) for item in self.steerQueue.value]
|
|
502
506
|
|
|
507
|
+
def _mirror_input(self) -> None:
|
|
508
|
+
# None publishes as an absent key, matching the TS harness shape
|
|
509
|
+
requests = self.inputRequests.value
|
|
510
|
+
if requests is None:
|
|
511
|
+
self.state.pop("inputRequests", None)
|
|
512
|
+
else:
|
|
513
|
+
self.state["inputRequests"] = requests
|
|
514
|
+
|
|
503
515
|
def _sync_steering(self) -> None:
|
|
504
516
|
ctx = self._run_live
|
|
505
517
|
if ctx is None:
|
harness_sdk/voice.py
CHANGED
|
@@ -7,9 +7,10 @@ lives in its own ephemeral tray, minted by ``voice/start`` and deleted when
|
|
|
7
7
|
the session ends. The host implements ``VoiceManager.Host``: the seed read
|
|
8
8
|
and the turn commit both stay host-owned.
|
|
9
9
|
|
|
10
|
-
``voice/start {offer
|
|
11
|
-
transcript (at most 128 messages, tail taken), hands the
|
|
12
|
-
function tool definitions
|
|
10
|
+
``voice/start {offer}`` seeds the provider from the thread
|
|
11
|
+
transcript (at most 128 messages, tail taken), hands the caller's frontend
|
|
12
|
+
function tool definitions (the ``tools`` map the client's synced model
|
|
13
|
+
context stores at ``ctx.caller.context``) to the provider, and projects ``state["voice"] =
|
|
13
14
|
{clientId, status, toolCalls, activeTask}`` — the SDP answer rides only the command's
|
|
14
15
|
result payload ``{answer}``. A named call matching a client-declared tool
|
|
15
16
|
projects into ``toolCalls`` as ``{callId, name, arguments}``; the owning
|
|
@@ -74,28 +75,32 @@ def _reject(reason: str, message: str) -> StatewireReject:
|
|
|
74
75
|
return StatewireReject(message, payload={"reason": reason})
|
|
75
76
|
|
|
76
77
|
|
|
77
|
-
def
|
|
78
|
+
def _context_tools(context: Any) -> "list[VoiceManager.ToolDef]":
|
|
79
|
+
tools = context.get("tools") if isinstance(context, dict) else None
|
|
78
80
|
if tools is None:
|
|
79
81
|
return []
|
|
80
|
-
if not isinstance(tools,
|
|
81
|
-
raise _reject("invalid-message", "tools must be a
|
|
82
|
-
|
|
83
|
-
for
|
|
82
|
+
if not isinstance(tools, dict):
|
|
83
|
+
raise _reject("invalid-message", "context tools must be a name-keyed object")
|
|
84
|
+
defs: "list[VoiceManager.ToolDef]" = []
|
|
85
|
+
for name, spec in tools.items():
|
|
84
86
|
if (
|
|
85
|
-
|
|
86
|
-
or not isinstance(
|
|
87
|
-
or
|
|
88
|
-
or not isinstance(
|
|
89
|
-
or not isinstance(tool.get("parameters"), dict)
|
|
87
|
+
name == ""
|
|
88
|
+
or not isinstance(spec, dict)
|
|
89
|
+
or not isinstance(spec.get("description", ""), str)
|
|
90
|
+
or not isinstance(spec.get("parameters"), dict)
|
|
90
91
|
):
|
|
91
92
|
raise _reject(
|
|
92
93
|
"invalid-message",
|
|
93
|
-
f"tools entries must be {{
|
|
94
|
+
f"context tools entries must be name -> {{description?, parameters}}, got {name!r}: {spec!r}",
|
|
94
95
|
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
defs.append(
|
|
97
|
+
{
|
|
98
|
+
"name": name,
|
|
99
|
+
"description": spec.get("description", ""),
|
|
100
|
+
"parameters": spec["parameters"],
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
return defs
|
|
99
104
|
|
|
100
105
|
|
|
101
106
|
def _capped_seed(seed: Any) -> "list[VoiceManager.SeedMessage]":
|
|
@@ -156,7 +161,7 @@ class VoiceManager(Tray):
|
|
|
156
161
|
text: str
|
|
157
162
|
|
|
158
163
|
class ToolDef(TypedDict):
|
|
159
|
-
"""One frontend function tool the
|
|
164
|
+
"""One frontend function tool from the caller's synced model context."""
|
|
160
165
|
|
|
161
166
|
name: str
|
|
162
167
|
description: str
|
|
@@ -593,7 +598,7 @@ class VoiceManagerNamespace(TrayMapNamespace):
|
|
|
593
598
|
offer = params.get("offer")
|
|
594
599
|
if not isinstance(offer, str) or offer == "":
|
|
595
600
|
raise _reject("invalid-message", "offer must be a non-empty SDP string")
|
|
596
|
-
tools =
|
|
601
|
+
tools = _context_tools(ctx.caller.context)
|
|
597
602
|
if self._starting:
|
|
598
603
|
raise _reject("voice-active", "a voice session is already live")
|
|
599
604
|
session = self.live()
|
|
@@ -3,8 +3,8 @@ harness_sdk/checkpoints.py,sha256=L02l1IyLDj2b-GD7_HGrck7uenALeI4kg8yJSzoNVmc,25
|
|
|
3
3
|
harness_sdk/deepagents.py,sha256=cMipZ9ndZ3zUR3-VqOMf5CiPVMmym74ytIeguQAjYZo,12280
|
|
4
4
|
harness_sdk/fenced_postgres.py,sha256=hbbrMR1WaqrHTNgd4wFrXfRrzQFGnyim-c8nZXgwWp4,13235
|
|
5
5
|
harness_sdk/linear_thread.py,sha256=laDRXKLFsMlyWS4snqNAM6t38wO26gevLWXRNivhHSM,1810
|
|
6
|
-
harness_sdk/run_manager.py,sha256=
|
|
7
|
-
harness_sdk/voice.py,sha256=
|
|
8
|
-
harness_sdk_python-0.
|
|
9
|
-
harness_sdk_python-0.
|
|
10
|
-
harness_sdk_python-0.
|
|
6
|
+
harness_sdk/run_manager.py,sha256=ECj7j_xkGn81IngEEGA3MWXUejk9ZqNcH6vzxG6o9yU,78085
|
|
7
|
+
harness_sdk/voice.py,sha256=KtUHmwfZQCwEvJaIolc-XgjAhxvltfMYEI5ycWcw53M,28590
|
|
8
|
+
harness_sdk_python-0.32.1.dist-info/METADATA,sha256=dEQKSIIg_0xmdy6B7wiHhjG8g2VdDIaeR8_mQXps5KA,2200
|
|
9
|
+
harness_sdk_python-0.32.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
10
|
+
harness_sdk_python-0.32.1.dist-info/RECORD,,
|
|
File without changes
|