dial-agentframework 0.16.0__tar.gz → 0.18.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.
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/PKG-INFO +4 -3
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/README.md +2 -1
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/dial_agentframework/__init__.py +2 -0
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/dial_agentframework/tools.py +104 -9
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/pyproject.toml +2 -2
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/tests/test_tools.py +3 -1
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/uv.lock +2 -2
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/.gitignore +0 -0
- {dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/examples/signup_agentframework.ipynb +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: dial-agentframework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.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.
|
|
7
|
+
Requires-Dist: dial-sdk>=0.28.0
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
|
|
10
10
|
# dial-agentframework
|
|
@@ -85,7 +85,8 @@ Each builder takes your shared `DialClient`:
|
|
|
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
87
|
| `reply_to_message_tool` | `reply_to_message` | Reply or react to a message |
|
|
88
|
-
| `list_messages_tool` | `list_messages` | List recent messages |
|
|
88
|
+
| `list_messages_tool` | `list_messages` | List recent messages (`contact` for one conversation, `search` by text) |
|
|
89
|
+
| `list_contacts_tool` | `list_contacts` | List the numbers your lines have talked to |
|
|
89
90
|
| `make_call_tool` | `make_call` | Place an AI voice call |
|
|
90
91
|
| `list_calls_tool` | `list_calls` | List recent calls |
|
|
91
92
|
| `get_call_tool` | `get_call` | Fetch one call by id |
|
|
@@ -76,7 +76,8 @@ Each builder takes your shared `DialClient`:
|
|
|
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
78
|
| `reply_to_message_tool` | `reply_to_message` | Reply or react to a message |
|
|
79
|
-
| `list_messages_tool` | `list_messages` | List recent messages |
|
|
79
|
+
| `list_messages_tool` | `list_messages` | List recent messages (`contact` for one conversation, `search` by text) |
|
|
80
|
+
| `list_contacts_tool` | `list_contacts` | List the numbers your lines have talked to |
|
|
80
81
|
| `make_call_tool` | `make_call` | Place an AI voice call |
|
|
81
82
|
| `list_calls_tool` | `list_calls` | List recent calls |
|
|
82
83
|
| `get_call_tool` | `get_call` | Fetch one call by id |
|
|
@@ -5,6 +5,7 @@ from .tools import (
|
|
|
5
5
|
set_number_properties_tool,
|
|
6
6
|
list_messages_tool,
|
|
7
7
|
list_groups_tool,
|
|
8
|
+
list_contacts_tool,
|
|
8
9
|
send_message_tool,
|
|
9
10
|
reply_to_message_tool,
|
|
10
11
|
start_typing_tool,
|
|
@@ -23,6 +24,7 @@ __all__ = [
|
|
|
23
24
|
"set_number_properties_tool",
|
|
24
25
|
"list_messages_tool",
|
|
25
26
|
"list_groups_tool",
|
|
27
|
+
"list_contacts_tool",
|
|
26
28
|
"send_message_tool",
|
|
27
29
|
"reply_to_message_tool",
|
|
28
30
|
"start_typing_tool",
|
|
@@ -114,7 +114,8 @@ def set_number_properties_tool(client: DialClient) -> FunctionTool:
|
|
|
114
114
|
name="set_number_properties",
|
|
115
115
|
description=(
|
|
116
116
|
"Update a Dial phone number's properties: its inbound instruction (the system "
|
|
117
|
-
"prompt its AI voice agent uses on inbound calls) and/or
|
|
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
119
|
"On iMessage numbers you can also set the display identity (first/last name "
|
|
119
120
|
"and avatar photo) shown to recipients. Provide at least one."
|
|
120
121
|
),
|
|
@@ -164,6 +165,14 @@ def set_number_properties_tool(client: DialClient) -> FunctionTool:
|
|
|
164
165
|
"as the number's iMessage display photo. iMessage numbers only (400 "
|
|
165
166
|
"otherwise). The photo can be replaced but not removed.",
|
|
166
167
|
] = None,
|
|
168
|
+
calling_enabled: Annotated[
|
|
169
|
+
Optional[bool],
|
|
170
|
+
"Switch calling on or off for this number, in both directions. False stops "
|
|
171
|
+
"inbound calls from being connected (the caller is never answered) and makes "
|
|
172
|
+
"place_call from this number fail with calling_disabled (409); messaging on "
|
|
173
|
+
"the number is unaffected. Takes effect on the next call — a call already in "
|
|
174
|
+
"progress is not ended. Omit to leave it unchanged.",
|
|
175
|
+
] = None,
|
|
167
176
|
) -> str:
|
|
168
177
|
kwargs: dict = {}
|
|
169
178
|
if inbound_instruction is not None:
|
|
@@ -187,15 +196,20 @@ def set_number_properties_tool(client: DialClient) -> FunctionTool:
|
|
|
187
196
|
kwargs["last_name"] = last_name
|
|
188
197
|
if avatar_url is not None:
|
|
189
198
|
kwargs["avatar"] = avatar_url
|
|
199
|
+
# False is a real value here; only None means "leave unchanged".
|
|
200
|
+
if calling_enabled is not None:
|
|
201
|
+
kwargs["calling_enabled"] = calling_enabled
|
|
190
202
|
if not kwargs:
|
|
191
203
|
return (
|
|
192
204
|
"Provide at least one of inbound_instruction, inbound_voice_gender, "
|
|
193
|
-
"inbound_language, nickname, max_call_duration_seconds,
|
|
194
|
-
"last_name, or avatar_url."
|
|
205
|
+
"inbound_language, nickname, max_call_duration_seconds, calling_enabled, "
|
|
206
|
+
"first_name, last_name, or avatar_url."
|
|
195
207
|
)
|
|
196
208
|
number = await client.set_number_properties(number_id, **kwargs)
|
|
197
209
|
nick = f' | nickname: "{number.nickname}"' if number.nickname else ""
|
|
198
|
-
|
|
210
|
+
# Reported only when off, so a messaging-only line isn't a silent surprise.
|
|
211
|
+
calling = "" if number.calling_enabled else " | calling: off (messaging only)"
|
|
212
|
+
return f"Updated {number.number} (id: {number.id}){nick}{calling}"
|
|
199
213
|
|
|
200
214
|
return set_number_properties
|
|
201
215
|
|
|
@@ -207,8 +221,10 @@ def list_messages_tool(client: DialClient) -> FunctionTool:
|
|
|
207
221
|
@tool(
|
|
208
222
|
name="list_messages",
|
|
209
223
|
description=(
|
|
210
|
-
"List recent inbound and outbound messages on the account.
|
|
211
|
-
"
|
|
224
|
+
"List recent inbound and outbound messages on the account. Unfiltered this "
|
|
225
|
+
"is the newest 100 messages across every conversation, so pass contact to "
|
|
226
|
+
"read one person's conversation, group_id to read one group's, or search to "
|
|
227
|
+
"find a message by its text anywhere in the history."
|
|
212
228
|
),
|
|
213
229
|
)
|
|
214
230
|
async def list_messages(
|
|
@@ -223,9 +239,24 @@ def list_messages_tool(client: DialClient) -> FunctionTool:
|
|
|
223
239
|
"Restrict to one group conversation (from list_groups). Combines with the "
|
|
224
240
|
"other filters.",
|
|
225
241
|
] = None,
|
|
242
|
+
contact: Annotated[
|
|
243
|
+
Optional[str],
|
|
244
|
+
"One contact's conversation: messages exchanged with this number (E.164), "
|
|
245
|
+
"both directions, across every line on the account. Group messages are never "
|
|
246
|
+
"included - use group_id for those.",
|
|
247
|
+
] = None,
|
|
248
|
+
search: Annotated[
|
|
249
|
+
Optional[str],
|
|
250
|
+
"Case-insensitive substring match on the message body. Searches the whole "
|
|
251
|
+
"history, then returns the 100 most recent matches.",
|
|
252
|
+
] = None,
|
|
226
253
|
) -> str:
|
|
227
254
|
messages = await client.list_messages(
|
|
228
|
-
number_id=number_id,
|
|
255
|
+
number_id=number_id,
|
|
256
|
+
direction=direction,
|
|
257
|
+
group_id=group_id,
|
|
258
|
+
contact=contact,
|
|
259
|
+
search=search,
|
|
229
260
|
)
|
|
230
261
|
if not messages:
|
|
231
262
|
return "No messages found."
|
|
@@ -439,13 +470,69 @@ def stop_typing_tool(client: DialClient) -> FunctionTool:
|
|
|
439
470
|
return stop_typing
|
|
440
471
|
|
|
441
472
|
|
|
473
|
+
# ── Contacts ─────────────────────────────────────────────────────────────────
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def list_contacts_tool(client: DialClient) -> FunctionTool:
|
|
477
|
+
@tool(
|
|
478
|
+
name="list_contacts",
|
|
479
|
+
description=(
|
|
480
|
+
"List every number the account's lines have exchanged a message or a call "
|
|
481
|
+
"with, newest activity first, with per-contact counts and a summary of the "
|
|
482
|
+
"most recent interaction. Derived from history - there is no address book to "
|
|
483
|
+
"add anyone to. Counts span every line, so one person reached on two of your "
|
|
484
|
+
"numbers is one contact. Groups are not contacts; use list_groups for those. "
|
|
485
|
+
"Pass a contact's number to list_messages as contact to read that "
|
|
486
|
+
"conversation."
|
|
487
|
+
),
|
|
488
|
+
)
|
|
489
|
+
async def list_contacts(
|
|
490
|
+
limit: Annotated[Optional[int], "Max contacts to return (1-1000; default 100)"] = None,
|
|
491
|
+
starting_after: Annotated[
|
|
492
|
+
Optional[str],
|
|
493
|
+
"ISO-8601 cursor - the last_at of the last contact you received. Only "
|
|
494
|
+
"contacts with strictly older activity are returned.",
|
|
495
|
+
] = None,
|
|
496
|
+
) -> str:
|
|
497
|
+
page = await client.list_contacts(limit=limit, starting_after=starting_after)
|
|
498
|
+
if not page.contacts:
|
|
499
|
+
return "No contacts found."
|
|
500
|
+
lines = []
|
|
501
|
+
for c in page.contacts:
|
|
502
|
+
if c.last_kind == "call":
|
|
503
|
+
preview = f"{c.last_direction} call"
|
|
504
|
+
if c.last_call_duration:
|
|
505
|
+
preview += f", {c.last_call_duration}s"
|
|
506
|
+
elif c.last_redacted:
|
|
507
|
+
preview = "(deleted by retention)"
|
|
508
|
+
elif not c.last_body:
|
|
509
|
+
preview = f"({c.last_media_count} media)" if c.last_media_count else ""
|
|
510
|
+
else:
|
|
511
|
+
preview = c.last_body[:50]
|
|
512
|
+
lines.append(
|
|
513
|
+
f"{c.number} | {c.message_count} msg, {c.call_count} call | "
|
|
514
|
+
f"{c.last_at} | {preview}"
|
|
515
|
+
)
|
|
516
|
+
if page.has_more:
|
|
517
|
+
# Named so the model can ask for the next page instead of assuming this is all.
|
|
518
|
+
lines.append(
|
|
519
|
+
f"(more contacts: call again with starting_after={page.contacts[-1].last_at})"
|
|
520
|
+
)
|
|
521
|
+
return "\n".join(lines)
|
|
522
|
+
|
|
523
|
+
return list_contacts
|
|
524
|
+
|
|
525
|
+
|
|
442
526
|
# ── Calls ────────────────────────────────────────────────────────────────────
|
|
443
527
|
|
|
444
528
|
|
|
445
529
|
def list_calls_tool(client: DialClient) -> FunctionTool:
|
|
446
530
|
@tool(
|
|
447
531
|
name="list_calls",
|
|
448
|
-
description=
|
|
532
|
+
description=(
|
|
533
|
+
"List recent inbound and outbound calls on the account. Pass contact to read "
|
|
534
|
+
"one person's call history."
|
|
535
|
+
),
|
|
449
536
|
)
|
|
450
537
|
async def list_calls(
|
|
451
538
|
number_id: Annotated[
|
|
@@ -454,8 +541,15 @@ def list_calls_tool(client: DialClient) -> FunctionTool:
|
|
|
454
541
|
direction: Annotated[
|
|
455
542
|
Optional[str], "Filter by direction: 'inbound' or 'outbound'"
|
|
456
543
|
] = None,
|
|
544
|
+
contact: Annotated[
|
|
545
|
+
Optional[str],
|
|
546
|
+
"One contact's calls: exchanged with this number (E.164), both directions, "
|
|
547
|
+
"across every line on the account.",
|
|
548
|
+
] = None,
|
|
457
549
|
) -> str:
|
|
458
|
-
calls = await client.list_calls(
|
|
550
|
+
calls = await client.list_calls(
|
|
551
|
+
number_id=number_id, direction=direction, contact=contact
|
|
552
|
+
)
|
|
459
553
|
if not calls:
|
|
460
554
|
return "No calls found."
|
|
461
555
|
return "\n".join(
|
|
@@ -655,6 +749,7 @@ def dial_tools(client: DialClient) -> list[FunctionTool]:
|
|
|
655
749
|
set_number_properties_tool(client),
|
|
656
750
|
list_messages_tool(client),
|
|
657
751
|
list_groups_tool(client),
|
|
752
|
+
list_contacts_tool(client),
|
|
658
753
|
send_message_tool(client),
|
|
659
754
|
reply_to_message_tool(client),
|
|
660
755
|
start_typing_tool(client),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dial-agentframework"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.18.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.
|
|
11
|
+
"dial-sdk>=0.28.0",
|
|
12
12
|
]
|
|
13
13
|
|
|
14
14
|
[tool.uv.sources]
|
|
@@ -33,6 +33,7 @@ class _FakeNumber:
|
|
|
33
33
|
number = "+14155550000"
|
|
34
34
|
country = "US"
|
|
35
35
|
nickname = None
|
|
36
|
+
calling_enabled = True
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
class FakeClient:
|
|
@@ -60,7 +61,7 @@ def _run(tool, **args) -> str:
|
|
|
60
61
|
# ── Tools ────────────────────────────────────────────────────────────────────
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
def
|
|
64
|
+
def test_dial_tools_bundles_all_fifteen():
|
|
64
65
|
names = sorted(t.name for t in dial_tools(FakeClient()))
|
|
65
66
|
assert names == sorted(
|
|
66
67
|
[
|
|
@@ -69,6 +70,7 @@ def test_dial_tools_bundles_all_fourteen():
|
|
|
69
70
|
"set_number_properties",
|
|
70
71
|
"list_messages",
|
|
71
72
|
"list_groups",
|
|
73
|
+
"list_contacts",
|
|
72
74
|
"send_message",
|
|
73
75
|
"reply_to_message",
|
|
74
76
|
"start_typing",
|
|
@@ -345,7 +345,7 @@ wheels = [
|
|
|
345
345
|
|
|
346
346
|
[[package]]
|
|
347
347
|
name = "dial-agentframework"
|
|
348
|
-
version = "0.
|
|
348
|
+
version = "0.18.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.
|
|
371
|
+
version = "0.28.0"
|
|
372
372
|
source = { editable = "../sdk-python" }
|
|
373
373
|
dependencies = [
|
|
374
374
|
{ name = "httpx" },
|
|
File without changes
|
{dial_agentframework-0.16.0 → dial_agentframework-0.18.0}/examples/signup_agentframework.ipynb
RENAMED
|
File without changes
|