harness-sdk-python 0.31.2__py3-none-any.whl → 0.32.0__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/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, tools?}`` seeds the provider from the thread
11
- transcript (at most 128 messages, tail taken), hands the client's frontend
12
- function tool definitions to the provider, and projects ``state["voice"] =
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 _validated_tools(tools: Any) -> "list[VoiceManager.ToolDef]":
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, list):
81
- raise _reject("invalid-message", "tools must be a list")
82
- names: set[str] = set()
83
- for tool in tools:
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
- not isinstance(tool, dict)
86
- or not isinstance(tool.get("name"), str)
87
- or tool["name"] == ""
88
- or not isinstance(tool.get("description"), str)
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 {{name, description, parameters}}, got {tool!r}",
94
+ f"context tools entries must be name -> {{description?, parameters}}, got {name!r}: {spec!r}",
94
95
  )
95
- if tool["name"] in names:
96
- raise _reject("invalid-message", f"duplicate tool name {tool['name']!r}")
97
- names.add(tool["name"])
98
- return tools
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 client declares at ``voice/start``."""
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 = _validated_tools(params.get("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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: harness-sdk-python
3
- Version: 0.31.2
3
+ Version: 0.32.0
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
@@ -4,7 +4,7 @@ harness_sdk/deepagents.py,sha256=cMipZ9ndZ3zUR3-VqOMf5CiPVMmym74ytIeguQAjYZo,122
4
4
  harness_sdk/fenced_postgres.py,sha256=hbbrMR1WaqrHTNgd4wFrXfRrzQFGnyim-c8nZXgwWp4,13235
5
5
  harness_sdk/linear_thread.py,sha256=laDRXKLFsMlyWS4snqNAM6t38wO26gevLWXRNivhHSM,1810
6
6
  harness_sdk/run_manager.py,sha256=CbY_4dpFeqGx3kJNI1ffsu3uIvzVc-Tc1UB6Nr9SiHY,77654
7
- harness_sdk/voice.py,sha256=BgMYJRVOVYTJgXOWSWgzJScGAVFD7WyUXMa2_LJghwI,28381
8
- harness_sdk_python-0.31.2.dist-info/METADATA,sha256=RRfWhlnnjOMYHLyLsTEVKTVCrw7b0hhuAYzcxQ2hJFc,2200
9
- harness_sdk_python-0.31.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
10
- harness_sdk_python-0.31.2.dist-info/RECORD,,
7
+ harness_sdk/voice.py,sha256=KtUHmwfZQCwEvJaIolc-XgjAhxvltfMYEI5ycWcw53M,28590
8
+ harness_sdk_python-0.32.0.dist-info/METADATA,sha256=aiPtfiHYq8Lij70AkH4QVyGWxYhhJJKt6RXaaSk518Y,2200
9
+ harness_sdk_python-0.32.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
10
+ harness_sdk_python-0.32.0.dist-info/RECORD,,