95 lines
2.8 KiB
Python
95 lines
2.8 KiB
Python
import logging
|
|
import os
|
|
from dotenv import load_dotenv
|
|
from livekit.agents import JobContext, JobProcess, WorkerOptions, cli
|
|
from livekit.agents.job import AutoSubscribe
|
|
from livekit.agents.llm import (
|
|
ChatContext,
|
|
)
|
|
from livekit.agents.pipeline import VoicePipelineAgent
|
|
from livekit.plugins import cartesia, silero, llama_index, assemblyai
|
|
|
|
load_dotenv()
|
|
|
|
logger = logging.getLogger("voice-assistant")
|
|
from llama_index.llms.ollama import Ollama
|
|
from llama_index.core import (
|
|
SimpleDirectoryReader,
|
|
StorageContext,
|
|
VectorStoreIndex,
|
|
load_index_from_storage,
|
|
Settings
|
|
)
|
|
from llama_index.core.chat_engine.types import ChatMode
|
|
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
|
|
|
|
load_dotenv()
|
|
|
|
embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
|
|
llm=Ollama(model="gemma3", request_timeout=120.0)
|
|
Settings.llm = llm
|
|
Settings.embed_model = embed_model
|
|
|
|
# check if storage already exists
|
|
PERSIST_DIR = "./chat-engine-storage"
|
|
if not os.path.exists(PERSIST_DIR):
|
|
# load the documents and create the index
|
|
documents = SimpleDirectoryReader("docs").load_data()
|
|
index = VectorStoreIndex.from_documents(documents)
|
|
# store it for later
|
|
index.storage_context.persist(persist_dir=PERSIST_DIR)
|
|
else:
|
|
# load the existing index
|
|
storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR)
|
|
index = load_index_from_storage(storage_context)
|
|
|
|
|
|
def prewarm(proc: JobProcess):
|
|
proc.userdata["vad"] = silero.VAD.load()
|
|
|
|
|
|
async def entrypoint(ctx: JobContext):
|
|
chat_context = ChatContext().append(
|
|
role="system",
|
|
text=(
|
|
"You are a funny, witty assistant."
|
|
"Respond with short and concise answers. Avoid using unpronouncable punctuation or emojis."
|
|
),
|
|
)
|
|
|
|
chat_engine = index.as_chat_engine(chat_mode=ChatMode.CONTEXT)
|
|
logger.info(f"Connecting to room {ctx.room.name}")
|
|
|
|
await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
|
|
|
|
participant = await ctx.wait_for_participant()
|
|
logger.info(f"Starting voice assistant for participant {participant.identity}")
|
|
|
|
stt_impl = assemblyai.STT()
|
|
agent = VoicePipelineAgent(
|
|
vad=ctx.proc.userdata["vad"],
|
|
stt=stt_impl,
|
|
llm=llama_index.LLM(chat_engine=chat_engine),
|
|
tts=cartesia.TTS(
|
|
model="sonic-2",
|
|
voice="794f9389-aac1-45b6-b726-9d9369183238",
|
|
),
|
|
chat_ctx=chat_context,
|
|
)
|
|
|
|
agent.start(ctx.room, participant)
|
|
|
|
await agent.say(
|
|
"Hey there! How can I help you today?",
|
|
allow_interruptions=True,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("Starting voice agent...")
|
|
cli.run_app(
|
|
WorkerOptions(
|
|
entrypoint_fnc=entrypoint,
|
|
prewarm_fnc=prewarm,
|
|
),
|
|
) |