dial-agentframework 0.19.2__tar.gz → 0.21.0__tar.gz

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.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dial-agentframework
3
- Version: 0.19.2
3
+ Version: 0.21.0
4
4
  Summary: Official Dial tools for the Microsoft Agent Framework — phone numbers, SMS, OTP, and voice calls
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: agent-framework-core>=1.0
7
- Requires-Dist: dial-sdk>=0.30.0
7
+ Requires-Dist: dial-sdk>=0.33.0
8
8
  Description-Content-Type: text/markdown
9
9
 
10
10
  # dial-agentframework
@@ -115,7 +115,8 @@ def set_number_properties_tool(client: DialClient) -> FunctionTool:
115
115
  description=(
116
116
  "Update a Dial phone number's properties: its inbound instruction (the system "
117
117
  "prompt its AI voice agent uses on inbound calls), its nickname, and/or whether "
118
- "calling is switched on at all (calling_enabled). "
118
+ "calling is switched on at all (calling_enabled) or forwards inbound calls to "
119
+ "another phone (forward_to). "
119
120
  "On iMessage numbers you can also set the display identity (first/last name "
120
121
  "and avatar photo) shown to recipients. Provide at least one."
121
122
  ),
@@ -173,6 +174,12 @@ def set_number_properties_tool(client: DialClient) -> FunctionTool:
173
174
  "the number is unaffected. Takes effect on the next call — a call already in "
174
175
  "progress is not ended. Omit to leave it unchanged.",
175
176
  ] = None,
177
+ forward_to: Annotated[
178
+ Optional[str],
179
+ "Forward inbound calls to this phone number (E.164) instead of having the AI "
180
+ "voice agent answer; the call ends if that phone is busy or doesn't answer. "
181
+ "Pass an empty string to stop forwarding. Omitted leaves it unchanged.",
182
+ ] = None,
176
183
  ) -> str:
177
184
  kwargs: dict = {}
178
185
  if inbound_instruction is not None:
@@ -199,17 +206,21 @@ def set_number_properties_tool(client: DialClient) -> FunctionTool:
199
206
  # False is a real value here; only None means "leave unchanged".
200
207
  if calling_enabled is not None:
201
208
  kwargs["calling_enabled"] = calling_enabled
209
+ # Empty string stops forwarding (sent as null); None means "leave unchanged".
210
+ if forward_to is not None:
211
+ kwargs["forward_to"] = forward_to or None
202
212
  if not kwargs:
203
213
  return (
204
214
  "Provide at least one of inbound_instruction, inbound_voice_gender, "
205
215
  "inbound_language, nickname, max_call_duration_seconds, calling_enabled, "
206
- "first_name, last_name, or avatar_url."
216
+ "forward_to, first_name, last_name, or avatar_url."
207
217
  )
208
218
  number = await client.set_number_properties(number_id, **kwargs)
209
219
  nick = f' | nickname: "{number.nickname}"' if number.nickname else ""
210
220
  # Reported only when off, so a messaging-only line isn't a silent surprise.
211
221
  calling = "" if number.calling_enabled else " | calling: off (messaging only)"
212
- return f"Updated {number.number} (id: {number.id}){nick}{calling}"
222
+ fwd = f" | forwarding calls to {number.forward_to}" if number.forward_to else ""
223
+ return f"Updated {number.number} (id: {number.id}){nick}{calling}{fwd}"
213
224
 
214
225
  return set_number_properties
215
226
 
@@ -288,7 +299,8 @@ def list_groups_tool(client: DialClient) -> FunctionTool:
288
299
  if not groups:
289
300
  return "No groups found."
290
301
  return "\n".join(
291
- f"{g.id}: {g.name or '(unnamed)'} (since {g.created_at})" for g in groups
302
+ f"{g.id} ({g.channel}): {g.name or '(unnamed)'} (since {g.created_at})"
303
+ for g in groups
292
304
  )
293
305
 
294
306
  return list_groups
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dial-agentframework"
3
- version = "0.19.2"
3
+ version = "0.21.0"
4
4
  description = "Official Dial tools for the Microsoft Agent Framework — phone numbers, SMS, OTP, and voice calls"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -8,7 +8,7 @@ dependencies = [
8
8
  # Core has `tool` + `FunctionTool` (all this package needs). Chat clients ship in
9
9
  # separate integration packages (agent-framework-openai, …) the user installs.
10
10
  "agent-framework-core>=1.0",
11
- "dial-sdk>=0.30.0",
11
+ "dial-sdk>=0.33.0",
12
12
  ]
13
13
 
14
14
  [tool.uv.sources]
@@ -35,6 +35,7 @@ class _FakeNumber:
35
35
  country = "US"
36
36
  nickname = None
37
37
  calling_enabled = True
38
+ forward_to = None
38
39
 
39
40
 
40
41
  class FakeClient:
@@ -162,22 +163,23 @@ def test_send_message_into_a_group():
162
163
 
163
164
  def test_list_groups_renders_ids_names_and_an_unnamed_group():
164
165
  class _FakeGroup:
165
- def __init__(self, id, name, created_at):
166
+ def __init__(self, id, channel, name, created_at):
166
167
  self.id = id
168
+ self.channel = channel
167
169
  self.name = name
168
170
  self.created_at = created_at
169
171
 
170
172
  client = FakeClient(
171
173
  list_groups=[
172
- _FakeGroup("grp_1", "Planning bday party", "2026-09-01T00:00:00Z"),
173
- _FakeGroup("grp_2", None, "2026-09-02T00:00:00Z"),
174
+ _FakeGroup("grp_1", "whatsapp", "Planning bday party", "2026-09-01T00:00:00Z"),
175
+ _FakeGroup("grp_2", "imessage", None, "2026-09-02T00:00:00Z"),
174
176
  ]
175
177
  )
176
178
  out = _run(list_groups_tool(client))
177
179
 
178
- assert "grp_1: Planning bday party" in out
180
+ assert "grp_1 (whatsapp): Planning bday party" in out
179
181
  # A group no line could name is still listed — never hidden.
180
- assert "grp_2: (unnamed)" in out
182
+ assert "grp_2 (imessage): (unnamed)" in out
181
183
 
182
184
 
183
185
  def test_stop_typing_invokes_client():
@@ -297,6 +299,34 @@ def test_set_number_properties_omits_unprovided_name_fields():
297
299
  assert kwargs["nickname"] == "Support"
298
300
 
299
301
 
302
+ def test_set_number_properties_forwards_forward_to():
303
+ client = FakeClient(set_number_properties=_FakeNumber())
304
+ tool = set_number_properties_tool(client)
305
+ _run(tool, number_id="pn_9", forward_to="+18005550100")
306
+
307
+ assert client.calls["set_number_properties"]["kwargs"]["forward_to"] == "+18005550100"
308
+
309
+
310
+ def test_set_number_properties_empty_forward_to_stops_forwarding():
311
+ # An empty string is sent to the SDK as None — an explicit null that stops
312
+ # forwarding — rather than being swallowed like an omitted argument.
313
+ client = FakeClient(set_number_properties=_FakeNumber())
314
+ tool = set_number_properties_tool(client)
315
+ _run(tool, number_id="pn_9", forward_to="")
316
+
317
+ kwargs = client.calls["set_number_properties"]["kwargs"]
318
+ assert "forward_to" in kwargs
319
+ assert kwargs["forward_to"] is None
320
+
321
+
322
+ def test_set_number_properties_omits_unprovided_forward_to():
323
+ client = FakeClient(set_number_properties=_FakeNumber())
324
+ tool = set_number_properties_tool(client)
325
+ _run(tool, number_id="pn_9", nickname="Support")
326
+
327
+ assert "forward_to" not in client.calls["set_number_properties"]["kwargs"]
328
+
329
+
300
330
  def test_make_call_passes_idempotency_key():
301
331
  client = FakeClient(make_call=_FakeCall())
302
332
  tool = make_call_tool(client)
@@ -345,7 +345,7 @@ wheels = [
345
345
 
346
346
  [[package]]
347
347
  name = "dial-agentframework"
348
- version = "0.19.2"
348
+ version = "0.21.0"
349
349
  source = { editable = "." }
350
350
  dependencies = [
351
351
  { name = "agent-framework-core" },
@@ -368,7 +368,7 @@ dev = [{ name = "pytest", specifier = ">=8" }]
368
368
 
369
369
  [[package]]
370
370
  name = "dial-sdk"
371
- version = "0.31.0"
371
+ version = "0.33.0"
372
372
  source = { editable = "../sdk-python" }
373
373
  dependencies = [
374
374
  { name = "httpx" },