Files
2026-08-28 11:43:34 -04:00

84 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""
Pipecat Bot Template — OpenAILLMService avec API Hermes
========================================================
Template minimal pour un bot texte via WebSocket.
Adapté de ~/workspace/jarvis-pipecat/bot.py
Prérequis: pipecat-ai[websocket]>=1.4.0, openai, uvicorn, websockets
"""
import json, os, subprocess, sys
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
import uvicorn
# ⚠️ Pipecat 1.4.0 : utiliser `import module as m` (from-import cassé)
import pipecat.frames.frames as pframes
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineTask
from pipecat.processors.frame_processor import FrameProcessor, FrameDirection
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams, FastAPIWebsocketTransport
# ─── Config ──────────────────────────────────────────────────
PORT = int(os.environ.get("BOT_PORT", "7081"))
HERMES_URL = os.environ.get("HERMES_API_URL", "http://localhost:8642/v1")
HERMES_KEY = os.environ.get("HERMES_API_KEY", "")
SYSTEM_PROMPT = "Tu es un assistant vocal concis. Parle en français, 3 phrases max."
# ─── Processeur custom : texte → LLMMessagesFrame ────────────
class TextToLLMProcessor(FrameProcessor):
"""Remplace le context aggregator (buggé en 1.4.0)."""
def __init__(self, system_prompt: str):
super().__init__()
self._messages = [{"role": "system", "content": system_prompt}]
async def process_frame(self, frame, direction):
await self.push_frame(frame, direction)
if isinstance(frame, pframes.InputTransportMessageFrame):
text = frame.message
if isinstance(text, str) and text.strip():
self._messages.append({"role": "user", "content": text})
if len(self._messages) > 21:
self._messages = [self._messages[0]] + self._messages[-20:]
await self.push_frame(pframes.LLMMessagesFrame(messages=list(self._messages)))
elif isinstance(frame, pframes.LLMFullResponseStartFrame):
self._response_text = ""
elif isinstance(frame, pframes.TextFrame):
if hasattr(self, '_response_text'):
self._response_text += frame.text
elif isinstance(frame, pframes.LLMFullResponseEndFrame):
if hasattr(self, '_response_text') and self._response_text.strip():
self._messages.append({"role": "assistant", "content": self._response_text.strip()})
del self._response_text
# ─── App ─────────────────────────────────────────────────────
app = FastAPI()
@app.websocket("/ws")
async def ws_endpoint(websocket: WebSocket):
transport = FastAPIWebsocketTransport(
websocket=websocket,
params=FastAPIWebsocketParams(add_wav_header=False, session_timeout=None),
)
llm = OpenAILLMService(
api_key=HERMES_KEY,
base_url=HERMES_URL,
settings=OpenAILLMService.Settings(model="Hermes Agent", temperature=0.8, max_tokens=300, system_instruction=SYSTEM_PROMPT),
)
proc = TextToLLMProcessor(SYSTEM_PROMPT)
pipeline = Pipeline(processors=[transport.input(), proc, llm, transport.output()])
task = PipelineTask(pipeline)
runner = PipelineRunner()
await runner.run(task)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=PORT)