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.

@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cartesia-line
3
- Version: 0.1.0a1
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 platform](https://cartesia.ai/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
- - **Full API reference**: [docs.cartesia.ai/line](https://docs.cartesia.ai/)
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.0a1.dist-info/licenses/LICENSE,sha256=ElpXuEGJlZ5XPeLe_tLbAmiEVE79EGxdy-aMw3JxE_Y,11347
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=53N5UizGKjrb6iR27OQbTtjgQZ-rNLjEJT0HqQiLa-M,14216
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=d_CtDTGvLyMwFjdO97FtpTwyBO8uSmCzB2CC7hYagiQ,5565
7
- line/harness.py,sha256=_4TJV3J2Sxdhz5JSuWXlthZLNmhfNlddHANzOLGjtec,8620
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=etnvSMALCDB7JOguW0imd41RSH3sHJ-oBHH2Hr7kSeQ,7620
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=Z0tDoaAtZ9GziBLSDklhE6NS91OoHrmGkgToP5nZD_o,3765
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.0a1.dist-info/METADATA,sha256=_47ursMi_LDtkwhqeKmOjbQFy51r6JQMzL_iptiVf1M,4001
25
- cartesia_line-0.1.0a1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- cartesia_line-0.1.0a1.dist-info/top_level.txt,sha256=xztzr4hR6ekbxrTcEufazgor-5McHQuLNu82cxn1jNE,5
27
- cartesia_line-0.1.0a1.dist-info/RECORD,,
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 EventInstance, EventsRegistry, EventTypeOrAlias
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()})")
@@ -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.