cartesia-line 0.1.0a1__py3-none-any.whl → 0.1.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.
Potentially problematic release.
This version of cartesia-line might be problematic. Click here for more details.
- {cartesia_line-0.1.0a1.dist-info → cartesia_line-0.1.1.dist-info}/METADATA +9 -5
- {cartesia_line-0.1.0a1.dist-info → cartesia_line-0.1.1.dist-info}/RECORD +10 -10
- line/bridge.py +5 -1
- line/events.py +19 -0
- line/harness.py +15 -0
- line/tools/system_tools.py +72 -0
- line/user_bridge.py +8 -0
- {cartesia_line-0.1.0a1.dist-info → cartesia_line-0.1.1.dist-info}/WHEEL +0 -0
- {cartesia_line-0.1.0a1.dist-info → cartesia_line-0.1.1.dist-info}/licenses/LICENSE +0 -0
- {cartesia_line-0.1.0a1.dist-info → cartesia_line-0.1.1.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cartesia-line
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Cartesia Voice Agents SDK
|
|
5
5
|
Author-email: "Cartesia AI, Inc." <support@cartesia.ai>
|
|
6
6
|
License: Apache 2.0
|
|
7
7
|
Project-URL: Repository, https://github.com/cartesia-ai/line
|
|
8
|
-
Project-URL: Documentation, https://docs.cartesia.ai/line
|
|
8
|
+
Project-URL: Documentation, https://docs.cartesia.ai/line/
|
|
9
9
|
Project-URL: Homepage, https://cartesia.ai/agents
|
|
10
10
|
Keywords: voice,agents,ai,cartesia
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -65,9 +65,9 @@ Build intelligent, low-latency voice agents with background reasoning.
|
|
|
65
65
|
|
|
66
66
|
## Quickstart (< 5 minutes)
|
|
67
67
|
|
|
68
|
-
The Line SDK is designed to be used with the Cartesia [Line
|
|
68
|
+
The Line SDK is designed to be used with the Cartesia's voice agent platform [Line](https://cartesia.ai/agents).
|
|
69
69
|
- Create a [Cartesia account](https://play.cartesia.ai).
|
|
70
|
-
- Follow the [quickstart guide](https://docs.cartesia.ai/).
|
|
70
|
+
- Follow the [quickstart guide](https://docs.cartesia.ai/line/start-building/talk-to-your-first-agent).
|
|
71
71
|
|
|
72
72
|
And you'll be able to make your first voice call in a few minutes.
|
|
73
73
|
|
|
@@ -90,5 +90,9 @@ pip install cartesia-line
|
|
|
90
90
|
## Going Deeper
|
|
91
91
|
|
|
92
92
|
- **More examples**: [examples/](examples/) - See all available examples and patterns
|
|
93
|
-
- **
|
|
93
|
+
- **3rd party integrations**: [example_integrations/](example_integrations/) - See example integrations for external services
|
|
94
|
+
|
|
95
|
+
> [!NOTE]
|
|
96
|
+
> While Cartesia approves each example, they are implemented and maintained by our partners.
|
|
97
|
+
- **Full API reference**: [docs.cartesia.ai/line](https://docs.cartesia.ai/line/)
|
|
94
98
|
- **Get help**: [Discord community](https://discord.gg/cartesia)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
cartesia_line-0.1.
|
|
1
|
+
cartesia_line-0.1.1.dist-info/licenses/LICENSE,sha256=ElpXuEGJlZ5XPeLe_tLbAmiEVE79EGxdy-aMw3JxE_Y,11347
|
|
2
2
|
line/__init__.py,sha256=glA0fvCL2xBmgC1cXZbiv6wcXS-yod7ctcL3EnoeEIk,790
|
|
3
|
-
line/bridge.py,sha256=
|
|
3
|
+
line/bridge.py,sha256=KjaZxEA1QuqtTASej5mYhe6C9OJdpu85b9jcEsBxMvA,14233
|
|
4
4
|
line/bus.py,sha256=AGMwHCNftVeWDzl0o1Jd2iDm51q1FTBEOlJzGZLub1Q,14324
|
|
5
5
|
line/call_request.py,sha256=JfYS4bAicuwjTHMXdK_PYbvSPj1dht44282yZACGUbU,847
|
|
6
|
-
line/events.py,sha256=
|
|
7
|
-
line/harness.py,sha256=
|
|
6
|
+
line/events.py,sha256=PiM6l9YqnvhLa3BqVM8WPIj5nkOJdFylG1ZX3RAWVeI,5922
|
|
7
|
+
line/harness.py,sha256=EG0nZ-KeVJ0_yKKbGUxVY_S3OI0aer82-amVtEatxJo,9060
|
|
8
8
|
line/harness_types.py,sha256=Lwu1n2fAA5o-b_PjLWDSMu4G-GYEpb3X1CAUy3pvelk,2214
|
|
9
9
|
line/routes.py,sha256=jR_bl96ujx5MYzdVb9YLkcAITCboosM9UM7f4Et9eBc,24446
|
|
10
|
-
line/user_bridge.py,sha256=
|
|
10
|
+
line/user_bridge.py,sha256=d8Hb8S7Q1D-yqAkz8I0nQGo_7dWv5Xy0iycSkFWirmw,7880
|
|
11
11
|
line/voice_agent_app.py,sha256=xlhtc9ZER2v5hOOWHz9W8e9jALlMAz_OvEcJL5QCNsQ,5421
|
|
12
12
|
line/voice_agent_system.py,sha256=8yywrjKdO43s7UkKqIs0jqte27vOF96NYnuGILsJz7k,8002
|
|
13
13
|
line/nodes/__init__.py,sha256=Rp-9gxMZ4hCTjbm6zaiTTBz6lg9d32UwxJgWTJfhnZU,128
|
|
@@ -15,13 +15,13 @@ line/nodes/base.py,sha256=HKfv_j3H-E-cwhtK9La1DUNmYdtbGUNeo-c51TGIQKM,1897
|
|
|
15
15
|
line/nodes/conversation_context.py,sha256=1ytf4q5GGy6tEjVD_KbKAkKLqosCkwi0kVWGymuLJhw,2138
|
|
16
16
|
line/nodes/reasoning.py,sha256=kyXxk6xJHEA4zwkHzriRtOC6cjk13E-N736qDvR5o-A,8567
|
|
17
17
|
line/tools/__init__.py,sha256=mYzcKIk1G_-EFQD6ugoCdcPjgywK_mC4dXu-ZyAtS7U,203
|
|
18
|
-
line/tools/system_tools.py,sha256=
|
|
18
|
+
line/tools/system_tools.py,sha256=E8buuDpjopqlqaW5vGG315x1GkIfspDt36BMhMhVC3M,6136
|
|
19
19
|
line/tools/tool_types.py,sha256=JJ6mfH9wB-dtaQFHkm8vsJIYsAiW6bIpgAh_nmDn744,1066
|
|
20
20
|
line/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
line/utils/aio.py,sha256=l1YgZ8tf1KtOyq6A-6RA_kE-FKGafDfHERFnDx0-PxM,1899
|
|
22
22
|
line/utils/gemini_utils.py,sha256=6YNyU3-I6Kn95j0qMroelO1WpjIPjIwhiub2lX34-kY,5396
|
|
23
23
|
line/utils/openai_utils.py,sha256=I9nIpHTFC98ChWzRmV-enMSIcicRVF9KYjifsWkoPCE,3596
|
|
24
|
-
cartesia_line-0.1.
|
|
25
|
-
cartesia_line-0.1.
|
|
26
|
-
cartesia_line-0.1.
|
|
27
|
-
cartesia_line-0.1.
|
|
24
|
+
cartesia_line-0.1.1.dist-info/METADATA,sha256=SlilXhNIOjlqsfAJOnaoIWsWgeo9FwE54rxLyM6AjP4,4299
|
|
25
|
+
cartesia_line-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
cartesia_line-0.1.1.dist-info/top_level.txt,sha256=xztzr4hR6ekbxrTcEufazgor-5McHQuLNu82cxn1jNE,5
|
|
27
|
+
cartesia_line-0.1.1.dist-info/RECORD,,
|
line/bridge.py
CHANGED
|
@@ -13,7 +13,11 @@ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Type, TypeVar,
|
|
|
13
13
|
from loguru import logger
|
|
14
14
|
|
|
15
15
|
from line.bus import Bus, Message
|
|
16
|
-
from line.events import
|
|
16
|
+
from line.events import (
|
|
17
|
+
EventInstance,
|
|
18
|
+
EventsRegistry,
|
|
19
|
+
EventTypeOrAlias,
|
|
20
|
+
)
|
|
17
21
|
from line.routes import RouteBuilder, RouteHandler
|
|
18
22
|
|
|
19
23
|
if TYPE_CHECKING:
|
line/events.py
CHANGED
|
@@ -38,6 +38,9 @@ __all__ = [
|
|
|
38
38
|
"AgentSpeechSent",
|
|
39
39
|
"UserUnknownInputReceived",
|
|
40
40
|
"LogMetric",
|
|
41
|
+
"DTMFInputEvent",
|
|
42
|
+
"DTMFOutputEvent",
|
|
43
|
+
"DTMFStoppedEvent",
|
|
41
44
|
]
|
|
42
45
|
|
|
43
46
|
|
|
@@ -183,6 +186,22 @@ class LogMetric(BaseModel):
|
|
|
183
186
|
value: Any
|
|
184
187
|
|
|
185
188
|
|
|
189
|
+
class DTMFInputEvent(BaseModel):
|
|
190
|
+
"""DTMF event for tracking DTMF input."""
|
|
191
|
+
|
|
192
|
+
button: str
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class DTMFOutputEvent(BaseModel):
|
|
196
|
+
"""DTMF event for tracking DTMF input."""
|
|
197
|
+
|
|
198
|
+
button: str
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class DTMFStoppedEvent(BaseModel):
|
|
202
|
+
"""DTMF stopped event for tracking DTMF input."""
|
|
203
|
+
|
|
204
|
+
|
|
186
205
|
class _EventsRegistry:
|
|
187
206
|
"""A singleton registry of all events.
|
|
188
207
|
|
line/harness.py
CHANGED
|
@@ -16,6 +16,7 @@ from line.events import (
|
|
|
16
16
|
AgentSpeechSent,
|
|
17
17
|
AgentStartedSpeaking,
|
|
18
18
|
AgentStoppedSpeaking,
|
|
19
|
+
DTMFInputEvent,
|
|
19
20
|
UserStartedSpeaking,
|
|
20
21
|
UserStoppedSpeaking,
|
|
21
22
|
UserTranscriptionReceived,
|
|
@@ -24,6 +25,8 @@ from line.events import (
|
|
|
24
25
|
from line.harness_types import (
|
|
25
26
|
AgentSpeechInput,
|
|
26
27
|
AgentStateInput,
|
|
28
|
+
DTMFInput,
|
|
29
|
+
DTMFOutput,
|
|
27
30
|
EndCallOutput,
|
|
28
31
|
ErrorOutput,
|
|
29
32
|
InputMessage,
|
|
@@ -199,6 +202,15 @@ class ConversationHarness:
|
|
|
199
202
|
logger.debug(f"📈 Logging metric: {name}={value}")
|
|
200
203
|
await self._send(LogMetricOutput(name=name, value=value))
|
|
201
204
|
|
|
205
|
+
async def send_dtmf(self, button: str):
|
|
206
|
+
"""
|
|
207
|
+
Send a DTMF event via WebSocket
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
button: The DTMF button to send
|
|
211
|
+
"""
|
|
212
|
+
await self._send(DTMFOutput(button=button))
|
|
213
|
+
|
|
202
214
|
async def cleanup(self):
|
|
203
215
|
"""
|
|
204
216
|
Clean up resources and stop all tasks
|
|
@@ -249,6 +261,9 @@ class ConversationHarness:
|
|
|
249
261
|
elif isinstance(message, AgentSpeechInput):
|
|
250
262
|
logger.info(f'🗣️ Agent speech sent: "{message.content}"')
|
|
251
263
|
return [AgentSpeechSent(content=message.content)]
|
|
264
|
+
elif isinstance(message, DTMFInput):
|
|
265
|
+
logger.info(f"🔔 DTMF received: {message.button}")
|
|
266
|
+
return [DTMFInputEvent(button=message.button)]
|
|
252
267
|
else:
|
|
253
268
|
# Fallback for unknown types.
|
|
254
269
|
logger.warning(f"Unknown message type: {type(message).__name__} ({message.model_dump_json()})")
|
line/tools/system_tools.py
CHANGED
|
@@ -118,3 +118,75 @@ async def end_call(
|
|
|
118
118
|
|
|
119
119
|
# End the call
|
|
120
120
|
yield EndCall()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class DTMFToolCall(ToolDefinition):
|
|
124
|
+
"""Arguments for the dtmf_tool_call tool."""
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def name(cls) -> str:
|
|
128
|
+
return "dtmf_tool_call"
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def description(cls) -> str:
|
|
132
|
+
return (
|
|
133
|
+
"Send a DTMF tone to the user. Use this when you find the "
|
|
134
|
+
"appropriate selection and the voice system asks you to press a button"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def parameters_description(cls) -> str:
|
|
139
|
+
return "The DTMF button to send"
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def to_gemini_tool(cls) -> "gemini_types.Tool":
|
|
143
|
+
"""Convert to Gemini tool format"""
|
|
144
|
+
return gemini_types.Tool(
|
|
145
|
+
function_declarations=[
|
|
146
|
+
gemini_types.FunctionDeclaration(
|
|
147
|
+
name=cls.name(),
|
|
148
|
+
description=cls.description(),
|
|
149
|
+
parameters={
|
|
150
|
+
"type": "object",
|
|
151
|
+
"properties": {
|
|
152
|
+
"button": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"description": cls.parameters_description(),
|
|
155
|
+
"enum": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "#"],
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": ["button"],
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
]
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def to_openai_tool(cls) -> Dict[str, object]:
|
|
166
|
+
"""Convert to OpenAI tool format for Responses API.
|
|
167
|
+
|
|
168
|
+
Note: This returns the format expected by OpenAI's Responses API,
|
|
169
|
+
not the Chat Completions API format.
|
|
170
|
+
"""
|
|
171
|
+
return {
|
|
172
|
+
"type": "function",
|
|
173
|
+
"name": cls.name(),
|
|
174
|
+
"description": cls.description(),
|
|
175
|
+
"parameters": {
|
|
176
|
+
"type": "object",
|
|
177
|
+
"properties": {
|
|
178
|
+
"button": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"enum": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "#"],
|
|
181
|
+
"description": cls.parameters_description(),
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
"required": ["button"],
|
|
185
|
+
"additionalProperties": False,
|
|
186
|
+
"strict": True,
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class DTMFToolCallTool(ToolDefinition):
|
|
192
|
+
"""DTMF tool call system tool definition."""
|
line/user_bridge.py
CHANGED
|
@@ -44,6 +44,7 @@ from line.events import (
|
|
|
44
44
|
AgentError,
|
|
45
45
|
AgentResponse,
|
|
46
46
|
Authorize,
|
|
47
|
+
DTMFOutputEvent,
|
|
47
48
|
EndCall,
|
|
48
49
|
EventType,
|
|
49
50
|
LogMetric,
|
|
@@ -111,6 +112,11 @@ def create_user_bridge(harness: "ConversationHarness", authorized_node: str) ->
|
|
|
111
112
|
event: LogMetric = message.event
|
|
112
113
|
return await harness.log_metric(event.name, event.value)
|
|
113
114
|
|
|
115
|
+
async def send_dtmf(message: Message):
|
|
116
|
+
"""Send DTMF event to harness."""
|
|
117
|
+
event: DTMFOutputEvent = message.event
|
|
118
|
+
return await harness.send_dtmf(event.button)
|
|
119
|
+
|
|
114
120
|
bridge = (
|
|
115
121
|
Bridge(harness)
|
|
116
122
|
.with_input_routing(harness) # Enable WebSocket → bus event routing
|
|
@@ -132,6 +138,8 @@ def create_user_bridge(harness: "ConversationHarness", authorized_node: str) ->
|
|
|
132
138
|
.map(send_transfer_call)
|
|
133
139
|
.on(LogMetric)
|
|
134
140
|
.map(send_log_metric)
|
|
141
|
+
.on(DTMFOutputEvent)
|
|
142
|
+
.map(send_dtmf)
|
|
135
143
|
)
|
|
136
144
|
|
|
137
145
|
# Add authorization handler after creation.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|