How to run livekit agent in frappe framework

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))

I believe that such automations must be handles in some other tools like (n8n, NiFi, etc) . That way agent run as expected and you can interact with the ERPNext data.

Your approach is too low level and requires more assistance and experience.

PS: I wonder how would be your solution look like. Can you share some screen shots?