import frappe
import asyncio
import os
from dotenv import load_dotenv
from livekit.agents import AutoSubscribe, JobContext, JobProcess, WorkerOptions, cli, llm, metrics
from livekit.agents.pipeline import VoicePipelineAgent
from livekit.plugins import deepgram, silero, turn_detector
from livekit.plugins.deepgram import tts
from livekit.plugins.openai import llm as llms
from mistralai import Mistral, DocumentURLChunk
import aiofiles
load_dotenv()
Initialize Clients
mistral_client = Mistral(api_key=MISTRAL_API_KEY)
deepgram_tts = tts.TTS(model=“aura-asteria-en”)
groq_llm = llms.LLM.with_groq(model=“llama3-8b-8192”, temperature=0.7)
Resume Processor
class ResumeProcessor(llm.FunctionContext):
def init(self, agent):
super().init()
self.agent = agent
@llm.ai_callable()
async def process_resume(self, file_url: str) -> str:
"""Processes resume from Frappe's public file storage."""
try:
# Access public file path
file_path = frappe.get_site_path("public", file_url)
# Upload and OCR
with open(file_path, "rb") as f:
contents = f.read()
response = mistral_client.files.upload(
file={"file_name": os.path.basename(file_path), "content": contents},
purpose="ocr",
)
signed_url = mistral_client.files.get_signed_url(
file_id=response.id, expiry=1
)
pdf_response = mistral_client.ocr.process(
document=DocumentURLChunk(document_url=signed_url.url),
model="mistral-ocr-latest",
include_image_base64=False,
)
content = self.extract_markdown(pdf_response)
# Notify the candidate
await self.agent.say("Resume received. Let's begin the interview.")
return content
except Exception as e:
await self.agent.say("I encountered an error while processing your resume.")
return str(e)
def extract_markdown(self, pdf_response) -> str:
return "\n\n".join(page.markdown for page in pdf_response.pages)
Prewarm function
LiveKit Entry Point
async def entrypoint(ctx: JobContext):
await ctx.connect(auto_subscribe=AutoSubscribe.AUDIO_ONLY)
participant = await ctx.wait_for_participant()
# Initial assistant prompt
chat_ctx = llm.ChatContext().append(
role="system",
text=(
"You are an interview assistant created by LiveKit. Your interface with users will be voice. "
"You should use professional, clear, and concise responses. "
"Your goal is to conduct a thorough job interview, asking relevant questions and "
"gathering information about the candidate's experience, skills, and qualifications. "
"Prompt the candidate to upload their resume and use it for contextual guidance."
),
)
# Initialize VoicePipelineAgent
agent = VoicePipelineAgent(
vad=silero.VAD.load(),
stt=deepgram.STT(),
llm=groq_llm,
tts=deepgram_tts,
turn_detector=turn_detector.EOUModel(),
min_endpointing_delay=0.5,
max_endpointing_delay=5.0,
chat_ctx=chat_ctx,
)
# Attach resume processor with agent reference
resume_processor = ResumeProcessor(agent)
agent.append.fnc_ctx = resume_processor
try:
agent.start(ctx.room, participant)
await agent.say("Welcome to the interview. Please upload your resume in PDF format.")
except Exception as e:
print(f"Error starting agent: {e}")
Start LiveKit Worker
def start_agent():
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
error i got
frappe@6aefa39fbefe:/workspace/development/frappe-test$ bench --site tests.localhost console
Apps in this namespace:
frappe, erpnext, test1, insights, crm, api_app, raven, hrms, chatbot, test, customer_portal, wiki
In [1]: import test.interview_agent
...: test.interview_agent.start_agent()
Usage: python -m frappe.utils.bench_helper [OPTIONS] COMMAND [ARGS]...
Try 'python -m frappe.utils.bench_helper --help' for help.
Error: No such command 'frappe'.
---------------------------------------------------------------------------
UsageError Traceback (most recent call last)
File /workspace/development/frappe-test/env/lib/python3.11/site-packages/click/core.py:1078, in BaseCommand.main(self, args, prog_name, complete_var, standalone_mode, windows_expand_args, **extra)
1077 with self.make_context(prog_name, args, **extra) as ctx:
-> 1078 rv = self.invoke(ctx)
1079 if not standalone_mode:
File /workspace/development/frappe-test/env/lib/python3.11/site-packages/click/core.py:1682, in MultiCommand.invoke(self, ctx)
1681 with ctx:
-> 1682 cmd_name, cmd, args = self.resolve_command(ctx, args)
1683 assert cmd is not None
File /workspace/development/frappe-test/env/lib/python3.11/site-packages/click/core.py:1746, in MultiCommand.resolve_command(self, ctx, args)
1745 self.parse_args(ctx, ctx.args)
-> 1746 ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name))
1747 return cmd_name if cmd else None, cmd, args[1:]
File /workspace/development/frappe-test/env/lib/python3.11/site-packages/click/core.py:684, in Context.fail(self, message)
679 """Aborts the execution of the program with a specific error
680 message.
681
682 :param message: the error message to fail with.
683 """
--> 684 raise UsageError(message, self)
UsageError: No such command 'frappe'.
During handling of the above exception, another exception occurred:
SystemExit Traceback (most recent call last)
[... skipping hidden 1 frame]
Cell In[1], line 2
1 import test.interview_agent
----> 2 test.interview_agent.start_agent()
File /workspace/development/frappe-test/apps/test/test/interview_agent.py:120, in start_agent()
119 def start_agent():
--> 120 cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))