Updating foundation examples to use SmallWebRTCTransport and pipecat-ai-small-webrtc-prebuilt (#1534)
Co-authored-by: Filipi Fuchter <filipi@daily.co>
This commit is contained in:
@@ -4,54 +4,54 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.services.piper.tts import PiperTTSService
|
from pipecat.services.piper.tts import PiperTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Create a transport using the WebRTC connection
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url, None, "Say One Thing", DailyParams(audio_out_enabled=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = PiperTTSService(
|
tts = PiperTTSService(
|
||||||
base_url=os.getenv("PIPER_BASE_URL"), aiohttp_session=session, sample_rate=24000
|
base_url=os.getenv("PIPER_BASE_URL"), aiohttp_session=session, sample_rate=24000
|
||||||
)
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
task = PipelineTask(Pipeline([tts, transport.output()]))
|
task = PipelineTask(Pipeline([tts, transport.output()]))
|
||||||
|
|
||||||
# Register an event handler so we can play the audio when the
|
# Register an event handler so we can play the audio when the client joins
|
||||||
# participant joins.
|
@transport.event_handler("on_client_connected")
|
||||||
@transport.event_handler("on_first_participant_joined")
|
async def on_client_connected(transport, client):
|
||||||
async def on_first_participant_joined(transport, participant):
|
await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()])
|
||||||
await task.queue_frames(
|
|
||||||
[TTSSpeakFrame(f"Hello there, how are you today ?"), EndFrame()]
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
)
|
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
59
examples/foundational/01-say-one-thing-rime.py
Normal file
59
examples/foundational/01-say-one-thing-rime.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024–2025, Daily
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
||||||
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
|
from pipecat.pipeline.task import PipelineTask
|
||||||
|
from pipecat.services.rime.tts import RimeHttpTTSService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
|
# Create a transport using the WebRTC connection
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
tts = RimeHttpTTSService(
|
||||||
|
api_key=os.getenv("RIME_API_KEY", ""),
|
||||||
|
voice_id="rex",
|
||||||
|
aiohttp_session=session,
|
||||||
|
)
|
||||||
|
|
||||||
|
task = PipelineTask(Pipeline([tts, transport.output()]))
|
||||||
|
|
||||||
|
# Register an event handler so we can play the audio when the client joins
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()])
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -4,56 +4,52 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Create a transport using the WebRTC connection
|
||||||
async with aiohttp.ClientSession() as session:
|
transport = SmallWebRTCTransport(
|
||||||
(room_url, _) = await configure(session)
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
tts = CartesiaTTSService(
|
||||||
room_url, None, "Say One Thing", DailyParams(audio_out_enabled=True)
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
task = PipelineTask(Pipeline([tts, transport.output()]))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
# Register an event handler so we can play the audio when the client joins
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()])
|
||||||
|
|
||||||
task = PipelineTask(Pipeline([tts, transport.output()]))
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
# Register an event handler so we can play the audio when the
|
await runner.run(task)
|
||||||
# participant joins.
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
participant_name = participant.get("info", {}).get("userName", "")
|
|
||||||
await task.queue_frames(
|
|
||||||
[TTSSpeakFrame(f"Hello there, {participant_name}!"), EndFrame()]
|
|
||||||
)
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,51 +4,49 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
from pipecat.frames.frames import EndFrame, TTSSpeakFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.services.riva.tts import FastPitchTTSService
|
from pipecat.services.riva.tts import FastPitchTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Create a transport using the WebRTC connection
|
||||||
async with aiohttp.ClientSession() as session:
|
transport = SmallWebRTCTransport(
|
||||||
(room_url, _) = await configure(session)
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
tts = FastPitchTTSService(api_key=os.getenv("NVIDIA_API_KEY"))
|
||||||
room_url, None, "Say One Thing", DailyParams(audio_out_enabled=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = FastPitchTTSService(api_key=os.getenv("NVIDIA_API_KEY"))
|
task = PipelineTask(Pipeline([tts, transport.output()]))
|
||||||
|
|
||||||
runner = PipelineRunner()
|
# Register an event handler so we can play the audio when the client joins
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
await task.queue_frames([TTSSpeakFrame(f"Hello there!"), EndFrame()])
|
||||||
|
|
||||||
task = PipelineTask(Pipeline([tts, transport.output()]))
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
# Register an event handler so we can play the audio when the
|
await runner.run(task)
|
||||||
# participant joins.
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
participant_name = participant.get("info", {}).get("userName", "")
|
|
||||||
await task.queue_frames([TTSSpeakFrame(f"Aloha, {participant_name}!"), EndFrame()])
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import EndFrame, LLMMessagesFrame
|
from pipecat.frames.frames import EndFrame, LLMMessagesFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -19,46 +15,51 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Create a transport using the WebRTC connection
|
||||||
async with aiohttp.ClientSession() as session:
|
transport = SmallWebRTCTransport(
|
||||||
(room_url, _) = await configure(session)
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
tts = CartesiaTTSService(
|
||||||
room_url, None, "Say One Thing From an LLM", DailyParams(audio_out_enabled=True)
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are an LLM in a WebRTC session, and this is a 'hello world' demo. Say hello to the world.",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
task = PipelineTask(Pipeline([llm, tts, transport.output()]))
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are an LLM in a WebRTC session, and this is a 'hello world' demo. Say hello to the world.",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
# Register an event handler so we can play the audio when the client joins
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
await task.queue_frames([LLMMessagesFrame(messages), EndFrame()])
|
||||||
|
|
||||||
task = PipelineTask(Pipeline([llm, tts, transport.output()]))
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
await runner.run(task)
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await task.queue_frames([LLMMessagesFrame(messages), EndFrame()])
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,59 +4,67 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import TextFrame
|
from pipecat.frames.frames import TextFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.services.fal.image import FalImageGenService
|
from pipecat.services.fal.image import FalImageGenService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Create a transport using the WebRTC connection
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
camera_out_enabled=True,
|
||||||
|
camera_out_width=1024,
|
||||||
|
camera_out_height=1024,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
None,
|
|
||||||
"Show a still frame image",
|
|
||||||
DailyParams(camera_out_enabled=True, camera_out_width=1024, camera_out_height=1024),
|
|
||||||
)
|
|
||||||
|
|
||||||
imagegen = FalImageGenService(
|
imagegen = FalImageGenService(
|
||||||
params=FalImageGenService.InputParams(image_size="square_hd"),
|
params=FalImageGenService.InputParams(image_size="square_hd"),
|
||||||
aiohttp_session=session,
|
aiohttp_session=session,
|
||||||
key=os.getenv("FAL_KEY"),
|
key=os.getenv("FAL_KEY"),
|
||||||
)
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
task = PipelineTask(Pipeline([imagegen, transport.output()]))
|
task = PipelineTask(Pipeline([imagegen, transport.output()]))
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
# Register an event handler so we can play the audio when the client joins
|
||||||
async def on_first_participant_joined(transport, participant):
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
await task.queue_frame(TextFrame("a cat in the style of picasso"))
|
await task.queue_frame(TextFrame("a cat in the style of picasso"))
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_participant_left(transport, participant, reason):
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
await task.cancel()
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,62 +4,67 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import EndFrame, TextFrame
|
from pipecat.frames.frames import TextFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.services.google.image import GoogleImageGenService
|
from pipecat.services.google.image import GoogleImageGenService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Create a transport using the WebRTC connection
|
||||||
async with aiohttp.ClientSession() as session:
|
transport = SmallWebRTCTransport(
|
||||||
(room_url, _) = await configure(session)
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
camera_out_enabled=True,
|
||||||
|
camera_out_width=1024,
|
||||||
|
camera_out_height=1024,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
imagegen = GoogleImageGenService(
|
||||||
room_url,
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
None,
|
)
|
||||||
"Show a still frame image",
|
|
||||||
DailyParams(camera_out_enabled=True, camera_out_width=1024, camera_out_height=1024),
|
|
||||||
)
|
|
||||||
|
|
||||||
imagegen = GoogleImageGenService(
|
task = PipelineTask(
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
Pipeline([imagegen, transport.output()]),
|
||||||
)
|
params=PipelineParams(enable_metrics=True),
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
# Register an event handler so we can play the audio when the client joins
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
await task.queue_frame(TextFrame("a cat in the style of picasso"))
|
||||||
|
await task.queue_frame(TextFrame("a dog in the style of picasso"))
|
||||||
|
await task.queue_frame(TextFrame("a fish in the style of picasso"))
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
Pipeline([imagegen, transport.output()]),
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(enable_metrics=True),
|
logger.info(f"Client disconnected")
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await task.queue_frame(TextFrame("a cat in the style of picasso"))
|
logger.info(f"Client closed connection")
|
||||||
await task.queue_frame(TextFrame("a dog in the style of picasso"))
|
await task.cancel()
|
||||||
await task.queue_frame(TextFrame("a fish in the style of picasso"))
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.queue_frame(EndFrame())
|
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from daily_runner import configure
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import EndPipeFrame, LLMMessagesFrame, TextFrame
|
from pipecat.frames.frames import EndPipeFrame, LLMMessagesFrame, TextFrame
|
||||||
from pipecat.pipeline.merge_pipeline import SequentialMergePipeline
|
from pipecat.pipeline.merge_pipeline import SequentialMergePipeline
|
||||||
|
|||||||
@@ -4,15 +4,12 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
DataFrame,
|
DataFrame,
|
||||||
@@ -30,13 +27,12 @@ from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
|||||||
from pipecat.services.cartesia.tts import CartesiaHttpTTSService
|
from pipecat.services.cartesia.tts import CartesiaHttpTTSService
|
||||||
from pipecat.services.fal.image import FalImageGenService
|
from pipecat.services.fal.image import FalImageGenService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MonthFrame(DataFrame):
|
class MonthFrame(DataFrame):
|
||||||
@@ -67,22 +63,28 @@ class MonthPrepender(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
"""Run the Calendar Month Narration bot using WebRTC transport.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
webrtc_connection: The WebRTC connection to use
|
||||||
|
room_name: Optional room name for display purposes
|
||||||
|
"""
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
|
# Create a transport using the WebRTC connection
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_out_enabled=True,
|
||||||
|
camera_out_width=1024,
|
||||||
|
camera_out_height=1024,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session for API calls
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
None,
|
|
||||||
"Month Narration Bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
camera_out_enabled=True,
|
|
||||||
camera_out_width=1024,
|
|
||||||
camera_out_height=1024,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
tts = CartesiaHttpTTSService(
|
tts = CartesiaHttpTTSService(
|
||||||
@@ -144,14 +146,30 @@ async def main():
|
|||||||
frames.append(MonthFrame(month=month))
|
frames.append(MonthFrame(month=month))
|
||||||
frames.append(LLMMessagesFrame(messages))
|
frames.append(LLMMessagesFrame(messages))
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
await task.queue_frames(frames)
|
# Set up transport event handlers
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Start the month narration once connected
|
||||||
|
await task.queue_frames(frames)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
# Run the pipeline
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import Frame, MetricsFrame
|
from pipecat.frames.frames import Frame, MetricsFrame
|
||||||
@@ -27,14 +23,14 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class MetricsLogger(FrameProcessor):
|
class MetricsLogger(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -56,76 +52,83 @@ class MetricsLogger(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
ml = MetricsLogger()
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
ml = MetricsLogger()
|
||||||
{
|
|
||||||
"role": "system",
|
messages = [
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
{
|
||||||
},
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
ml,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
ml,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
await runner.run(task)
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
@@ -20,7 +16,6 @@ from pipecat.frames.frames import (
|
|||||||
BotStoppedSpeakingFrame,
|
BotStoppedSpeakingFrame,
|
||||||
Frame,
|
Frame,
|
||||||
OutputImageRawFrame,
|
OutputImageRawFrame,
|
||||||
TextFrame,
|
|
||||||
)
|
)
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
@@ -28,14 +23,14 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class ImageSyncAggregator(FrameProcessor):
|
class ImageSyncAggregator(FrameProcessor):
|
||||||
def __init__(self, speaking_path: str, waiting_path: str):
|
def __init__(self, speaking_path: str, waiting_path: str):
|
||||||
@@ -72,83 +67,90 @@ class ImageSyncAggregator(FrameProcessor):
|
|||||||
await self.push_frame(frame)
|
await self.push_frame(frame)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
camera_out_enabled=True,
|
||||||
camera_out_enabled=True,
|
camera_out_width=1024,
|
||||||
camera_out_width=1024,
|
camera_out_height=1024,
|
||||||
camera_out_height=1024,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
messages = [
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
{
|
|
||||||
"role": "system",
|
messages = [
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
{
|
||||||
},
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
image_sync_aggregator = ImageSyncAggregator(
|
||||||
|
os.path.join(os.path.dirname(__file__), "assets", "speaking.png"),
|
||||||
|
os.path.join(os.path.dirname(__file__), "assets", "waiting.png"),
|
||||||
|
)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
image_sync_aggregator,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
image_sync_aggregator = ImageSyncAggregator(
|
@transport.event_handler("on_client_connected")
|
||||||
os.path.join(os.path.dirname(__file__), "assets", "speaking.png"),
|
async def on_client_connected(transport, client):
|
||||||
os.path.join(os.path.dirname(__file__), "assets", "waiting.png"),
|
logger.info(f"Client connected")
|
||||||
)
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_disconnected")
|
||||||
[
|
async def on_client_disconnected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client disconnected")
|
||||||
context_aggregator.user(),
|
|
||||||
llm,
|
|
||||||
tts,
|
|
||||||
image_sync_aggregator,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_closed")
|
||||||
pipeline,
|
async def on_client_closed(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client closed connection")
|
||||||
allow_interruptions=True,
|
await task.cancel()
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_first_participant_joined(transport, participant):
|
await runner.run(task)
|
||||||
participant_name = participant.get("info", {}).get("userName", "")
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
await task.queue_frames([TextFrame(f"Hi there {participant_name}!")])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2024–2025, Daily
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
|
||||||
#
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from loguru import logger
|
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
||||||
from pipecat.processors.audio.vad.silero import SileroVAD
|
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
|
||||||
|
|
||||||
load_dotenv(override=True)
|
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_in_enabled=True,
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
vad = SileroVAD()
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(),
|
|
||||||
vad,
|
|
||||||
context_aggregator.user(),
|
|
||||||
llm,
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
asyncio.run(main())
|
|
||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -19,84 +15,92 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2024–2025, Daily
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
|
||||||
#
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from loguru import logger
|
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
||||||
from pipecat.services.anthropic.llm import AnthropicLLMService
|
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
|
||||||
|
|
||||||
load_dotenv(override=True)
|
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = AnthropicLLMService(
|
|
||||||
api_key=os.getenv("ANTHROPIC_API_KEY"), model="claude-3-opus-20240229"
|
|
||||||
)
|
|
||||||
|
|
||||||
# todo: think more about how to handle system prompts in a more general way. OpenAI,
|
|
||||||
# Google, and Anthropic all have slightly different approaches to providing a system
|
|
||||||
# prompt.
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative, helpful, and brief way. Say hello.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
asyncio.run(main())
|
|
||||||
106
examples/foundational/07a-interruptible-vad.py
Normal file
106
examples/foundational/07a-interruptible-vad.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024–2025, Daily
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.processors.audio.vad.silero import SileroVAD
|
||||||
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
vad = SileroVAD()
|
||||||
|
|
||||||
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
vad,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -4,11 +4,8 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||||
from langchain_community.chat_message_histories import ChatMessageHistory
|
from langchain_community.chat_message_histories import ChatMessageHistory
|
||||||
@@ -16,7 +13,6 @@ from langchain_core.chat_history import BaseChatMessageHistory
|
|||||||
from langchain_core.runnables.history import RunnableWithMessageHistory
|
from langchain_core.runnables.history import RunnableWithMessageHistory
|
||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import ChatOpenAI
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import LLMMessagesFrame
|
from pipecat.frames.frames import LLMMessagesFrame
|
||||||
@@ -29,14 +25,14 @@ from pipecat.processors.aggregators.llm_response import (
|
|||||||
)
|
)
|
||||||
from pipecat.processors.frameworks.langchain import LangchainProcessor
|
from pipecat.processors.frameworks.langchain import LangchainProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
message_store = {}
|
message_store = {}
|
||||||
|
|
||||||
|
|
||||||
@@ -46,90 +42,97 @@ def get_session_history(session_id: str) -> BaseChatMessageHistory:
|
|||||||
return message_store[session_id]
|
return message_store[session_id]
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
|
prompt = ChatPromptTemplate.from_messages(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"system",
|
||||||
|
"Be nice and helpful. Answer very briefly and without special characters like `#` or `*`. "
|
||||||
|
"Your response will be synthesized to voice and those characters will create unnatural sounds.",
|
||||||
),
|
),
|
||||||
)
|
MessagesPlaceholder("chat_history"),
|
||||||
|
("human", "{input}"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
chain = prompt | ChatOpenAI(model="gpt-4o", temperature=0.7)
|
||||||
|
history_chain = RunnableWithMessageHistory(
|
||||||
|
chain,
|
||||||
|
get_session_history,
|
||||||
|
history_messages_key="chat_history",
|
||||||
|
input_messages_key="input",
|
||||||
|
)
|
||||||
|
lc = LangchainProcessor(history_chain)
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tma_in = LLMUserResponseAggregator()
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
tma_out = LLMAssistantResponseAggregator()
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
prompt = ChatPromptTemplate.from_messages(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
(
|
transport.input(), # Transport user input
|
||||||
"system",
|
stt,
|
||||||
"Be nice and helpful. Answer very briefly and without special characters like `#` or `*`. "
|
tma_in, # User responses
|
||||||
"Your response will be synthesized to voice and those characters will create unnatural sounds.",
|
lc, # Langchain
|
||||||
),
|
tts, # TTS
|
||||||
MessagesPlaceholder("chat_history"),
|
transport.output(), # Transport bot output
|
||||||
("human", "{input}"),
|
tma_out, # Assistant spoken responses
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
chain = prompt | ChatOpenAI(model="gpt-4o", temperature=0.7)
|
|
||||||
history_chain = RunnableWithMessageHistory(
|
|
||||||
chain,
|
|
||||||
get_session_history,
|
|
||||||
history_messages_key="chat_history",
|
|
||||||
input_messages_key="input",
|
|
||||||
)
|
|
||||||
lc = LangchainProcessor(history_chain)
|
|
||||||
|
|
||||||
tma_in = LLMUserResponseAggregator()
|
task = PipelineTask(
|
||||||
tma_out = LLMAssistantResponseAggregator()
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
tma_in, # User responses
|
# Kick off the conversation.
|
||||||
lc, # Langchain
|
# the `LLMMessagesFrame` will be picked up by the LangchainProcessor using
|
||||||
tts, # TTS
|
# only the content of the last message to inject it in the prompt defined
|
||||||
transport.output(), # Transport bot output
|
# above. So no role is required here.
|
||||||
tma_out, # Assistant spoken responses
|
messages = [({"content": "Please briefly introduce yourself to the user."})]
|
||||||
]
|
await task.queue_frames([LLMMessagesFrame(messages)])
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
lc.set_participant_id(participant["id"])
|
await task.cancel()
|
||||||
# Kick off the conversation.
|
|
||||||
# the `LLMMessagesFrame` will be picked up by the LangchainProcessor using
|
|
||||||
# only the content of the last message to inject it in the prompt defined
|
|
||||||
# above. So no role is required here.
|
|
||||||
messages = [({"content": "Please briefly introduce yourself to the user."})]
|
|
||||||
await task.queue_frames([LLMMessagesFrame(messages)])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from deepgram import LiveOptions
|
from deepgram import LiveOptions
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
BotInterruptionFrame,
|
BotInterruptionFrame,
|
||||||
@@ -27,91 +23,95 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.deepgram.tts import DeepgramTTSService
|
from pipecat.services.deepgram.tts import DeepgramTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(
|
||||||
room_url,
|
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
||||||
None,
|
live_options=LiveOptions(vad_events=True, utterance_end_ms="1000"),
|
||||||
"Respond bot",
|
)
|
||||||
DailyParams(
|
|
||||||
audio_in_enabled=True,
|
|
||||||
audio_out_enabled=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(
|
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
||||||
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
|
||||||
live_options=LiveOptions(vad_events=True, utterance_end_ms="1000"),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@stt.event_handler("on_speech_started")
|
||||||
[
|
async def on_speech_started(stt, *args, **kwargs):
|
||||||
transport.input(), # Transport user input
|
await task.queue_frames([BotInterruptionFrame(), UserStartedSpeakingFrame()])
|
||||||
stt, # STT
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@stt.event_handler("on_utterance_end")
|
||||||
pipeline,
|
async def on_utterance_end(stt, *args, **kwargs):
|
||||||
params=PipelineParams(
|
await task.queue_frames([StopInterruptionFrame(), UserStoppedSpeakingFrame()])
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@stt.event_handler("on_speech_started")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_speech_started(stt, *args, **kwargs):
|
async def on_client_connected(transport, client):
|
||||||
await task.queue_frames([BotInterruptionFrame(), UserStartedSpeakingFrame()])
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
@stt.event_handler("on_utterance_end")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_utterance_end(stt, *args, **kwargs):
|
async def on_client_disconnected(transport, client):
|
||||||
await task.queue_frames([StopInterruptionFrame(), UserStoppedSpeakingFrame()])
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
# Kick off the conversation.
|
logger.info(f"Client closed connection")
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,82 +17,87 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.deepgram.tts import DeepgramTTSService
|
from pipecat.services.deepgram.tts import DeepgramTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
None,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
||||||
|
|
||||||
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
# Kick off the conversation.
|
logger.info(f"Client closed connection")
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,45 +4,44 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.elevenlabs.tts import ElevenLabsHttpTTSService
|
from pipecat.services.elevenlabs.tts import ElevenLabsHttpTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, token) = await configure(session)
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = ElevenLabsHttpTTSService(
|
tts = ElevenLabsHttpTTSService(
|
||||||
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
||||||
@@ -65,6 +64,7 @@ async def main():
|
|||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
context_aggregator.user(), # User responses
|
context_aggregator.user(), # User responses
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
tts, # TTS
|
tts, # TTS
|
||||||
@@ -83,21 +83,28 @@ async def main():
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_participant_left(transport, participant, reason):
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
await task.cancel()
|
await task.cancel()
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,99 +4,103 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
|
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = ElevenLabsTTSService(
|
tts = ElevenLabsTTSService(
|
||||||
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
||||||
voice_id=os.getenv("ELEVENLABS_VOICE_ID", ""),
|
voice_id=os.getenv("ELEVENLABS_VOICE_ID", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,100 +4,104 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.services.playht.tts import PlayHTHttpTTSService
|
from pipecat.services.playht.tts import PlayHTHttpTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = PlayHTHttpTTSService(
|
tts = PlayHTHttpTTSService(
|
||||||
user_id=os.getenv("PLAYHT_USER_ID"),
|
user_id=os.getenv("PLAYHT_USER_ID"),
|
||||||
api_key=os.getenv("PLAYHT_API_KEY"),
|
api_key=os.getenv("PLAYHT_API_KEY"),
|
||||||
voice_url="s3://voice-cloning-zero-shot/d9ff78ba-d016-47f6-b0ef-dd630f59414e/female-cs/manifest.json",
|
voice_url="s3://voice-cloning-zero-shot/d9ff78ba-d016-47f6-b0ef-dd630f59414e/female-cs/manifest.json",
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,102 +4,106 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.services.playht.tts import PlayHTTTSService
|
from pipecat.services.playht.tts import PlayHTTTSService
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = PlayHTTTSService(
|
tts = PlayHTTTSService(
|
||||||
user_id=os.getenv("PLAYHT_USER_ID"),
|
user_id=os.getenv("PLAYHT_USER_ID"),
|
||||||
api_key=os.getenv("PLAYHT_API_KEY"),
|
api_key=os.getenv("PLAYHT_API_KEY"),
|
||||||
voice_url="s3://voice-cloning-zero-shot/e46b4027-b38d-4d24-b292-38fbca2be0ef/original/manifest.json",
|
voice_url="s3://voice-cloning-zero-shot/e46b4027-b38d-4d24-b292-38fbca2be0ef/original/manifest.json",
|
||||||
params=PlayHTTTSService.InputParams(language=Language.EN),
|
params=PlayHTTTSService.InputParams(language=Language.EN),
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,93 +17,97 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.azure.llm import AzureLLMService
|
from pipecat.services.azure.llm import AzureLLMService
|
||||||
from pipecat.services.azure.stt import AzureSTTService
|
from pipecat.services.azure.stt import AzureSTTService
|
||||||
from pipecat.services.azure.tts import AzureTTSService
|
from pipecat.services.azure.tts import AzureTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = AzureSTTService(
|
||||||
room_url,
|
api_key=os.getenv("AZURE_SPEECH_API_KEY"),
|
||||||
token,
|
region=os.getenv("AZURE_SPEECH_REGION"),
|
||||||
"Respond bot",
|
)
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = AzureSTTService(
|
tts = AzureTTSService(
|
||||||
api_key=os.getenv("AZURE_SPEECH_API_KEY"),
|
api_key=os.getenv("AZURE_SPEECH_API_KEY"),
|
||||||
region=os.getenv("AZURE_SPEECH_REGION"),
|
region=os.getenv("AZURE_SPEECH_REGION"),
|
||||||
)
|
)
|
||||||
|
|
||||||
tts = AzureTTSService(
|
llm = AzureLLMService(
|
||||||
api_key=os.getenv("AZURE_SPEECH_API_KEY"),
|
api_key=os.getenv("AZURE_CHATGPT_API_KEY"),
|
||||||
region=os.getenv("AZURE_SPEECH_REGION"),
|
endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"),
|
||||||
)
|
model=os.getenv("AZURE_CHATGPT_MODEL"),
|
||||||
|
)
|
||||||
|
|
||||||
llm = AzureLLMService(
|
messages = [
|
||||||
api_key=os.getenv("AZURE_CHATGPT_API_KEY"),
|
{
|
||||||
endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"),
|
"role": "system",
|
||||||
model=os.getenv("AZURE_CHATGPT_MODEL"),
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
)
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,95 +17,92 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.services.openai.stt import OpenAISTTService
|
from pipecat.services.openai.stt import OpenAISTTService
|
||||||
from pipecat.services.openai.tts import OpenAITTSService
|
from pipecat.services.openai.tts import OpenAITTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = OpenAISTTService(
|
||||||
room_url,
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
token,
|
model="gpt-4o-transcribe-latest",
|
||||||
"Respond bot",
|
prompt="Expect words related to dogs, such as breed names.",
|
||||||
DailyParams(
|
)
|
||||||
audio_out_enabled=True,
|
|
||||||
audio_out_sample_rate=24000,
|
|
||||||
transcription_enabled=False,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# You can use the OpenAI compatible API like Groq.
|
tts = OpenAITTSService(api_key=os.getenv("OPENAI_API_KEY"), voice="ballad")
|
||||||
# stt = OpenAISTTService(
|
|
||||||
# base_url="https://api.groq.com/openai/v1",
|
|
||||||
# api_key="gsk_***",
|
|
||||||
# model="whisper-large-v3",
|
|
||||||
# )
|
|
||||||
stt = OpenAISTTService(
|
|
||||||
api_key=os.getenv("OPENAI_API_KEY"),
|
|
||||||
model="gpt-4o-transcribe-latest",
|
|
||||||
prompt="Expect words related to dogs, such as breed names.",
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = OpenAITTSService(api_key=os.getenv("OPENAI_API_KEY"), voice="ballad")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are very knowledgable about dogs. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are very knowledgable about dogs. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
audio_out_sample_rate=24000,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -20,90 +16,98 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openpipe.llm import OpenPipeLLMService
|
from pipecat.services.openpipe.llm import OpenPipeLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
llm = OpenPipeLLMService(
|
llm = OpenPipeLLMService(
|
||||||
api_key=os.getenv("OPENAI_API_KEY"),
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
openpipe_api_key=os.getenv("OPENPIPE_API_KEY"),
|
openpipe_api_key=os.getenv("OPENPIPE_API_KEY"),
|
||||||
model="gpt-4o",
|
model="gpt-4o",
|
||||||
tags={"conversation_id": f"pipecat-{timestamp}"},
|
tags={"conversation_id": f"pipecat-{timestamp}"},
|
||||||
)
|
)
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,45 +4,44 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.services.xtts.tts import XTTSService
|
from pipecat.services.xtts.tts import XTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, token) = await configure(session)
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = XTTSService(
|
tts = XTTSService(
|
||||||
aiohttp_session=session,
|
aiohttp_session=session,
|
||||||
@@ -65,6 +64,7 @@ async def main():
|
|||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
context_aggregator.user(), # User responses
|
context_aggregator.user(), # User responses
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
tts, # TTS
|
tts, # TTS
|
||||||
@@ -83,21 +83,28 @@ async def main():
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_participant_left(transport, participant, reason):
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
await task.cancel()
|
await task.cancel()
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -23,94 +19,96 @@ from pipecat.services.gladia.config import GladiaInputParams, LanguageConfig
|
|||||||
from pipecat.services.gladia.stt import GladiaSTTService
|
from pipecat.services.gladia.stt import GladiaSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = GladiaSTTService(
|
||||||
room_url,
|
api_key=os.getenv("GLADIA_API_KEY", ""),
|
||||||
token,
|
params=GladiaInputParams(
|
||||||
"Respond bot",
|
language_config=LanguageConfig(
|
||||||
DailyParams(
|
languages=[Language.EN],
|
||||||
audio_out_enabled=True,
|
)
|
||||||
vad_enabled=True,
|
),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
)
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = GladiaSTTService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("GLADIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY", ""),
|
||||||
params=GladiaInputParams(
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
language_config=LanguageConfig(
|
)
|
||||||
languages=[Language.EN],
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY", ""), model="gpt-4o")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": f"You are a helpful LLM. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
# Register an event handler to exit the application when the user leaves.
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
@transport.event_handler("on_participant_left")
|
await runner.run(task)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,96 +4,100 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.lmnt.tts import LmntTTSService
|
from pipecat.services.lmnt.tts import LmntTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = LmntTTSService(api_key=os.getenv("LMNT_API_KEY"), voice_id="morgan")
|
tts = LmntTTSService(api_key=os.getenv("LMNT_API_KEY"), voice_id="morgan")
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User respones
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User respones
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
102
examples/foundational/07l-interruptible-groq.py
Normal file
102
examples/foundational/07l-interruptible-groq.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024–2025, Daily
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.groq.llm import GroqLLMService
|
||||||
|
from pipecat.services.groq.stt import GroqSTTService
|
||||||
|
from pipecat.services.groq.tts import GroqTTSService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY"))
|
||||||
|
|
||||||
|
llm = GroqLLMService(api_key=os.getenv("GROQ_API_KEY"), model="llama-3.3-70b-versatile")
|
||||||
|
|
||||||
|
tts = GroqTTSService(api_key=os.getenv("GROQ_API_KEY"))
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2024–2025, Daily
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
|
||||||
#
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from loguru import logger
|
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
|
||||||
from pipecat.services.together.llm import TogetherLLMService
|
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
|
||||||
|
|
||||||
load_dotenv(override=True)
|
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = TogetherLLMService(
|
|
||||||
api_key=os.getenv("TOGETHER_API_KEY"),
|
|
||||||
model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
|
||||||
params=TogetherLLMService.InputParams(
|
|
||||||
temperature=1.0,
|
|
||||||
top_p=0.9,
|
|
||||||
top_k=40,
|
|
||||||
extra={
|
|
||||||
"frequency_penalty": 2.0,
|
|
||||||
"presence_penalty": 0.0,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond in plain language. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
user_aggregator = context_aggregator.user()
|
|
||||||
assistant_aggregator = context_aggregator.assistant()
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
user_aggregator, # User responses
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
assistant_aggregator, # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
asyncio.run(main())
|
|
||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,89 +17,93 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.aws.tts import PollyTTSService
|
from pipecat.services.aws.tts import PollyTTSService
|
||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
None,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
tts = PollyTTSService(
|
||||||
|
api_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
|
||||||
|
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
|
||||||
|
region=os.getenv("AWS_REGION"),
|
||||||
|
voice_id="Amy",
|
||||||
|
params=PollyTTSService.InputParams(engine="neural", language="en-GB", rate="1.05"),
|
||||||
|
)
|
||||||
|
|
||||||
tts = PollyTTSService(
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
api_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
|
|
||||||
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
|
|
||||||
region=os.getenv("AWS_REGION"),
|
|
||||||
voice_id="Amy",
|
|
||||||
params=PollyTTSService.InputParams(engine="neural", language="en-GB", rate="1.05"),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -22,88 +18,94 @@ from pipecat.services.google.llm import GoogleLLMService
|
|||||||
from pipecat.services.google.stt import GoogleSTTService
|
from pipecat.services.google.stt import GoogleSTTService
|
||||||
from pipecat.services.google.tts import GoogleTTSService
|
from pipecat.services.google.tts import GoogleTTSService
|
||||||
from pipecat.transcriptions.language import Language
|
from pipecat.transcriptions.language import Language
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = GoogleSTTService(
|
||||||
room_url,
|
params=GoogleSTTService.InputParams(languages=Language.EN_US),
|
||||||
None,
|
credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"),
|
||||||
"Respond bot",
|
)
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = GoogleSTTService(
|
tts = GoogleTTSService(
|
||||||
params=GoogleSTTService.InputParams(languages=Language.EN_US),
|
voice_id="en-US-Chirp3-HD-Charon",
|
||||||
)
|
params=GoogleTTSService.InputParams(language=Language.EN_US),
|
||||||
|
credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"),
|
||||||
|
)
|
||||||
|
|
||||||
tts = GoogleTTSService(
|
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"))
|
||||||
voice_id="en-US-Journey-F",
|
|
||||||
params=GoogleTTSService.InputParams(language=Language.EN_US),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"))
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User respones
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User respones
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,88 +17,92 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.assemblyai.stt import AssemblyAISTTService
|
from pipecat.services.assemblyai.stt import AssemblyAISTTService
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = AssemblyAISTTService(
|
||||||
room_url,
|
api_key=os.getenv("ASSEMBLYAI_API_KEY"),
|
||||||
token,
|
)
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = AssemblyAISTTService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("ASSEMBLYAI_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.filters.krisp_filter import KrispFilter
|
from pipecat.audio.filters.krisp_filter import KrispFilter
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
@@ -22,83 +18,88 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.deepgram.tts import DeepgramTTSService
|
from pipecat.services.deepgram.tts import DeepgramTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
audio_in_filter=KrispFilter(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
audio_in_filter=KrispFilter(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
||||||
|
|
||||||
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
# Kick off the conversation.
|
logger.info(f"Client closed connection")
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,45 +4,44 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.services.rime.tts import RimeHttpTTSService
|
from pipecat.services.rime.tts import RimeHttpTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, token) = await configure(session)
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = RimeHttpTTSService(
|
tts = RimeHttpTTSService(
|
||||||
api_key=os.getenv("RIME_API_KEY", ""),
|
api_key=os.getenv("RIME_API_KEY", ""),
|
||||||
@@ -65,6 +64,7 @@ async def main():
|
|||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
context_aggregator.user(), # User responses
|
context_aggregator.user(), # User responses
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
tts, # TTS
|
tts, # TTS
|
||||||
@@ -83,21 +83,28 @@ async def main():
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_participant_left(transport, participant, reason):
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
await task.cancel()
|
await task.cancel()
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,99 +4,103 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.services.rime.tts import RimeTTSService
|
from pipecat.services.rime.tts import RimeTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = RimeTTSService(
|
tts = RimeTTSService(
|
||||||
api_key=os.getenv("RIME_API_KEY", ""),
|
api_key=os.getenv("RIME_API_KEY", ""),
|
||||||
voice_id="rex",
|
voice_id="rex",
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,76 +17,87 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.nim.llm import NimLLMService
|
from pipecat.services.nim.llm import NimLLMService
|
||||||
from pipecat.services.riva.stt import ParakeetSTTService
|
from pipecat.services.riva.stt import ParakeetSTTService
|
||||||
from pipecat.services.riva.tts import FastPitchTTSService
|
from pipecat.services.riva.tts import FastPitchTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = ParakeetSTTService(api_key=os.getenv("NVIDIA_API_KEY"))
|
||||||
room_url,
|
|
||||||
None,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = ParakeetSTTService(api_key=os.getenv("NVIDIA_API_KEY"))
|
llm = NimLLMService(api_key=os.getenv("NVIDIA_API_KEY"), model="meta/llama-3.1-405b-instruct")
|
||||||
|
|
||||||
llm = NimLLMService(
|
tts = FastPitchTTSService(api_key=os.getenv("NVIDIA_API_KEY"))
|
||||||
api_key=os.getenv("NVIDIA_API_KEY"), model="meta/llama-3.1-405b-instruct"
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = FastPitchTTSService(api_key=os.getenv("NVIDIA_API_KEY"))
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
# Kick off the conversation.
|
logger.info(f"Client closed connection")
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,16 +4,12 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import google.ai.generativelanguage as glm
|
import google.ai.generativelanguage as glm
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
@@ -32,14 +28,15 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.processors.frame_processor import FrameProcessor
|
from pipecat.processors.frame_processor import FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
|
||||||
from pipecat.services.google.llm import GoogleLLMService
|
from pipecat.services.google.llm import GoogleLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.google.tts import GoogleTTSService
|
||||||
|
from pipecat.transcriptions.language import Language
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
marker = "|----|"
|
marker = "|----|"
|
||||||
system_message = f"""
|
system_message = f"""
|
||||||
@@ -193,85 +190,92 @@ class TanscriptionContextFixup(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
# No transcription at all. just audio input to Gemini!
|
||||||
# No transcription at all. just audio input to Gemini!
|
# transcription_enabled=True,
|
||||||
# transcription_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash-001")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash-001")
|
tts = GoogleTTSService(
|
||||||
|
voice_id="en-US-Chirp3-HD-Charon",
|
||||||
|
params=GoogleTTSService.InputParams(language=Language.EN_US),
|
||||||
|
credentials=os.getenv("GOOGLE_TEST_CREDENTIALS"),
|
||||||
|
)
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": system_message,
|
"content": system_message,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "Start by saying hello.",
|
"content": "Start by saying hello.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
audio_collector = UserAudioCollector(context, context_aggregator.user())
|
||||||
|
pull_transcript_out_of_llm_output = TranscriptExtractor(context)
|
||||||
|
fixup_context_messages = TanscriptionContextFixup(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
audio_collector,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
pull_transcript_out_of_llm_output,
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
|
fixup_context_messages,
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
audio_collector = UserAudioCollector(context, context_aggregator.user())
|
params=PipelineParams(
|
||||||
pull_transcript_out_of_llm_output = TranscriptExtractor(context)
|
allow_interruptions=True,
|
||||||
fixup_context_messages = TanscriptionContextFixup(context)
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
audio_collector,
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
pull_transcript_out_of_llm_output,
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
fixup_context_messages,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,99 +4,103 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.fish.tts import FishAudioTTSService
|
from pipecat.services.fish.tts import FishAudioTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = FishAudioTTSService(
|
tts = FishAudioTTSService(
|
||||||
api_key=os.getenv("FISH_API_KEY"),
|
api_key=os.getenv("FISH_API_KEY"),
|
||||||
model="4ce7e917cedd4bc2bb2e6ff3a46acaa1", # Barack Obama
|
model="4ce7e917cedd4bc2bb2e6ff3a46acaa1", # Barack Obama
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -20,7 +16,9 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.ultravox.stt import UltravoxSTTService
|
from pipecat.services.ultravox.stt import UltravoxSTTService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
@@ -28,8 +26,6 @@ load_dotenv(override=True)
|
|||||||
# The Ultravox model is compute-intensive and performs best with GPU acceleration.
|
# The Ultravox model is compute-intensive and performs best with GPU acceleration.
|
||||||
# This can be deployed on cloud GPU providers like Cerebrium.ai for optimal performance.
|
# This can be deployed on cloud GPU providers like Cerebrium.ai for optimal performance.
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
# Want to initialize the ultravox processor since it takes time to load the model and dont
|
# Want to initialize the ultravox processor since it takes time to load the model and dont
|
||||||
# want to load it every time the pipeline is run
|
# want to load it every time the pipeline is run
|
||||||
@@ -39,53 +35,61 @@ ultravox_processor = UltravoxSTTService(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=False,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
|
),
|
||||||
vad_audio_passthrough=True,
|
)
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.environ.get("CARTESIA_API_KEY"),
|
api_key=os.environ.get("CARTESIA_API_KEY"),
|
||||||
voice_id="97f4b8fb-f2fe-444b-bb9a-c109783a857a",
|
voice_id="97f4b8fb-f2fe-444b-bb9a-c109783a857a",
|
||||||
)
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
ultravox_processor,
|
ultravox_processor,
|
||||||
tts, # TTS
|
tts, # TTS
|
||||||
transport.output(), # Transport bot output
|
transport.output(), # Transport bot output
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
allow_interruptions=True,
|
allow_interruptions=True,
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_participant_left(transport, participant, reason):
|
async def on_client_connected(transport, client):
|
||||||
await task.cancel()
|
logger.info(f"Client connected")
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,99 +4,103 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.neuphonic.tts import NeuphonicHttpTTSService
|
from pipecat.services.neuphonic.tts import NeuphonicHttpTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = NeuphonicHttpTTSService(
|
tts = NeuphonicHttpTTSService(
|
||||||
api_key=os.getenv("NEUPHONIC_API_KEY"),
|
api_key=os.getenv("NEUPHONIC_API_KEY"),
|
||||||
voice_id="fc854436-2dac-4d21-aa69-ae17b54e98eb", # Emily
|
voice_id="fc854436-2dac-4d21-aa69-ae17b54e98eb", # Emily
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,99 +4,103 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.neuphonic.tts import NeuphonicTTSService
|
from pipecat.services.neuphonic.tts import NeuphonicTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = NeuphonicTTSService(
|
tts = NeuphonicTTSService(
|
||||||
api_key=os.getenv("NEUPHONIC_API_KEY"),
|
api_key=os.getenv("NEUPHONIC_API_KEY"),
|
||||||
voice_id="fc854436-2dac-4d21-aa69-ae17b54e98eb", # Emily
|
voice_id="fc854436-2dac-4d21-aa69-ae17b54e98eb", # Emily
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(), # User responses
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
tts, # TTS
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -21,89 +17,92 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.fal.stt import FalSTTService
|
from pipecat.services.fal.stt import FalSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = FalSTTService(
|
||||||
room_url,
|
api_key=os.getenv("FAL_KEY"),
|
||||||
token,
|
)
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = FalSTTService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("FAL_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
messages = [
|
context = OpenAILLMContext(messages)
|
||||||
{
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
pipeline = Pipeline(
|
||||||
},
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected")
|
||||||
stt, # STT
|
# Kick off the conversation.
|
||||||
context_aggregator.user(), # User responses
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
# Register an event handler to exit the application when the user leaves.
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
@transport.event_handler("on_participant_left")
|
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2024–2025, Daily
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
|
||||||
#
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from loguru import logger
|
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|
||||||
from pipecat.services.groq.llm import GroqLLMService
|
|
||||||
from pipecat.services.groq.stt import GroqSTTService
|
|
||||||
from pipecat.services.groq.tts import GroqTTSService
|
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
|
||||||
|
|
||||||
load_dotenv(override=True)
|
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
# transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY"))
|
|
||||||
|
|
||||||
llm = GroqLLMService(api_key=os.getenv("GROQ_API_KEY"), model="llama-3.3-70b-versatile")
|
|
||||||
|
|
||||||
tts = GroqTTSService(api_key=os.getenv("GROQ_API_KEY"))
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
stt,
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_participant_left")
|
|
||||||
async def on_participant_left(transport, participant, reason):
|
|
||||||
await task.cancel()
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
asyncio.run(main())
|
|
||||||
@@ -4,8 +4,8 @@ import os
|
|||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from daily_runner import configure
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import AudioFrame, EndFrame, ImageFrame, LLMMessagesFrame, TextFrame
|
from pipecat.frames.frames import AudioFrame, EndFrame, ImageFrame, LLMMessagesFrame, TextFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -72,7 +72,8 @@ async def main():
|
|||||||
|
|
||||||
async def get_text_and_audio(messages) -> Tuple[str, bytearray]:
|
async def get_text_and_audio(messages) -> Tuple[str, bytearray]:
|
||||||
"""This function streams text from the LLM and uses the TTS service to convert
|
"""This function streams text from the LLM and uses the TTS service to convert
|
||||||
that text to speech as it's received."""
|
that text to speech as it's received.
|
||||||
|
"""
|
||||||
source_queue = asyncio.Queue()
|
source_queue = asyncio.Queue()
|
||||||
sink_queue = asyncio.Queue()
|
sink_queue = asyncio.Queue()
|
||||||
sentence_aggregator = SentenceAggregator()
|
sentence_aggregator = SentenceAggregator()
|
||||||
|
|||||||
@@ -4,13 +4,9 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
Frame,
|
Frame,
|
||||||
@@ -23,13 +19,12 @@ from pipecat.pipeline.pipeline import Pipeline
|
|||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class MirrorProcessor(FrameProcessor):
|
class MirrorProcessor(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -44,6 +39,7 @@ class MirrorProcessor(FrameProcessor):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif isinstance(frame, InputImageRawFrame):
|
elif isinstance(frame, InputImageRawFrame):
|
||||||
|
print(f"Received image frame: {frame.size} {frame.format}")
|
||||||
await self.push_frame(
|
await self.push_frame(
|
||||||
OutputImageRawFrame(image=frame.image, size=frame.size, format=frame.format)
|
OutputImageRawFrame(image=frame.image, size=frame.size, format=frame.format)
|
||||||
)
|
)
|
||||||
@@ -51,42 +47,48 @@ class MirrorProcessor(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Test",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_in_enabled=True,
|
camera_in_enabled=True,
|
||||||
audio_out_enabled=True,
|
camera_out_enabled=True,
|
||||||
camera_out_enabled=True,
|
camera_out_is_live=True,
|
||||||
camera_out_is_live=True,
|
camera_out_width=1280,
|
||||||
camera_out_width=1280,
|
camera_out_height=720,
|
||||||
camera_out_height=720,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
pipeline = Pipeline([transport.input(), MirrorProcessor(), transport.output()])
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_video(participant["id"])
|
|
||||||
|
|
||||||
pipeline = Pipeline([transport.input(), MirrorProcessor(), transport.output()])
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(),
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
audio_in_sample_rate=24000,
|
|
||||||
audio_out_sample_rate=24000,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -5,13 +5,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
Frame,
|
Frame,
|
||||||
@@ -24,14 +21,13 @@ from pipecat.pipeline.pipeline import Pipeline
|
|||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
from pipecat.transports.local.tk import TkLocalTransport, TkTransportParams
|
from pipecat.transports.local.tk import TkLocalTransport, TkTransportParams
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class MirrorProcessor(FrameProcessor):
|
class MirrorProcessor(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -53,52 +49,59 @@ class MirrorProcessor(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
tk_root = tk.Tk()
|
p2p_transport = SmallWebRTCTransport(
|
||||||
tk_root.title("Local Mirror")
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=True,
|
||||||
|
camera_out_enabled=True,
|
||||||
|
camera_out_is_live=True,
|
||||||
|
camera_out_width=1280,
|
||||||
|
camera_out_height=720,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
daily_transport = DailyTransport(
|
tk_root = tk.Tk()
|
||||||
room_url, token, "Test", DailyParams(audio_in_enabled=True)
|
tk_root.title("Local Mirror")
|
||||||
)
|
|
||||||
|
|
||||||
tk_transport = TkLocalTransport(
|
tk_transport = TkLocalTransport(
|
||||||
tk_root,
|
tk_root,
|
||||||
TkTransportParams(
|
TkTransportParams(
|
||||||
audio_out_enabled=True,
|
audio_out_enabled=True,
|
||||||
camera_out_enabled=True,
|
camera_out_enabled=True,
|
||||||
camera_out_is_live=True,
|
camera_out_is_live=True,
|
||||||
camera_out_width=1280,
|
camera_out_width=1280,
|
||||||
camera_out_height=720,
|
camera_out_height=720,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@daily_transport.event_handler("on_first_participant_joined")
|
@p2p_transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_video(participant["id"])
|
logger.info(f"Client connected")
|
||||||
|
|
||||||
pipeline = Pipeline([daily_transport.input(), MirrorProcessor(), tk_transport.output()])
|
pipeline = Pipeline([p2p_transport.input(), MirrorProcessor(), tk_transport.output()])
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(),
|
||||||
audio_in_sample_rate=24000,
|
)
|
||||||
audio_out_sample_rate=24000,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
async def run_tk():
|
async def run_tk():
|
||||||
while not task.has_finished():
|
while not task.has_finished():
|
||||||
tk_root.update()
|
tk_root.update()
|
||||||
tk_root.update_idletasks()
|
tk_root.update_idletasks()
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await asyncio.gather(runner.run(task), run_tk())
|
await asyncio.gather(runner.run(task), run_tk())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,89 +4,99 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
|
from pipecat.frames.frames import TTSSpeakFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.processors.filters.wake_check_filter import WakeCheckFilter
|
from pipecat.processors.filters.wake_check_filter import WakeCheckFilter
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Robot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful assistant. Respond to what the user said in a creative and helpful way. Keep your responses brief.",
|
"content": "You are a helpful assistant. Respond to what the user said in a creative and helpful way. Keep your responses brief.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
hey_robot_filter = WakeCheckFilter(["hey robot", "hey, robot"])
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
hey_robot_filter, # Filter out speech not directed at the robot
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
hey_robot_filter = WakeCheckFilter(["hey robot", "hey, robot"])
|
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
@transport.event_handler("on_client_connected")
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frame(TTSSpeakFrame("Hi! If you want to talk to me, just say 'Hey Robot'"))
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_disconnected")
|
||||||
[
|
async def on_client_disconnected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client disconnected")
|
||||||
hey_robot_filter, # Filter out speech not directed at the robot
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
await tts.say("Hi! If you want to talk to me, just say 'Hey Robot'.")
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,21 +4,18 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import wave
|
import wave
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
Frame,
|
Frame,
|
||||||
LLMFullResponseEndFrame,
|
LLMFullResponseEndFrame,
|
||||||
OutputAudioRawFrame,
|
OutputAudioRawFrame,
|
||||||
|
TTSSpeakFrame,
|
||||||
)
|
)
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
@@ -30,14 +27,14 @@ from pipecat.processors.aggregators.openai_llm_context import (
|
|||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.processors.logger import FrameLogger
|
from pipecat.processors.logger import FrameLogger
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
sounds = {}
|
sounds = {}
|
||||||
sound_files = ["ding1.wav", "ding2.wav"]
|
sound_files = ["ding1.wav", "ding2.wav"]
|
||||||
@@ -80,70 +77,83 @@ class InboundSoundEffectWrapper(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
messages = [
|
tts = CartesiaTTSService(
|
||||||
{
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
"role": "system",
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio. Respond to what the user said in a creative and helpful way.",
|
)
|
||||||
},
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
out_sound = OutboundSoundEffectWrapper()
|
||||||
|
in_sound = InboundSoundEffectWrapper()
|
||||||
|
fl = FrameLogger("LLM Out")
|
||||||
|
fl2 = FrameLogger("Transcription In")
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
in_sound,
|
||||||
|
fl2,
|
||||||
|
llm,
|
||||||
|
fl,
|
||||||
|
tts,
|
||||||
|
out_sound,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(pipeline)
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
out_sound = OutboundSoundEffectWrapper()
|
|
||||||
in_sound = InboundSoundEffectWrapper()
|
|
||||||
fl = FrameLogger("LLM Out")
|
|
||||||
fl2 = FrameLogger("Transcription In")
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
in_sound,
|
await task.queue_frame(TTSSpeakFrame("Hi, I'm listening!"))
|
||||||
fl2,
|
await transport.send_audio(sounds["ding1.wav"])
|
||||||
llm,
|
|
||||||
fl,
|
|
||||||
tts,
|
|
||||||
out_sound,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_disconnected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client disconnected")
|
||||||
await tts.say("Hi, I'm listening!")
|
|
||||||
await transport.send_audio(sounds["ding1.wav"])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
||||||
@@ -23,14 +19,14 @@ from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
|||||||
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.moondream.vision import MoondreamService
|
from pipecat.services.moondream.vision import MoondreamService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class UserImageRequester(FrameProcessor):
|
class UserImageRequester(FrameProcessor):
|
||||||
def __init__(self, participant_id: Optional[str] = None):
|
def __init__(self, participant_id: Optional[str] = None):
|
||||||
@@ -50,61 +46,81 @@ class UserImageRequester(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
# Get WebRTC peer connection ID
|
||||||
(room_url, token) = await configure(session)
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
|
|
||||||
transport = DailyTransport(
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Describe participant video",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
user_response = UserResponseAggregator()
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
image_requester = UserImageRequester()
|
user_response = UserResponseAggregator()
|
||||||
|
|
||||||
vision_aggregator = VisionImageFrameAggregator()
|
# Initialize the image requester without setting the participant ID yet
|
||||||
|
image_requester = UserImageRequester()
|
||||||
|
|
||||||
# If you run into weird description, try with use_cpu=True
|
vision_aggregator = VisionImageFrameAggregator()
|
||||||
moondream = MoondreamService()
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
# If you run into weird description, try with use_cpu=True
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
moondream = MoondreamService()
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await tts.say("Hi there! Feel free to ask me what I see.")
|
|
||||||
await transport.capture_participant_video(participant["id"], framerate=0)
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
image_requester.set_participant_id(participant["id"])
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
tts = CartesiaTTSService(
|
||||||
[
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
transport.input(),
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
user_response,
|
)
|
||||||
image_requester,
|
|
||||||
vision_aggregator,
|
|
||||||
moondream,
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
user_response,
|
||||||
|
image_requester,
|
||||||
|
vision_aggregator,
|
||||||
|
moondream,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected: {client}")
|
||||||
|
|
||||||
|
# Welcome message
|
||||||
|
await tts.say("Hi there! Feel free to ask me what I see.")
|
||||||
|
|
||||||
|
# Set the participant ID in the image requester
|
||||||
|
image_requester.set_participant_id(webrtc_peer_id)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,33 +4,29 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
||||||
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.google.llm import GoogleLLMService
|
from pipecat.services.google.llm import GoogleLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class UserImageRequester(FrameProcessor):
|
class UserImageRequester(FrameProcessor):
|
||||||
def __init__(self, participant_id: Optional[str] = None):
|
def __init__(self, participant_id: Optional[str] = None):
|
||||||
@@ -50,61 +46,84 @@ class UserImageRequester(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
# Get WebRTC peer connection ID
|
||||||
(room_url, token) = await configure(session)
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
|
|
||||||
transport = DailyTransport(
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Describe participant video",
|
|
||||||
DailyParams(
|
|
||||||
audio_in_enabled=True, # This is so Silero VAD can get audio data
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
user_response = UserResponseAggregator()
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
image_requester = UserImageRequester()
|
user_response = UserResponseAggregator()
|
||||||
|
|
||||||
vision_aggregator = VisionImageFrameAggregator()
|
# Initialize the image requester without setting the participant ID yet
|
||||||
|
image_requester = UserImageRequester()
|
||||||
|
|
||||||
google = GoogleLLMService(model="gemini-2.0-flash-001", api_key=os.getenv("GOOGLE_API_KEY"))
|
vision_aggregator = VisionImageFrameAggregator()
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
# Google Gemini model for vision analysis
|
||||||
async def on_first_participant_joined(transport, participant):
|
google = GoogleLLMService(model="gemini-2.0-flash-001", api_key=os.getenv("GOOGLE_API_KEY"))
|
||||||
await tts.say("Hi there! Feel free to ask me what I see.")
|
|
||||||
await transport.capture_participant_video(participant["id"], framerate=0)
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
image_requester.set_participant_id(participant["id"])
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
tts = CartesiaTTSService(
|
||||||
[
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
transport.input(),
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
user_response,
|
)
|
||||||
image_requester,
|
|
||||||
vision_aggregator,
|
|
||||||
google,
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
user_response,
|
||||||
|
image_requester,
|
||||||
|
vision_aggregator,
|
||||||
|
google,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(allow_interruptions=True),
|
||||||
|
)
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected: {client}")
|
||||||
|
|
||||||
|
# Welcome message
|
||||||
|
await tts.say("Hi there! Feel free to ask me what I see.")
|
||||||
|
|
||||||
|
# Set the participant ID in the image requester
|
||||||
|
image_requester.set_participant_id(webrtc_peer_id)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,33 +4,29 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
||||||
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class UserImageRequester(FrameProcessor):
|
class UserImageRequester(FrameProcessor):
|
||||||
def __init__(self, participant_id: Optional[str] = None):
|
def __init__(self, participant_id: Optional[str] = None):
|
||||||
@@ -50,60 +46,84 @@ class UserImageRequester(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
# Get WebRTC peer connection ID
|
||||||
(room_url, token) = await configure(session)
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
|
|
||||||
transport = DailyTransport(
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Describe participant video",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
user_response = UserResponseAggregator()
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
image_requester = UserImageRequester()
|
user_response = UserResponseAggregator()
|
||||||
|
|
||||||
vision_aggregator = VisionImageFrameAggregator()
|
# Initialize the image requester without setting the participant ID yet
|
||||||
|
image_requester = UserImageRequester()
|
||||||
|
|
||||||
openai = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
vision_aggregator = VisionImageFrameAggregator()
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
# OpenAI GPT-4o for vision analysis
|
||||||
async def on_first_participant_joined(transport, participant):
|
openai = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
await tts.say("Hi there! Feel free to ask me what I see.")
|
|
||||||
await transport.capture_participant_video(participant["id"], framerate=0)
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
image_requester.set_participant_id(participant["id"])
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
tts = CartesiaTTSService(
|
||||||
[
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
transport.input(),
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
user_response,
|
)
|
||||||
image_requester,
|
|
||||||
vision_aggregator,
|
|
||||||
openai,
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
user_response,
|
||||||
|
image_requester,
|
||||||
|
vision_aggregator,
|
||||||
|
openai,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(allow_interruptions=True),
|
||||||
|
)
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected: {client}")
|
||||||
|
|
||||||
|
# Welcome message
|
||||||
|
await tts.say("Hi there! Feel free to ask me what I see.")
|
||||||
|
|
||||||
|
# Set the participant ID in the image requester
|
||||||
|
image_requester.set_participant_id(webrtc_peer_id)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,33 +4,29 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
from pipecat.frames.frames import Frame, TextFrame, UserImageRequestFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
from pipecat.processors.aggregators.user_response import UserResponseAggregator
|
||||||
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
from pipecat.processors.aggregators.vision_image_frame import VisionImageFrameAggregator
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.anthropic.llm import AnthropicLLMService
|
from pipecat.services.anthropic.llm import AnthropicLLMService
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class UserImageRequester(FrameProcessor):
|
class UserImageRequester(FrameProcessor):
|
||||||
def __init__(self, participant_id: Optional[str] = None):
|
def __init__(self, participant_id: Optional[str] = None):
|
||||||
@@ -50,60 +46,84 @@ class UserImageRequester(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
# Get WebRTC peer connection ID
|
||||||
(room_url, token) = await configure(session)
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
|
|
||||||
transport = DailyTransport(
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Describe participant video",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
user_response = UserResponseAggregator()
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
image_requester = UserImageRequester()
|
user_response = UserResponseAggregator()
|
||||||
|
|
||||||
vision_aggregator = VisionImageFrameAggregator()
|
# Initialize the image requester without setting the participant ID yet
|
||||||
|
image_requester = UserImageRequester()
|
||||||
|
|
||||||
anthropic = AnthropicLLMService(api_key=os.getenv("ANTHROPIC_API_KEY"))
|
vision_aggregator = VisionImageFrameAggregator()
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
# Anthropic for vision analysis
|
||||||
async def on_first_participant_joined(transport, participant):
|
anthropic = AnthropicLLMService(api_key=os.getenv("ANTHROPIC_API_KEY"))
|
||||||
await tts.say("Hi there! Feel free to ask me what I see.")
|
|
||||||
await transport.capture_participant_video(participant["id"], framerate=0)
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
image_requester.set_participant_id(participant["id"])
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
tts = CartesiaTTSService(
|
||||||
[
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
transport.input(),
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
user_response,
|
)
|
||||||
image_requester,
|
|
||||||
vision_aggregator,
|
|
||||||
anthropic,
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
user_response,
|
||||||
|
image_requester,
|
||||||
|
vision_aggregator,
|
||||||
|
anthropic,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(allow_interruptions=True),
|
||||||
|
)
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected: {client}")
|
||||||
|
|
||||||
|
# Welcome message
|
||||||
|
await tts.say("Hi there! Feel free to ask me what I see.")
|
||||||
|
|
||||||
|
# Set the participant ID in the image requester
|
||||||
|
image_requester.set_participant_id(webrtc_peer_id)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,13 +4,9 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import Frame, TranscriptionFrame
|
from pipecat.frames.frames import Frame, TranscriptionFrame
|
||||||
@@ -19,13 +15,12 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.whisper.stt import WhisperSTTService
|
from pipecat.services.whisper.stt import WhisperSTTService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class TranscriptionLogger(FrameProcessor):
|
class TranscriptionLogger(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -35,34 +30,42 @@ class TranscriptionLogger(FrameProcessor):
|
|||||||
print(f"Transcription: {frame.text}")
|
print(f"Transcription: {frame.text}")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
None,
|
params=TransportParams(
|
||||||
"Transcription bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
vad_enabled=True,
|
||||||
audio_in_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
vad_audio_passthrough=True,
|
)
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = WhisperSTTService()
|
stt = WhisperSTTService()
|
||||||
|
|
||||||
tl = TranscriptionLogger()
|
tl = TranscriptionLogger()
|
||||||
|
|
||||||
pipeline = Pipeline([transport.input(), stt, tl])
|
pipeline = Pipeline([transport.input(), stt, tl])
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import os
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import Frame, TranscriptionFrame
|
from pipecat.frames.frames import Frame, TranscriptionFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -19,13 +16,12 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.deepgram.stt import DeepgramSTTService, Language, LiveOptions
|
from pipecat.services.deepgram.stt import DeepgramSTTService, Language, LiveOptions
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class TranscriptionLogger(FrameProcessor):
|
class TranscriptionLogger(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -35,29 +31,40 @@ class TranscriptionLogger(FrameProcessor):
|
|||||||
print(f"Transcription: {frame.text}")
|
print(f"Transcription: {frame.text}")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url, None, "Transcription bot", DailyParams(audio_in_enabled=True)
|
webrtc_connection=webrtc_connection,
|
||||||
)
|
params=TransportParams(audio_in_enabled=True),
|
||||||
|
)
|
||||||
|
|
||||||
stt = DeepgramSTTService(
|
stt = DeepgramSTTService(
|
||||||
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
api_key=os.getenv("DEEPGRAM_API_KEY"),
|
||||||
# live_options=LiveOptions(language=Language.FR),
|
live_options=LiveOptions(language=Language.EN),
|
||||||
)
|
)
|
||||||
|
|
||||||
tl = TranscriptionLogger()
|
tl = TranscriptionLogger()
|
||||||
|
|
||||||
pipeline = Pipeline([transport.input(), stt, tl])
|
pipeline = Pipeline([transport.input(), stt, tl])
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import Frame, TranscriptionFrame
|
from pipecat.frames.frames import Frame, TranscriptionFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -19,13 +15,12 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.gladia import GladiaSTTService
|
from pipecat.services.gladia import GladiaSTTService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class TranscriptionLogger(FrameProcessor):
|
class TranscriptionLogger(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -35,29 +30,40 @@ class TranscriptionLogger(FrameProcessor):
|
|||||||
print(f"Transcription: {frame.text}")
|
print(f"Transcription: {frame.text}")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url, None, "Transcription bot", DailyParams(audio_in_enabled=True)
|
webrtc_connection=webrtc_connection,
|
||||||
)
|
params=TransportParams(audio_in_enabled=True),
|
||||||
|
)
|
||||||
|
|
||||||
stt = GladiaSTTService(
|
stt = GladiaSTTService(
|
||||||
api_key=os.getenv("GLADIA_API_KEY"),
|
api_key=os.getenv("GLADIA_API_KEY"),
|
||||||
# live_options=LiveOptions(language=Language.FR),
|
# live_options=LiveOptions(language=Language.FR),
|
||||||
)
|
)
|
||||||
|
|
||||||
tl = TranscriptionLogger()
|
tl = TranscriptionLogger()
|
||||||
|
|
||||||
pipeline = Pipeline([transport.input(), stt, tl])
|
pipeline = Pipeline([transport.input(), stt, tl])
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.frames.frames import Frame, TranscriptionFrame
|
from pipecat.frames.frames import Frame, TranscriptionFrame
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -19,13 +15,12 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.assemblyai.stt import AssemblyAISTTService
|
from pipecat.services.assemblyai.stt import AssemblyAISTTService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
class TranscriptionLogger(FrameProcessor):
|
class TranscriptionLogger(FrameProcessor):
|
||||||
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
async def process_frame(self, frame: Frame, direction: FrameDirection):
|
||||||
@@ -35,28 +30,39 @@ class TranscriptionLogger(FrameProcessor):
|
|||||||
print(f"Transcription: {frame.text}")
|
print(f"Transcription: {frame.text}")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url, None, "Transcription bot", DailyParams(audio_in_enabled=True)
|
webrtc_connection=webrtc_connection,
|
||||||
)
|
params=TransportParams(audio_in_enabled=True),
|
||||||
|
)
|
||||||
|
|
||||||
stt = AssemblyAISTTService(
|
stt = AssemblyAISTTService(
|
||||||
api_key=os.getenv("ASSEMBLYAI_API_KEY"),
|
api_key=os.getenv("ASSEMBLYAI_API_KEY"),
|
||||||
)
|
)
|
||||||
|
|
||||||
tl = TranscriptionLogger()
|
tl = TranscriptionLogger()
|
||||||
|
|
||||||
pipeline = Pipeline([transport.input(), stt, tl])
|
pipeline = Pipeline([transport.input(), stt, tl])
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -21,13 +18,12 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
from pipecat.processors.frame_processor import FrameDirection, FrameProcessor
|
||||||
from pipecat.services.whisper.stt import MLXModel, WhisperSTTServiceMLX
|
from pipecat.services.whisper.stt import MLXModel, WhisperSTTServiceMLX
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
STOP_SECS = 2.0
|
STOP_SECS = 2.0
|
||||||
|
|
||||||
@@ -56,40 +52,48 @@ class TranscriptionLogger(FrameProcessor):
|
|||||||
self._last_transcription_time = time.time()
|
self._last_transcription_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
None,
|
params=TransportParams(
|
||||||
"Transcription bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
vad_enabled=True,
|
||||||
audio_in_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=STOP_SECS)),
|
),
|
||||||
vad_audio_passthrough=True,
|
)
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
stt = WhisperSTTServiceMLX(model=MLXModel.LARGE_V3_TURBO)
|
stt = WhisperSTTServiceMLX(model=MLXModel.LARGE_V3_TURBO)
|
||||||
|
|
||||||
tl = TranscriptionLogger()
|
tl = TranscriptionLogger()
|
||||||
|
|
||||||
pipeline = Pipeline([transport.input(), stt, tl])
|
pipeline = Pipeline([transport.input(), stt, tl])
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
report_only_initial_ttfb=False,
|
report_only_initial_ttfb=False,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,106 +18,118 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
# You can also register a function_name of None to get all functions
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
# sent to the same callback with an additional function_name parameter.
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
# You can also register a function_name of None to get all functions
|
||||||
name="get_current_weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
description="Get the current weather",
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
properties={
|
|
||||||
"location": {
|
weather_function = FunctionSchema(
|
||||||
"type": "string",
|
name="get_current_weather",
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
description="Get the current weather",
|
||||||
},
|
properties={
|
||||||
"format": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,99 +18,111 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.anthropic.llm import AnthropicLLMService
|
from pipecat.services.anthropic.llm import AnthropicLLMService
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
location = arguments["location"]
|
location = arguments["location"]
|
||||||
await result_callback(f"The weather in {location} is currently 72 degrees and sunny.")
|
await result_callback(f"The weather in {location} is currently 72 degrees and sunny.")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = AnthropicLLMService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("ANTHROPIC_API_KEY"), model="claude-3-7-sonnet-latest"
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
llm.register_function("get_weather", get_weather)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = AnthropicLLMService(
|
||||||
name="get_weather",
|
api_key=os.getenv("ANTHROPIC_API_KEY"), model="claude-3-7-sonnet-latest"
|
||||||
description="Get the current weather",
|
)
|
||||||
properties={
|
llm.register_function("get_weather", get_weather)
|
||||||
"location": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
name="get_weather",
|
||||||
},
|
description="Get the current weather",
|
||||||
|
properties={
|
||||||
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location"],
|
},
|
||||||
)
|
required=["location"],
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
|
||||||
# todo: test with very short initial user message
|
# todo: test with very short initial user message
|
||||||
|
|
||||||
# messages = [{"role": "system",
|
# messages = [{"role": "system",
|
||||||
# "content": "You are a helpful assistant who can report the weather in any location in the universe. Respond concisely. Your response will be turned into speech so use only simple words and punctuation."},
|
# "content": "You are a helpful assistant who can report the weather in any location in the universe. Respond concisely. Your response will be turned into speech so use only simple words and punctuation."},
|
||||||
# {"role": "user",
|
# {"role": "user",
|
||||||
# "content": " Start the conversation by introducing yourself."}]
|
# "content": " Start the conversation by introducing yourself."}]
|
||||||
|
|
||||||
messages = [{"role": "user", "content": "Say 'hello' to start the conversation."}]
|
messages = [{"role": "user", "content": "Say 'hello' to start the conversation."}]
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
context = OpenAILLMContext(messages, tools)
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
context_aggregator.user(), # User spoken responses
|
stt,
|
||||||
llm, # LLM
|
context_aggregator.user(), # User spoken responses
|
||||||
tts, # TTS
|
llm, # LLM
|
||||||
transport.output(), # Transport bot output
|
tts, # TTS
|
||||||
context_aggregator.assistant(), # Assistant spoken responses and tool context
|
transport.output(), # Transport bot output
|
||||||
]
|
context_aggregator.assistant(), # Assistant spoken responses and tool context
|
||||||
)
|
]
|
||||||
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
allow_interruptions=True,
|
allow_interruptions=True,
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -6,12 +6,9 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,14 +19,16 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.anthropic.llm import AnthropicLLMService
|
from pipecat.services.anthropic.llm import AnthropicLLMService
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
video_participant_id = None
|
# Global variable to store the peer connection ID
|
||||||
|
webrtc_peer_id = None
|
||||||
|
|
||||||
|
|
||||||
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
@@ -39,72 +38,83 @@ async def get_weather(function_name, tool_call_id, arguments, llm, context, resu
|
|||||||
|
|
||||||
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
question = arguments["question"]
|
question = arguments["question"]
|
||||||
|
logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}")
|
||||||
|
|
||||||
|
# Request the image frame
|
||||||
await llm.request_image_frame(
|
await llm.request_image_frame(
|
||||||
user_id=video_participant_id,
|
user_id=webrtc_peer_id,
|
||||||
function_name=function_name,
|
function_name=function_name,
|
||||||
tool_call_id=tool_call_id,
|
tool_call_id=tool_call_id,
|
||||||
text_content=question,
|
text_content=question,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Wait a short time for the frame to be processed
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
async def main():
|
# Return a result to complete the function call
|
||||||
global llm
|
await result_callback(
|
||||||
|
f"I've captured an image from your camera and I'm analyzing what you asked about: {question}"
|
||||||
|
)
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
room_url,
|
global webrtc_peer_id
|
||||||
token,
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = AnthropicLLMService(
|
transport = SmallWebRTCTransport(
|
||||||
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
webrtc_connection=webrtc_connection,
|
||||||
model="claude-3-7-sonnet-latest",
|
params=TransportParams(
|
||||||
enable_prompt_caching_beta=True,
|
audio_in_enabled=True,
|
||||||
)
|
audio_out_enabled=True,
|
||||||
llm.register_function("get_weather", get_weather)
|
camera_in_enabled=True, # Make sure camera input is enabled
|
||||||
llm.register_function("get_image", get_image)
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
name="get_weather",
|
|
||||||
description="Get the current weather",
|
tts = CartesiaTTSService(
|
||||||
properties={
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
"location": {
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
"type": "string",
|
)
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
|
||||||
},
|
llm = AnthropicLLMService(
|
||||||
|
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
||||||
|
model="claude-3-7-sonnet-latest",
|
||||||
|
enable_prompt_caching_beta=True,
|
||||||
|
)
|
||||||
|
llm.register_function("get_weather", get_weather)
|
||||||
|
llm.register_function("get_image", get_image)
|
||||||
|
|
||||||
|
weather_function = FunctionSchema(
|
||||||
|
name="get_weather",
|
||||||
|
description="Get the current weather",
|
||||||
|
properties={
|
||||||
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location"],
|
},
|
||||||
)
|
required=["location"],
|
||||||
get_image_function = FunctionSchema(
|
)
|
||||||
name="get_image",
|
get_image_function = FunctionSchema(
|
||||||
description="Get an image from the video stream.",
|
name="get_image",
|
||||||
properties={
|
description="Get an image from the video stream.",
|
||||||
"question": {
|
properties={
|
||||||
"type": "string",
|
"question": {
|
||||||
"description": "The question that the user is asking about the image.",
|
"type": "string",
|
||||||
}
|
"description": "The question that the user is asking about the image.",
|
||||||
},
|
}
|
||||||
required=["question"],
|
},
|
||||||
)
|
required=["question"],
|
||||||
tools = ToolsSchema(standard_tools=[weather_function, get_image_function])
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function, get_image_function])
|
||||||
|
|
||||||
# todo: test with very short initial user message
|
system_prompt = """\
|
||||||
|
|
||||||
system_prompt = """\
|
|
||||||
You are a helpful assistant who converses with a user and answers questions. Respond concisely to general questions.
|
You are a helpful assistant who converses with a user and answers questions. Respond concisely to general questions.
|
||||||
|
|
||||||
Your response will be turned into speech so use only simple words and punctuation.
|
Your response will be turned into speech so use only simple words and punctuation.
|
||||||
@@ -115,63 +125,73 @@ You can respond to questions about the weather using the get_weather tool.
|
|||||||
|
|
||||||
You can answer questions about the user's video stream using the get_image tool. Some examples of phrases that \
|
You can answer questions about the user's video stream using the get_image tool. Some examples of phrases that \
|
||||||
indicate you should use the get_image tool are:
|
indicate you should use the get_image tool are:
|
||||||
- What do you see?
|
- What do you see?
|
||||||
- What's in the video?
|
- What's in the video?
|
||||||
- Can you describe the video?
|
- Can you describe the video?
|
||||||
- Tell me about what you see.
|
- Tell me about what you see.
|
||||||
- Tell me something interesting about what you see.
|
- Tell me something interesting about what you see.
|
||||||
- What's happening in the video?
|
- What's happening in the video?
|
||||||
|
|
||||||
If you need to use a tool, simply use the tool. Do not tell the user the tool you are using. Be brief and concise.
|
If you need to use a tool, simply use the tool. Do not tell the user the tool you are using. Be brief and concise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": [
|
"content": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"text": system_prompt,
|
"text": system_prompt,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{"role": "user", "content": "Start the conversation by introducing yourself."},
|
{"role": "user", "content": "Start the conversation by introducing yourself."},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User speech to text
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses and tool context
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client connected: {client}")
|
||||||
context_aggregator.user(), # User speech to text
|
# Kick off the conversation.
|
||||||
llm, # LLM
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses and tool context
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
global video_participant_id
|
logger.info(f"Client closed connection")
|
||||||
video_participant_id = participant["id"]
|
await task.cancel()
|
||||||
await transport.capture_participant_transcription(video_participant_id)
|
|
||||||
await transport.capture_participant_video(video_participant_id, framerate=0)
|
|
||||||
# Kick off the conversation.
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
await runner.run(task)
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,99 +18,111 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.together.llm import TogetherLLMService
|
from pipecat.services.together.llm import TogetherLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = TogetherLLMService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("TOGETHER_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
# You can also register a function_name of None to get all functions
|
|
||||||
# sent to the same callback with an additional function_name parameter.
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = TogetherLLMService(
|
||||||
name="get_current_weather",
|
api_key=os.getenv("TOGETHER_API_KEY"),
|
||||||
description="Get the current weather",
|
model="meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||||
properties={
|
)
|
||||||
"location": {
|
# You can also register a function_name of None to get all functions
|
||||||
"type": "string",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
},
|
|
||||||
"format": {
|
weather_function = FunctionSchema(
|
||||||
"type": "string",
|
name="get_current_weather",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
description="Get the current weather",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
properties={
|
||||||
},
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(pipeline)
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -6,12 +6,9 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -21,15 +18,17 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
video_participant_id = None
|
# Global variable to store the peer connection ID
|
||||||
|
webrtc_peer_id = None
|
||||||
|
|
||||||
|
|
||||||
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
@@ -38,71 +37,85 @@ async def get_weather(function_name, tool_call_id, arguments, llm, context, resu
|
|||||||
|
|
||||||
|
|
||||||
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
logger.debug(f"!!! IN get_image {video_participant_id}, {arguments}")
|
|
||||||
question = arguments["question"]
|
question = arguments["question"]
|
||||||
|
logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}")
|
||||||
|
|
||||||
|
# Request the image frame
|
||||||
await llm.request_image_frame(
|
await llm.request_image_frame(
|
||||||
user_id=video_participant_id,
|
user_id=webrtc_peer_id,
|
||||||
function_name=function_name,
|
function_name=function_name,
|
||||||
tool_call_id=tool_call_id,
|
tool_call_id=tool_call_id,
|
||||||
text_content=question,
|
text_content=question,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Wait a short time for the frame to be processed
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
async def main():
|
# Return a result to complete the function call
|
||||||
async with aiohttp.ClientSession() as session:
|
await result_callback(
|
||||||
(room_url, token) = await configure(session)
|
f"I've captured an image from your camera and I'm analyzing what you asked about: {question}"
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
global webrtc_peer_id
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
llm.register_function("get_weather", get_weather)
|
|
||||||
llm.register_function("get_image", get_image)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
transport = SmallWebRTCTransport(
|
||||||
name="get_weather",
|
webrtc_connection=webrtc_connection,
|
||||||
description="Get the current weather",
|
params=TransportParams(
|
||||||
properties={
|
audio_in_enabled=True,
|
||||||
"location": {
|
audio_out_enabled=True,
|
||||||
"type": "string",
|
camera_in_enabled=True, # Make sure camera input is enabled
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
vad_enabled=True,
|
||||||
},
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
"format": {
|
vad_audio_passthrough=True,
|
||||||
"type": "string",
|
),
|
||||||
"enum": ["celsius", "fahrenheit"],
|
)
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
llm.register_function("get_weather", get_weather)
|
||||||
|
llm.register_function("get_image", get_image)
|
||||||
|
|
||||||
|
weather_function = FunctionSchema(
|
||||||
|
name="get_weather",
|
||||||
|
description="Get the current weather",
|
||||||
|
properties={
|
||||||
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
get_image_function = FunctionSchema(
|
"enum": ["celsius", "fahrenheit"],
|
||||||
name="get_image",
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
description="Get an image from the video stream.",
|
|
||||||
properties={
|
|
||||||
"question": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The question that the user is asking about the image.",
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
required=["question"],
|
},
|
||||||
)
|
required=["location"],
|
||||||
tools = ToolsSchema(standard_tools=[weather_function, get_image_function])
|
)
|
||||||
|
get_image_function = FunctionSchema(
|
||||||
|
name="get_image",
|
||||||
|
description="Get an image from the video stream.",
|
||||||
|
properties={
|
||||||
|
"question": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The question that the user is asking about the image.",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required=["question"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function, get_image_function])
|
||||||
|
|
||||||
system_prompt = """\
|
system_prompt = """\
|
||||||
You are a helpful assistant who converses with a user and answers questions. Respond concisely to general questions.
|
You are a helpful assistant who converses with a user and answers questions. Respond concisely to general questions.
|
||||||
|
|
||||||
Your response will be turned into speech so use only simple words and punctuation.
|
Your response will be turned into speech so use only simple words and punctuation.
|
||||||
@@ -113,46 +126,55 @@ You can respond to questions about the weather using the get_weather tool.
|
|||||||
|
|
||||||
You can answer questions about the user's video stream using the get_image tool. Some examples of phrases that \
|
You can answer questions about the user's video stream using the get_image tool. Some examples of phrases that \
|
||||||
indicate you should use the get_image tool are:
|
indicate you should use the get_image tool are:
|
||||||
- What do you see?
|
- What do you see?
|
||||||
- What's in the video?
|
- What's in the video?
|
||||||
- Can you describe the video?
|
- Can you describe the video?
|
||||||
- Tell me about what you see.
|
- Tell me about what you see.
|
||||||
- Tell me something interesting about what you see.
|
- Tell me something interesting about what you see.
|
||||||
- What's happening in the video?
|
- What's happening in the video?
|
||||||
"""
|
"""
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(pipeline)
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
global video_participant_id
|
logger.info(f"Client closed connection")
|
||||||
video_participant_id = participant["id"]
|
await task.cancel()
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
await transport.capture_participant_video(video_participant_id, framerate=0)
|
|
||||||
# Kick off the conversation.
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -6,12 +6,9 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,15 +19,17 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.google.llm import GoogleLLMService
|
from pipecat.services.google.llm import GoogleLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
video_participant_id = None
|
# Global variable to store the peer connection ID
|
||||||
|
webrtc_peer_id = None
|
||||||
|
|
||||||
|
|
||||||
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_weather(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
@@ -40,71 +39,85 @@ async def get_weather(function_name, tool_call_id, arguments, llm, context, resu
|
|||||||
|
|
||||||
|
|
||||||
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
logger.debug(f"!!! IN get_image {video_participant_id}, {arguments}")
|
|
||||||
question = arguments["question"]
|
question = arguments["question"]
|
||||||
|
logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}")
|
||||||
|
|
||||||
|
# Request the image frame
|
||||||
await llm.request_image_frame(
|
await llm.request_image_frame(
|
||||||
user_id=video_participant_id,
|
user_id=webrtc_peer_id,
|
||||||
function_name=function_name,
|
function_name=function_name,
|
||||||
tool_call_id=tool_call_id,
|
tool_call_id=tool_call_id,
|
||||||
text_content=question,
|
text_content=question,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Wait a short time for the frame to be processed
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
async def main():
|
# Return a result to complete the function call
|
||||||
async with aiohttp.ClientSession() as session:
|
await result_callback(
|
||||||
(room_url, token) = await configure(session)
|
f"I've captured an image from your camera and I'm analyzing what you asked about: {question}"
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
global webrtc_peer_id
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
)
|
|
||||||
|
|
||||||
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash-001")
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
llm.register_function("get_weather", get_weather)
|
|
||||||
llm.register_function("get_image", get_image)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
transport = SmallWebRTCTransport(
|
||||||
name="get_weather",
|
webrtc_connection=webrtc_connection,
|
||||||
description="Get the current weather",
|
params=TransportParams(
|
||||||
properties={
|
audio_in_enabled=True,
|
||||||
"location": {
|
audio_out_enabled=True,
|
||||||
"type": "string",
|
camera_in_enabled=True, # Make sure camera input is enabled
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
vad_enabled=True,
|
||||||
},
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
"format": {
|
vad_audio_passthrough=True,
|
||||||
"type": "string",
|
),
|
||||||
"enum": ["celsius", "fahrenheit"],
|
)
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
|
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"), model="gemini-2.0-flash-001")
|
||||||
|
llm.register_function("get_weather", get_weather)
|
||||||
|
llm.register_function("get_image", get_image)
|
||||||
|
|
||||||
|
weather_function = FunctionSchema(
|
||||||
|
name="get_weather",
|
||||||
|
description="Get the current weather",
|
||||||
|
properties={
|
||||||
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
get_image_function = FunctionSchema(
|
"enum": ["celsius", "fahrenheit"],
|
||||||
name="get_image",
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
description="Get an image from the video stream.",
|
|
||||||
properties={
|
|
||||||
"question": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The question that the user is asking about the image.",
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
required=["question"],
|
},
|
||||||
)
|
required=["location", "format"],
|
||||||
tools = ToolsSchema(standard_tools=[weather_function, get_image_function])
|
)
|
||||||
|
get_image_function = FunctionSchema(
|
||||||
|
name="get_image",
|
||||||
|
description="Get an image from the video stream.",
|
||||||
|
properties={
|
||||||
|
"question": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The question that the user is asking about the image.",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required=["question"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function, get_image_function])
|
||||||
|
|
||||||
system_prompt = """\
|
system_prompt = """\
|
||||||
You are a helpful assistant who converses with a user and answers questions. Respond concisely to general questions.
|
You are a helpful assistant who converses with a user and answers questions. Respond concisely to general questions.
|
||||||
|
|
||||||
Your response will be turned into speech so use only simple words and punctuation.
|
Your response will be turned into speech so use only simple words and punctuation.
|
||||||
@@ -115,54 +128,63 @@ You can respond to questions about the weather using the get_weather tool.
|
|||||||
|
|
||||||
You can answer questions about the user's video stream using the get_image tool. Some examples of phrases that \
|
You can answer questions about the user's video stream using the get_image tool. Some examples of phrases that \
|
||||||
indicate you should use the get_image tool are:
|
indicate you should use the get_image tool are:
|
||||||
- What do you see?
|
- What do you see?
|
||||||
- What's in the video?
|
- What's in the video?
|
||||||
- Can you describe the video?
|
- Can you describe the video?
|
||||||
- Tell me about what you see.
|
- Tell me about what you see.
|
||||||
- Tell me something interesting about what you see.
|
- Tell me something interesting about what you see.
|
||||||
- What's happening in the video?
|
- What's happening in the video?
|
||||||
"""
|
"""
|
||||||
messages = [
|
messages = [
|
||||||
{"role": "system", "content": system_prompt},
|
{"role": "system", "content": system_prompt},
|
||||||
{"role": "user", "content": "Say hello."},
|
{"role": "user", "content": "Say hello."},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected: {client}")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
global video_participant_id
|
logger.info(f"Client closed connection")
|
||||||
video_participant_id = participant["id"]
|
await task.cancel()
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
await transport.capture_participant_video(video_participant_id, framerate=0)
|
|
||||||
# Kick off the conversation.
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -24,105 +20,113 @@ from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
|||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.groq.llm import GroqLLMService
|
from pipecat.services.groq.llm import GroqLLMService
|
||||||
from pipecat.services.groq.stt import GroqSTTService
|
from pipecat.services.groq.stt import GroqSTTService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY"), model="distil-whisper-large-v3-en")
|
stt = GroqSTTService(api_key=os.getenv("GROQ_API_KEY"), model="distil-whisper-large-v3-en")
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = GroqLLMService(api_key=os.getenv("GROQ_API_KEY"), model="llama-3.3-70b-versatile")
|
llm = GroqLLMService(api_key=os.getenv("GROQ_API_KEY"), model="llama-3.3-70b-versatile")
|
||||||
# You can also register a function_name of None to get all functions
|
# You can also register a function_name of None to get all functions
|
||||||
# sent to the same callback with an additional function_name parameter.
|
# sent to the same callback with an additional function_name parameter.
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
weather_function = FunctionSchema(
|
||||||
name="get_current_weather",
|
name="get_current_weather",
|
||||||
description="Get the current weather",
|
description="Get the current weather",
|
||||||
properties={
|
properties={
|
||||||
"location": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
"format": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["celsius", "fahrenheit"],
|
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
stt,
|
# Kick off the conversation.
|
||||||
context_aggregator.user(),
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
llm,
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -21,102 +17,114 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.grok.llm import GrokLLMService
|
from pipecat.services.grok.llm import GrokLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = GrokLLMService(api_key=os.getenv("GROK_API_KEY"))
|
tts = CartesiaTTSService(
|
||||||
# You can also register a function_name of None to get all functions
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
# sent to the same callback with an additional function_name parameter.
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = GrokLLMService(api_key=os.getenv("GROK_API_KEY"))
|
||||||
name="get_current_weather",
|
# You can also register a function_name of None to get all functions
|
||||||
description="Get the current weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
properties={
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"location": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
name="get_current_weather",
|
||||||
},
|
description="Get the current weather",
|
||||||
"format": {
|
properties={
|
||||||
"type": "string",
|
"location": {
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"type": "string",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -23,106 +19,118 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.azure.llm import AzureLLMService
|
from pipecat.services.azure.llm import AzureLLMService
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = AzureLLMService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("AZURE_CHATGPT_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"),
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
model=os.getenv("AZURE_CHATGPT_MODEL"),
|
)
|
||||||
)
|
|
||||||
# You can also register a function_name of None to get all functions
|
|
||||||
# sent to the same callback with an additional function_name parameter.
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = AzureLLMService(
|
||||||
name="get_current_weather",
|
api_key=os.getenv("AZURE_CHATGPT_API_KEY"),
|
||||||
description="Get the current weather",
|
endpoint=os.getenv("AZURE_CHATGPT_ENDPOINT"),
|
||||||
properties={
|
model=os.getenv("AZURE_CHATGPT_MODEL"),
|
||||||
"location": {
|
)
|
||||||
"type": "string",
|
# You can also register a function_name of None to get all functions
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
},
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"format": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"enum": ["celsius", "fahrenheit"],
|
name="get_current_weather",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
description="Get the current weather",
|
||||||
},
|
properties={
|
||||||
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,106 +18,118 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.fireworks.llm import FireworksLLMService
|
from pipecat.services.fireworks.llm import FireworksLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = FireworksLLMService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("FIREWORKS_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
model="accounts/fireworks/models/llama-v3p1-405b-instruct",
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
# You can also register a function_name of None to get all functions
|
|
||||||
# sent to the same callback with an additional function_name parameter.
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = FireworksLLMService(
|
||||||
name="get_current_weather",
|
api_key=os.getenv("FIREWORKS_API_KEY"),
|
||||||
description="Get the current weather",
|
model="accounts/fireworks/models/llama-v3p1-405b-instruct",
|
||||||
properties={
|
)
|
||||||
"location": {
|
# You can also register a function_name of None to get all functions
|
||||||
"type": "string",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
},
|
|
||||||
"format": {
|
weather_function = FunctionSchema(
|
||||||
"type": "string",
|
name="get_current_weather",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
description="Get the current weather",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
properties={
|
||||||
},
|
"location": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,106 +18,116 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.nim.llm import NimLLMService
|
from pipecat.services.nim.llm import NimLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
# text_filters=[MarkdownTextFilter()],
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = NimLLMService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("NVIDIA_API_KEY"), model="meta/llama-3.3-70b-instruct"
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
# You can also register a function_name of None to get all functions
|
# text_filters=[MarkdownTextFilter()],
|
||||||
# sent to the same callback with an additional function_name parameter.
|
)
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = NimLLMService(api_key=os.getenv("NVIDIA_API_KEY"), model="meta/llama-3.3-70b-instruct")
|
||||||
name="get_current_weather",
|
# You can also register a function_name of None to get all functions
|
||||||
description="Get the current weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
properties={
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"location": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
name="get_current_weather",
|
||||||
},
|
description="Get the current weather",
|
||||||
"format": {
|
properties={
|
||||||
"type": "string",
|
"location": {
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"type": "string",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -23,66 +19,66 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.cerebras.llm import CerebrasLLMService
|
from pipecat.services.cerebras.llm import CerebrasLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = CerebrasLLMService(api_key=os.getenv("CEREBRAS_API_KEY"), model="llama-3.3-70b")
|
tts = CartesiaTTSService(
|
||||||
# You can also register a function_name of None to get all functions
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
# sent to the same callback with an additional function_name parameter.
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = CerebrasLLMService(api_key=os.getenv("CEREBRAS_API_KEY"), model="llama-3.3-70b")
|
||||||
name="get_current_weather",
|
# You can also register a function_name of None to get all functions
|
||||||
description="Get the current weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
properties={
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"location": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
name="get_current_weather",
|
||||||
},
|
description="Get the current weather",
|
||||||
"format": {
|
properties={
|
||||||
"type": "string",
|
"location": {
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"type": "string",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
},
|
||||||
"role": "system",
|
},
|
||||||
"content": """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way.
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way.
|
||||||
|
|
||||||
You have one functions available:
|
You have one functions available:
|
||||||
|
|
||||||
@@ -92,44 +88,56 @@ Infer whether to use Fahrenheit or Celsius automatically based on the location,
|
|||||||
|
|
||||||
Start by asking me for my location. Then, use 'get_weather_current' to give me a forecast.
|
Start by asking me for my location. Then, use 'get_weather_current' to give me a forecast.
|
||||||
|
|
||||||
Respond to what the user said in a creative and helpful way.""",
|
Respond to what the user said in a creative and helpful way.""",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,67 +18,67 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.deepseek.llm import DeepSeekLLMService
|
from pipecat.services.deepseek.llm import DeepSeekLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = DeepSeekLLMService(api_key=os.getenv("DEEPSEEK_API_KEY"), model="deepseek-chat")
|
tts = CartesiaTTSService(
|
||||||
# You can also register a function_name of None to get all functions
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
# sent to the same callback with an additional function_name parameter.
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = DeepSeekLLMService(api_key=os.getenv("DEEPSEEK_API_KEY"), model="deepseek-chat")
|
||||||
name="get_current_weather",
|
# You can also register a function_name of None to get all functions
|
||||||
description="Get the current weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
properties={
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"location": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
name="get_current_weather",
|
||||||
},
|
description="Get the current weather",
|
||||||
"format": {
|
properties={
|
||||||
"type": "string",
|
"location": {
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"type": "string",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
},
|
||||||
"role": "system",
|
},
|
||||||
"content": """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way.
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": """You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way.
|
||||||
|
|
||||||
You have one functions available:
|
You have one functions available:
|
||||||
|
|
||||||
@@ -92,44 +88,56 @@ Infer whether to use Fahrenheit or Celsius automatically based on the location,
|
|||||||
|
|
||||||
Start by asking me for my location. Then, use 'get_weather_current' to give me a forecast.
|
Start by asking me for my location. Then, use 'get_weather_current' to give me a forecast.
|
||||||
|
|
||||||
Respond to what the user said in a creative and helpful way.""",
|
Respond to what the user said in a creative and helpful way.""",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,108 +18,120 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.azure.tts import AzureTTSService
|
from pipecat.services.azure.tts import AzureTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openrouter.llm import OpenRouterLLMService
|
from pipecat.services.openrouter.llm import OpenRouterLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = AzureTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("AZURE_API_KEY"),
|
|
||||||
region="eastus",
|
|
||||||
voice="en-US-JennyNeural",
|
|
||||||
params=AzureTTSService.InputParams(language="en-US", rate="1.1", style="cheerful"),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenRouterLLMService(
|
tts = AzureTTSService(
|
||||||
api_key=os.getenv("OPENROUTER_API_KEY"), model="openai/gpt-4o-2024-11-20"
|
api_key=os.getenv("AZURE_API_KEY"),
|
||||||
)
|
region="eastus",
|
||||||
# You can also register a function_name of None to get all functions
|
voice="en-US-JennyNeural",
|
||||||
# sent to the same callback with an additional function_name parameter.
|
params=AzureTTSService.InputParams(language="en-US", rate="1.1", style="cheerful"),
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = OpenRouterLLMService(
|
||||||
name="get_current_weather",
|
api_key=os.getenv("OPENROUTER_API_KEY"), model="openai/gpt-4o-2024-11-20"
|
||||||
description="Get the current weather",
|
)
|
||||||
properties={
|
# You can also register a function_name of None to get all functions
|
||||||
"location": {
|
# sent to the same callback with an additional function_name parameter.
|
||||||
"type": "string",
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
|
||||||
},
|
weather_function = FunctionSchema(
|
||||||
"format": {
|
name="get_current_weather",
|
||||||
"type": "string",
|
description="Get the current weather",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
properties={
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
"location": {
|
||||||
},
|
"type": "string",
|
||||||
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -11,14 +11,10 @@ currently support function calling. The example shows basic chat completion func
|
|||||||
using Perplexity's API while maintaining compatibility with the OpenAI interface.
|
using Perplexity's API while maintaining compatibility with the OpenAI interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
@@ -26,79 +22,91 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.perplexity.llm import PerplexityLLMService
|
from pipecat.services.perplexity.llm import PerplexityLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = PerplexityLLMService(api_key=os.getenv("PERPLEXITY_API_KEY"), model="sonar")
|
llm = PerplexityLLMService(api_key=os.getenv("PERPLEXITY_API_KEY"), model="sonar")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -21,104 +17,116 @@ from pipecat.pipeline.pipeline import Pipeline
|
|||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
|
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
|
||||||
from pipecat.services.google.llm_openai import GoogleLLMOpenAIBetaService
|
from pipecat.services.google.llm_openai import GoogleLLMOpenAIBetaService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = ElevenLabsTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
|
||||||
voice_id=os.getenv("ELEVENLABS_VOICE_ID", ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = GoogleLLMOpenAIBetaService(api_key=os.getenv("GEMINI_API_KEY"))
|
tts = ElevenLabsTTSService(
|
||||||
# You can aslo register a function_name of None to get all functions
|
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
||||||
# sent to the same callback with an additional function_name parameter.
|
voice_id=os.getenv("ELEVENLABS_VOICE_ID", ""),
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
llm = GoogleLLMOpenAIBetaService(api_key=os.getenv("GEMINI_API_KEY"))
|
||||||
name="get_current_weather",
|
# You can aslo register a function_name of None to get all functions
|
||||||
description="Get the current weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
properties={
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
"location": {
|
|
||||||
"type": "string",
|
weather_function = FunctionSchema(
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
name="get_current_weather",
|
||||||
},
|
description="Get the current weather",
|
||||||
"format": {
|
properties={
|
||||||
"type": "string",
|
"location": {
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"type": "string",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
messages = [
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "Start a conversation with 'Hey there' to get the current weather.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Start a conversation with 'Hey there' to get the current weather.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -21,110 +17,122 @@ from pipecat.pipeline.pipeline import Pipeline
|
|||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
|
from pipecat.services.elevenlabs.tts import ElevenLabsTTSService
|
||||||
from pipecat.services.google.llm_vertex import GoogleVertexLLMService
|
from pipecat.services.google.llm_vertex import GoogleVertexLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
|
|
||||||
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
tts = ElevenLabsTTSService(
|
||||||
|
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
||||||
|
voice_id=os.getenv("ELEVENLABS_VOICE_ID", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
llm = GoogleVertexLLMService(
|
||||||
|
# credentials="<json-credentials>",
|
||||||
|
params=GoogleVertexLLMService.InputParams(
|
||||||
|
project_id="<google-project-id>",
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
# You can aslo register a function_name of None to get all functions
|
||||||
|
# sent to the same callback with an additional function_name parameter.
|
||||||
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
tts = ElevenLabsTTSService(
|
weather_function = FunctionSchema(
|
||||||
api_key=os.getenv("ELEVENLABS_API_KEY", ""),
|
name="get_current_weather",
|
||||||
voice_id=os.getenv("ELEVENLABS_VOICE_ID", ""),
|
description="Get the current weather",
|
||||||
)
|
properties={
|
||||||
|
"location": {
|
||||||
llm = GoogleVertexLLMService(
|
"type": "string",
|
||||||
# credentials="<json-credentials>",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
params=GoogleVertexLLMService.InputParams(
|
|
||||||
project_id="<google-project-id>",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# You can aslo register a function_name of None to get all functions
|
|
||||||
# sent to the same callback with an additional function_name parameter.
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
|
||||||
name="get_current_weather",
|
|
||||||
description="Get the current weather",
|
|
||||||
properties={
|
|
||||||
"location": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
|
||||||
},
|
|
||||||
"format": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["celsius", "fahrenheit"],
|
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "Start a conversation with 'Hey there' to get the current weather.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Start a conversation with 'Hey there' to get the current weather.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -22,106 +18,118 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.qwen.llm import QwenLLMService
|
from pipecat.services.qwen.llm import QwenLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
await llm.push_frame(TTSSpeakFrame("Let me check on that."))
|
||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = QwenLLMService(api_key=os.getenv("QWEN_API_KEY"), model="qwen2.5-72b-instruct")
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
# You can also register a function_name of None to get all functions
|
llm = QwenLLMService(api_key=os.getenv("QWEN_API_KEY"), model="qwen2.5-72b-instruct")
|
||||||
# sent to the same callback with an additional function_name parameter.
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
# You can also register a function_name of None to get all functions
|
||||||
name="get_current_weather",
|
# sent to the same callback with an additional function_name parameter.
|
||||||
description="Get the current weather",
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
properties={
|
|
||||||
"location": {
|
weather_function = FunctionSchema(
|
||||||
"type": "string",
|
name="get_current_weather",
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
description="Get the current weather",
|
||||||
},
|
properties={
|
||||||
"format": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
"enum": ["celsius", "fahrenheit"],
|
||||||
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm,
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
context_aggregator.user(),
|
# Kick off the conversation.
|
||||||
llm,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from openai.types.chat import ChatCompletionToolParam
|
from openai.types.chat import ChatCompletionToolParam
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
||||||
@@ -22,13 +18,14 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.processors.filters.function_filter import FunctionFilter
|
from pipecat.processors.filters.function_filter import FunctionFilter
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
current_voice = "News Lady"
|
current_voice = "News Lady"
|
||||||
|
|
||||||
@@ -55,105 +52,117 @@ async def barbershop_man_filter(frame) -> bool:
|
|||||||
return current_voice == "Barbershop Man"
|
return current_voice == "Barbershop Man"
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Pipecat",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
news_lady = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="bf991597-6c13-47e4-8411-91ec2de5c466", # Newslady
|
|
||||||
)
|
|
||||||
|
|
||||||
british_lady = CartesiaTTSService(
|
news_lady = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="bf991597-6c13-47e4-8411-91ec2de5c466", # Newslady
|
||||||
)
|
)
|
||||||
|
|
||||||
barbershop_man = CartesiaTTSService(
|
british_lady = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="a0e99841-438c-4a64-b679-ae501e7d6091", # Barbershop Man
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
barbershop_man = CartesiaTTSService(
|
||||||
llm.register_function("switch_voice", switch_voice)
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="a0e99841-438c-4a64-b679-ae501e7d6091", # Barbershop Man
|
||||||
|
)
|
||||||
|
|
||||||
tools = [
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
ChatCompletionToolParam(
|
llm.register_function("switch_voice", switch_voice)
|
||||||
type="function",
|
|
||||||
function={
|
tools = [
|
||||||
"name": "switch_voice",
|
ChatCompletionToolParam(
|
||||||
"description": "Switch your voice only when the user asks you to",
|
type="function",
|
||||||
"parameters": {
|
function={
|
||||||
"type": "object",
|
"name": "switch_voice",
|
||||||
"properties": {
|
"description": "Switch your voice only when the user asks you to",
|
||||||
"voice": {
|
"parameters": {
|
||||||
"type": "string",
|
"type": "object",
|
||||||
"description": "The voice the user wants you to use",
|
"properties": {
|
||||||
},
|
"voice": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The voice the user wants you to use",
|
||||||
},
|
},
|
||||||
"required": ["voice"],
|
|
||||||
},
|
},
|
||||||
|
"required": ["voice"],
|
||||||
},
|
},
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can do the following voices: 'News Lady', 'British Lady' and 'Barbershop Man'.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
ParallelPipeline( # TTS (one of the following vocies)
|
||||||
|
[FunctionFilter(news_lady_filter), news_lady], # News Lady voice
|
||||||
|
[
|
||||||
|
FunctionFilter(british_lady_filter),
|
||||||
|
british_lady,
|
||||||
|
], # British Reading Lady voice
|
||||||
|
[FunctionFilter(barbershop_man_filter), barbershop_man], # Barbershop Man voice
|
||||||
|
),
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
messages = [
|
)
|
||||||
|
|
||||||
|
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append(
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can do the following voices: 'News Lady', 'British Lady' and 'Barbershop Man'.",
|
"content": f"Please introduce yourself to the user and let them know the voices you can do. Your initial responses should be as if you were a {current_voice}.",
|
||||||
},
|
}
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
ParallelPipeline( # TTS (one of the following vocies)
|
|
||||||
[FunctionFilter(news_lady_filter), news_lady], # News Lady voice
|
|
||||||
[
|
|
||||||
FunctionFilter(british_lady_filter),
|
|
||||||
british_lady,
|
|
||||||
], # British Reading Lady voice
|
|
||||||
[FunctionFilter(barbershop_man_filter), barbershop_man], # Barbershop Man voice
|
|
||||||
),
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append(
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": f"Please introduce yourself to the user and let them know the voices you can do. Your initial responses should be as if you were a {current_voice}.",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,16 +4,12 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from deepgram import LiveOptions
|
from deepgram import LiveOptions
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from openai.types.chat import ChatCompletionToolParam
|
from openai.types.chat import ChatCompletionToolParam
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
from pipecat.pipeline.parallel_pipeline import ParallelPipeline
|
||||||
@@ -25,12 +21,12 @@ from pipecat.processors.filters.function_filter import FunctionFilter
|
|||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
current_language = "English"
|
current_language = "English"
|
||||||
|
|
||||||
@@ -49,101 +45,110 @@ async def spanish_filter(frame) -> bool:
|
|||||||
return current_language == "Spanish"
|
return current_language == "Spanish"
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Pipecat",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(
|
stt = DeepgramSTTService(
|
||||||
api_key=os.getenv("DEEPGRAM_API_KEY"), live_options=LiveOptions(language="multi")
|
api_key=os.getenv("DEEPGRAM_API_KEY"), live_options=LiveOptions(language="multi")
|
||||||
)
|
)
|
||||||
|
|
||||||
english_tts = CartesiaTTSService(
|
english_tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
spanish_tts = CartesiaTTSService(
|
spanish_tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="846d6cb0-2301-48b6-9683-48f5618ea2f6", # Spanish-speaking Lady
|
voice_id="d4db5fb9-f44b-4bd1-85fa-192e0f0d75f9", # Spanish-speaking Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
llm.register_function("switch_language", switch_language)
|
llm.register_function("switch_language", switch_language)
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
ChatCompletionToolParam(
|
ChatCompletionToolParam(
|
||||||
type="function",
|
type="function",
|
||||||
function={
|
function={
|
||||||
"name": "switch_language",
|
"name": "switch_language",
|
||||||
"description": "Switch to another language when the user asks you to",
|
"description": "Switch to another language when the user asks you to",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"language": {
|
"language": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The language the user wants you to speak",
|
"description": "The language the user wants you to speak",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"required": ["language"],
|
|
||||||
},
|
},
|
||||||
|
"required": ["language"],
|
||||||
},
|
},
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can speak the following languages: 'English' and 'Spanish'.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
ParallelPipeline( # TTS (bot will speak the chosen language)
|
||||||
|
[FunctionFilter(english_filter), english_tts], # English
|
||||||
|
[FunctionFilter(spanish_filter), spanish_tts], # Spanish
|
||||||
|
),
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
messages = [
|
)
|
||||||
|
|
||||||
|
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append(
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities. Respond to what the user said in a creative and helpful way. Your output should not include non-alphanumeric characters. You can speak the following languages: 'English' and 'Spanish'.",
|
"content": f"Please introduce yourself to the user and let them know the languages you speak. Your initial responses should be in {current_language}.",
|
||||||
},
|
}
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
stt, # STT
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
ParallelPipeline( # TTS (bot will speak the chosen language)
|
|
||||||
[FunctionFilter(english_filter), english_tts], # English
|
|
||||||
[FunctionFilter(spanish_filter), spanish_tts], # Spanish
|
|
||||||
),
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append(
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": f"Please introduce yourself to the user and let them know the languages you speak. Your initial responses should be in {current_language}.",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,49 +4,45 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.deepgram.tts import DeepgramTTSService
|
from pipecat.services.deepgram.tts import DeepgramTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import (
|
from pipecat.transports.base_transport import TransportParams
|
||||||
DailyParams,
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
DailyTransport,
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
DailyTransportMessageFrame,
|
from pipecat.transports.services.daily import DailyTransportMessageFrame
|
||||||
)
|
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an HTTP session
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
(room_url, token) = await configure(session)
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
transport = DailyTransport(
|
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = DeepgramTTSService(
|
tts = DeepgramTTSService(
|
||||||
aiohttp_session=session,
|
aiohttp_session=session,
|
||||||
@@ -77,6 +73,7 @@ async def main():
|
|||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT
|
||||||
context_aggregator.user(),
|
context_aggregator.user(),
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
tts, # TTS
|
tts, # TTS
|
||||||
@@ -93,15 +90,11 @@ async def main():
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# When a participant joins, start transcription for that participant so the
|
|
||||||
# bot can "hear" and respond to them.
|
|
||||||
@transport.event_handler("on_participant_joined")
|
|
||||||
async def on_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
|
|
||||||
# When the first participant joins, the bot should introduce itself.
|
# When the first participant joins, the bot should introduce itself.
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
@@ -134,9 +127,21 @@ async def main():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"message handling error: {e} - {message}")
|
logger.debug(f"message handling error: {e} - {message}")
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import EndFrame, LLMMessagesFrame, TTSSpeakFrame
|
from pipecat.frames.frames import EndFrame, LLMMessagesFrame, TTSSpeakFrame
|
||||||
@@ -21,111 +17,123 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.processors.user_idle_processor import UserIdleProcessor
|
from pipecat.processors.user_idle_processor import UserIdleProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, token) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
messages = [
|
messages = [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
async def handle_user_idle(user_idle: UserIdleProcessor, retry_count: int) -> bool:
|
||||||
|
if retry_count == 1:
|
||||||
|
# First attempt: Add a gentle prompt to the conversation
|
||||||
|
messages.append(
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "The user has been quiet. Politely and briefly ask if they're still there.",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await user_idle.push_frame(LLMMessagesFrame(messages))
|
||||||
|
return True
|
||||||
|
elif retry_count == 2:
|
||||||
|
# Second attempt: More direct prompt
|
||||||
|
messages.append(
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "The user is still inactive. Ask if they'd like to continue our conversation.",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await user_idle.push_frame(LLMMessagesFrame(messages))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# Third attempt: End the conversation
|
||||||
|
await user_idle.push_frame(
|
||||||
|
TTSSpeakFrame("It seems like you're busy right now. Have a nice day!")
|
||||||
|
)
|
||||||
|
await task.queue_frame(EndFrame())
|
||||||
|
return False
|
||||||
|
|
||||||
|
user_idle = UserIdleProcessor(callback=handle_user_idle, timeout=5.0)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
user_idle, # Idle user check-in
|
||||||
|
context_aggregator.user(),
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
async def handle_user_idle(user_idle: UserIdleProcessor, retry_count: int) -> bool:
|
@transport.event_handler("on_client_connected")
|
||||||
if retry_count == 1:
|
async def on_client_connected(transport, client):
|
||||||
# First attempt: Add a gentle prompt to the conversation
|
logger.info(f"Client connected")
|
||||||
messages.append(
|
# Kick off the conversation.
|
||||||
{
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
"role": "system",
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
"content": "The user has been quiet. Politely and briefly ask if they're still there.",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await user_idle.push_frame(LLMMessagesFrame(messages))
|
|
||||||
return True
|
|
||||||
elif retry_count == 2:
|
|
||||||
# Second attempt: More direct prompt
|
|
||||||
messages.append(
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "The user is still inactive. Ask if they'd like to continue our conversation.",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await user_idle.push_frame(LLMMessagesFrame(messages))
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
# Third attempt: End the conversation
|
|
||||||
await user_idle.push_frame(
|
|
||||||
TTSSpeakFrame("It seems like you're busy right now. Have a nice day!")
|
|
||||||
)
|
|
||||||
await task.queue_frame(EndFrame())
|
|
||||||
return False
|
|
||||||
|
|
||||||
user_idle = UserIdleProcessor(callback=handle_user_idle, timeout=5.0)
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_closed")
|
||||||
[
|
async def on_client_closed(transport, client):
|
||||||
transport.input(), # Transport user input
|
logger.info(f"Client closed connection")
|
||||||
user_idle, # Idle user check-in
|
await task.cancel()
|
||||||
context_aggregator.user(),
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
await runner.run(task)
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -5,67 +5,71 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure_with_args
|
|
||||||
|
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.gstreamer.pipeline_source import GStreamerPipelineSource
|
from pipecat.processors.gstreamer.pipeline_source import GStreamerPipelineSource
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
# Parse command line arguments
|
||||||
|
# This will be used to pass the input video file to the bot
|
||||||
|
# You can run the bot with a command like:
|
||||||
|
# python 18-gstreamer-filesrc.py -i path/to/video.mp4
|
||||||
|
def parse_arguments():
|
||||||
|
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
|
||||||
|
parser.add_argument("-i", "--input", type=str, required=True, help="Input video file")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
args = parse_arguments()
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
parser = argparse.ArgumentParser(description="Daily AI SDK Bot Sample")
|
|
||||||
parser.add_argument("-i", "--input", type=str, required=True, help="Input video file")
|
|
||||||
|
|
||||||
(room_url, _, args) = await configure_with_args(session, parser)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
room_url,
|
logger.info(f"Starting bot with video input: {args.input}")
|
||||||
None,
|
|
||||||
"GStreamer",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
camera_out_enabled=True,
|
|
||||||
camera_out_width=1280,
|
|
||||||
camera_out_height=720,
|
|
||||||
camera_out_is_live=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
gst = GStreamerPipelineSource(
|
transport = SmallWebRTCTransport(
|
||||||
pipeline=f"filesrc location={args.input}",
|
webrtc_connection=webrtc_connection,
|
||||||
out_params=GStreamerPipelineSource.OutputParams(
|
params=TransportParams(
|
||||||
video_width=1280,
|
audio_in_enabled=True,
|
||||||
video_height=720,
|
camera_out_enabled=True,
|
||||||
),
|
camera_out_is_live=True,
|
||||||
)
|
camera_out_width=1280,
|
||||||
|
camera_out_height=720,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
gst = GStreamerPipelineSource(
|
||||||
[
|
pipeline=f"filesrc location={args.input}",
|
||||||
gst, # GStreamer file source
|
out_params=GStreamerPipelineSource.OutputParams(
|
||||||
transport.output(), # Transport bot output
|
video_width=1280,
|
||||||
]
|
video_height=720,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
gst, # GStreamer file source
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(pipeline)
|
||||||
|
|
||||||
await runner.run(task)
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,62 +4,57 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineTask
|
from pipecat.pipeline.task import PipelineTask
|
||||||
from pipecat.processors.gstreamer.pipeline_source import GStreamerPipelineSource
|
from pipecat.processors.gstreamer.pipeline_source import GStreamerPipelineSource
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot with video test source")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
camera_out_enabled=True,
|
||||||
|
camera_out_is_live=True,
|
||||||
|
camera_out_width=1280,
|
||||||
|
camera_out_height=720,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
gst = GStreamerPipelineSource(
|
||||||
room_url,
|
pipeline='videotestsrc ! capsfilter caps="video/x-raw,width=1280,height=720,framerate=30/1"',
|
||||||
None,
|
out_params=GStreamerPipelineSource.OutputParams(
|
||||||
"GStreamer",
|
video_width=1280, video_height=720, clock_sync=False
|
||||||
DailyParams(
|
),
|
||||||
camera_out_enabled=True,
|
)
|
||||||
camera_out_width=1280,
|
|
||||||
camera_out_height=720,
|
|
||||||
camera_out_is_live=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
gst = GStreamerPipelineSource(
|
pipeline = Pipeline(
|
||||||
pipeline='videotestsrc ! capsfilter caps="video/x-raw,width=1280,height=720,framerate=30/1"',
|
[
|
||||||
out_params=GStreamerPipelineSource.OutputParams(
|
gst, # GStreamer test source
|
||||||
video_width=1280, video_height=720, clock_sync=False
|
transport.output(), # Transport bot output
|
||||||
),
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
task = PipelineTask(pipeline)
|
||||||
[
|
|
||||||
gst, # GStreamer file source
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(pipeline)
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
await runner.run(task)
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -29,13 +25,12 @@ from pipecat.services.openai_realtime_beta import (
|
|||||||
SemanticTurnDetection,
|
SemanticTurnDetection,
|
||||||
SessionProperties,
|
SessionProperties,
|
||||||
)
|
)
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
temperature = 75 if args["format"] == "fahrenheit" else 24
|
temperature = 75 if args["format"] == "fahrenheit" else 24
|
||||||
@@ -70,34 +65,30 @@ weather_function = FunctionSchema(
|
|||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_in_enabled=True,
|
vad_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
||||||
transcription_enabled=False,
|
vad_audio_passthrough=True,
|
||||||
vad_enabled=True,
|
),
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
)
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
session_properties = SessionProperties(
|
session_properties = SessionProperties(
|
||||||
input_audio_transcription=InputAudioTranscription(),
|
input_audio_transcription=InputAudioTranscription(),
|
||||||
# Set openai TurnDetection parameters. Not setting this at all will turn it
|
# Set openai TurnDetection parameters. Not setting this at all will turn it
|
||||||
# on by default
|
# on by default
|
||||||
turn_detection=SemanticTurnDetection(),
|
turn_detection=SemanticTurnDetection(),
|
||||||
# Or set to False to disable openai turn detection and use transport VAD
|
# Or set to False to disable openai turn detection and use transport VAD
|
||||||
# turn_detection=False,
|
# turn_detection=False,
|
||||||
input_audio_noise_reduction=InputAudioNoiseReduction(type="near_field"),
|
input_audio_noise_reduction=InputAudioNoiseReduction(type="near_field"),
|
||||||
# tools=tools,
|
# tools=tools,
|
||||||
instructions="""Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.
|
instructions="""Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.
|
||||||
|
|
||||||
Act like a human, but remember that you aren't a human and that you can't do human
|
Act like a human, but remember that you aren't a human and that you can't do human
|
||||||
things in the real world. Your voice and personality should be warm and engaging, with a lively and
|
things in the real world. Your voice and personality should be warm and engaging, with a lively and
|
||||||
@@ -111,68 +102,79 @@ You are participating in a voice conversation. Keep your responses concise, shor
|
|||||||
unless specifically asked to elaborate on a topic.
|
unless specifically asked to elaborate on a topic.
|
||||||
|
|
||||||
Remember, your responses should be short. Just one or two sentences, usually.""",
|
Remember, your responses should be short. Just one or two sentences, usually.""",
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAIRealtimeBetaLLMService(
|
llm = OpenAIRealtimeBetaLLMService(
|
||||||
api_key=os.getenv("OPENAI_API_KEY"),
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
session_properties=session_properties,
|
session_properties=session_properties,
|
||||||
start_audio_paused=False,
|
start_audio_paused=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# you can either register a single function for all function calls, or specific functions
|
# you can either register a single function for all function calls, or specific functions
|
||||||
# llm.register_function(None, fetch_weather_from_api)
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
# Create a standard OpenAI LLM context object using the normal messages format. The
|
# Create a standard OpenAI LLM context object using the normal messages format. The
|
||||||
# OpenAIRealtimeBetaLLMService will convert this internally to messages that the
|
# OpenAIRealtimeBetaLLMService will convert this internally to messages that the
|
||||||
# openai WebSocket API can understand.
|
# openai WebSocket API can understand.
|
||||||
context = OpenAILLMContext(
|
context = OpenAILLMContext(
|
||||||
[{"role": "user", "content": "Say hello!"}],
|
[{"role": "user", "content": "Say hello!"}],
|
||||||
# [{"role": "user", "content": [{"type": "text", "text": "Say hello!"}]}],
|
# [{"role": "user", "content": [{"type": "text", "text": "Say hello!"}]}],
|
||||||
# [
|
# [
|
||||||
# {
|
# {
|
||||||
# "role": "user",
|
# "role": "user",
|
||||||
# "content": [
|
# "content": [
|
||||||
# {"type": "text", "text": "Say"},
|
# {"type": "text", "text": "Say"},
|
||||||
# {"type": "text", "text": "yo what's up!"},
|
# {"type": "text", "text": "yo what's up!"},
|
||||||
# ],
|
# ],
|
||||||
# }
|
# }
|
||||||
# ],
|
# ],
|
||||||
tools,
|
tools,
|
||||||
)
|
)
|
||||||
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
context_aggregator.user(),
|
context_aggregator.user(),
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
transport.output(), # Transport bot output
|
transport.output(), # Transport bot output
|
||||||
context_aggregator.assistant(),
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
allow_interruptions=True,
|
allow_interruptions=True,
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
enable_usage_metrics=True,
|
enable_usage_metrics=True,
|
||||||
# report_only_initial_ttfb=True,
|
report_only_initial_ttfb=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
@@ -27,13 +23,12 @@ from pipecat.services.openai_realtime_beta import (
|
|||||||
InputAudioTranscription,
|
InputAudioTranscription,
|
||||||
SessionProperties,
|
SessionProperties,
|
||||||
)
|
)
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
temperature = 75 if args["format"] == "fahrenheit" else 24
|
temperature = 75 if args["format"] == "fahrenheit" else 24
|
||||||
@@ -69,33 +64,29 @@ weather_function = FunctionSchema(
|
|||||||
tools = ToolsSchema(standard_tools=[weather_function])
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_in_enabled=True,
|
vad_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
||||||
transcription_enabled=False,
|
vad_audio_passthrough=True,
|
||||||
vad_enabled=True,
|
),
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
)
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
session_properties = SessionProperties(
|
session_properties = SessionProperties(
|
||||||
input_audio_transcription=InputAudioTranscription(),
|
input_audio_transcription=InputAudioTranscription(model="whisper-1"),
|
||||||
# Set openai TurnDetection parameters. Not setting this at all will turn it
|
# Set openai TurnDetection parameters. Not setting this at all will turn it
|
||||||
# on by default
|
# on by default
|
||||||
# turn_detection=TurnDetection(silence_duration_ms=1000),
|
# turn_detection=TurnDetection(silence_duration_ms=1000),
|
||||||
# Or set to False to disable openai turn detection and use transport VAD
|
# Or set to False to disable openai turn detection and use transport VAD
|
||||||
# turn_detection=False,
|
# turn_detection=False,
|
||||||
# tools=tools,
|
# tools=tools,
|
||||||
instructions="""Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.
|
instructions="""Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.
|
||||||
|
|
||||||
Act like a human, but remember that you aren't a human and that you can't do human
|
Act like a human, but remember that you aren't a human and that you can't do human
|
||||||
things in the real world. Your voice and personality should be warm and engaging, with a lively and
|
things in the real world. Your voice and personality should be warm and engaging, with a lively and
|
||||||
@@ -109,69 +100,80 @@ You are participating in a voice conversation. Keep your responses concise, shor
|
|||||||
unless specifically asked to elaborate on a topic.
|
unless specifically asked to elaborate on a topic.
|
||||||
|
|
||||||
Remember, your responses should be short. Just one or two sentences, usually.""",
|
Remember, your responses should be short. Just one or two sentences, usually.""",
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = AzureRealtimeBetaLLMService(
|
llm = AzureRealtimeBetaLLMService(
|
||||||
api_key=os.getenv("AZURE_REALTIME_API_KEY"),
|
api_key=os.getenv("AZURE_REALTIME_API_KEY"),
|
||||||
base_url=os.getenv("AZURE_REALTIME_BASE_URL"),
|
base_url=os.getenv("AZURE_REALTIME_BASE_URL"),
|
||||||
session_properties=session_properties,
|
session_properties=session_properties,
|
||||||
start_audio_paused=False,
|
start_audio_paused=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# you can either register a single function for all function calls, or specific functions
|
# you can either register a single function for all function calls, or specific functions
|
||||||
# llm.register_function(None, fetch_weather_from_api)
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
# Create a standard OpenAI LLM context object using the normal messages format. The
|
# Create a standard OpenAI LLM context object using the normal messages format. The
|
||||||
# OpenAIRealtimeBetaLLMService will convert this internally to messages that the
|
# OpenAIRealtimeBetaLLMService will convert this internally to messages that the
|
||||||
# openai WebSocket API can understand.
|
# openai WebSocket API can understand.
|
||||||
context = OpenAILLMContext(
|
context = OpenAILLMContext(
|
||||||
[{"role": "user", "content": "Say hello!"}],
|
[{"role": "user", "content": "Say hello!"}],
|
||||||
# [{"role": "user", "content": [{"type": "text", "text": "Say hello!"}]}],
|
# [{"role": "user", "content": [{"type": "text", "text": "Say hello!"}]}],
|
||||||
# [
|
# [
|
||||||
# {
|
# {
|
||||||
# "role": "user",
|
# "role": "user",
|
||||||
# "content": [
|
# "content": [
|
||||||
# {"type": "text", "text": "Say"},
|
# {"type": "text", "text": "Say"},
|
||||||
# {"type": "text", "text": "yo what's up!"},
|
# {"type": "text", "text": "yo what's up!"},
|
||||||
# ],
|
# ],
|
||||||
# }
|
# }
|
||||||
# ],
|
# ],
|
||||||
tools,
|
tools,
|
||||||
)
|
)
|
||||||
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
context_aggregator.user(),
|
context_aggregator.user(),
|
||||||
llm, # LLM
|
llm, # LLM
|
||||||
transport.output(), # Transport bot output
|
transport.output(), # Transport bot output
|
||||||
context_aggregator.assistant(),
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
allow_interruptions=True,
|
allow_interruptions=True,
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
enable_usage_metrics=True,
|
enable_usage_metrics=True,
|
||||||
# report_only_initial_ttfb=True,
|
report_only_initial_ttfb=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,17 +4,13 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -25,13 +21,14 @@ from pipecat.processors.aggregators.openai_llm_context import (
|
|||||||
OpenAILLMContext,
|
OpenAILLMContext,
|
||||||
)
|
)
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
||||||
tts = None
|
tts = None
|
||||||
@@ -165,71 +162,84 @@ tools = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
global tts
|
global tts
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
# you can either register a single function for all function calls, or specific functions
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
# llm.register_function(None, fetch_weather_from_api)
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
llm.register_function("save_conversation", save_conversation)
|
|
||||||
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
|
||||||
llm.register_function("load_conversation", load_conversation)
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
# you can either register a single function for all function calls, or specific functions
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
llm.register_function("save_conversation", save_conversation)
|
||||||
|
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
||||||
|
llm.register_function("load_conversation", load_conversation)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
context = OpenAILLMContext(messages, tools)
|
||||||
[
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
transport.input(), # Transport user input
|
|
||||||
context_aggregator.user(),
|
|
||||||
llm, # LLM
|
|
||||||
tts,
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
pipeline = Pipeline(
|
||||||
pipeline,
|
[
|
||||||
params=PipelineParams(
|
transport.input(), # Transport user input
|
||||||
allow_interruptions=True,
|
stt, # STT
|
||||||
enable_metrics=True,
|
context_aggregator.user(),
|
||||||
enable_usage_metrics=True,
|
llm, # LLM
|
||||||
# report_only_initial_ttfb=True,
|
tts,
|
||||||
),
|
transport.output(), # Transport bot output
|
||||||
)
|
context_aggregator.assistant(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
task = PipelineTask(
|
||||||
async def on_first_participant_joined(transport, participant):
|
pipeline,
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
params=PipelineParams(
|
||||||
# Kick off the conversation.
|
allow_interruptions=True,
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -8,13 +8,10 @@ import asyncio
|
|||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -24,19 +21,19 @@ from pipecat.pipeline.task import PipelineParams, PipelineTask
|
|||||||
from pipecat.processors.aggregators.openai_llm_context import (
|
from pipecat.processors.aggregators.openai_llm_context import (
|
||||||
OpenAILLMContext,
|
OpenAILLMContext,
|
||||||
)
|
)
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai_realtime_beta import (
|
from pipecat.services.openai_realtime_beta import (
|
||||||
InputAudioTranscription,
|
InputAudioTranscription,
|
||||||
OpenAIRealtimeBetaLLMService,
|
OpenAIRealtimeBetaLLMService,
|
||||||
SessionProperties,
|
SessionProperties,
|
||||||
TurnDetection,
|
TurnDetection,
|
||||||
)
|
)
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
||||||
|
|
||||||
|
|
||||||
@@ -167,33 +164,31 @@ tools = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_in_enabled=True,
|
vad_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
||||||
transcription_enabled=False,
|
vad_audio_passthrough=True,
|
||||||
vad_enabled=True,
|
),
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
)
|
||||||
vad_audio_passthrough=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
session_properties = SessionProperties(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
input_audio_transcription=InputAudioTranscription(),
|
|
||||||
# Set openai TurnDetection parameters. Not setting this at all will turn it
|
session_properties = SessionProperties(
|
||||||
# on by default
|
input_audio_transcription=InputAudioTranscription(),
|
||||||
turn_detection=TurnDetection(silence_duration_ms=1000),
|
# Set openai TurnDetection parameters. Not setting this at all will turn it
|
||||||
# Or set to False to disable openai turn detection and use transport VAD
|
# on by default
|
||||||
# turn_detection=False,
|
turn_detection=TurnDetection(silence_duration_ms=1000),
|
||||||
# tools=tools,
|
# Or set to False to disable openai turn detection and use transport VAD
|
||||||
instructions="""Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.
|
# turn_detection=False,
|
||||||
|
# tools=tools,
|
||||||
|
instructions="""Your knowledge cutoff is 2023-10. You are a helpful and friendly AI.
|
||||||
|
|
||||||
Act like a human, but remember that you aren't a human and that you can't do human
|
Act like a human, but remember that you aren't a human and that you can't do human
|
||||||
things in the real world. Your voice and personality should be warm and engaging, with a lively and
|
things in the real world. Your voice and personality should be warm and engaging, with a lively and
|
||||||
@@ -207,54 +202,66 @@ You are participating in a voice conversation. Keep your responses concise, shor
|
|||||||
unless specifically asked to elaborate on a topic.
|
unless specifically asked to elaborate on a topic.
|
||||||
|
|
||||||
Remember, your responses should be short. Just one or two sentences, usually.""",
|
Remember, your responses should be short. Just one or two sentences, usually.""",
|
||||||
)
|
)
|
||||||
|
|
||||||
llm = OpenAIRealtimeBetaLLMService(
|
llm = OpenAIRealtimeBetaLLMService(
|
||||||
api_key=os.getenv("OPENAI_API_KEY"),
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
session_properties=session_properties,
|
session_properties=session_properties,
|
||||||
start_audio_paused=False,
|
start_audio_paused=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
# you can either register a single function for all function calls, or specific functions
|
# you can either register a single function for all function calls, or specific functions
|
||||||
# llm.register_function(None, fetch_weather_from_api)
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
llm.register_function("save_conversation", save_conversation)
|
llm.register_function("save_conversation", save_conversation)
|
||||||
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
||||||
llm.register_function("load_conversation", load_conversation)
|
llm.register_function("load_conversation", load_conversation)
|
||||||
|
|
||||||
context = OpenAILLMContext([], tools)
|
context = OpenAILLMContext([], tools)
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(), # Transport user input
|
transport.input(), # Transport user input
|
||||||
context_aggregator.user(),
|
stt, # STT
|
||||||
llm, # LLM
|
context_aggregator.user(),
|
||||||
transport.output(), # Transport bot output
|
llm, # LLM
|
||||||
context_aggregator.assistant(),
|
transport.output(), # Transport bot output
|
||||||
]
|
context_aggregator.assistant(),
|
||||||
)
|
]
|
||||||
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
allow_interruptions=True,
|
allow_interruptions=True,
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
enable_usage_metrics=True,
|
enable_usage_metrics=True,
|
||||||
# report_only_initial_ttfb=True,
|
report_only_initial_ttfb=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client connected")
|
||||||
# Kick off the conversation.
|
# Kick off the conversation.
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,17 +4,13 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -26,12 +22,13 @@ from pipecat.processors.aggregators.openai_llm_context import (
|
|||||||
)
|
)
|
||||||
from pipecat.services.anthropic.llm import AnthropicLLMService
|
from pipecat.services.anthropic.llm import AnthropicLLMService
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
||||||
tts = None
|
tts = None
|
||||||
@@ -160,73 +157,86 @@ tools = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
global tts
|
global tts
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
transcription_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
||||||
vad_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = AnthropicLLMService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("ANTHROPIC_API_KEY"), model="claude-3-5-sonnet-latest"
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
# you can either register a single function for all function calls, or specific functions
|
llm = AnthropicLLMService(
|
||||||
# llm.register_function(None, fetch_weather_from_api)
|
api_key=os.getenv("ANTHROPIC_API_KEY"), model="claude-3-5-sonnet-latest"
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
)
|
||||||
llm.register_function("save_conversation", save_conversation)
|
|
||||||
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
|
||||||
llm.register_function("load_conversation", load_conversation)
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
# you can either register a single function for all function calls, or specific functions
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
llm.register_function("save_conversation", save_conversation)
|
||||||
|
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
||||||
|
llm.register_function("load_conversation", load_conversation)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
context = OpenAILLMContext(messages, tools)
|
||||||
[
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
transport.input(), # Transport user input
|
|
||||||
context_aggregator.user(),
|
|
||||||
llm, # LLM
|
|
||||||
tts,
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
pipeline = Pipeline(
|
||||||
pipeline,
|
[
|
||||||
params=PipelineParams(
|
transport.input(), # Transport user input
|
||||||
allow_interruptions=True,
|
stt, # STT
|
||||||
enable_metrics=True,
|
context_aggregator.user(),
|
||||||
enable_usage_metrics=True,
|
llm, # LLM
|
||||||
# report_only_initial_ttfb=True,
|
tts,
|
||||||
),
|
transport.output(), # Transport bot output
|
||||||
)
|
context_aggregator.assistant(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
task = PipelineTask(
|
||||||
async def on_first_participant_joined(transport, participant):
|
pipeline,
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
params=PipelineParams(
|
||||||
# Kick off the conversation.
|
allow_interruptions=True,
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
# report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,17 +4,13 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import glob
|
import glob
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -25,19 +21,21 @@ from pipecat.processors.aggregators.openai_llm_context import (
|
|||||||
OpenAILLMContext,
|
OpenAILLMContext,
|
||||||
)
|
)
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.google.llm import GoogleLLMService
|
from pipecat.services.google.llm import GoogleLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
video_participant_id = None
|
video_participant_id = None
|
||||||
|
|
||||||
|
|
||||||
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
BASE_FILENAME = "/tmp/pipecat_conversation_"
|
||||||
tts = None
|
tts = None
|
||||||
|
webrtc_peer_id = None
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
@@ -54,8 +52,11 @@ async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context
|
|||||||
|
|
||||||
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
async def get_image(function_name, tool_call_id, arguments, llm, context, result_callback):
|
||||||
question = arguments["question"]
|
question = arguments["question"]
|
||||||
|
logger.debug(f"Requesting image with user_id={webrtc_peer_id}, question={question}")
|
||||||
|
|
||||||
|
# Request the image frame
|
||||||
await llm.request_image_frame(
|
await llm.request_image_frame(
|
||||||
user_id=video_participant_id,
|
user_id=webrtc_peer_id,
|
||||||
function_name=function_name,
|
function_name=function_name,
|
||||||
tool_call_id=tool_call_id,
|
tool_call_id=tool_call_id,
|
||||||
text_content=question,
|
text_content=question,
|
||||||
@@ -220,75 +221,87 @@ tools = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
global tts
|
global tts, webrtc_peer_id
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_peer_id = webrtc_connection.pc_id
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
logger.info(f"Starting bot with peer_id: {webrtc_peer_id}")
|
||||||
room_url,
|
|
||||||
token,
|
|
||||||
"Respond bot",
|
|
||||||
DailyParams(
|
|
||||||
audio_out_enabled=True,
|
|
||||||
transcription_enabled=True,
|
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
transport = SmallWebRTCTransport(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
webrtc_connection=webrtc_connection,
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
params=TransportParams(
|
||||||
)
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.8)),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
llm = GoogleLLMService(model="gemini-2.0-flash-001", api_key=os.getenv("GOOGLE_API_KEY"))
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
# you can either register a single function for all function calls, or specific functions
|
tts = CartesiaTTSService(
|
||||||
# llm.register_function(None, fetch_weather_from_api)
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
llm.register_function("save_conversation", save_conversation)
|
)
|
||||||
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
|
||||||
llm.register_function("load_conversation", load_conversation)
|
|
||||||
llm.register_function("get_image", get_image)
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
llm = GoogleLLMService(model="gemini-2.0-flash-001", api_key=os.getenv("GOOGLE_API_KEY"))
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
# you can either register a single function for all function calls, or specific functions
|
||||||
[
|
# llm.register_function(None, fetch_weather_from_api)
|
||||||
transport.input(), # Transport user input
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
context_aggregator.user(),
|
llm.register_function("save_conversation", save_conversation)
|
||||||
llm, # LLM
|
llm.register_function("get_saved_conversation_filenames", get_saved_conversation_filenames)
|
||||||
tts,
|
llm.register_function("load_conversation", load_conversation)
|
||||||
transport.output(), # Transport bot output
|
llm.register_function("get_image", get_image)
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
context = OpenAILLMContext(messages, tools)
|
||||||
pipeline,
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
# report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
pipeline = Pipeline(
|
||||||
async def on_first_participant_joined(transport, participant):
|
[
|
||||||
global video_participant_id
|
transport.input(), # Transport user input
|
||||||
video_participant_id = participant["id"]
|
stt, # STT
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
context_aggregator.user(),
|
||||||
await transport.capture_participant_video(video_participant_id, framerate=0)
|
llm, # LLM
|
||||||
# Kick off the conversation.
|
tts,
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
# report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import TextFrame
|
from pipecat.frames.frames import TextFrame
|
||||||
@@ -28,142 +24,146 @@ from pipecat.services.cartesia.tts import CartesiaTTSService
|
|||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.sync.event_notifier import EventNotifier
|
from pipecat.sync.event_notifier import EventNotifier
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
transport = SmallWebRTCTransport(
|
||||||
async with aiohttp.ClientSession() as session:
|
webrtc_connection=webrtc_connection,
|
||||||
(room_url, _) = await configure(session)
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
room_url,
|
|
||||||
None,
|
tts = CartesiaTTSService(
|
||||||
"Respond bot",
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
DailyParams(
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
audio_out_enabled=True,
|
)
|
||||||
vad_enabled=True,
|
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
# This is the LLM that will be used to detect if the user has finished a
|
||||||
vad_audio_passthrough=True,
|
# statement. This doesn't really need to be an LLM, we could use NLP
|
||||||
|
# libraries for that, but it was easier as an example because we
|
||||||
|
# leverage the context aggregators.
|
||||||
|
statement_llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
|
statement_messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "Determine if the user's statement is a complete sentence or question, ending in a natural pause or punctuation. Return 'YES' if it is complete and 'NO' if it seems to leave a thought unfinished.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
statement_context = OpenAILLMContext(statement_messages)
|
||||||
|
statement_context_aggregator = statement_llm.create_context_aggregator(statement_context)
|
||||||
|
|
||||||
|
# This is the regular LLM.
|
||||||
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
# We have instructed the LLM to return 'YES' if it thinks the user
|
||||||
|
# completed a sentence. So, if it's 'YES' we will return true in this
|
||||||
|
# predicate which will wake up the notifier.
|
||||||
|
async def wake_check_filter(frame):
|
||||||
|
return frame.text == "YES"
|
||||||
|
|
||||||
|
# This is a notifier that we use to synchronize the two LLMs.
|
||||||
|
notifier = EventNotifier()
|
||||||
|
|
||||||
|
# This a filter that will wake up the notifier if the given predicate
|
||||||
|
# (wake_check_filter) returns true.
|
||||||
|
completness_check = WakeNotifierFilter(notifier, types=(TextFrame,), filter=wake_check_filter)
|
||||||
|
|
||||||
|
# This processor keeps the last context and will let it through once the
|
||||||
|
# notifier is woken up. We start with the gate open because we send an
|
||||||
|
# initial context frame to start the conversation.
|
||||||
|
gated_context_aggregator = GatedOpenAILLMContextAggregator(notifier=notifier, start_open=True)
|
||||||
|
|
||||||
|
# Notify if the user hasn't said anything.
|
||||||
|
async def user_idle_notifier(frame):
|
||||||
|
await notifier.notify()
|
||||||
|
|
||||||
|
# Sometimes the LLM will fail detecting if a user has completed a
|
||||||
|
# sentence, this will wake up the notifier if that happens.
|
||||||
|
user_idle = UserIdleProcessor(callback=user_idle_notifier, timeout=3.0)
|
||||||
|
|
||||||
|
# The ParallePipeline input are the user transcripts. We have two
|
||||||
|
# contexts. The first one will be used to determine if the user finished
|
||||||
|
# a statement and if so the notifier will be woken up. The second
|
||||||
|
# context is simply the regular context but it's gated waiting for the
|
||||||
|
# notifier to be woken up.
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt,
|
||||||
|
ParallelPipeline(
|
||||||
|
[
|
||||||
|
statement_context_aggregator.user(),
|
||||||
|
statement_llm,
|
||||||
|
completness_check,
|
||||||
|
NullFilter(),
|
||||||
|
],
|
||||||
|
[context_aggregator.user(), gated_context_aggregator, llm],
|
||||||
),
|
),
|
||||||
)
|
user_idle,
|
||||||
|
tts, # TTS
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
tts = CartesiaTTSService(
|
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
# This is the LLM that will be used to detect if the user has finished a
|
|
||||||
# statement. This doesn't really need to be an LLM, we could use NLP
|
|
||||||
# libraries for that, but it was easier as an example because we
|
|
||||||
# leverage the context aggregators.
|
|
||||||
statement_llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
|
||||||
|
|
||||||
statement_messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "Determine if the user's statement is a complete sentence or question, ending in a natural pause or punctuation. Return 'YES' if it is complete and 'NO' if it seems to leave a thought unfinished.",
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
statement_context = OpenAILLMContext(statement_messages)
|
task = PipelineTask(
|
||||||
statement_context_aggregator = statement_llm.create_context_aggregator(statement_context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# This is the regular LLM.
|
@transport.event_handler("on_client_connected")
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
messages = [
|
@transport.event_handler("on_client_disconnected")
|
||||||
{
|
async def on_client_disconnected(transport, client):
|
||||||
"role": "system",
|
logger.info(f"Client disconnected")
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
@transport.event_handler("on_client_closed")
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
# We have instructed the LLM to return 'YES' if it thinks the user
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
# completed a sentence. So, if it's 'YES' we will return true in this
|
|
||||||
# predicate which will wake up the notifier.
|
|
||||||
async def wake_check_filter(frame):
|
|
||||||
return frame.text == "YES"
|
|
||||||
|
|
||||||
# This is a notifier that we use to synchronize the two LLMs.
|
await runner.run(task)
|
||||||
notifier = EventNotifier()
|
|
||||||
|
|
||||||
# This a filter that will wake up the notifier if the given predicate
|
|
||||||
# (wake_check_filter) returns true.
|
|
||||||
completness_check = WakeNotifierFilter(
|
|
||||||
notifier, types=(TextFrame,), filter=wake_check_filter
|
|
||||||
)
|
|
||||||
|
|
||||||
# This processor keeps the last context and will let it through once the
|
|
||||||
# notifier is woken up. We start with the gate open because we send an
|
|
||||||
# initial context frame to start the conversation.
|
|
||||||
gated_context_aggregator = GatedOpenAILLMContextAggregator(
|
|
||||||
notifier=notifier, start_open=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Notify if the user hasn't said anything.
|
|
||||||
async def user_idle_notifier(frame):
|
|
||||||
await notifier.notify()
|
|
||||||
|
|
||||||
# Sometimes the LLM will fail detecting if a user has completed a
|
|
||||||
# sentence, this will wake up the notifier if that happens.
|
|
||||||
user_idle = UserIdleProcessor(callback=user_idle_notifier, timeout=3.0)
|
|
||||||
|
|
||||||
# The ParallePipeline input are the user transcripts. We have two
|
|
||||||
# contexts. The first one will be used to determine if the user finished
|
|
||||||
# a statement and if so the notifier will be woken up. The second
|
|
||||||
# context is simply the regular context but it's gated waiting for the
|
|
||||||
# notifier to be woken up.
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
stt,
|
|
||||||
ParallelPipeline(
|
|
||||||
[
|
|
||||||
statement_context_aggregator.user(),
|
|
||||||
statement_llm,
|
|
||||||
completness_check,
|
|
||||||
NullFilter(),
|
|
||||||
],
|
|
||||||
[context_aggregator.user(), gated_context_aggregator, llm],
|
|
||||||
),
|
|
||||||
user_idle,
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -6,14 +6,11 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from openai.types.chat import ChatCompletionToolParam
|
from openai.types.chat import ChatCompletionToolParam
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
@@ -49,13 +46,12 @@ from pipecat.services.deepgram.stt import DeepgramSTTService
|
|||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.sync.base_notifier import BaseNotifier
|
from pipecat.sync.base_notifier import BaseNotifier
|
||||||
from pipecat.sync.event_notifier import EventNotifier
|
from pipecat.sync.event_notifier import EventNotifier
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
classifier_statement = "Determine if the user's statement ends with a complete thought and you should respond. The user text is transcribed speech. It may contain multiple fragments concatentated together. You are trying to determine only the completeness of the last user statement. The previous assistant statement is provided only for context. Categorize the text as either complete with the user now expecting a response, or incomplete. Return 'YES' if text is likely complete and the user is expecting a response. Return 'NO' if the text seems to be a partial expression or unfinished thought."
|
classifier_statement = "Determine if the user's statement ends with a complete thought and you should respond. The user text is transcribed speech. It may contain multiple fragments concatentated together. You are trying to determine only the completeness of the last user statement. The previous assistant statement is provided only for context. Categorize the text as either complete with the user now expecting a response, or incomplete. Return 'YES' if text is likely complete and the user is expecting a response. Return 'NO' if the text seems to be a partial expression or unfinished thought."
|
||||||
|
|
||||||
@@ -204,186 +200,194 @@ async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context
|
|||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
None,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the LLM that will be used to detect if the user has finished a
|
# This is the LLM that will be used to detect if the user has finished a
|
||||||
# statement. This doesn't really need to be an LLM, we could use NLP
|
# statement. This doesn't really need to be an LLM, we could use NLP
|
||||||
# libraries for that, but we have the machinery to use an LLM, so we might as well!
|
# libraries for that, but we have the machinery to use an LLM, so we might as well!
|
||||||
statement_llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
statement_llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
# This is the regular LLM.
|
# This is the regular LLM.
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
# You can also register a function_name of None to get all functions
|
# You can also register a function_name of None to get all functions
|
||||||
# sent to the same callback with an additional function_name parameter.
|
# sent to the same callback with an additional function_name parameter.
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
ChatCompletionToolParam(
|
ChatCompletionToolParam(
|
||||||
type="function",
|
type="function",
|
||||||
function={
|
function={
|
||||||
"name": "get_current_weather",
|
"name": "get_current_weather",
|
||||||
"description": "Get the current weather",
|
"description": "Get the current weather",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"location": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"enum": ["celsius", "fahrenheit"],
|
||||||
"description": "The temperature unit to use. Infer this from the users location.",
|
"description": "The temperature unit to use. Infer this from the users location.",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"required": ["location", "format"],
|
|
||||||
},
|
},
|
||||||
|
"required": ["location", "format"],
|
||||||
},
|
},
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
# We have instructed the LLM to return 'YES' if it thinks the user
|
||||||
|
# completed a sentence. So, if it's 'YES' we will return true in this
|
||||||
|
# predicate which will wake up the notifier.
|
||||||
|
async def wake_check_filter(frame):
|
||||||
|
logger.debug(f"Completeness check frame: {frame}")
|
||||||
|
return frame.text == "YES"
|
||||||
|
|
||||||
|
# This is a notifier that we use to synchronize the two LLMs.
|
||||||
|
notifier = EventNotifier()
|
||||||
|
|
||||||
|
# This turns the LLM context into an inference request to classify the user's speech
|
||||||
|
# as complete or incomplete.
|
||||||
|
statement_judge_context_filter = StatementJudgeContextFilter(notifier=notifier)
|
||||||
|
|
||||||
|
# This sends a UserStoppedSpeakingFrame and triggers the notifier event
|
||||||
|
completeness_check = CompletenessCheck(notifier=notifier)
|
||||||
|
|
||||||
|
# # Notify if the user hasn't said anything.
|
||||||
|
async def user_idle_notifier(frame):
|
||||||
|
await notifier.notify()
|
||||||
|
|
||||||
|
# Sometimes the LLM will fail detecting if a user has completed a
|
||||||
|
# sentence, this will wake up the notifier if that happens.
|
||||||
|
user_idle = UserIdleProcessor(callback=user_idle_notifier, timeout=5.0)
|
||||||
|
|
||||||
|
# We start with the gate open because we send an initial context frame
|
||||||
|
# to start the conversation.
|
||||||
|
bot_output_gate = OutputGate(notifier=notifier, start_open=True)
|
||||||
|
|
||||||
|
async def block_user_stopped_speaking(frame):
|
||||||
|
return not isinstance(frame, UserStoppedSpeakingFrame)
|
||||||
|
|
||||||
|
async def pass_only_llm_trigger_frames(frame):
|
||||||
|
return (
|
||||||
|
isinstance(frame, OpenAILLMContextFrame)
|
||||||
|
or isinstance(frame, LLMMessagesFrame)
|
||||||
|
or isinstance(frame, StartInterruptionFrame)
|
||||||
|
or isinstance(frame, StopInterruptionFrame)
|
||||||
|
or isinstance(frame, FunctionCallInProgressFrame)
|
||||||
|
or isinstance(frame, FunctionCallResultFrame)
|
||||||
|
)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
ParallelPipeline(
|
||||||
|
[
|
||||||
|
# Pass everything except UserStoppedSpeaking to the elements after
|
||||||
|
# this ParallelPipeline
|
||||||
|
FunctionFilter(filter=block_user_stopped_speaking),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
# Ignore everything except an OpenAILLMContextFrame. Pass a specially constructed
|
||||||
|
# LLMMessagesFrame to the statement classifier LLM. The only frame this
|
||||||
|
# sub-pipeline will output is a UserStoppedSpeakingFrame.
|
||||||
|
statement_judge_context_filter,
|
||||||
|
statement_llm,
|
||||||
|
completeness_check,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
# Block everything except OpenAILLMContextFrame and LLMMessagesFrame
|
||||||
|
FunctionFilter(filter=pass_only_llm_trigger_frames),
|
||||||
|
llm,
|
||||||
|
bot_output_gate, # Buffer all llm/tts output until notified.
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tts,
|
||||||
|
user_idle,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# We have instructed the LLM to return 'YES' if it thinks the user
|
@transport.event_handler("on_client_connected")
|
||||||
# completed a sentence. So, if it's 'YES' we will return true in this
|
async def on_client_connected(transport, client):
|
||||||
# predicate which will wake up the notifier.
|
logger.info(f"Client connected")
|
||||||
async def wake_check_filter(frame):
|
# Kick off the conversation.
|
||||||
logger.debug(f"Completeness check frame: {frame}")
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
return frame.text == "YES"
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
# This is a notifier that we use to synchronize the two LLMs.
|
@transport.event_handler("on_app_message")
|
||||||
notifier = EventNotifier()
|
async def on_app_message(transport, message):
|
||||||
|
logger.debug(f"Received app message: {message}")
|
||||||
|
if "message" not in message:
|
||||||
|
return
|
||||||
|
|
||||||
# This turns the LLM context into an inference request to classify the user's speech
|
await task.queue_frames(
|
||||||
# as complete or incomplete.
|
|
||||||
statement_judge_context_filter = StatementJudgeContextFilter(notifier=notifier)
|
|
||||||
|
|
||||||
# This sends a UserStoppedSpeakingFrame and triggers the notifier event
|
|
||||||
completeness_check = CompletenessCheck(notifier=notifier)
|
|
||||||
|
|
||||||
# # Notify if the user hasn't said anything.
|
|
||||||
async def user_idle_notifier(frame):
|
|
||||||
await notifier.notify()
|
|
||||||
|
|
||||||
# Sometimes the LLM will fail detecting if a user has completed a
|
|
||||||
# sentence, this will wake up the notifier if that happens.
|
|
||||||
user_idle = UserIdleProcessor(callback=user_idle_notifier, timeout=5.0)
|
|
||||||
|
|
||||||
# We start with the gate open because we send an initial context frame
|
|
||||||
# to start the conversation.
|
|
||||||
bot_output_gate = OutputGate(notifier=notifier, start_open=True)
|
|
||||||
|
|
||||||
async def block_user_stopped_speaking(frame):
|
|
||||||
return not isinstance(frame, UserStoppedSpeakingFrame)
|
|
||||||
|
|
||||||
async def pass_only_llm_trigger_frames(frame):
|
|
||||||
return (
|
|
||||||
isinstance(frame, OpenAILLMContextFrame)
|
|
||||||
or isinstance(frame, LLMMessagesFrame)
|
|
||||||
or isinstance(frame, StartInterruptionFrame)
|
|
||||||
or isinstance(frame, StopInterruptionFrame)
|
|
||||||
or isinstance(frame, FunctionCallInProgressFrame)
|
|
||||||
or isinstance(frame, FunctionCallResultFrame)
|
|
||||||
)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
[
|
||||||
transport.input(),
|
UserStartedSpeakingFrame(),
|
||||||
stt,
|
TranscriptionFrame(user_id="", timestamp=time.time(), text=message["message"]),
|
||||||
context_aggregator.user(),
|
UserStoppedSpeakingFrame(),
|
||||||
ParallelPipeline(
|
|
||||||
[
|
|
||||||
# Pass everything except UserStoppedSpeaking to the elements after
|
|
||||||
# this ParallelPipeline
|
|
||||||
FunctionFilter(filter=block_user_stopped_speaking),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
# Ignore everything except an OpenAILLMContextFrame. Pass a specially constructed
|
|
||||||
# LLMMessagesFrame to the statement classifier LLM. The only frame this
|
|
||||||
# sub-pipeline will output is a UserStoppedSpeakingFrame.
|
|
||||||
statement_judge_context_filter,
|
|
||||||
statement_llm,
|
|
||||||
completeness_check,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
# Block everything except OpenAILLMContextFrame and LLMMessagesFrame
|
|
||||||
FunctionFilter(filter=pass_only_llm_trigger_frames),
|
|
||||||
llm,
|
|
||||||
bot_output_gate, # Buffer all llm/tts output until notified.
|
|
||||||
],
|
|
||||||
),
|
|
||||||
tts,
|
|
||||||
user_idle,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
report_only_initial_ttfb=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_app_message")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_app_message(transport, message, sender):
|
|
||||||
logger.debug(f"Received app message: {message} - {sender}")
|
|
||||||
if "message" not in message:
|
|
||||||
return
|
|
||||||
|
|
||||||
await task.queue_frames(
|
await runner.run(task)
|
||||||
[
|
|
||||||
UserStartedSpeakingFrame(),
|
|
||||||
TranscriptionFrame(
|
|
||||||
user_id=sender, timestamp=time.time(), text=message["message"]
|
|
||||||
),
|
|
||||||
UserStoppedSpeakingFrame(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -6,14 +6,11 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from openai.types.chat import ChatCompletionToolParam
|
from openai.types.chat import ChatCompletionToolParam
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
@@ -50,13 +47,12 @@ from pipecat.services.deepgram.stt import DeepgramSTTService
|
|||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.sync.base_notifier import BaseNotifier
|
from pipecat.sync.base_notifier import BaseNotifier
|
||||||
from pipecat.sync.event_notifier import EventNotifier
|
from pipecat.sync.event_notifier import EventNotifier
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
classifier_statement = """CRITICAL INSTRUCTION:
|
classifier_statement = """CRITICAL INSTRUCTION:
|
||||||
You are a BINARY CLASSIFIER that must ONLY output "YES" or "NO".
|
You are a BINARY CLASSIFIER that must ONLY output "YES" or "NO".
|
||||||
@@ -408,195 +404,203 @@ async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context
|
|||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
None,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the LLM that will be used to detect if the user has finished a
|
# This is the LLM that will be used to detect if the user has finished a
|
||||||
# statement. This doesn't really need to be an LLM, we could use NLP
|
# statement. This doesn't really need to be an LLM, we could use NLP
|
||||||
# libraries for that, but we have the machinery to use an LLM, so we might as well!
|
# libraries for that, but we have the machinery to use an LLM, so we might as well!
|
||||||
statement_llm = AnthropicLLMService(
|
statement_llm = AnthropicLLMService(
|
||||||
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
||||||
model="claude-3-5-sonnet-20241022",
|
model="claude-3-5-sonnet-20241022",
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the regular LLM.
|
# This is the regular LLM.
|
||||||
llm = OpenAILLMService(
|
llm = OpenAILLMService(
|
||||||
api_key=os.getenv("OPENAI_API_KEY"),
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
model="gpt-4o",
|
model="gpt-4o",
|
||||||
)
|
)
|
||||||
# Register a function_name of None to get all functions
|
# Register a function_name of None to get all functions
|
||||||
# sent to the same callback with an additional function_name parameter.
|
# sent to the same callback with an additional function_name parameter.
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
ChatCompletionToolParam(
|
ChatCompletionToolParam(
|
||||||
type="function",
|
type="function",
|
||||||
function={
|
function={
|
||||||
"name": "get_current_weather",
|
"name": "get_current_weather",
|
||||||
"description": "Get the current weather",
|
"description": "Get the current weather",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"location": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
"enum": ["celsius", "fahrenheit"],
|
||||||
"description": "The temperature unit to use. Infer this from the users location.",
|
"description": "The temperature unit to use. Infer this from the users location.",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"required": ["location", "format"],
|
|
||||||
},
|
},
|
||||||
|
"required": ["location", "format"],
|
||||||
},
|
},
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": conversational_system_message,
|
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": conversational_system_message,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
# We have instructed the LLM to return 'YES' if it thinks the user
|
||||||
|
# completed a sentence. So, if it's 'YES' we will return true in this
|
||||||
|
# predicate which will wake up the notifier.
|
||||||
|
async def wake_check_filter(frame):
|
||||||
|
return frame.text == "YES"
|
||||||
|
|
||||||
|
# This is a notifier that we use to synchronize the two LLMs.
|
||||||
|
notifier = EventNotifier()
|
||||||
|
|
||||||
|
# This turns the LLM context into an inference request to classify the user's speech
|
||||||
|
# as complete or incomplete.
|
||||||
|
statement_judge_context_filter = StatementJudgeContextFilter(notifier=notifier)
|
||||||
|
|
||||||
|
# This sends a UserStoppedSpeakingFrame and triggers the notifier event
|
||||||
|
completeness_check = CompletenessCheck(notifier=notifier)
|
||||||
|
|
||||||
|
# # Notify if the user hasn't said anything.
|
||||||
|
async def user_idle_notifier(frame):
|
||||||
|
await notifier.notify()
|
||||||
|
|
||||||
|
# Sometimes the LLM will fail detecting if a user has completed a
|
||||||
|
# sentence, this will wake up the notifier if that happens.
|
||||||
|
user_idle = UserIdleProcessor(callback=user_idle_notifier, timeout=5.0)
|
||||||
|
|
||||||
|
# We start with the gate open because we send an initial context frame
|
||||||
|
# to start the conversation.
|
||||||
|
bot_output_gate = OutputGate(notifier=notifier, start_open=True)
|
||||||
|
|
||||||
|
async def block_user_stopped_speaking(frame):
|
||||||
|
return not isinstance(frame, UserStoppedSpeakingFrame)
|
||||||
|
|
||||||
|
async def pass_only_llm_trigger_frames(frame):
|
||||||
|
return (
|
||||||
|
isinstance(frame, OpenAILLMContextFrame)
|
||||||
|
or isinstance(frame, LLMMessagesFrame)
|
||||||
|
or isinstance(frame, StartInterruptionFrame)
|
||||||
|
or isinstance(frame, StopInterruptionFrame)
|
||||||
|
or isinstance(frame, FunctionCallInProgressFrame)
|
||||||
|
or isinstance(frame, FunctionCallResultFrame)
|
||||||
|
)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
stt,
|
||||||
|
context_aggregator.user(),
|
||||||
|
ParallelPipeline(
|
||||||
|
[
|
||||||
|
# Pass everything except UserStoppedSpeaking to the elements after
|
||||||
|
# this ParallelPipeline
|
||||||
|
FunctionFilter(filter=block_user_stopped_speaking),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
# Ignore everything except an OpenAILLMContextFrame. Pass a specially constructed
|
||||||
|
# LLMMessagesFrame to the statement classifier LLM. The only frame this
|
||||||
|
# sub-pipeline will output is a UserStoppedSpeakingFrame.
|
||||||
|
statement_judge_context_filter,
|
||||||
|
statement_llm,
|
||||||
|
completeness_check,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
# Block everything except OpenAILLMContextFrame and LLMMessagesFrame
|
||||||
|
FunctionFilter(filter=pass_only_llm_trigger_frames),
|
||||||
|
llm,
|
||||||
|
bot_output_gate, # Buffer all llm/tts output until notified.
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tts,
|
||||||
|
user_idle,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
task = PipelineTask(
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# We have instructed the LLM to return 'YES' if it thinks the user
|
@transport.event_handler("on_client_connected")
|
||||||
# completed a sentence. So, if it's 'YES' we will return true in this
|
async def on_client_connected(transport, client):
|
||||||
# predicate which will wake up the notifier.
|
logger.info(f"Client connected")
|
||||||
async def wake_check_filter(frame):
|
# Kick off the conversation.
|
||||||
return frame.text == "YES"
|
messages.append(
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Start by just saying \"Hello I'm ready.\" Don't say anything else.",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
# This is a notifier that we use to synchronize the two LLMs.
|
@transport.event_handler("on_app_message")
|
||||||
notifier = EventNotifier()
|
async def on_app_message(transport, message):
|
||||||
|
logger.debug(f"Received app message: {message}")
|
||||||
|
if "message" not in message:
|
||||||
|
return
|
||||||
|
|
||||||
# This turns the LLM context into an inference request to classify the user's speech
|
await task.queue_frames(
|
||||||
# as complete or incomplete.
|
|
||||||
statement_judge_context_filter = StatementJudgeContextFilter(notifier=notifier)
|
|
||||||
|
|
||||||
# This sends a UserStoppedSpeakingFrame and triggers the notifier event
|
|
||||||
completeness_check = CompletenessCheck(notifier=notifier)
|
|
||||||
|
|
||||||
# # Notify if the user hasn't said anything.
|
|
||||||
async def user_idle_notifier(frame):
|
|
||||||
await notifier.notify()
|
|
||||||
|
|
||||||
# Sometimes the LLM will fail detecting if a user has completed a
|
|
||||||
# sentence, this will wake up the notifier if that happens.
|
|
||||||
user_idle = UserIdleProcessor(callback=user_idle_notifier, timeout=5.0)
|
|
||||||
|
|
||||||
# We start with the gate open because we send an initial context frame
|
|
||||||
# to start the conversation.
|
|
||||||
bot_output_gate = OutputGate(notifier=notifier, start_open=True)
|
|
||||||
|
|
||||||
async def block_user_stopped_speaking(frame):
|
|
||||||
return not isinstance(frame, UserStoppedSpeakingFrame)
|
|
||||||
|
|
||||||
async def pass_only_llm_trigger_frames(frame):
|
|
||||||
return (
|
|
||||||
isinstance(frame, OpenAILLMContextFrame)
|
|
||||||
or isinstance(frame, LLMMessagesFrame)
|
|
||||||
or isinstance(frame, StartInterruptionFrame)
|
|
||||||
or isinstance(frame, StopInterruptionFrame)
|
|
||||||
or isinstance(frame, FunctionCallInProgressFrame)
|
|
||||||
or isinstance(frame, FunctionCallResultFrame)
|
|
||||||
)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
[
|
||||||
transport.input(),
|
UserStartedSpeakingFrame(),
|
||||||
stt,
|
TranscriptionFrame(user_id="", timestamp=time.time(), text=message["message"]),
|
||||||
context_aggregator.user(),
|
UserStoppedSpeakingFrame(),
|
||||||
ParallelPipeline(
|
|
||||||
[
|
|
||||||
# Pass everything except UserStoppedSpeaking to the elements after
|
|
||||||
# this ParallelPipeline
|
|
||||||
FunctionFilter(filter=block_user_stopped_speaking),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
# Ignore everything except an OpenAILLMContextFrame. Pass a specially constructed
|
|
||||||
# LLMMessagesFrame to the statement classifier LLM. The only frame this
|
|
||||||
# sub-pipeline will output is a UserStoppedSpeakingFrame.
|
|
||||||
statement_judge_context_filter,
|
|
||||||
statement_llm,
|
|
||||||
completeness_check,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
# Block everything except OpenAILLMContextFrame and LLMMessagesFrame
|
|
||||||
FunctionFilter(filter=pass_only_llm_trigger_frames),
|
|
||||||
llm,
|
|
||||||
bot_output_gate, # Buffer all llm/tts output until notified.
|
|
||||||
],
|
|
||||||
),
|
|
||||||
tts,
|
|
||||||
user_idle,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
logger.info(f"Client closed connection")
|
||||||
# Kick off the conversation.
|
await task.cancel()
|
||||||
messages.append(
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "Start by just saying \"Hello I'm ready.\" Don't say anything else.",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_app_message")
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
async def on_app_message(transport, message, sender):
|
|
||||||
logger.debug(f"Received app message: {message} - {sender}")
|
|
||||||
if "message" not in message:
|
|
||||||
return
|
|
||||||
|
|
||||||
await task.queue_frames(
|
await runner.run(task)
|
||||||
[
|
|
||||||
UserStartedSpeakingFrame(),
|
|
||||||
TranscriptionFrame(
|
|
||||||
user_id=sender, timestamp=time.time(), text=message["message"]
|
|
||||||
),
|
|
||||||
UserStoppedSpeakingFrame(),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
|
||||||
await runner.run(task)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -6,14 +6,11 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import google.ai.generativelanguage as glm
|
import google.ai.generativelanguage as glm
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
@@ -47,12 +44,12 @@ from pipecat.services.cartesia.tts import CartesiaTTSService
|
|||||||
from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService
|
from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService
|
||||||
from pipecat.sync.base_notifier import BaseNotifier
|
from pipecat.sync.base_notifier import BaseNotifier
|
||||||
from pipecat.sync.event_notifier import EventNotifier
|
from pipecat.sync.event_notifier import EventNotifier
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
TRANSCRIBER_MODEL = "gemini-2.0-flash-001"
|
TRANSCRIBER_MODEL = "gemini-2.0-flash-001"
|
||||||
CLASSIFIER_MODEL = "gemini-2.0-flash-001"
|
CLASSIFIER_MODEL = "gemini-2.0-flash-001"
|
||||||
@@ -626,149 +623,155 @@ class OutputGate(FrameProcessor):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
None,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
tts = CartesiaTTSService(
|
tts = CartesiaTTSService(
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the LLM that will transcribe user speech.
|
# This is the LLM that will transcribe user speech.
|
||||||
tx_llm = GoogleLLMService(
|
tx_llm = GoogleLLMService(
|
||||||
name="Transcriber",
|
name="Transcriber",
|
||||||
model=TRANSCRIBER_MODEL,
|
model=TRANSCRIBER_MODEL,
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
system_instruction=transcriber_system_instruction,
|
system_instruction=transcriber_system_instruction,
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the LLM that will classify user speech as complete or incomplete.
|
# This is the LLM that will classify user speech as complete or incomplete.
|
||||||
classifier_llm = GoogleLLMService(
|
classifier_llm = GoogleLLMService(
|
||||||
name="Classifier",
|
name="Classifier",
|
||||||
model=CLASSIFIER_MODEL,
|
model=CLASSIFIER_MODEL,
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
system_instruction=classifier_system_instruction,
|
system_instruction=classifier_system_instruction,
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the regular LLM that responds conversationally.
|
# This is the regular LLM that responds conversationally.
|
||||||
conversation_llm = GoogleLLMService(
|
conversation_llm = GoogleLLMService(
|
||||||
name="Conversation",
|
name="Conversation",
|
||||||
model=CONVERSATION_MODEL,
|
model=CONVERSATION_MODEL,
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
system_instruction=conversation_system_instruction,
|
system_instruction=conversation_system_instruction,
|
||||||
)
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext()
|
context = OpenAILLMContext()
|
||||||
context_aggregator = conversation_llm.create_context_aggregator(context)
|
context_aggregator = conversation_llm.create_context_aggregator(context)
|
||||||
|
|
||||||
# This is a notifier that we use to synchronize the two LLMs.
|
# This is a notifier that we use to synchronize the two LLMs.
|
||||||
notifier = EventNotifier()
|
notifier = EventNotifier()
|
||||||
|
|
||||||
# This turns the LLM context into an inference request to classify the user's speech
|
# This turns the LLM context into an inference request to classify the user's speech
|
||||||
# as complete or incomplete.
|
# as complete or incomplete.
|
||||||
# statement_judge_context_filter = StatementJudgeAudioContextAccumulator(notifier=notifier)
|
# statement_judge_context_filter = StatementJudgeAudioContextAccumulator(notifier=notifier)
|
||||||
|
|
||||||
audio_accumulater = AudioAccumulator()
|
audio_accumulater = AudioAccumulator()
|
||||||
# This sends a UserStoppedSpeakingFrame and triggers the notifier event
|
# This sends a UserStoppedSpeakingFrame and triggers the notifier event
|
||||||
completeness_check = CompletenessCheck(
|
completeness_check = CompletenessCheck(notifier=notifier, audio_accumulator=audio_accumulater)
|
||||||
notifier=notifier, audio_accumulator=audio_accumulater
|
|
||||||
)
|
|
||||||
|
|
||||||
async def block_user_stopped_speaking(frame):
|
async def block_user_stopped_speaking(frame):
|
||||||
return not isinstance(frame, UserStoppedSpeakingFrame)
|
return not isinstance(frame, UserStoppedSpeakingFrame)
|
||||||
|
|
||||||
conversation_audio_context_assembler = ConversationAudioContextAssembler(context=context)
|
conversation_audio_context_assembler = ConversationAudioContextAssembler(context=context)
|
||||||
|
|
||||||
llm_aggregator_buffer = LLMAggregatorBuffer()
|
llm_aggregator_buffer = LLMAggregatorBuffer()
|
||||||
|
|
||||||
bot_output_gate = OutputGate(
|
bot_output_gate = OutputGate(
|
||||||
notifier=notifier, context=context, llm_transcription_buffer=llm_aggregator_buffer
|
notifier=notifier, context=context, llm_transcription_buffer=llm_aggregator_buffer
|
||||||
)
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(),
|
transport.input(),
|
||||||
audio_accumulater,
|
audio_accumulater,
|
||||||
ParallelPipeline(
|
ParallelPipeline(
|
||||||
[
|
|
||||||
# Pass everything except UserStoppedSpeaking to the elements after
|
|
||||||
# this ParallelPipeline
|
|
||||||
FunctionFilter(filter=block_user_stopped_speaking),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
ParallelPipeline(
|
|
||||||
[
|
|
||||||
classifier_llm,
|
|
||||||
completeness_check,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
tx_llm,
|
|
||||||
llm_aggregator_buffer,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
[
|
|
||||||
conversation_audio_context_assembler,
|
|
||||||
conversation_llm,
|
|
||||||
bot_output_gate, # buffer output until notified, then flush frames and update context
|
|
||||||
# TempPrinter(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
|
||||||
pipeline,
|
|
||||||
params=PipelineParams(
|
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
|
||||||
async def on_first_participant_joined(transport, participant):
|
|
||||||
await transport.capture_participant_transcription(participant["id"])
|
|
||||||
# Kick off the conversation.
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
@transport.event_handler("on_app_message")
|
|
||||||
async def on_app_message(transport, message, sender):
|
|
||||||
logger.debug(f"Received app message: {message} - {sender}")
|
|
||||||
if "message" not in message:
|
|
||||||
return
|
|
||||||
|
|
||||||
await task.queue_frames(
|
|
||||||
[
|
[
|
||||||
UserStartedSpeakingFrame(),
|
# Pass everything except UserStoppedSpeaking to the elements after
|
||||||
TranscriptionFrame(
|
# this ParallelPipeline
|
||||||
user_id=sender, timestamp=time.time(), text=message["message"]
|
FunctionFilter(filter=block_user_stopped_speaking),
|
||||||
),
|
],
|
||||||
UserStoppedSpeakingFrame(),
|
[
|
||||||
]
|
ParallelPipeline(
|
||||||
)
|
[
|
||||||
|
classifier_llm,
|
||||||
|
completeness_check,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
tx_llm,
|
||||||
|
llm_aggregator_buffer,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
conversation_audio_context_assembler,
|
||||||
|
conversation_llm,
|
||||||
|
bot_output_gate, # buffer output until notified, then flush frames and update context
|
||||||
|
# TempPrinter(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
tts,
|
||||||
|
transport.output(),
|
||||||
|
context_aggregator.assistant(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
runner = PipelineRunner()
|
task = PipelineTask(
|
||||||
await runner.run(task)
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
|
@transport.event_handler("on_app_message")
|
||||||
|
async def on_app_message(transport, message):
|
||||||
|
logger.debug(f"Received app message: {message}")
|
||||||
|
if "message" not in message:
|
||||||
|
return
|
||||||
|
|
||||||
|
await task.queue_frames(
|
||||||
|
[
|
||||||
|
UserStartedSpeakingFrame(),
|
||||||
|
TranscriptionFrame(user_id="", timestamp=time.time(), text=message["message"]),
|
||||||
|
UserStoppedSpeakingFrame(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from daily_runner import configure_with_args
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure_with_args
|
|
||||||
|
|
||||||
from pipecat.audio.mixers.soundfile_mixer import SoundfileMixer
|
from pipecat.audio.mixers.soundfile_mixer import SoundfileMixer
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
140
examples/foundational/23-bot-background-sound-p2p.py
Normal file
140
examples/foundational/23-bot-background-sound-p2p.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2024–2025, Daily
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
Set the path to your background audio file using the `INPUT_AUDIO_PATH` environment variable, then run the bot using:
|
||||||
|
|
||||||
|
INPUT_AUDIO_PATH=path/to/your_audio.mp3 python 23-bot-background-sound.py
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
INPUT_AUDIO_PATH=my_audio.mp3 python 23-bot-background-sound.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from pipecat.audio.mixers.soundfile_mixer import SoundfileMixer
|
||||||
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
|
from pipecat.frames.frames import MixerEnableFrame, MixerUpdateSettingsFrame
|
||||||
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
audio_path = os.getenv("INPUT_AUDIO_PATH")
|
||||||
|
if not audio_path:
|
||||||
|
raise ValueError("No INPUT_AUDIO_PATH specified in environment variables")
|
||||||
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
|
soundfile_mixer = SoundfileMixer(
|
||||||
|
sound_files={"office": audio_path},
|
||||||
|
default_sound="office",
|
||||||
|
volume=2.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
transport = SmallWebRTCTransport(
|
||||||
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
audio_out_mixer=soundfile_mixer,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
|
|
||||||
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful LLM in a WebRTC call. Your goal is to demonstrate your capabilities in a succinct way. Your output will be converted to audio so don't include special characters in your answers. Respond to what the user said in a creative and helpful way.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt, # STT service
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
report_only_initial_ttfb=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected: {client}")
|
||||||
|
# Show how to use mixer control frames.
|
||||||
|
await asyncio.sleep(10.0)
|
||||||
|
await task.queue_frame(MixerUpdateSettingsFrame({"volume": 0.5}))
|
||||||
|
await asyncio.sleep(5.0)
|
||||||
|
await task.queue_frame(MixerEnableFrame(False))
|
||||||
|
await asyncio.sleep(5.0)
|
||||||
|
await task.queue_frame(MixerEnableFrame(True))
|
||||||
|
await asyncio.sleep(5.0)
|
||||||
|
# Kick off the conversation.
|
||||||
|
messages.append({"role": "system", "content": "Please introduce yourself to the user."})
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -6,14 +6,12 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from openai.types.chat import ChatCompletionToolParam
|
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
|
from pipecat.adapters.schemas.tools_schema import ToolsSchema
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.pipeline.pipeline import Pipeline
|
from pipecat.pipeline.pipeline import Pipeline
|
||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
@@ -23,13 +21,12 @@ from pipecat.processors.filters.stt_mute_filter import STTMuteConfig, STTMuteFil
|
|||||||
from pipecat.services.deepgram.stt import DeepgramSTTService
|
from pipecat.services.deepgram.stt import DeepgramSTTService
|
||||||
from pipecat.services.deepgram.tts import DeepgramTTSService
|
from pipecat.services.deepgram.tts import DeepgramTTSService
|
||||||
from pipecat.services.openai.llm import OpenAILLMService
|
from pipecat.services.openai.llm import OpenAILLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
# Add a delay to test interruption during function calls
|
# Add a delay to test interruption during function calls
|
||||||
@@ -39,103 +36,107 @@ async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context
|
|||||||
await result_callback({"conditions": "nice", "temperature": "75"})
|
await result_callback({"conditions": "nice", "temperature": "75"})
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, _) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
None,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
vad_enabled=True,
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
vad_audio_passthrough=True,
|
||||||
vad_audio_passthrough=True,
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
|
||||||
# Configure the mute processor with both strategies
|
|
||||||
stt_mute_processor = STTMuteFilter(
|
|
||||||
config=STTMuteConfig(
|
|
||||||
strategies={
|
|
||||||
STTMuteStrategy.MUTE_UNTIL_FIRST_BOT_COMPLETE,
|
|
||||||
STTMuteStrategy.FUNCTION_CALL,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
# Configure the mute processor with both strategies
|
||||||
|
stt_mute_processor = STTMuteFilter(
|
||||||
|
config=STTMuteConfig(
|
||||||
|
strategies={
|
||||||
|
STTMuteStrategy.MUTE_UNTIL_FIRST_BOT_COMPLETE,
|
||||||
|
STTMuteStrategy.FUNCTION_CALL,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
tts = DeepgramTTSService(api_key=os.getenv("DEEPGRAM_API_KEY"), voice="aura-helios-en")
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
|
||||||
|
|
||||||
tools = [
|
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o")
|
||||||
ChatCompletionToolParam(
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
type="function",
|
|
||||||
function={
|
weather_function = FunctionSchema(
|
||||||
"name": "get_current_weather",
|
name="get_current_weather",
|
||||||
"description": "Get the current weather",
|
description="Get the current weather",
|
||||||
"parameters": {
|
properties={
|
||||||
"type": "object",
|
"location": {
|
||||||
"properties": {
|
"type": "string",
|
||||||
"location": {
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
"type": "string",
|
},
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
"format": {
|
||||||
},
|
"type": "string",
|
||||||
"format": {
|
"enum": ["celsius", "fahrenheit"],
|
||||||
"type": "string",
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
"enum": ["celsius", "fahrenheit"],
|
},
|
||||||
"description": "The temperature unit to use. Infer this from the users location.",
|
},
|
||||||
},
|
required=["location", "format"],
|
||||||
},
|
)
|
||||||
"required": ["location", "format"],
|
tools = ToolsSchema(standard_tools=[weather_function])
|
||||||
},
|
|
||||||
},
|
messages = [
|
||||||
)
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a helpful assistant who can check the weather. Always check the weather when a location is mentioned. Respond concisely and naturally. Your output will be converted to audio so use only simple words and punctuation.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages, tools)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(), # Transport user input
|
||||||
|
stt_mute_processor, # Add the mute processor before STT
|
||||||
|
stt, # STT
|
||||||
|
context_aggregator.user(), # User responses
|
||||||
|
llm, # LLM
|
||||||
|
tts, # TTS
|
||||||
|
transport.output(), # Transport bot output
|
||||||
|
context_aggregator.assistant(), # Assistant spoken responses
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
messages = [
|
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
||||||
|
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation with a weather-related prompt
|
||||||
|
messages.append(
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a helpful assistant who can check the weather. Always check the weather when a location is mentioned. Respond concisely and naturally. Your output will be converted to audio so use only simple words and punctuation.",
|
"content": "Ask the user what city they'd like to know the weather for.",
|
||||||
},
|
}
|
||||||
]
|
|
||||||
|
|
||||||
context = OpenAILLMContext(messages, tools)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
|
||||||
[
|
|
||||||
transport.input(), # Transport user input
|
|
||||||
stt_mute_processor, # Add the mute processor before STT
|
|
||||||
stt, # STT
|
|
||||||
context_aggregator.user(), # User responses
|
|
||||||
llm, # LLM
|
|
||||||
tts, # TTS
|
|
||||||
transport.output(), # Transport bot output
|
|
||||||
context_aggregator.assistant(), # Assistant spoken responses
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True))
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
# Kick off the conversation with a weather-related prompt
|
logger.info(f"Client closed connection")
|
||||||
messages.append(
|
await task.cancel()
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": "Ask the user what city they'd like to know the weather for.",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,16 +4,12 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import google.ai.generativelanguage as glm
|
import google.ai.generativelanguage as glm
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
@@ -37,13 +33,12 @@ from pipecat.processors.aggregators.openai_llm_context import (
|
|||||||
from pipecat.processors.frame_processor import FrameProcessor
|
from pipecat.processors.frame_processor import FrameProcessor
|
||||||
from pipecat.services.cartesia.tts import CartesiaTTSService
|
from pipecat.services.cartesia.tts import CartesiaTTSService
|
||||||
from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService
|
from pipecat.services.google.llm import GoogleLLMContext, GoogleLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# The system prompt for the main conversation.
|
# The system prompt for the main conversation.
|
||||||
#
|
#
|
||||||
@@ -273,102 +268,110 @@ class TranscriptionContextFixup(FrameProcessor):
|
|||||||
await self.push_frame(frame, direction)
|
await self.push_frame(frame, direction)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
transport = SmallWebRTCTransport(
|
||||||
room_url,
|
webrtc_connection=webrtc_connection,
|
||||||
token,
|
params=TransportParams(
|
||||||
"Respond bot",
|
audio_in_enabled=True,
|
||||||
DailyParams(
|
audio_out_enabled=True,
|
||||||
audio_out_enabled=True,
|
vad_enabled=True,
|
||||||
# No transcription at all. just audio input to Gemini!
|
vad_analyzer=SileroVADAnalyzer(),
|
||||||
# transcription_enabled=True,
|
vad_audio_passthrough=True,
|
||||||
vad_enabled=True,
|
),
|
||||||
vad_analyzer=SileroVADAnalyzer(),
|
)
|
||||||
vad_audio_passthrough=True,
|
|
||||||
|
tts = CartesiaTTSService(
|
||||||
|
api_key=os.getenv("CARTESIA_API_KEY"),
|
||||||
|
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
||||||
|
)
|
||||||
|
|
||||||
|
conversation_llm = GoogleLLMService(
|
||||||
|
name="Conversation",
|
||||||
|
model="gemini-2.0-flash-001",
|
||||||
|
# model="gemini-exp-1121",
|
||||||
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
|
# we can give the GoogleLLMService a system instruction to use directly
|
||||||
|
# in the GenerativeModel constructor. Let's do that rather than put
|
||||||
|
# our system message in the messages list.
|
||||||
|
system_instruction=conversation_system_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
input_transcription_llm = GoogleLLMService(
|
||||||
|
name="Transcription",
|
||||||
|
model="gemini-2.0-flash-001",
|
||||||
|
# model="gemini-exp-1121",
|
||||||
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
|
system_instruction=transcriber_system_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "Start by saying hello.",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
context = OpenAILLMContext(messages)
|
||||||
|
context_aggregator = conversation_llm.create_context_aggregator(context)
|
||||||
|
audio_collector = UserAudioCollector(context, context_aggregator.user())
|
||||||
|
input_transcription_context_filter = InputTranscriptionContextFilter()
|
||||||
|
transcription_frames_emitter = InputTranscriptionFrameEmitter()
|
||||||
|
fixup_context_messages = TranscriptionContextFixup(context)
|
||||||
|
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
audio_collector,
|
||||||
|
context_aggregator.user(),
|
||||||
|
ParallelPipeline(
|
||||||
|
[ # transcribe
|
||||||
|
input_transcription_context_filter,
|
||||||
|
input_transcription_llm,
|
||||||
|
transcription_frames_emitter,
|
||||||
|
],
|
||||||
|
[ # conversation inference
|
||||||
|
conversation_llm,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
)
|
tts,
|
||||||
|
transport.output(),
|
||||||
tts = CartesiaTTSService(
|
context_aggregator.assistant(),
|
||||||
api_key=os.getenv("CARTESIA_API_KEY"),
|
fixup_context_messages,
|
||||||
voice_id="71a7ad14-091c-4e8e-a314-022ece01c121", # British Reading Lady
|
|
||||||
)
|
|
||||||
|
|
||||||
conversation_llm = GoogleLLMService(
|
|
||||||
name="Conversation",
|
|
||||||
model="gemini-2.0-flash-001",
|
|
||||||
# model="gemini-exp-1121",
|
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
|
||||||
# we can give the GoogleLLMService a system instruction to use directly
|
|
||||||
# in the GenerativeModel constructor. Let's do that rather than put
|
|
||||||
# our system message in the messages list.
|
|
||||||
system_instruction=conversation_system_message,
|
|
||||||
)
|
|
||||||
|
|
||||||
input_transcription_llm = GoogleLLMService(
|
|
||||||
name="Transcription",
|
|
||||||
model="gemini-2.0-flash-001",
|
|
||||||
# model="gemini-exp-1121",
|
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
|
||||||
system_instruction=transcriber_system_message,
|
|
||||||
)
|
|
||||||
|
|
||||||
messages = [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "Start by saying hello.",
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
context = OpenAILLMContext(messages)
|
task = PipelineTask(
|
||||||
context_aggregator = conversation_llm.create_context_aggregator(context)
|
pipeline,
|
||||||
audio_collector = UserAudioCollector(context, context_aggregator.user())
|
params=PipelineParams(
|
||||||
input_transcription_context_filter = InputTranscriptionContextFilter()
|
allow_interruptions=True,
|
||||||
transcription_frames_emitter = InputTranscriptionFrameEmitter()
|
enable_metrics=True,
|
||||||
fixup_context_messages = TranscriptionContextFixup(context)
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
@transport.event_handler("on_client_connected")
|
||||||
[
|
async def on_client_connected(transport, client):
|
||||||
transport.input(),
|
logger.info(f"Client connected")
|
||||||
audio_collector,
|
# Kick off the conversation.
|
||||||
context_aggregator.user(),
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
ParallelPipeline(
|
|
||||||
[ # transcribe
|
|
||||||
input_transcription_context_filter,
|
|
||||||
input_transcription_llm,
|
|
||||||
transcription_frames_emitter,
|
|
||||||
],
|
|
||||||
[ # conversation inference
|
|
||||||
conversation_llm,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
tts,
|
|
||||||
transport.output(),
|
|
||||||
context_aggregator.assistant(),
|
|
||||||
fixup_context_messages,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_disconnected")
|
||||||
pipeline,
|
async def on_client_disconnected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client disconnected")
|
||||||
allow_interruptions=True,
|
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
# Kick off the conversation.
|
logger.info(f"Client closed connection")
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
await task.cancel()
|
||||||
|
|
||||||
runner = PipelineRunner()
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -20,75 +16,100 @@ from pipecat.pipeline.pipeline import Pipeline
|
|||||||
from pipecat.pipeline.runner import PipelineRunner
|
from pipecat.pipeline.runner import PipelineRunner
|
||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService
|
from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
|
# Load environment variables
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Initialize the SmallWebRTCTransport with the connection
|
||||||
async with aiohttp.ClientSession() as session:
|
transport = SmallWebRTCTransport(
|
||||||
(room_url, token) = await configure(session)
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
camera_in_enabled=False,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
# set stop_secs to something roughly similar to the internal setting
|
||||||
|
# of the Multimodal Live api, just to align events.
|
||||||
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
# Create the Gemini Multimodal Live LLM service
|
||||||
room_url,
|
system_instruction = f"""
|
||||||
token,
|
You are a helpful AI assistant.
|
||||||
"Respond bot",
|
Your goal is to demonstrate your capabilities in a helpful and engaging way.
|
||||||
DailyParams(
|
Your output will be converted to audio so don't include special characters in your answers.
|
||||||
audio_out_enabled=True,
|
Respond to what the user said in a creative and helpful way.
|
||||||
vad_enabled=True,
|
"""
|
||||||
vad_audio_passthrough=True,
|
|
||||||
# set stop_secs to something roughly similar to the internal setting
|
|
||||||
# of the Multimodal Live api, just to align events. This doesn't really
|
|
||||||
# matter because we can only use the Multimodal Live API's phrase
|
|
||||||
# endpointing, for now.
|
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = GeminiMultimodalLiveLLMService(
|
llm = GeminiMultimodalLiveLLMService(
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
# system_instruction="Talk like a pirate."
|
system_instruction=system_instruction,
|
||||||
)
|
voice_id="Puck", # Aoede, Charon, Fenrir, Kore, Puck
|
||||||
|
transcribe_user_audio=True,
|
||||||
|
)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
# Build the pipeline
|
||||||
|
pipeline = Pipeline(
|
||||||
|
[
|
||||||
|
transport.input(),
|
||||||
|
llm,
|
||||||
|
transport.output(),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure the pipeline task
|
||||||
|
task = PipelineTask(
|
||||||
|
pipeline,
|
||||||
|
params=PipelineParams(
|
||||||
|
allow_interruptions=True,
|
||||||
|
enable_metrics=True,
|
||||||
|
enable_usage_metrics=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Handle client connection event
|
||||||
|
@transport.event_handler("on_client_connected")
|
||||||
|
async def on_client_connected(transport, client):
|
||||||
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames(
|
||||||
[
|
[
|
||||||
transport.input(),
|
LLMMessagesAppendFrame(
|
||||||
llm,
|
messages=[
|
||||||
transport.output(),
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": f"Greet the user and introduce yourself.",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
# Handle client disconnection events
|
||||||
pipeline,
|
@transport.event_handler("on_client_disconnected")
|
||||||
params=PipelineParams(
|
async def on_client_disconnected(transport, client):
|
||||||
allow_interruptions=True,
|
logger.info(f"Client disconnected")
|
||||||
enable_metrics=True,
|
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_closed")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_closed(transport, client):
|
||||||
await task.queue_frames(
|
logger.info(f"Client closed connection")
|
||||||
[
|
await task.cancel()
|
||||||
LLMMessagesAppendFrame(
|
|
||||||
messages=[
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "Greet the user.",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
runner = PipelineRunner()
|
# Run the pipeline
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
await runner.run(task)
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -20,90 +16,100 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService
|
from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
|
logger.info(f"Starting bot")
|
||||||
|
|
||||||
async def main():
|
# Initialize the SmallWebRTCTransport with the connection
|
||||||
async with aiohttp.ClientSession() as session:
|
transport = SmallWebRTCTransport(
|
||||||
(room_url, token) = await configure(session)
|
webrtc_connection=webrtc_connection,
|
||||||
|
params=TransportParams(
|
||||||
|
audio_in_enabled=True,
|
||||||
|
audio_out_enabled=True,
|
||||||
|
vad_enabled=True,
|
||||||
|
vad_audio_passthrough=True,
|
||||||
|
# set stop_secs to something roughly similar to the internal setting
|
||||||
|
# of the Multimodal Live api, just to align events. This doesn't really
|
||||||
|
# matter because we can only use the Multimodal Live API's phrase
|
||||||
|
# endpointing, for now.
|
||||||
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
transport = DailyTransport(
|
llm = GeminiMultimodalLiveLLMService(
|
||||||
room_url,
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
token,
|
voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede
|
||||||
"Respond bot",
|
# system_instruction="Talk like a pirate."
|
||||||
DailyParams(
|
transcribe_user_audio=True,
|
||||||
audio_out_enabled=True,
|
# inference_on_context_initialization=False,
|
||||||
vad_enabled=True,
|
)
|
||||||
vad_audio_passthrough=True,
|
|
||||||
# set stop_secs to something roughly similar to the internal setting
|
|
||||||
# of the Multimodal Live api, just to align events. This doesn't really
|
|
||||||
# matter because we can only use the Multimodal Live API's phrase
|
|
||||||
# endpointing, for now.
|
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
llm = GeminiMultimodalLiveLLMService(
|
context = OpenAILLMContext(
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
[
|
||||||
voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede
|
{
|
||||||
# system_instruction="Talk like a pirate."
|
"role": "user",
|
||||||
transcribe_user_audio=True,
|
"content": "Say hello. Then ask if I want to hear a joke.",
|
||||||
transcribe_model_audio=True,
|
},
|
||||||
# inference_on_context_initialization=False,
|
# {"role": "assistant", "content": "Hello! Why don't scientists trust atoms?"},
|
||||||
)
|
# {
|
||||||
|
# "role": "user",
|
||||||
|
# "content": [
|
||||||
|
# {
|
||||||
|
# "type": "text",
|
||||||
|
# "text": "Oh, I know this one: because they make up everything.",
|
||||||
|
# }
|
||||||
|
# ],
|
||||||
|
# },
|
||||||
|
],
|
||||||
|
)
|
||||||
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
context = OpenAILLMContext(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
{
|
transport.input(),
|
||||||
"role": "user",
|
context_aggregator.user(),
|
||||||
"content": "Say hello. Then ask if I want to hear a joke.",
|
llm,
|
||||||
},
|
transport.output(),
|
||||||
# {"role": "assistant", "content": "Hello! Why don't scientists trust atoms?"},
|
context_aggregator.assistant(),
|
||||||
# {
|
]
|
||||||
# "role": "user",
|
)
|
||||||
# "content": [
|
|
||||||
# {
|
|
||||||
# "type": "text",
|
|
||||||
# "text": "Oh, I know this one: because they make up everything.",
|
|
||||||
# }
|
|
||||||
# ],
|
|
||||||
# },
|
|
||||||
],
|
|
||||||
)
|
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
|
||||||
|
|
||||||
pipeline = Pipeline(
|
task = PipelineTask(
|
||||||
[
|
pipeline,
|
||||||
transport.input(),
|
params=PipelineParams(
|
||||||
context_aggregator.user(),
|
allow_interruptions=True,
|
||||||
llm,
|
enable_metrics=True,
|
||||||
transport.output(),
|
enable_usage_metrics=True,
|
||||||
context_aggregator.assistant(),
|
),
|
||||||
]
|
)
|
||||||
)
|
|
||||||
|
|
||||||
task = PipelineTask(
|
@transport.event_handler("on_client_connected")
|
||||||
pipeline,
|
async def on_client_connected(transport, client):
|
||||||
params=PipelineParams(
|
logger.info(f"Client connected")
|
||||||
allow_interruptions=True,
|
# Kick off the conversation.
|
||||||
enable_metrics=True,
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
enable_usage_metrics=True,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_disconnected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_disconnected(transport, client):
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
await runner.run(task)
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
from pipecat.adapters.schemas.function_schema import FunctionSchema
|
||||||
from pipecat.adapters.schemas.tools_schema import AdapterType, ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import AdapterType, ToolsSchema
|
||||||
@@ -23,13 +19,12 @@ from pipecat.pipeline.runner import PipelineRunner
|
|||||||
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
from pipecat.pipeline.task import PipelineParams, PipelineTask
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService
|
from pipecat.services.gemini_multimodal_live.gemini import GeminiMultimodalLiveLLMService
|
||||||
from pipecat.transports.services.daily import DailyParams, DailyTransport
|
from pipecat.transports.base_transport import TransportParams
|
||||||
|
from pipecat.transports.network.small_webrtc import SmallWebRTCTransport
|
||||||
|
from pipecat.transports.network.webrtc_connection import SmallWebRTCConnection
|
||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
logger.remove(0)
|
|
||||||
logger.add(sys.stderr, level="DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
async def fetch_weather_from_api(function_name, tool_call_id, args, llm, context, result_callback):
|
||||||
temperature = 75 if args["format"] == "fahrenheit" else 24
|
temperature = 75 if args["format"] == "fahrenheit" else 24
|
||||||
@@ -51,87 +46,99 @@ for the weather, call this function.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def run_bot(webrtc_connection: SmallWebRTCConnection):
|
||||||
async with aiohttp.ClientSession() as session:
|
logger.info(f"Starting bot")
|
||||||
(room_url, token) = await configure(session)
|
|
||||||
|
|
||||||
transport = DailyTransport(
|
# Initialize the SmallWebRTCTransport with the connection
|
||||||
room_url,
|
transport = SmallWebRTCTransport(
|
||||||
token,
|
webrtc_connection=webrtc_connection,
|
||||||
"Respond bot",
|
params=TransportParams(
|
||||||
DailyParams(
|
audio_in_enabled=True,
|
||||||
audio_out_enabled=True,
|
audio_out_enabled=True,
|
||||||
vad_enabled=True,
|
vad_enabled=True,
|
||||||
vad_audio_passthrough=True,
|
vad_audio_passthrough=True,
|
||||||
# set stop_secs to something roughly similar to the internal setting
|
# set stop_secs to something roughly similar to the internal setting
|
||||||
# of the Multimodal Live api, just to align events. This doesn't really
|
# of the Multimodal Live api, just to align events. This doesn't really
|
||||||
# matter because we can only use the Multimodal Live API's phrase
|
# matter because we can only use the Multimodal Live API's phrase
|
||||||
# endpointing, for now.
|
# endpointing, for now.
|
||||||
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
|
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.5)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
weather_function = FunctionSchema(
|
weather_function = FunctionSchema(
|
||||||
name="get_current_weather",
|
name="get_current_weather",
|
||||||
description="Get the current weather",
|
description="Get the current weather",
|
||||||
properties={
|
properties={
|
||||||
"location": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The city and state, e.g. San Francisco, CA",
|
"description": "The city and state, e.g. San Francisco, CA",
|
||||||
},
|
|
||||||
"format": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["celsius", "fahrenheit"],
|
|
||||||
"description": "The temperature unit to use. Infer this from the user's location.",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
required=["location", "format"],
|
"format": {
|
||||||
)
|
"type": "string",
|
||||||
search_tool = {"google_search": {}}
|
"enum": ["celsius", "fahrenheit"],
|
||||||
tools = ToolsSchema(
|
"description": "The temperature unit to use. Infer this from the user's location.",
|
||||||
standard_tools=[weather_function], custom_tools={AdapterType.GEMINI: [search_tool]}
|
},
|
||||||
)
|
},
|
||||||
|
required=["location", "format"],
|
||||||
|
)
|
||||||
|
search_tool = {"google_search": {}}
|
||||||
|
tools = ToolsSchema(
|
||||||
|
standard_tools=[weather_function], custom_tools={AdapterType.GEMINI: [search_tool]}
|
||||||
|
)
|
||||||
|
|
||||||
llm = GeminiMultimodalLiveLLMService(
|
llm = GeminiMultimodalLiveLLMService(
|
||||||
api_key=os.getenv("GOOGLE_API_KEY"),
|
api_key=os.getenv("GOOGLE_API_KEY"),
|
||||||
system_instruction=system_instruction,
|
system_instruction=system_instruction,
|
||||||
tools=tools,
|
tools=tools,
|
||||||
)
|
)
|
||||||
|
|
||||||
llm.register_function("get_current_weather", fetch_weather_from_api)
|
llm.register_function("get_current_weather", fetch_weather_from_api)
|
||||||
|
|
||||||
context = OpenAILLMContext(
|
context = OpenAILLMContext(
|
||||||
[{"role": "user", "content": "Say hello."}],
|
[{"role": "user", "content": "Say hello."}],
|
||||||
)
|
)
|
||||||
context_aggregator = llm.create_context_aggregator(context)
|
context_aggregator = llm.create_context_aggregator(context)
|
||||||
|
|
||||||
pipeline = Pipeline(
|
pipeline = Pipeline(
|
||||||
[
|
[
|
||||||
transport.input(),
|
transport.input(),
|
||||||
context_aggregator.user(),
|
context_aggregator.user(),
|
||||||
llm,
|
llm,
|
||||||
transport.output(),
|
transport.output(),
|
||||||
context_aggregator.assistant(),
|
context_aggregator.assistant(),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
task = PipelineTask(
|
task = PipelineTask(
|
||||||
pipeline,
|
pipeline,
|
||||||
params=PipelineParams(
|
params=PipelineParams(
|
||||||
allow_interruptions=True,
|
allow_interruptions=True,
|
||||||
enable_metrics=True,
|
enable_metrics=True,
|
||||||
enable_usage_metrics=True,
|
enable_usage_metrics=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@transport.event_handler("on_first_participant_joined")
|
@transport.event_handler("on_client_connected")
|
||||||
async def on_first_participant_joined(transport, participant):
|
async def on_client_connected(transport, client):
|
||||||
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
logger.info(f"Client connected")
|
||||||
|
# Kick off the conversation.
|
||||||
|
await task.queue_frames([context_aggregator.user().get_context_frame()])
|
||||||
|
|
||||||
runner = PipelineRunner()
|
@transport.event_handler("on_client_disconnected")
|
||||||
|
async def on_client_disconnected(transport, client):
|
||||||
|
logger.info(f"Client disconnected")
|
||||||
|
|
||||||
await runner.run(task)
|
@transport.event_handler("on_client_closed")
|
||||||
|
async def on_client_closed(transport, client):
|
||||||
|
logger.info(f"Client closed connection")
|
||||||
|
await task.cancel()
|
||||||
|
|
||||||
|
runner = PipelineRunner(handle_sigint=False)
|
||||||
|
|
||||||
|
await runner.run(task)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
from run import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from daily_runner import configure
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from runner import configure
|
|
||||||
|
|
||||||
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
from pipecat.audio.vad.silero import SileroVADAnalyzer
|
||||||
from pipecat.audio.vad.vad_analyzer import VADParams
|
from pipecat.audio.vad.vad_analyzer import VADParams
|
||||||
@@ -53,7 +53,6 @@ async def main():
|
|||||||
voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede
|
voice_id="Aoede", # Puck, Charon, Kore, Fenrir, Aoede
|
||||||
# system_instruction="Talk like a pirate."
|
# system_instruction="Talk like a pirate."
|
||||||
transcribe_user_audio=True,
|
transcribe_user_audio=True,
|
||||||
transcribe_model_audio=True,
|
|
||||||
# inference_on_context_initialization=False,
|
# inference_on_context_initialization=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user