unmute-core 0.14.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.
Files changed (154) hide show
  1. unmute_core-0.14.0/LICENSE +21 -0
  2. unmute_core-0.14.0/PKG-INFO +203 -0
  3. unmute_core-0.14.0/README.md +160 -0
  4. unmute_core-0.14.0/pyproject.toml +119 -0
  5. unmute_core-0.14.0/pyproject.toml.orig +96 -0
  6. unmute_core-0.14.0/src/unmute_core/__init__.py +61 -0
  7. unmute_core-0.14.0/src/unmute_core/_pydantic.py +194 -0
  8. unmute_core-0.14.0/src/unmute_core/agent/__init__.py +11 -0
  9. unmute_core-0.14.0/src/unmute_core/agent/_setup.py +102 -0
  10. unmute_core-0.14.0/src/unmute_core/agent/_spec.py +84 -0
  11. unmute_core-0.14.0/src/unmute_core/call/__init__.py +39 -0
  12. unmute_core-0.14.0/src/unmute_core/call/_end.py +149 -0
  13. unmute_core-0.14.0/src/unmute_core/call/_events.py +183 -0
  14. unmute_core-0.14.0/src/unmute_core/call/_goodbye.py +5 -0
  15. unmute_core-0.14.0/src/unmute_core/call/_hooks.py +524 -0
  16. unmute_core-0.14.0/src/unmute_core/call/_start.py +115 -0
  17. unmute_core-0.14.0/src/unmute_core/call/_transcript.py +28 -0
  18. unmute_core-0.14.0/src/unmute_core/dev/__init__.py +38 -0
  19. unmute_core-0.14.0/src/unmute_core/dev/_recorder.py +884 -0
  20. unmute_core-0.14.0/src/unmute_core/dev/_records.py +117 -0
  21. unmute_core-0.14.0/src/unmute_core/dev/_store.py +90 -0
  22. unmute_core-0.14.0/src/unmute_core/greeting/__init__.py +12 -0
  23. unmute_core-0.14.0/src/unmute_core/greeting/_greeting.py +135 -0
  24. unmute_core-0.14.0/src/unmute_core/handoff/__init__.py +54 -0
  25. unmute_core-0.14.0/src/unmute_core/handoff/_context.py +115 -0
  26. unmute_core-0.14.0/src/unmute_core/handoff/_history.py +232 -0
  27. unmute_core-0.14.0/src/unmute_core/hooks/__init__.py +57 -0
  28. unmute_core-0.14.0/src/unmute_core/hooks/_event_tool.py +81 -0
  29. unmute_core-0.14.0/src/unmute_core/hooks/_hooks.py +441 -0
  30. unmute_core-0.14.0/src/unmute_core/hooks/_prompt_suffix.py +74 -0
  31. unmute_core-0.14.0/src/unmute_core/idle/__init__.py +12 -0
  32. unmute_core-0.14.0/src/unmute_core/idle/_limit.py +64 -0
  33. unmute_core-0.14.0/src/unmute_core/idle/_nudges.py +322 -0
  34. unmute_core-0.14.0/src/unmute_core/knowledge/__init__.py +16 -0
  35. unmute_core-0.14.0/src/unmute_core/knowledge/_base.py +578 -0
  36. unmute_core-0.14.0/src/unmute_core/livekit/__init__.py +70 -0
  37. unmute_core-0.14.0/src/unmute_core/livekit/_agent.py +699 -0
  38. unmute_core-0.14.0/src/unmute_core/livekit/_call.py +297 -0
  39. unmute_core-0.14.0/src/unmute_core/livekit/_calls.py +152 -0
  40. unmute_core-0.14.0/src/unmute_core/livekit/_close.py +57 -0
  41. unmute_core-0.14.0/src/unmute_core/livekit/_coval.py +109 -0
  42. unmute_core-0.14.0/src/unmute_core/livekit/_delegate.py +292 -0
  43. unmute_core-0.14.0/src/unmute_core/livekit/_dev.py +651 -0
  44. unmute_core-0.14.0/src/unmute_core/livekit/_dev_timings.py +111 -0
  45. unmute_core-0.14.0/src/unmute_core/livekit/_dev_tools.py +132 -0
  46. unmute_core-0.14.0/src/unmute_core/livekit/_finish.py +172 -0
  47. unmute_core-0.14.0/src/unmute_core/livekit/_gate.py +50 -0
  48. unmute_core-0.14.0/src/unmute_core/livekit/_history.py +222 -0
  49. unmute_core-0.14.0/src/unmute_core/livekit/_idle.py +78 -0
  50. unmute_core-0.14.0/src/unmute_core/livekit/_model.py +1232 -0
  51. unmute_core-0.14.0/src/unmute_core/livekit/_phone.py +396 -0
  52. unmute_core-0.14.0/src/unmute_core/livekit/_realtime.py +75 -0
  53. unmute_core-0.14.0/src/unmute_core/livekit/_replies.py +97 -0
  54. unmute_core-0.14.0/src/unmute_core/livekit/_step.py +459 -0
  55. unmute_core-0.14.0/src/unmute_core/livekit/_sync.py +145 -0
  56. unmute_core-0.14.0/src/unmute_core/livekit/_tools.py +95 -0
  57. unmute_core-0.14.0/src/unmute_core/livekit/_trace.py +433 -0
  58. unmute_core-0.14.0/src/unmute_core/livekit/_turn.py +21 -0
  59. unmute_core-0.14.0/src/unmute_core/livekit/_yield.py +153 -0
  60. unmute_core-0.14.0/src/unmute_core/model/__init__.py +107 -0
  61. unmute_core-0.14.0/src/unmute_core/model/_call.py +291 -0
  62. unmute_core-0.14.0/src/unmute_core/model/_chunks.py +39 -0
  63. unmute_core-0.14.0/src/unmute_core/model/_failover.py +188 -0
  64. unmute_core-0.14.0/src/unmute_core/model/_fallback.py +339 -0
  65. unmute_core-0.14.0/src/unmute_core/model/_openai.py +168 -0
  66. unmute_core-0.14.0/src/unmute_core/model/_prompt.py +240 -0
  67. unmute_core-0.14.0/src/unmute_core/model/_reasoning.py +79 -0
  68. unmute_core-0.14.0/src/unmute_core/model/_signatures.py +96 -0
  69. unmute_core-0.14.0/src/unmute_core/pipecat/__init__.py +101 -0
  70. unmute_core-0.14.0/src/unmute_core/pipecat/_call.py +451 -0
  71. unmute_core-0.14.0/src/unmute_core/pipecat/_coval.py +93 -0
  72. unmute_core-0.14.0/src/unmute_core/pipecat/_dev.py +429 -0
  73. unmute_core-0.14.0/src/unmute_core/pipecat/_dev_timings.py +206 -0
  74. unmute_core-0.14.0/src/unmute_core/pipecat/_dev_tools.py +121 -0
  75. unmute_core-0.14.0/src/unmute_core/pipecat/_end.py +107 -0
  76. unmute_core-0.14.0/src/unmute_core/pipecat/_frames.py +36 -0
  77. unmute_core-0.14.0/src/unmute_core/pipecat/_history.py +174 -0
  78. unmute_core-0.14.0/src/unmute_core/pipecat/_idle.py +197 -0
  79. unmute_core-0.14.0/src/unmute_core/pipecat/_interruption.py +329 -0
  80. unmute_core-0.14.0/src/unmute_core/pipecat/_model.py +339 -0
  81. unmute_core-0.14.0/src/unmute_core/pipecat/_phone.py +407 -0
  82. unmute_core-0.14.0/src/unmute_core/pipecat/_realtime.py +437 -0
  83. unmute_core-0.14.0/src/unmute_core/pipecat/_replies.py +196 -0
  84. unmute_core-0.14.0/src/unmute_core/pipecat/_requests.py +281 -0
  85. unmute_core-0.14.0/src/unmute_core/pipecat/_runs.py +727 -0
  86. unmute_core-0.14.0/src/unmute_core/pipecat/_services.py +94 -0
  87. unmute_core-0.14.0/src/unmute_core/pipecat/_side.py +398 -0
  88. unmute_core-0.14.0/src/unmute_core/pipecat/_tasks.py +1340 -0
  89. unmute_core-0.14.0/src/unmute_core/pipecat/_telemetry.py +473 -0
  90. unmute_core-0.14.0/src/unmute_core/pipecat/_tools.py +454 -0
  91. unmute_core-0.14.0/src/unmute_core/pipecat/_trace_requests.py +292 -0
  92. unmute_core-0.14.0/src/unmute_core/pipecat/_trace_speech.py +97 -0
  93. unmute_core-0.14.0/src/unmute_core/pipecat/_turn.py +29 -0
  94. unmute_core-0.14.0/src/unmute_core/pipecat/_voicemail.py +84 -0
  95. unmute_core-0.14.0/src/unmute_core/pipecat/_voices.py +25 -0
  96. unmute_core-0.14.0/src/unmute_core/prefetch/__init__.py +34 -0
  97. unmute_core-0.14.0/src/unmute_core/prefetch/_prefetch.py +587 -0
  98. unmute_core-0.14.0/src/unmute_core/py.typed +0 -0
  99. unmute_core-0.14.0/src/unmute_core/realtime/__init__.py +28 -0
  100. unmute_core-0.14.0/src/unmute_core/realtime/_rules.py +117 -0
  101. unmute_core-0.14.0/src/unmute_core/settings.py +210 -0
  102. unmute_core-0.14.0/src/unmute_core/slng/__init__.py +66 -0
  103. unmute_core-0.14.0/src/unmute_core/slng/_blocked.py +84 -0
  104. unmute_core-0.14.0/src/unmute_core/slng/_config.py +380 -0
  105. unmute_core-0.14.0/src/unmute_core/slng/_headers.py +205 -0
  106. unmute_core-0.14.0/src/unmute_core/slng/_router.py +283 -0
  107. unmute_core-0.14.0/src/unmute_core/slng/_settings.py +106 -0
  108. unmute_core-0.14.0/src/unmute_core/state/__init__.py +80 -0
  109. unmute_core-0.14.0/src/unmute_core/state/_call_info.py +301 -0
  110. unmute_core-0.14.0/src/unmute_core/state/_call_state.py +653 -0
  111. unmute_core-0.14.0/src/unmute_core/state/_events.py +69 -0
  112. unmute_core-0.14.0/src/unmute_core/state/_grammar.py +442 -0
  113. unmute_core-0.14.0/src/unmute_core/state/_paths.py +236 -0
  114. unmute_core-0.14.0/src/unmute_core/state/_schema.py +381 -0
  115. unmute_core-0.14.0/src/unmute_core/state/_types.py +355 -0
  116. unmute_core-0.14.0/src/unmute_core/tasks/__init__.py +86 -0
  117. unmute_core-0.14.0/src/unmute_core/tasks/_closing.py +338 -0
  118. unmute_core-0.14.0/src/unmute_core/tasks/_finish.py +475 -0
  119. unmute_core-0.14.0/src/unmute_core/tasks/_opening.py +265 -0
  120. unmute_core-0.14.0/src/unmute_core/tasks/_plan.py +505 -0
  121. unmute_core-0.14.0/src/unmute_core/tasks/_progress.py +227 -0
  122. unmute_core-0.14.0/src/unmute_core/tasks/_record.py +359 -0
  123. unmute_core-0.14.0/src/unmute_core/tasks/_run.py +1212 -0
  124. unmute_core-0.14.0/src/unmute_core/tasks/_spec.py +705 -0
  125. unmute_core-0.14.0/src/unmute_core/tasks/_texts.py +728 -0
  126. unmute_core-0.14.0/src/unmute_core/tasks/_values.py +531 -0
  127. unmute_core-0.14.0/src/unmute_core/telemetry/__init__.py +77 -0
  128. unmute_core-0.14.0/src/unmute_core/telemetry/_coval.py +675 -0
  129. unmute_core-0.14.0/src/unmute_core/telemetry/_logfire.py +137 -0
  130. unmute_core-0.14.0/src/unmute_core/telemetry/_metadata.py +183 -0
  131. unmute_core-0.14.0/src/unmute_core/telemetry/_settings.py +74 -0
  132. unmute_core-0.14.0/src/unmute_core/telemetry/_shape.py +164 -0
  133. unmute_core-0.14.0/src/unmute_core/telemetry/_sinks.py +175 -0
  134. unmute_core-0.14.0/src/unmute_core/telemetry/_telemetry.py +667 -0
  135. unmute_core-0.14.0/src/unmute_core/telemetry/_turns.py +75 -0
  136. unmute_core-0.14.0/src/unmute_core/telephony/__init__.py +59 -0
  137. unmute_core-0.14.0/src/unmute_core/telephony/_numbers.py +99 -0
  138. unmute_core-0.14.0/src/unmute_core/telephony/_transfer.py +278 -0
  139. unmute_core-0.14.0/src/unmute_core/tools/__init__.py +95 -0
  140. unmute_core-0.14.0/src/unmute_core/tools/_announce.py +53 -0
  141. unmute_core-0.14.0/src/unmute_core/tools/_builtins.py +150 -0
  142. unmute_core-0.14.0/src/unmute_core/tools/_errors.py +69 -0
  143. unmute_core-0.14.0/src/unmute_core/tools/_executors.py +870 -0
  144. unmute_core-0.14.0/src/unmute_core/tools/_fill.py +226 -0
  145. unmute_core-0.14.0/src/unmute_core/tools/_http.py +227 -0
  146. unmute_core-0.14.0/src/unmute_core/tools/_lines.py +393 -0
  147. unmute_core-0.14.0/src/unmute_core/tools/_save.py +187 -0
  148. unmute_core-0.14.0/src/unmute_core/tools/_schema.py +486 -0
  149. unmute_core-0.14.0/src/unmute_core/tools/_spec.py +1444 -0
  150. unmute_core-0.14.0/src/unmute_core/tools/_spoken.py +205 -0
  151. unmute_core-0.14.0/src/unmute_core/turn/__init__.py +29 -0
  152. unmute_core-0.14.0/src/unmute_core/turn/_backchannels.py +10 -0
  153. unmute_core-0.14.0/src/unmute_core/turn/_interruption.py +73 -0
  154. unmute_core-0.14.0/src/unmute_core/turn/_pace.py +103 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 slng.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: unmute-core
3
+ Version: 0.14.0
4
+ Summary: Typed call state, tasks, tools, handoffs and tracing for voice agents on LiveKit and Pipecat
5
+ Keywords: voice agents,livekit,pipecat,llm,telephony,opentelemetry
6
+ Author: Nicola Croon
7
+ Author-email: Nicola Croon <nicola@slng.ai>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Classifier: Typing :: Typed
18
+ Requires-Dist: babel>=2.16,<3
19
+ Requires-Dist: email-validator>=2.2,<3
20
+ Requires-Dist: httpx>=0.28,<1
21
+ Requires-Dist: jsonschema>=4.23,<5
22
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.40,<2
23
+ Requires-Dist: opentelemetry-sdk>=1.40,<2
24
+ Requires-Dist: phonenumbers>=8.13,<10
25
+ Requires-Dist: pydantic>=2.11,<3
26
+ Requires-Dist: pydantic-settings>=2.8,<3
27
+ Requires-Dist: llama-index-core>=0.14,<0.15 ; extra == 'knowledge'
28
+ Requires-Dist: llama-index-readers-file>=0.5,<0.8 ; extra == 'knowledge'
29
+ Requires-Dist: llama-index-retrievers-bm25>=0.6,<0.9 ; extra == 'knowledge'
30
+ Requires-Dist: livekit-agents>=1.8.7,<1.9 ; extra == 'livekit'
31
+ Requires-Dist: pipecat-ai>=1.12.0,<1.13 ; extra == 'pipecat'
32
+ Requires-Dist: openai>=1.109,<4 ; extra == 'slng'
33
+ Requires-Python: >=3.12
34
+ Project-URL: Repository, https://github.com/slng-ai/unmute-core
35
+ Project-URL: Documentation, https://github.com/slng-ai/unmute-core/tree/main/docs
36
+ Project-URL: Issues, https://github.com/slng-ai/unmute-core/issues
37
+ Project-URL: Changelog, https://github.com/slng-ai/unmute-core/blob/main/CHANGELOG.md
38
+ Provides-Extra: knowledge
39
+ Provides-Extra: livekit
40
+ Provides-Extra: pipecat
41
+ Provides-Extra: slng
42
+ Description-Content-Type: text/markdown
43
+
44
+ # unmute-core
45
+
46
+ [![PyPI](https://img.shields.io/pypi/v/unmute-core)](https://pypi.org/project/unmute-core/)
47
+ [![Python](https://img.shields.io/pypi/pyversions/unmute-core)](https://pypi.org/project/unmute-core/)
48
+ [![CI](https://github.com/slng-ai/unmute-core/actions/workflows/ci.yml/badge.svg)](https://github.com/slng-ai/unmute-core/actions/workflows/ci.yml)
49
+ [![License: MIT](https://img.shields.io/pypi/l/unmute-core)](https://github.com/slng-ai/unmute-core/blob/main/LICENSE)
50
+
51
+ unmute-core is a Python library for voice agents on [LiveKit Agents](https://github.com/livekit/agents) and [Pipecat](https://github.com/pipecat-ai/pipecat). You keep your framework, your speech and model plugins, and your pipeline. The library adds what a real phone or web call needs on top: typed call state, task steps, tools, handoffs, idle callers, transfers and tracing.
52
+
53
+ [Unmute](https://github.com/slng-ai/unmute) compiles an agent spec into a LiveKit or Pipecat project. Those projects import this library. You can also use it in an agent you write by hand, one piece at a time or all of it.
54
+
55
+ ## Example
56
+
57
+ A plain LiveKit agent with one tool from the library:
58
+
59
+ ```python
60
+ import os
61
+
62
+ from livekit.agents import Agent, AgentSession
63
+ from livekit.plugins import openai, silero, slng
64
+ from pydantic import BaseModel
65
+
66
+ from unmute_core.livekit import function_tools
67
+ from unmute_core.tools import ToolRegistry, local_tool
68
+
69
+
70
+ class OrderStatus(BaseModel):
71
+ order_id: str
72
+ status: str
73
+
74
+
75
+ def order_status(order_id: str) -> OrderStatus:
76
+ """Look up an order by its id. The return annotation is the result's checked shape."""
77
+ return OrderStatus(order_id=order_id, status="out for delivery")
78
+
79
+
80
+ registry = ToolRegistry([local_tool(order_status, announce="One moment, let me check.")])
81
+
82
+ agent = Agent(instructions="You help with orders.", tools=function_tools(registry))
83
+ slng_host = f"{os.environ['SLNG_WORLD_PART']}.api.slng.ai"
84
+ stt = slng.STT(slng_base_url=slng_host, model="soniox/speech-ai:rt-v5")
85
+ session = AgentSession(
86
+ stt=stt,
87
+ llm=openai.LLM(),
88
+ tts=slng.TTS(
89
+ slng_base_url=slng_host,
90
+ model="slng/fish/tts:s2.1-pro",
91
+ voice="8d8aec63a56c460cb7def795bdf754fa",
92
+ ),
93
+ vad=silero.VAD.load(),
94
+ )
95
+ stt.attach_to_session(session)
96
+ ```
97
+
98
+ The agent stays a normal LiveKit `Agent`. The library adds the tool and the line the agent says while the tool runs. Speech runs on SLNG: Soniox hears the caller and Fish Audio speaks, with your `SLNG_API_KEY`. On Pipecat, put `tools_schema(registry)` on the `LLMContext` instead.
99
+
100
+ ## How it fits
101
+
102
+ ```mermaid
103
+ flowchart TD
104
+ U[Unmute compiles agent.yaml] --> P[Your LiveKit or Pipecat project]
105
+ H[You write agent.py by hand] --> P
106
+ P -->|builds the pipeline, calls| A[unmute_core.livekit or unmute_core.pipecat]
107
+ A --> C[Core: state, tasks, tools, hooks, handoff, telemetry, model call]
108
+ ```
109
+
110
+ 1. Your project builds the pipeline. Unmute writes that project for you, or you write it yourself.
111
+ 2. The project calls an adapter: `unmute_core.livekit` or `unmute_core.pipecat`.
112
+ 3. The adapter turns framework events into calls to the core. The core never imports LiveKit or Pipecat.
113
+ 4. The same core runs on both frameworks. Shared test scenarios run on both adapters and must give the same result.
114
+
115
+ The library never reads `agent.yaml` and never builds the pipeline. Your speech-to-text, text-to-speech and LLM plugins stay yours.
116
+
117
+ ## Three ways to use it
118
+
119
+ | Way | What you write | Start here |
120
+ |---|---|---|
121
+ | One piece at a time | A plain `Agent` or pipeline, plus the pieces you want: tools, greeting, idle nudges, tracing | [standalone](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/standalone.mdx), [examples/minimal](https://github.com/slng-ai/unmute-core/tree/main/examples/minimal) |
122
+ | A whole agent | One `AgentSpec` with typed state, tasks and handoffs, run by `UnmuteAgent` on LiveKit or `UnmuteFlows` on Pipecat | [livekit](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/livekit.mdx), [pipecat](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/pipecat.mdx), [examples/salon-concierge-unmute](https://github.com/slng-ai/unmute-core/tree/main/examples/salon-concierge-unmute) |
123
+ | Compiled by Unmute | `agent.yaml` and Markdown prompts. Unmute writes the Python. | [Unmute](https://github.com/slng-ai/unmute), [examples/salon-concierge](https://github.com/slng-ai/unmute-core/tree/main/examples/salon-concierge) |
124
+
125
+ An agent can also be defined as data: state as type text like `Phone | None`, tasks and tools as JSON. See [examples/parcel-desk](https://github.com/slng-ai/unmute-core/tree/main/examples/parcel-desk).
126
+
127
+ Four whole agents show other shapes of call, each on LiveKit and Pipecat from one package, on SLNG speech and Gemini in the EU: a restaurant finder with tools only ([nosh](https://github.com/slng-ai/unmute-core/tree/main/examples/nosh)), a car claim intake ([kestrel](https://github.com/slng-ai/unmute-core/tree/main/examples/kestrel)), an outbound payment reminder ([wasla](https://github.com/slng-ai/unmute-core/tree/main/examples/wasla)) and a bilingual language tutor ([faro](https://github.com/slng-ai/unmute-core/tree/main/examples/faro)). See [example agents](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/overview.mdx) and [type the path](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/typed-path.mdx).
128
+
129
+ ## Why use it
130
+
131
+ LiveKit and Pipecat move audio, run the models and call your tools. They do not decide what a good phone call looks like. Every team learns that from its first real calls. unmute-core holds what we learned, so you start from it.
132
+
133
+ 1. **One agent, two frameworks.** State, tasks, tools, prompts and traces run the same on LiveKit and Pipecat. 90 test scenarios run on both frameworks, with plain and realtime models, and must give the same result.
134
+ 2. **The model cannot break your data.** Each value the model saves is checked against its type. A wrong value is refused with a reason the model can act on, like `caller_phone: expected a phone number with its country code`. The model asks the caller again.
135
+ 3. **Over 100 call optimizations, built in.** Some examples:
136
+
137
+ | On a call without it | What unmute-core does |
138
+ |---|---|
139
+ | A weak model says "Noted, thank you" and never finishes the step. | The finish check asks the model one narrow question: finish, or not yet. |
140
+ | The caller pauses, goes on, and the reply plays over them. | `yield_to_caller` drops that reply. The next one answers everything they said. |
141
+ | A slow tool leaves the caller in silence. | The tool says a line as it starts, and the caller's voice never cancels it. |
142
+ | A tool returns the wrong shape, and the model tells the caller it worked. | Every tool names the Pydantic model of its result. A result that does not fit fails the call, and the model reads which field is wrong. |
143
+ | The new agent hands the call straight back after a handoff. | Handoff tools are hidden on the first reply after a handoff. |
144
+ | A bad key is tried on every fallback model while the caller waits. | Errors that every model would give stop the fallback chain at once. |
145
+ | Gemini 3 refuses the next request after a tool call. | Thought signatures are kept on replayed tool calls. |
146
+ | The model or voice fails and the caller hears silence. | A short apology plays, then the call ends with a clear reason. |
147
+ | A webhook tool reaches `localhost` or a cloud metadata address. | Each webhook address must be public, and the connection is pinned to it. |
148
+ | Each framework traces differently, so dashboards break when you switch. | One trace shape on both, sent to Langfuse, Coval, Logfire or any OpenTelemetry collector. |
149
+
150
+ [Why unmute-core](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/why-unmute-core.mdx) follows one call from start to end. Each feature page then starts with what it fixes.
151
+
152
+ ## Install
153
+
154
+ Python 3.12 or newer. Pick the extra for your framework:
155
+
156
+ ```bash
157
+ uv add "unmute-core[livekit]"
158
+ ```
159
+
160
+ ```bash
161
+ uv add "unmute-core[pipecat]"
162
+ ```
163
+
164
+ | Extra | What it adds |
165
+ |---|---|
166
+ | `livekit` | The LiveKit adapter. LiveKit Agents 1.8.7 or newer within 1.8. |
167
+ | `pipecat` | The Pipecat adapter. Pipecat 1.12 or newer within 1.12. |
168
+ | `slng` | The `openai` package, for `OpenAIModelCall` and the SLNG Context Router as a model call. See [slng](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/slng.mdx). |
169
+ | `knowledge` | Search over a folder of documents, with llama-index. See [knowledge](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/knowledge.mdx). |
170
+
171
+ Everything works with your framework's own plugins and no SLNG package. [installation](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/installation.mdx) covers version ranges and the version check at import.
172
+
173
+ ## Docs
174
+
175
+ The docs tell one story. Start small, then go deeper where you need to.
176
+
177
+ 1. **Get started:** [why unmute-core](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/why-unmute-core.mdx), [installation](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/installation.mdx) and the [quickstart](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/quickstart.mdx).
178
+ 2. **Build a salon agent:** a [tutorial](https://github.com/slng-ai/unmute-core/blob/main/docs/core/tutorial/overview.mdx) that grows one agent, one piece per page. It ends at [examples/salon-concierge-unmute](https://github.com/slng-ai/unmute-core/tree/main/examples/salon-concierge-unmute).
179
+ 3. **On your framework:** [add one piece to your agent](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/standalone.mdx), or run a whole agent on [LiveKit](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/livekit.mdx) or [Pipecat](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/pipecat.mdx).
180
+ 4. **Guides:** [state](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/state.mdx), [tools](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/tools.mdx), [knowledge](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/knowledge.mdx), [tasks](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/tasks.mdx), [handoffs](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/handoffs.mdx), [call lifecycle](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/call-lifecycle.mdx), [prefetch](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/prefetch.mdx), [turn taking](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/turn-taking.mdx), [hooks](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/hooks.mdx), [model call](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/model-call.mdx), [slng](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/slng.mdx), [realtime](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/realtime.mdx), [telephony](https://github.com/slng-ai/unmute-core/blob/main/docs/core/telephony.mdx) and [telemetry](https://github.com/slng-ai/unmute-core/blob/main/docs/core/observe/telemetry.mdx).
181
+ 5. **Example agents:** [overview](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/overview.mdx), [type the path](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/typed-path.mdx), and one page each for [nosh](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/nosh.mdx), [kestrel](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/kestrel.mdx), [wasla](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/wasla.mdx) and [faro](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/faro.mdx).
182
+ 6. **Reference:** [settings](https://github.com/slng-ai/unmute-core/blob/main/docs/core/reference/settings.mdx) and [tasks](https://github.com/slng-ai/unmute-core/blob/main/docs/core/reference/tasks.mdx).
183
+
184
+ ## Versions
185
+
186
+ The library is 0.x. Code written for `0.14.x` runs on any later `0.14.x`. A new minor version, like `0.15.0`, may break it. `unmute_core.require()` checks this when your code imports.
187
+
188
+ [CHANGELOG.md](https://github.com/slng-ai/unmute-core/blob/main/CHANGELOG.md) lists what each release changed, and what can break you first.
189
+
190
+ ## Develop
191
+
192
+ ```bash
193
+ uv sync --all-extras
194
+ uv run pytest
195
+ uv run ruff check . && uv run ruff format --check .
196
+ uvx ty check
197
+ ```
198
+
199
+ [CONTRIBUTING.md](https://github.com/slng-ai/unmute-core/blob/main/CONTRIBUTING.md) explains how to send a change. [SECURITY.md](https://github.com/slng-ai/unmute-core/blob/main/SECURITY.md) explains how to report a security problem.
200
+
201
+ ## License
202
+
203
+ MIT. See [LICENSE](https://github.com/slng-ai/unmute-core/blob/main/LICENSE).
@@ -0,0 +1,160 @@
1
+ # unmute-core
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/unmute-core)](https://pypi.org/project/unmute-core/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/unmute-core)](https://pypi.org/project/unmute-core/)
5
+ [![CI](https://github.com/slng-ai/unmute-core/actions/workflows/ci.yml/badge.svg)](https://github.com/slng-ai/unmute-core/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/pypi/l/unmute-core)](https://github.com/slng-ai/unmute-core/blob/main/LICENSE)
7
+
8
+ unmute-core is a Python library for voice agents on [LiveKit Agents](https://github.com/livekit/agents) and [Pipecat](https://github.com/pipecat-ai/pipecat). You keep your framework, your speech and model plugins, and your pipeline. The library adds what a real phone or web call needs on top: typed call state, task steps, tools, handoffs, idle callers, transfers and tracing.
9
+
10
+ [Unmute](https://github.com/slng-ai/unmute) compiles an agent spec into a LiveKit or Pipecat project. Those projects import this library. You can also use it in an agent you write by hand, one piece at a time or all of it.
11
+
12
+ ## Example
13
+
14
+ A plain LiveKit agent with one tool from the library:
15
+
16
+ ```python
17
+ import os
18
+
19
+ from livekit.agents import Agent, AgentSession
20
+ from livekit.plugins import openai, silero, slng
21
+ from pydantic import BaseModel
22
+
23
+ from unmute_core.livekit import function_tools
24
+ from unmute_core.tools import ToolRegistry, local_tool
25
+
26
+
27
+ class OrderStatus(BaseModel):
28
+ order_id: str
29
+ status: str
30
+
31
+
32
+ def order_status(order_id: str) -> OrderStatus:
33
+ """Look up an order by its id. The return annotation is the result's checked shape."""
34
+ return OrderStatus(order_id=order_id, status="out for delivery")
35
+
36
+
37
+ registry = ToolRegistry([local_tool(order_status, announce="One moment, let me check.")])
38
+
39
+ agent = Agent(instructions="You help with orders.", tools=function_tools(registry))
40
+ slng_host = f"{os.environ['SLNG_WORLD_PART']}.api.slng.ai"
41
+ stt = slng.STT(slng_base_url=slng_host, model="soniox/speech-ai:rt-v5")
42
+ session = AgentSession(
43
+ stt=stt,
44
+ llm=openai.LLM(),
45
+ tts=slng.TTS(
46
+ slng_base_url=slng_host,
47
+ model="slng/fish/tts:s2.1-pro",
48
+ voice="8d8aec63a56c460cb7def795bdf754fa",
49
+ ),
50
+ vad=silero.VAD.load(),
51
+ )
52
+ stt.attach_to_session(session)
53
+ ```
54
+
55
+ The agent stays a normal LiveKit `Agent`. The library adds the tool and the line the agent says while the tool runs. Speech runs on SLNG: Soniox hears the caller and Fish Audio speaks, with your `SLNG_API_KEY`. On Pipecat, put `tools_schema(registry)` on the `LLMContext` instead.
56
+
57
+ ## How it fits
58
+
59
+ ```mermaid
60
+ flowchart TD
61
+ U[Unmute compiles agent.yaml] --> P[Your LiveKit or Pipecat project]
62
+ H[You write agent.py by hand] --> P
63
+ P -->|builds the pipeline, calls| A[unmute_core.livekit or unmute_core.pipecat]
64
+ A --> C[Core: state, tasks, tools, hooks, handoff, telemetry, model call]
65
+ ```
66
+
67
+ 1. Your project builds the pipeline. Unmute writes that project for you, or you write it yourself.
68
+ 2. The project calls an adapter: `unmute_core.livekit` or `unmute_core.pipecat`.
69
+ 3. The adapter turns framework events into calls to the core. The core never imports LiveKit or Pipecat.
70
+ 4. The same core runs on both frameworks. Shared test scenarios run on both adapters and must give the same result.
71
+
72
+ The library never reads `agent.yaml` and never builds the pipeline. Your speech-to-text, text-to-speech and LLM plugins stay yours.
73
+
74
+ ## Three ways to use it
75
+
76
+ | Way | What you write | Start here |
77
+ |---|---|---|
78
+ | One piece at a time | A plain `Agent` or pipeline, plus the pieces you want: tools, greeting, idle nudges, tracing | [standalone](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/standalone.mdx), [examples/minimal](https://github.com/slng-ai/unmute-core/tree/main/examples/minimal) |
79
+ | A whole agent | One `AgentSpec` with typed state, tasks and handoffs, run by `UnmuteAgent` on LiveKit or `UnmuteFlows` on Pipecat | [livekit](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/livekit.mdx), [pipecat](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/pipecat.mdx), [examples/salon-concierge-unmute](https://github.com/slng-ai/unmute-core/tree/main/examples/salon-concierge-unmute) |
80
+ | Compiled by Unmute | `agent.yaml` and Markdown prompts. Unmute writes the Python. | [Unmute](https://github.com/slng-ai/unmute), [examples/salon-concierge](https://github.com/slng-ai/unmute-core/tree/main/examples/salon-concierge) |
81
+
82
+ An agent can also be defined as data: state as type text like `Phone | None`, tasks and tools as JSON. See [examples/parcel-desk](https://github.com/slng-ai/unmute-core/tree/main/examples/parcel-desk).
83
+
84
+ Four whole agents show other shapes of call, each on LiveKit and Pipecat from one package, on SLNG speech and Gemini in the EU: a restaurant finder with tools only ([nosh](https://github.com/slng-ai/unmute-core/tree/main/examples/nosh)), a car claim intake ([kestrel](https://github.com/slng-ai/unmute-core/tree/main/examples/kestrel)), an outbound payment reminder ([wasla](https://github.com/slng-ai/unmute-core/tree/main/examples/wasla)) and a bilingual language tutor ([faro](https://github.com/slng-ai/unmute-core/tree/main/examples/faro)). See [example agents](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/overview.mdx) and [type the path](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/typed-path.mdx).
85
+
86
+ ## Why use it
87
+
88
+ LiveKit and Pipecat move audio, run the models and call your tools. They do not decide what a good phone call looks like. Every team learns that from its first real calls. unmute-core holds what we learned, so you start from it.
89
+
90
+ 1. **One agent, two frameworks.** State, tasks, tools, prompts and traces run the same on LiveKit and Pipecat. 90 test scenarios run on both frameworks, with plain and realtime models, and must give the same result.
91
+ 2. **The model cannot break your data.** Each value the model saves is checked against its type. A wrong value is refused with a reason the model can act on, like `caller_phone: expected a phone number with its country code`. The model asks the caller again.
92
+ 3. **Over 100 call optimizations, built in.** Some examples:
93
+
94
+ | On a call without it | What unmute-core does |
95
+ |---|---|
96
+ | A weak model says "Noted, thank you" and never finishes the step. | The finish check asks the model one narrow question: finish, or not yet. |
97
+ | The caller pauses, goes on, and the reply plays over them. | `yield_to_caller` drops that reply. The next one answers everything they said. |
98
+ | A slow tool leaves the caller in silence. | The tool says a line as it starts, and the caller's voice never cancels it. |
99
+ | A tool returns the wrong shape, and the model tells the caller it worked. | Every tool names the Pydantic model of its result. A result that does not fit fails the call, and the model reads which field is wrong. |
100
+ | The new agent hands the call straight back after a handoff. | Handoff tools are hidden on the first reply after a handoff. |
101
+ | A bad key is tried on every fallback model while the caller waits. | Errors that every model would give stop the fallback chain at once. |
102
+ | Gemini 3 refuses the next request after a tool call. | Thought signatures are kept on replayed tool calls. |
103
+ | The model or voice fails and the caller hears silence. | A short apology plays, then the call ends with a clear reason. |
104
+ | A webhook tool reaches `localhost` or a cloud metadata address. | Each webhook address must be public, and the connection is pinned to it. |
105
+ | Each framework traces differently, so dashboards break when you switch. | One trace shape on both, sent to Langfuse, Coval, Logfire or any OpenTelemetry collector. |
106
+
107
+ [Why unmute-core](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/why-unmute-core.mdx) follows one call from start to end. Each feature page then starts with what it fixes.
108
+
109
+ ## Install
110
+
111
+ Python 3.12 or newer. Pick the extra for your framework:
112
+
113
+ ```bash
114
+ uv add "unmute-core[livekit]"
115
+ ```
116
+
117
+ ```bash
118
+ uv add "unmute-core[pipecat]"
119
+ ```
120
+
121
+ | Extra | What it adds |
122
+ |---|---|
123
+ | `livekit` | The LiveKit adapter. LiveKit Agents 1.8.7 or newer within 1.8. |
124
+ | `pipecat` | The Pipecat adapter. Pipecat 1.12 or newer within 1.12. |
125
+ | `slng` | The `openai` package, for `OpenAIModelCall` and the SLNG Context Router as a model call. See [slng](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/slng.mdx). |
126
+ | `knowledge` | Search over a folder of documents, with llama-index. See [knowledge](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/knowledge.mdx). |
127
+
128
+ Everything works with your framework's own plugins and no SLNG package. [installation](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/installation.mdx) covers version ranges and the version check at import.
129
+
130
+ ## Docs
131
+
132
+ The docs tell one story. Start small, then go deeper where you need to.
133
+
134
+ 1. **Get started:** [why unmute-core](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/why-unmute-core.mdx), [installation](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/installation.mdx) and the [quickstart](https://github.com/slng-ai/unmute-core/blob/main/docs/core/start/quickstart.mdx).
135
+ 2. **Build a salon agent:** a [tutorial](https://github.com/slng-ai/unmute-core/blob/main/docs/core/tutorial/overview.mdx) that grows one agent, one piece per page. It ends at [examples/salon-concierge-unmute](https://github.com/slng-ai/unmute-core/tree/main/examples/salon-concierge-unmute).
136
+ 3. **On your framework:** [add one piece to your agent](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/standalone.mdx), or run a whole agent on [LiveKit](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/livekit.mdx) or [Pipecat](https://github.com/slng-ai/unmute-core/blob/main/docs/core/frameworks/pipecat.mdx).
137
+ 4. **Guides:** [state](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/state.mdx), [tools](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/tools.mdx), [knowledge](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/knowledge.mdx), [tasks](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/tasks.mdx), [handoffs](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/handoffs.mdx), [call lifecycle](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/call-lifecycle.mdx), [prefetch](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/prefetch.mdx), [turn taking](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/turn-taking.mdx), [hooks](https://github.com/slng-ai/unmute-core/blob/main/docs/core/build/hooks.mdx), [model call](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/model-call.mdx), [slng](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/slng.mdx), [realtime](https://github.com/slng-ai/unmute-core/blob/main/docs/core/models/realtime.mdx), [telephony](https://github.com/slng-ai/unmute-core/blob/main/docs/core/telephony.mdx) and [telemetry](https://github.com/slng-ai/unmute-core/blob/main/docs/core/observe/telemetry.mdx).
138
+ 5. **Example agents:** [overview](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/overview.mdx), [type the path](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/typed-path.mdx), and one page each for [nosh](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/nosh.mdx), [kestrel](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/kestrel.mdx), [wasla](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/wasla.mdx) and [faro](https://github.com/slng-ai/unmute-core/blob/main/docs/core/examples/faro.mdx).
139
+ 6. **Reference:** [settings](https://github.com/slng-ai/unmute-core/blob/main/docs/core/reference/settings.mdx) and [tasks](https://github.com/slng-ai/unmute-core/blob/main/docs/core/reference/tasks.mdx).
140
+
141
+ ## Versions
142
+
143
+ The library is 0.x. Code written for `0.14.x` runs on any later `0.14.x`. A new minor version, like `0.15.0`, may break it. `unmute_core.require()` checks this when your code imports.
144
+
145
+ [CHANGELOG.md](https://github.com/slng-ai/unmute-core/blob/main/CHANGELOG.md) lists what each release changed, and what can break you first.
146
+
147
+ ## Develop
148
+
149
+ ```bash
150
+ uv sync --all-extras
151
+ uv run pytest
152
+ uv run ruff check . && uv run ruff format --check .
153
+ uvx ty check
154
+ ```
155
+
156
+ [CONTRIBUTING.md](https://github.com/slng-ai/unmute-core/blob/main/CONTRIBUTING.md) explains how to send a change. [SECURITY.md](https://github.com/slng-ai/unmute-core/blob/main/SECURITY.md) explains how to report a security problem.
157
+
158
+ ## License
159
+
160
+ MIT. See [LICENSE](https://github.com/slng-ai/unmute-core/blob/main/LICENSE).
@@ -0,0 +1,119 @@
1
+ [project]
2
+ name = "unmute-core"
3
+ version = "0.14.0"
4
+ description = "Typed call state, tasks, tools, handoffs and tracing for voice agents on LiveKit and Pipecat"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ keywords = [
9
+ "voice agents",
10
+ "livekit",
11
+ "pipecat",
12
+ "llm",
13
+ "telephony",
14
+ "opentelemetry",
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
23
+ "Topic :: Software Development :: Libraries",
24
+ "Typing :: Typed",
25
+ ]
26
+ requires-python = ">=3.12"
27
+ dependencies = [
28
+ "babel>=2.16,<3",
29
+ "email-validator>=2.2,<3",
30
+ "httpx>=0.28,<1",
31
+ "jsonschema>=4.23,<5",
32
+ "opentelemetry-exporter-otlp-proto-http>=1.40,<2",
33
+ "opentelemetry-sdk>=1.40,<2",
34
+ "phonenumbers>=8.13,<10",
35
+ "pydantic>=2.11,<3",
36
+ "pydantic-settings>=2.8,<3",
37
+ ]
38
+
39
+ [[project.authors]]
40
+ name = "Nicola Croon"
41
+ email = "nicola@slng.ai"
42
+
43
+ [project.urls]
44
+ Repository = "https://github.com/slng-ai/unmute-core"
45
+ Documentation = "https://github.com/slng-ai/unmute-core/tree/main/docs"
46
+ Issues = "https://github.com/slng-ai/unmute-core/issues"
47
+ Changelog = "https://github.com/slng-ai/unmute-core/blob/main/CHANGELOG.md"
48
+
49
+ [project.optional-dependencies]
50
+ livekit = ["livekit-agents>=1.8.7,<1.9"]
51
+ pipecat = ["pipecat-ai>=1.12.0,<1.13"]
52
+ slng = ["openai>=1.109,<4"]
53
+ knowledge = [
54
+ "llama-index-core>=0.14,<0.15",
55
+ "llama-index-readers-file>=0.5,<0.8",
56
+ "llama-index-retrievers-bm25>=0.6,<0.9",
57
+ ]
58
+
59
+ [dependency-groups]
60
+ dev = [
61
+ "pipecat-ai[google]",
62
+ "pytest>=8",
63
+ "pytest-asyncio>=1",
64
+ "ruff>=0.15",
65
+ "ty",
66
+ ]
67
+
68
+ [build-system]
69
+ requires = ["uv_build>=0.12.18,<0.13.0"]
70
+ build-backend = "uv_build"
71
+
72
+ [tool.pytest.ini_options]
73
+ testpaths = ["tests"]
74
+ addopts = "--ignore=examples -m 'not live'"
75
+ markers = ["live: needs real API keys. Runs nightly and before a release: pytest -m live"]
76
+ asyncio_mode = "auto"
77
+
78
+ [tool.ruff]
79
+ line-length = 100
80
+ target-version = "py312"
81
+ extend-exclude = ["examples"]
82
+
83
+ [tool.ruff.lint]
84
+ select = [
85
+ "E",
86
+ "F",
87
+ "W",
88
+ "I",
89
+ "UP",
90
+ "B",
91
+ "SIM",
92
+ "C4",
93
+ "PERF",
94
+ "FURB",
95
+ "RUF",
96
+ "BLE",
97
+ "D",
98
+ "ANN",
99
+ "N",
100
+ ]
101
+ ignore = [
102
+ "E501",
103
+ "ANN401",
104
+ ]
105
+
106
+ [tool.ruff.lint.pydocstyle]
107
+ convention = "google"
108
+
109
+ [tool.ruff.lint.per-file-ignores]
110
+ "tests/**" = [
111
+ "D",
112
+ "ANN",
113
+ ]
114
+
115
+ [tool.ty.environment]
116
+ python-version = "3.12"
117
+
118
+ [tool.ty.src]
119
+ exclude = ["examples"]
@@ -0,0 +1,96 @@
1
+ [project]
2
+ name = "unmute-core"
3
+ version = "0.14.0"
4
+ description = "Typed call state, tasks, tools, handoffs and tracing for voice agents on LiveKit and Pipecat"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [
9
+ { name = "Nicola Croon", email = "nicola@slng.ai" }
10
+ ]
11
+ keywords = ["voice agents", "livekit", "pipecat", "llm", "telephony", "opentelemetry"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Topic :: Multimedia :: Sound/Audio :: Speech",
19
+ "Topic :: Software Development :: Libraries",
20
+ "Typing :: Typed",
21
+ ]
22
+ requires-python = ">=3.12"
23
+ dependencies = [
24
+ "babel>=2.16,<3",
25
+ "email-validator>=2.2,<3",
26
+ "httpx>=0.28,<1",
27
+ "jsonschema>=4.23,<5",
28
+ "opentelemetry-exporter-otlp-proto-http>=1.40,<2",
29
+ "opentelemetry-sdk>=1.40,<2",
30
+ "phonenumbers>=8.13,<10",
31
+ "pydantic>=2.11,<3",
32
+ "pydantic-settings>=2.8,<3",
33
+ ]
34
+
35
+ [project.urls]
36
+ Repository = "https://github.com/slng-ai/unmute-core"
37
+ Documentation = "https://github.com/slng-ai/unmute-core/tree/main/docs"
38
+ Issues = "https://github.com/slng-ai/unmute-core/issues"
39
+ Changelog = "https://github.com/slng-ai/unmute-core/blob/main/CHANGELOG.md"
40
+
41
+ [project.optional-dependencies]
42
+ # Each framework is on its newest release. Unmute's internal/target/driver.go follows.
43
+ # LiveKit 1.8.3 added ToolResult, which the adapter needs.
44
+ livekit = ["livekit-agents>=1.8.7,<1.9"]
45
+ pipecat = ["pipecat-ai>=1.12.0,<1.13"]
46
+ # openai 1.109 added `omit`. livekit-agents 1.8.7 needs openai 3, so LiveKit apps get it
47
+ # anyway; a Pipecat app may stay on openai 1 or 2.
48
+ slng = ["openai>=1.109,<4"]
49
+ # No embedding package: the caller passes its own llama-index embedder.
50
+ knowledge = [
51
+ "llama-index-core>=0.14,<0.15",
52
+ "llama-index-readers-file>=0.5,<0.8",
53
+ "llama-index-retrievers-bm25>=0.6,<0.9",
54
+ ]
55
+
56
+ [dependency-groups]
57
+ dev = [
58
+ # The Gemini service, for the side requests a Pipecat call sends it.
59
+ "pipecat-ai[google]",
60
+ "pytest>=8",
61
+ "pytest-asyncio>=1",
62
+ "ruff>=0.15",
63
+ "ty",
64
+ ]
65
+
66
+ [build-system]
67
+ requires = ["uv_build>=0.12.18,<0.13.0"]
68
+ build-backend = "uv_build"
69
+
70
+ [tool.pytest.ini_options]
71
+ testpaths = ["tests"]
72
+ addopts = "--ignore=examples -m 'not live'"
73
+ markers = ["live: needs real API keys. Runs nightly and before a release: pytest -m live"]
74
+ asyncio_mode = "auto"
75
+
76
+ # Each example project keeps the ruff and ty config Unmute emits for it.
77
+ [tool.ruff]
78
+ line-length = 100
79
+ target-version = "py312"
80
+ extend-exclude = ["examples"]
81
+
82
+ [tool.ruff.lint]
83
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "C4", "PERF", "FURB", "RUF", "BLE", "D", "ANN", "N"]
84
+ ignore = ["E501", "ANN401"]
85
+
86
+ [tool.ruff.lint.pydocstyle]
87
+ convention = "google"
88
+
89
+ [tool.ruff.lint.per-file-ignores]
90
+ "tests/**" = ["D", "ANN"]
91
+
92
+ [tool.ty.environment]
93
+ python-version = "3.12"
94
+
95
+ [tool.ty.src]
96
+ exclude = ["examples"]