dial-agentframework 0.12.0__tar.gz → 0.13.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.4
2
2
  Name: dial-agentframework
3
- Version: 0.12.0
3
+ Version: 0.13.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.13.0
7
+ Requires-Dist: dial-sdk>=0.14.0
8
8
  Description-Content-Type: text/markdown
9
9
 
10
10
  # dial-agentframework
@@ -84,6 +84,7 @@ Each builder takes your shared `DialClient`:
84
84
  | `purchase_number_tool` | `purchase_number` | Provision a new number (billable) |
85
85
  | `set_number_properties_tool` | `set_number_properties` | Update a number's nickname / inbound instruction |
86
86
  | `send_message_tool` | `send_message` | Send an SMS (optionally MMS) |
87
+ | `reply_to_message_tool` | `reply_to_message` | Reply or react to a message |
87
88
  | `list_messages_tool` | `list_messages` | List recent messages |
88
89
  | `make_call_tool` | `make_call` | Place an AI voice call |
89
90
  | `list_calls_tool` | `list_calls` | List recent calls |
@@ -75,6 +75,7 @@ Each builder takes your shared `DialClient`:
75
75
  | `purchase_number_tool` | `purchase_number` | Provision a new number (billable) |
76
76
  | `set_number_properties_tool` | `set_number_properties` | Update a number's nickname / inbound instruction |
77
77
  | `send_message_tool` | `send_message` | Send an SMS (optionally MMS) |
78
+ | `reply_to_message_tool` | `reply_to_message` | Reply or react to a message |
78
79
  | `list_messages_tool` | `list_messages` | List recent messages |
79
80
  | `make_call_tool` | `make_call` | Place an AI voice call |
80
81
  | `list_calls_tool` | `list_calls` | List recent calls |
@@ -5,6 +5,7 @@ from .tools import (
5
5
  set_number_properties_tool,
6
6
  list_messages_tool,
7
7
  send_message_tool,
8
+ reply_to_message_tool,
8
9
  list_calls_tool,
9
10
  make_call_tool,
10
11
  get_call_tool,
@@ -19,6 +20,7 @@ __all__ = [
19
20
  "set_number_properties_tool",
20
21
  "list_messages_tool",
21
22
  "send_message_tool",
23
+ "reply_to_message_tool",
22
24
  "list_calls_tool",
23
25
  "make_call_tool",
24
26
  "get_call_tool",
@@ -226,6 +226,43 @@ def send_message_tool(client: DialClient) -> FunctionTool:
226
226
  return send_message
227
227
 
228
228
 
229
+ def reply_to_message_tool(client: DialClient) -> FunctionTool:
230
+ @tool(
231
+ name="reply_to_message",
232
+ description=(
233
+ "Reply in-thread or react to an existing message. The reply goes out from "
234
+ "the Dial number the target message belongs to, to the other party — no "
235
+ "from/to needed. Provide exactly one of body or reaction. On iMessage "
236
+ "numbers replies thread and reactions are native; recipients that can only "
237
+ "receive SMS get an emoji reaction as a regular text, and named reactions "
238
+ "are rejected."
239
+ ),
240
+ )
241
+ async def reply_to_message(
242
+ message_id: Annotated[
243
+ str, "ID of the message to reply or react to (from list_messages or a message.received event)"
244
+ ],
245
+ body: Annotated[
246
+ Optional[str], "Reply text; on an iMessage number it threads under the target message"
247
+ ] = None,
248
+ reaction: Annotated[
249
+ Optional[str],
250
+ "Reaction to send instead of a body: love, like, dislike, laugh, "
251
+ "emphasize, question, or a single emoji",
252
+ ] = None,
253
+ ) -> str:
254
+ if (body is None) == (reaction is None):
255
+ return "Provide exactly one of body or reaction."
256
+ if body is not None:
257
+ msg = await client.reply_to_message(message_id, body=body)
258
+ else:
259
+ msg = await client.reply_to_message(message_id, reaction=reaction)
260
+ note = f", reaction: {msg.reaction}" if getattr(msg, "reaction", None) else ""
261
+ return f"Reply sent successfully. ID: {msg.id}, status: {msg.status}{note}"
262
+
263
+ return reply_to_message
264
+
265
+
229
266
  # ── Calls ────────────────────────────────────────────────────────────────────
230
267
 
231
268
 
@@ -432,6 +469,7 @@ def dial_tools(client: DialClient) -> list[FunctionTool]:
432
469
  set_number_properties_tool(client),
433
470
  list_messages_tool(client),
434
471
  send_message_tool(client),
472
+ reply_to_message_tool(client),
435
473
  list_calls_tool(client),
436
474
  make_call_tool(client),
437
475
  get_call_tool(client),
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dial-agentframework"
3
- version = "0.12.0"
3
+ version = "0.13.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.13.0",
11
+ "dial-sdk>=0.14.0",
12
12
  ]
13
13
 
14
14
  [tool.uv.sources]
@@ -4,6 +4,7 @@ from dial_agentframework import (
4
4
  dial_tools,
5
5
  purchase_number_tool,
6
6
  send_message_tool,
7
+ reply_to_message_tool,
7
8
  make_call_tool,
8
9
  wait_for_message_tool,
9
10
  )
@@ -54,7 +55,7 @@ def _run(tool, **args) -> str:
54
55
  # ── Tools ────────────────────────────────────────────────────────────────────
55
56
 
56
57
 
57
- def test_dial_tools_bundles_all_ten():
58
+ def test_dial_tools_bundles_all_eleven():
58
59
  names = sorted(t.name for t in dial_tools(FakeClient()))
59
60
  assert names == sorted(
60
61
  [
@@ -63,6 +64,7 @@ def test_dial_tools_bundles_all_ten():
63
64
  "set_number_properties",
64
65
  "list_messages",
65
66
  "send_message",
67
+ "reply_to_message",
66
68
  "list_calls",
67
69
  "make_call",
68
70
  "get_call",
@@ -102,6 +104,27 @@ def test_send_message_media_only_with_force_audio_file():
102
104
  assert params.media == ["https://cdn.example.com/note.m4a"]
103
105
 
104
106
 
107
+ def test_reply_to_message_sends_body_or_reaction():
108
+ client = FakeClient(reply_to_message=_FakeMessage())
109
+ tool = reply_to_message_tool(client)
110
+ out = _run(tool, message_id="msg_t", body="on my way")
111
+ assert "msg_1" in out
112
+ call = client.calls["reply_to_message"]
113
+ assert call["args"] == ("msg_t",)
114
+ assert call["kwargs"] == {"body": "on my way"}
115
+
116
+ _run(tool, message_id="msg_t", reaction="🔥")
117
+ assert client.calls["reply_to_message"]["kwargs"] == {"reaction": "🔥"}
118
+
119
+
120
+ def test_reply_to_message_requires_exactly_one():
121
+ client = FakeClient(reply_to_message=_FakeMessage())
122
+ tool = reply_to_message_tool(client)
123
+ assert "exactly one" in _run(tool, message_id="msg_t").lower()
124
+ assert "exactly one" in _run(tool, message_id="msg_t", body="x", reaction="love").lower()
125
+ assert "reply_to_message" not in client.calls
126
+
127
+
105
128
  def test_purchase_number_builds_params_with_consent():
106
129
  client = FakeClient(purchase_number=_FakeNumber())
107
130
  tool = purchase_number_tool(client)
@@ -345,7 +345,7 @@ wheels = [
345
345
 
346
346
  [[package]]
347
347
  name = "dial-agentframework"
348
- version = "0.12.0"
348
+ version = "0.13.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.13.0"
371
+ version = "0.14.0"
372
372
  source = { editable = "../sdk-python" }
373
373
  dependencies = [
374
374
  { name = "httpx" },