livekit-agents 1.0.0.dev0__tar.gz → 1.0.0.dev2__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 (123) hide show
  1. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/PKG-INFO +3 -3
  2. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/__init__.py +4 -2
  3. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/_exceptions.py +9 -9
  4. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/cli/cli.py +42 -33
  5. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/cli/log.py +16 -13
  6. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/cli/proto.py +3 -0
  7. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/cli/watcher.py +2 -6
  8. livekit_agents-1.0.0.dev2/livekit/agents/debug/index.html +532 -0
  9. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/debug/tracing.py +2 -6
  10. livekit_agents-1.0.0.dev2/livekit/agents/http_server.py +37 -0
  11. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/inference_runner.py +1 -3
  12. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/channel.py +1 -3
  13. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/inference_proc_lazy_main.py +2 -6
  14. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/job_proc_executor.py +3 -9
  15. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/job_proc_lazy_main.py +21 -17
  16. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/job_thread_executor.py +5 -15
  17. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/log_queue.py +1 -3
  18. livekit_agents-1.0.0.dev2/livekit/agents/ipc/mock_room.py +12 -0
  19. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/proc_client.py +3 -9
  20. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/proc_pool.py +1 -5
  21. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/supervised_proc.py +6 -20
  22. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/job.py +25 -19
  23. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/__init__.py +4 -0
  24. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/_strict.py +6 -18
  25. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/chat_context.py +3 -7
  26. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/fallback_adapter.py +61 -23
  27. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/function_context.py +1 -3
  28. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/llm.py +2 -6
  29. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/realtime.py +21 -7
  30. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/remote_chat_context.py +4 -3
  31. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/llm/utils.py +10 -13
  32. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/metrics/__init__.py +2 -14
  33. livekit_agents-1.0.0.dev2/livekit/agents/metrics/base.py +129 -0
  34. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/metrics/utils.py +0 -4
  35. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/stt/fallback_adapter.py +8 -25
  36. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/stt/stream_adapter.py +3 -9
  37. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/stt/stt.py +17 -15
  38. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/_basic_sent.py +1 -3
  39. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/_basic_word.py +1 -3
  40. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/basic.py +2 -6
  41. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tts/fallback_adapter.py +25 -51
  42. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tts/stream_adapter.py +1 -3
  43. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tts/tts.py +50 -31
  44. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/types.py +19 -1
  45. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/__init__.py +2 -0
  46. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/audio.py +1 -3
  47. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/codecs/decoder.py +31 -12
  48. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/codecs/mp3.py +1 -3
  49. livekit_agents-1.0.0.dev2/livekit/agents/utils/connection_pool.py +152 -0
  50. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/version.py +1 -1
  51. livekit_agents-1.0.0.dev2/livekit/agents/voice/__init__.py +32 -0
  52. livekit_agents-1.0.0.dev0/livekit/agents/pipeline/task.py → livekit_agents-1.0.0.dev2/livekit/agents/voice/agent_task.py +108 -15
  53. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/audio_recognition.py +6 -14
  54. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/avatar/_runner.py +8 -16
  55. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/chat_cli.py +91 -41
  56. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/datastream_io.py +11 -27
  57. livekit_agents-1.0.0.dev2/livekit/agents/voice/events.py +107 -0
  58. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/generation.py +47 -19
  59. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/io.py +135 -10
  60. livekit_agents-1.0.0.dev2/livekit/agents/voice/room_io.py +949 -0
  61. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/speech_handle.py +2 -4
  62. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/task_activity.py +197 -69
  63. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/transcription/_utils.py +1 -3
  64. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/transcription/synchronizer.py +75 -73
  65. livekit_agents-1.0.0.dev0/livekit/agents/pipeline/pipeline_agent.py → livekit_agents-1.0.0.dev2/livekit/agents/voice/voice_agent.py +149 -78
  66. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/worker.py +52 -88
  67. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit_agents.egg-info/PKG-INFO +3 -3
  68. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit_agents.egg-info/SOURCES.txt +19 -17
  69. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit_agents.egg-info/requires.txt +2 -4
  70. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/setup.py +3 -7
  71. livekit_agents-1.0.0.dev0/livekit/agents/debug/index.html +0 -518
  72. livekit_agents-1.0.0.dev0/livekit/agents/http_server.py +0 -35
  73. livekit_agents-1.0.0.dev0/livekit/agents/metrics/base.py +0 -143
  74. livekit_agents-1.0.0.dev0/livekit/agents/pipeline/__init__.py +0 -15
  75. livekit_agents-1.0.0.dev0/livekit/agents/pipeline/events.py +0 -26
  76. livekit_agents-1.0.0.dev0/livekit/agents/pipeline/room_io.py +0 -471
  77. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/README.md +0 -0
  78. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/cli/__init__.py +0 -0
  79. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/debug/__init__.py +0 -0
  80. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/__init__.py +0 -0
  81. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/inference_executor.py +0 -0
  82. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/inference_proc_executor.py +0 -0
  83. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/job_executor.py +0 -0
  84. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/ipc/proto.py +0 -0
  85. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/log.py +0 -0
  86. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/metrics/usage_collector.py +0 -0
  87. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/plugin.py +0 -0
  88. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/py.typed +0 -0
  89. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/stt/__init__.py +0 -0
  90. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/__init__.py +0 -0
  91. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/_basic_hyphenator.py +0 -0
  92. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/_basic_paragraph.py +0 -0
  93. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/token_stream.py +0 -0
  94. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/tokenizer.py +0 -0
  95. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tokenize/utils.py +0 -0
  96. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/tts/__init__.py +0 -0
  97. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/__init__.py +0 -0
  98. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/channel.py +0 -0
  99. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/debug.py +0 -0
  100. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/duplex_unix.py +0 -0
  101. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/interval.py +0 -0
  102. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/itertools.py +0 -0
  103. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/sleep.py +0 -0
  104. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/task_set.py +0 -0
  105. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/utils.py +0 -0
  106. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/aio/wait_group.py +0 -0
  107. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/codecs/__init__.py +0 -0
  108. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/exp_filter.py +0 -0
  109. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/http_context.py +0 -0
  110. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/hw/__init__.py +0 -0
  111. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/hw/cpu.py +0 -0
  112. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/images/__init__.py +0 -0
  113. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/images/image.py +0 -0
  114. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/log.py +0 -0
  115. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/misc.py +0 -0
  116. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/utils/moving_average.py +0 -0
  117. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit/agents/vad.py +0 -0
  118. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/avatar/__init__.py +0 -0
  119. {livekit_agents-1.0.0.dev0/livekit/agents/pipeline → livekit_agents-1.0.0.dev2/livekit/agents/voice}/transcription/__init__.py +0 -0
  120. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit_agents.egg-info/dependency_links.txt +0 -0
  121. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/livekit_agents.egg-info/top_level.txt +0 -0
  122. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/pyproject.toml +0 -0
  123. {livekit_agents-1.0.0.dev0 → livekit_agents-1.0.0.dev2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: livekit-agents
3
- Version: 1.0.0.dev0
3
+ Version: 1.0.0.dev2
4
4
  Summary: LiveKit Python Agents
5
5
  Home-page: https://github.com/livekit/agents
6
6
  License: Apache-2.0
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
20
20
  Requires-Python: >=3.9.0
21
21
  Description-Content-Type: text/markdown
22
22
  Requires-Dist: click~=8.1
23
- Requires-Dist: livekit>=0.20.0
23
+ Requires-Dist: livekit>=0.21.1
24
24
  Requires-Dist: livekit-api~=0.8.2
25
25
  Requires-Dist: livekit-protocol~=0.8.2
26
26
  Requires-Dist: protobuf>=3
@@ -30,8 +30,8 @@ Requires-Dist: watchfiles~=0.22
30
30
  Requires-Dist: psutil~=5.9
31
31
  Requires-Dist: aiohttp~=3.10
32
32
  Requires-Dist: typing-extensions~=4.12
33
+ Requires-Dist: sounddevice>=0.5
33
34
  Requires-Dist: colorama; sys_platform == "win32"
34
- Requires-Dist: aiodns~=3.2; sys_platform != "win32"
35
35
  Provides-Extra: codecs
36
36
  Requires-Dist: av>=12.0.0; extra == "codecs"
37
37
  Requires-Dist: numpy>=1.26.0; extra == "codecs"
@@ -17,12 +17,12 @@ from . import (
17
17
  ipc,
18
18
  llm,
19
19
  metrics,
20
- pipeline,
21
20
  stt,
22
21
  tokenize,
23
22
  tts,
24
23
  utils,
25
24
  vad,
25
+ voice,
26
26
  )
27
27
  from ._exceptions import (
28
28
  APIConnectionError,
@@ -43,6 +43,7 @@ from .types import (
43
43
  NotGivenOr,
44
44
  )
45
45
  from .version import __version__
46
+ from .voice import VoiceAgent
46
47
  from .worker import Worker, WorkerOptions, WorkerPermissions, WorkerType
47
48
 
48
49
  __all__ = [
@@ -66,7 +67,8 @@ __all__ = [
66
67
  "tokenize",
67
68
  "llm",
68
69
  "metrics",
69
- "pipeline",
70
+ "voice",
71
+ "VoiceAgent",
70
72
  "cli",
71
73
  "AssignmentTimeoutError",
72
74
  "APIConnectionError",
@@ -31,9 +31,7 @@ class APIError(Exception):
31
31
  retryable: bool = False
32
32
  """Whether the error can be retried."""
33
33
 
34
- def __init__(
35
- self, message: str, *, body: object | None, retryable: bool = True
36
- ) -> None:
34
+ def __init__(self, message: str, *, body: object | None, retryable: bool = True) -> None:
37
35
  super().__init__(message)
38
36
 
39
37
  self.message = message
@@ -70,20 +68,22 @@ class APIStatusError(APIError):
70
68
  self.status_code = status_code
71
69
  self.request_id = request_id
72
70
 
71
+ def __str__(self):
72
+ return (
73
+ f"{self.message} "
74
+ f"(status_code={self.status_code}, request_id={self.request_id}, body={self.body})"
75
+ )
76
+
73
77
 
74
78
  class APIConnectionError(APIError):
75
79
  """Raised when an API request failed due to a connection error."""
76
80
 
77
- def __init__(
78
- self, message: str = "Connection error.", *, retryable: bool = True
79
- ) -> None:
81
+ def __init__(self, message: str = "Connection error.", *, retryable: bool = True) -> None:
80
82
  super().__init__(message, body=None, retryable=retryable)
81
83
 
82
84
 
83
85
  class APITimeoutError(APIConnectionError):
84
86
  """Raised when an API request timed out."""
85
87
 
86
- def __init__(
87
- self, message: str = "Request timed out.", *, retryable: bool = True
88
- ) -> None:
88
+ def __init__(self, message: str = "Request timed out.", *, retryable: bool = True) -> None:
89
89
  super().__init__(message, retryable=retryable)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import pathlib
3
5
  import signal
@@ -14,6 +16,12 @@ from ..worker import JobExecutorType, Worker, WorkerOptions
14
16
  from . import proto
15
17
  from .log import setup_logging
16
18
 
19
+ CLI_ARGUMENTS: proto.CliArgs | None = None
20
+
21
+
22
+ def _esc(*codes: int) -> str:
23
+ return "\033[" + ";".join(str(c) for c in codes) + "m"
24
+
17
25
 
18
26
  def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) -> None:
19
27
  """Run the CLI to interact with the worker"""
@@ -23,9 +31,7 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
23
31
  @click.option(
24
32
  "--log-level",
25
33
  default="INFO",
26
- type=click.Choice(
27
- ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False
28
- ),
34
+ type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False),
29
35
  help="Set the logging level",
30
36
  )
31
37
  @click.option(
@@ -48,9 +54,7 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
48
54
  default=60,
49
55
  help="Time in seconds to wait for jobs to finish before shutting down",
50
56
  )
51
- def start(
52
- log_level: str, url: str, api_key: str, api_secret: str, drain_timeout: int
53
- ) -> None:
57
+ def start(log_level: str, url: str, api_key: str, api_secret: str, drain_timeout: int) -> None:
54
58
  opts.ws_url = url or opts.ws_url
55
59
  opts.api_key = api_key or opts.api_key
56
60
  opts.api_secret = api_secret or opts.api_secret
@@ -69,9 +73,7 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
69
73
  @click.option(
70
74
  "--log-level",
71
75
  default="DEBUG",
72
- type=click.Choice(
73
- ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False
74
- ),
76
+ type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False),
75
77
  help="Set the logging level",
76
78
  )
77
79
  @click.option(
@@ -138,7 +140,7 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
138
140
  envvar="LIVEKIT_API_SECRET",
139
141
  help="LiveKit server or Cloud project's API secret",
140
142
  )
141
- def chat(
143
+ def console(
142
144
  url: str,
143
145
  api_key: str,
144
146
  api_secret: str,
@@ -149,29 +151,26 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
149
151
  opts.api_key = api_key or opts.api_key
150
152
  opts.api_secret = api_secret or opts.api_secret
151
153
 
152
- chat_name = utils.shortuuid("chat_cli_")
153
-
154
154
  args = proto.CliArgs(
155
155
  opts=opts,
156
- log_level="ERROR",
156
+ log_level="INFO",
157
157
  devmode=True,
158
158
  asyncio_debug=False,
159
159
  watch=False,
160
+ console=True,
160
161
  drain_timeout=0,
161
162
  register=False,
162
163
  simulate_job=proto.SimulateJobArgs(
163
- room=chat_name,
164
+ room="mock-console",
164
165
  ),
165
166
  )
166
- _run_dev(args)
167
+ run_worker(args)
167
168
 
168
169
  @cli.command(help="Connect to a specific room")
169
170
  @click.option(
170
171
  "--log-level",
171
172
  default="DEBUG",
172
- type=click.Choice(
173
- ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False
174
- ),
173
+ type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False),
175
174
  help="Set the logging level",
176
175
  )
177
176
  @click.option(
@@ -200,9 +199,7 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
200
199
  help="Watch for changes in the current directory and plugins in editable mode",
201
200
  )
202
201
  @click.option("--room", help="Room name to connect to", required=True)
203
- @click.option(
204
- "--participant-identity", help="Participant identity (JobType.JT_PUBLISHER)"
205
- )
202
+ @click.option("--participant-identity", help="Participant identity (JobType.JT_PUBLISHER)")
206
203
  def connect(
207
204
  log_level: str,
208
205
  url: str,
@@ -236,13 +233,11 @@ def run_app(opts: WorkerOptions, *, hot_reload: NotGivenOr[bool] = NOT_GIVEN) ->
236
233
  @click.option(
237
234
  "--log-level",
238
235
  default="DEBUG",
239
- type=click.Choice(
240
- ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False
241
- ),
236
+ type=click.Choice(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], case_sensitive=False),
242
237
  help="Set the logging level",
243
238
  )
244
239
  def download_files(log_level: str) -> None:
245
- setup_logging(log_level, True)
240
+ setup_logging(log_level, True, False)
246
241
 
247
242
  for plugin in Plugin.registered_plugins:
248
243
  logger.info(f"Downloading files for {plugin}")
@@ -258,13 +253,11 @@ def _run_dev(
258
253
  if args.watch:
259
254
  from .watcher import WatchServer
260
255
 
261
- setup_logging(args.log_level, args.devmode)
256
+ setup_logging(args.log_level, args.devmode, args.console)
262
257
  main_file = pathlib.Path(sys.argv[0]).parent
263
258
 
264
259
  async def _run_loop():
265
- server = WatchServer(
266
- run_worker, main_file, args, loop=asyncio.get_event_loop()
267
- )
260
+ server = WatchServer(run_worker, main_file, args, loop=asyncio.get_event_loop())
268
261
  await server.run()
269
262
 
270
263
  try:
@@ -276,12 +269,21 @@ def _run_dev(
276
269
 
277
270
 
278
271
  def run_worker(args: proto.CliArgs) -> None:
279
- setup_logging(args.log_level, args.devmode)
272
+ global CLI_ARGUMENTS
273
+ CLI_ARGUMENTS = args
274
+
275
+ setup_logging(args.log_level, args.devmode, args.console)
280
276
  args.opts.validate_config(args.devmode)
281
277
 
282
278
  loop = asyncio.new_event_loop()
283
279
  asyncio.set_event_loop(loop)
284
280
 
281
+ if args.console:
282
+ print(_esc(34) + "=" * 50 + _esc(0))
283
+ print(_esc(34) + " Livekit Agents - Console" + _esc(0))
284
+ print(_esc(34) + "=" * 50 + _esc(0))
285
+ print("Press [Ctrl+B] to toggle between Text/Audio mode, [Q] to quit.\n")
286
+
285
287
  worker = Worker(args.opts, devmode=args.devmode, register=args.register, loop=loop)
286
288
 
287
289
  loop.set_debug(args.asyncio_debug)
@@ -293,9 +295,16 @@ def run_worker(args: proto.CliArgs) -> None:
293
295
  if args.simulate_job and args.reload_count == 0:
294
296
  logger.info("connecting to room %s", args.simulate_job.room)
295
297
  loop.create_task(
296
- worker.simulate_job(
297
- args.simulate_job.room, args.simulate_job.participant_identity
298
- )
298
+ worker.simulate_job(args.simulate_job.room, args.simulate_job.participant_identity)
299
+ )
300
+
301
+ if args.devmode:
302
+ logger.info(
303
+ f"{_esc(1)}see tracing information at http://localhost:{worker.worker_info.http_port}/debug{_esc(0)}"
304
+ )
305
+ else:
306
+ logger.info(
307
+ f"see tracing information at http://localhost:{worker.worker_info.http_port}/debug"
299
308
  )
300
309
 
301
310
  try:
@@ -19,6 +19,8 @@ NOISY_LOGGERS = [
19
19
  "watchfiles",
20
20
  "anthropic",
21
21
  "websockets.client",
22
+ "aiohttp.access",
23
+ "livekit",
22
24
  ]
23
25
 
24
26
 
@@ -60,9 +62,7 @@ _RESERVED_ATTRS: Tuple[str, ...] = (
60
62
 
61
63
  def _merge_record_extra(record: logging.LogRecord, target: Dict[Any, Any]):
62
64
  for key, value in record.__dict__.items():
63
- if key not in _RESERVED_ATTRS and not (
64
- hasattr(key, "startswith") and key.startswith("_")
65
- ):
65
+ if key not in _RESERVED_ATTRS and not (hasattr(key, "startswith") and key.startswith("_")):
66
66
  target[key] = value
67
67
 
68
68
 
@@ -136,9 +136,7 @@ class JsonFormatter(logging.Formatter):
136
136
  log_record.update(message_dict)
137
137
  _merge_record_extra(record, log_record)
138
138
 
139
- log_record["timestamp"] = datetime.fromtimestamp(
140
- record.created, tz=timezone.utc
141
- )
139
+ log_record["timestamp"] = datetime.fromtimestamp(record.created, tz=timezone.utc)
142
140
 
143
141
  return json.dumps(log_record, cls=JsonFormatter.JsonEncoder, ensure_ascii=True)
144
142
 
@@ -188,9 +186,7 @@ class ColoredFormatter(logging.Formatter):
188
186
  args.update(self._esc_codes)
189
187
 
190
188
  if extra:
191
- args["extra"] = json.dumps(
192
- extra, cls=JsonFormatter.JsonEncoder, ensure_ascii=True
193
- )
189
+ args["extra"] = json.dumps(extra, cls=JsonFormatter.JsonEncoder, ensure_ascii=True)
194
190
 
195
191
  for field in self._required_fields:
196
192
  if field in extra:
@@ -200,14 +196,21 @@ class ColoredFormatter(logging.Formatter):
200
196
  return msg + self._esc_codes["esc_reset"]
201
197
 
202
198
 
203
- def setup_logging(log_level: str, devmode: bool) -> None:
199
+ def setup_logging(log_level: str, devmode: bool, console: bool) -> None:
204
200
  handler = logging.StreamHandler()
205
201
 
206
202
  if devmode:
207
203
  # colorful logs for dev (improves readability)
208
- colored_formatter = ColoredFormatter(
209
- "%(asctime)s - %(esc_levelcolor)s%(levelname)-4s%(esc_reset)s %(name)s - %(message)s %(esc_gray)s%(extra)s"
210
- )
204
+ if console:
205
+ # reset the line before each log message
206
+ colored_formatter = ColoredFormatter(
207
+ "\r%(asctime)s - %(esc_levelcolor)s%(levelname)-4s%(esc_reset)s %(name)s - %(message)s %(esc_gray)s%(extra)s"
208
+ )
209
+ else:
210
+ colored_formatter = ColoredFormatter(
211
+ "%(asctime)s - %(esc_levelcolor)s%(levelname)-4s%(esc_reset)s %(name)s - %(message)s %(esc_gray)s%(extra)s"
212
+ )
213
+
211
214
  handler.setFormatter(colored_formatter)
212
215
  else:
213
216
  # production logs (serialized of json)
@@ -27,6 +27,9 @@ class CliArgs:
27
27
  watch: bool
28
28
  drain_timeout: int
29
29
 
30
+ console: bool = False
31
+ # whether to run the worker in console mode (console subcommand
32
+
30
33
  # register the worker to the worker pool
31
34
  register: bool = True
32
35
 
@@ -126,9 +126,7 @@ class WatchServer:
126
126
  with contextlib.suppress(asyncio.InvalidStateError):
127
127
  self._recv_jobs_fut.set_result(None)
128
128
  if isinstance(msg, proto.ReloadJobsRequest):
129
- await channel.asend_message(
130
- self._pch, proto.ReloadJobsResponse(jobs=active_jobs)
131
- )
129
+ await channel.asend_message(self._pch, proto.ReloadJobsResponse(jobs=active_jobs))
132
130
  if isinstance(msg, proto.Reloaded):
133
131
  self._worker_reloading = False
134
132
 
@@ -151,9 +149,7 @@ class WatchClient:
151
149
  async def _run(self) -> None:
152
150
  assert self._cli_args.mp_cch
153
151
  try:
154
- self._cch = await utils.aio.duplex_unix._AsyncDuplex.open(
155
- self._cli_args.mp_cch
156
- )
152
+ self._cch = await utils.aio.duplex_unix._AsyncDuplex.open(self._cli_args.mp_cch)
157
153
 
158
154
  await channel.asend_message(self._cch, proto.ReloadJobsRequest())
159
155