chore: nettoyage repo JARVIS v3 + docs à jour

- Suppression 37 fichiers scratch/debug (Pipecat exploration : _debug*, check_*, test_*, ws_test*, bot.py, start.sh, run_bot*, run_direct.py)
- Garde uniquement l'essentiel : bot_direct.py, launcher.py, README.md, .gitignore
- .gitignore renforcé (patterns scratch/debug)
- README réécrit selon l'état réel (Phase 1 terminée, Phase 2 audio partielle TTS/STT)
- Roadmap 6 phases documentée
This commit is contained in:
2026-08-26 09:59:28 -04:00
parent 15985a6bfa
commit 1f653dedcd
38 changed files with 104 additions and 1238 deletions
+14
View File
@@ -1,6 +1,20 @@
__pycache__/
*.pyc
*.pyo
venv/
.env
*.log
*.pid
# fichiers scratch / debug (jamais commités)
_check*
_debug*
_get_key*
_key_out*
_show*
check_*.py
quick_test*.py
run_test*.py
ws_test*.py
test_*.py
*_out.txt
+90 -89
View File
@@ -1,106 +1,47 @@
# JARVIS v3 — Assistant Vocal Intelligent
> Phase 1 : Relay WebSocket → Hermes A.L.E.X.A.
> PoC fonctionnel — interface chat texte en temps réel
Assistant vocal local connecté à l'agent Hermes (DeepSeek v4). Contexte multi-tours,
interface navigateur, synthèse vocale (TTS), liaison vocale (STT navigateur).
## 🎯 Objectif
Assistant vocal intelligent avec mémoire persistante, connecté à l'API Hermes A.L.E.X.A.
Phase 1 valide la communication texte en temps réel via WebSocket.
> **Était le projet `jarvis-pipecat`** (historique nommé d'après Pipecat, non utilisé actuellement).
## 🏗️ Architecture
```
┌──────────────┐ WebSocket ┌──────────────┐ HTTP/POST ┌──────────────┐
│ Navigateur │ ◄──────────────► │ bot_direct │ ◄───────────────► │ Hermes API │
│ (test.html) │ ws://host/ws │ .py :7081 │ localhost:8642 │ A.L.E.X.A. │
└──────────────┘ └──────────────┘ └──────────────┘
│
▼
┌──────────────┐
│ Infisical │
│ (clé API) │
└──────────────┘
┌──────────────┐ WebSocket ┌──────────────┐ HTTP REST ┌──────────────┐
│ Navigateur │ ◄────────────► │ bot_direct │ ◄─────────────► │ Hermes API │
│ (HTML/JS) │ ws://:7081/ws │ .py :7081 │ :8642 /v1/chat │ A.L.E.X.A. │
│ 🎤 STT 🔊 │ │ TTS edge │ /completions │ (DeepSeek v4)│
└──────────────┘ │ (serveur) │ └──────────────┘
└──────┬───────┘
▼ Infisical (clé) / fallback .env
```
- **STT** : Web Speech API navigateur (Chrome) — nécessite HTTPS ou flag (voir Bloqueurs).
- **TTS** : `edge-tts` côté serveur (voix `fr-FR-DeniseNeural`), MP3 → base64 → `<audio>`.
- **LLM** : API Hermes (`Hermes Agent` / DeepSeek v4), temperature 0.8, max 300 tokens, 20 messages de contexte.
## 📦 Fichiers
| Fichier | Rôle |
|---------|------|
| `bot_direct.py` | Serveur WebSocket + interface HTML inline |
| `launcher.py` | Récupère la clé Hermes via Infisical puis lance `bot_direct.py` |
| `run_direct.py` | Lanceur manuel (alternative à systemd) |
| `bot.py` | Tentative Pipecat 1.4.0 (non fonctionnel — pour référence) |
| `test.html` | Interface chat (intégrée inline dans `bot_direct.py`) |
| `start.sh` | Script Pipecat original (obsolète) |
| `bot_direct.py` | Serveur WebSocket + interface HTML embarquée (entrée principale) |
| `launcher.py` | Récupère la clé Hermes (Infisical → fallback `.env`) puis lance `bot_direct.py` |
| `README.md` | Ce document |
## 🚀 Démarrage
### Avec systemd (recommandé)
### Service systemd (recommandé)
```bash
# Statut
systemctl --user status jarvis-v3
# Activer + démarrer
systemctl --user enable --now jarvis-v3
# Logs
# Statut / logs
systemctl --user status jarvis-v3
journalctl --user -u jarvis-v3 -f
# Redémarrer
systemctl --user restart jarvis-v3
```
### Manuel
```bash
cd ~/workspace/jarvis-pipecat
source venv/bin/activate
python3 run_direct.py
```
## 🌐 Accès
- **URL** : http://openclaw1.dev.home:7081/
- **WebSocket** : ws://openclaw1.dev.home:7081/ws
- **Port** : 7081 (configurable via `JARVIS_PORT`)
## ⚙️ Configuration
| Variable | Valeur | Description |
|----------|--------|-------------|
| `JARVIS_PORT` | `7081` | Port d'écoute |
| `HERMES_API_KEY` | *(Infisical)* | Clé API Hermes |
| `HERMES_URL` | `http://localhost:8642/v1/chat/completions` | Endpoint API |
## 🔑 Secret
La clé API Hermes est stockée dans **Infisical** (`/hermes-claw/API_SERVER_KEY`).
Le launcher (`launcher.py`) la récupère automatiquement au démarrage.
## 🛠️ Dépendances
```
websockets==16.0
httpx
```
Installées dans `venv/`.
## 📊 Roadmap
| Phase | Contenu | Statut |
|-------|---------|--------|
| **Phase 1** | Chat texte WebSocket + Hermes | ✅ Stable |
| **Phase 2** | Audio (STT Parakeet + TTS Edge/Kokoro) | 🔜 Planifié |
| **Phase 3** | Intégration Pipecat WebRTC | 🔜 Planifié |
| **Phase 4** | Multi-agent, barge-in, wake word | 📋 Backlog |
## 🔧 Maintenance
```bash
# Voir les logs
journalctl --user -u jarvis-v3 -n 50
# Redémarrer après modification
# Redémarrer après modification de code
rm -rf ~/workspace/jarvis-pipecat/__pycache__/
systemctl --user restart jarvis-v3
@@ -108,14 +49,74 @@ systemctl --user restart jarvis-v3
systemctl --user stop jarvis-v3
```
## 📝 Notes techniques
### Manuel (debug)
- **websockets 16.0** : `respond()` ne prend que `(status, text)`. Pour les headers custom,
retourner un objet `Response` manuellement (`from websockets.http11 import Response, Headers`).
- **Content-Type** : doit être `text/html; charset=utf-8` pour que le navigateur rende l'interface.
- **Pipecat 1.4.0** : API massivement refactorée, `LLMMessagesFrame` supprimée, `LLMContext`
n'est plus un processeur. Abandonné pour la Phase 1, à réévaluer en Phase 3.
```bash
cd ~/workspace/jarvis-pipecat
source venv/bin/activate
python3 launcher.py
```
## 🌐 Accès
- **Interface** : http://openclaw1.dev.home:7081/
- **WebSocket** : ws://openclaw1.dev.home:7081/ws
- **Port** : `7081`, configurable via `JARVIS_PORT`
## ⚙️ Configuration
| Variable | Valeur | Description |
|----------|--------|-------------|
| `JARVIS_PORT` | `7081` | Port d'écoute |
| `HERMES_API_KEY` | — | Clé API Hermes (via Infisical ou `.env`) |
| `HERMES_URL` | `http://localhost:8642/v1/chat/completions` | Endpoint Hermes |
La clé est récupérée par `launcher.py` : **Infisical** (`/hermes-claw/API_SERVER_KEY`)
puis **fallback** `~/workspace/.env` (`HERMES_ALEXA_KEY`).
## 📊 Roadmap
| Phase | Contenu | Statut |
|-------|---------|--------|
| **Phase 1** | Chat texte WebSocket → Hermes (contexte 20 msgs, reconnexion auto, timeout) | ✅ Terminé |
| **Phase 2** | **Audio** — TTS edge-tts + STT navigateur | 🟡 Partiel (TTS ✅ / STT ⚠️) |
| **Phase 3** | Stabilité & qualité vocale — STT local, TTS naturel, streaming | 🔜 |
| **Phase 4** | Orchestration Pipecat — flux STT→LLM→TTS, barge-in, WebRTC | 🔜 |
| **Phase 5** | Fonctionnalités — wake word, tool calling, mémoire Honcho | 📋 |
| **Phase 6** | Production — HTTPS/WSS, auth, multi-user, Docker, monitoring | 📋 |
Détail complet : **Obsidian** `3.5_PROJETS/JARVIS/JARVIS.md` (roadmap + statut).
## ⚠️ Bloqueurs connus
1. **STT `network` error** — la Web Speech API est bloquée par Chrome en HTTP.
→ Solution rapide : flag `chrome://flags/#unsafely-treat-insecure-origin-as-secure` + `http://openclaw1.dev.home:7081`.
→ Solution durable (Phase 3) : **Whisper local** (faster-whisper / whisper.cpp) — STT côté serveur, plus HTTPS requis.
2. **TTS non streamé** — attend la fin de la réponse LLM (~7 s ajoutées). Cible Phase 3 : < 5 s.
3. **HTTP non chiffré** — pas encore de HTTPS/WSS (Phase 6).
4. **Toujours en écoute impossible** — il faut déclencher le micro ; wake word prévu Phase 5.
## 🛠️ Dépendances
```
websockets==16.0
httpx
edge-tts
```
→ installées dans `venv/`.
## 📝 Notes techniques (pitfalls)
- **websockets 16.0** : `respond()` ne prend que `(status, text)`. Pour headers custom,
retourner un objet `Response` du module `websockets.http11` (`Response, Headers`).
- **Cache navigateur** : `bot_direct.py` renvoie `Cache-Control: no-store` sur les pages HTML.
- **Cache Python** : après modification de `bot_direct.py`, supprimer `__pycache__/` avant le redémarrage.
- **Pipecat abandonné** pour le moment : API 1.4.0 massivement refactorée (`LLMMessagesFrame`
supprimée, `LLMContext` n'est plus un processeur). À réévaluer en Phase 4.
- **Ne pas committer** les fichiers scratch/debug : `.gitignore` couvre `_check*`, `_debug*`,
`test_*.py`, `check_*.py`, `*_out.txt`, etc.
## 📄 Licence
Projet personnel — Bruno Charest, 2026.
Projet personnel — Bruno Charest, 2026.
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
cd ~/workspace/jarvis-pipecat
echo "=== head ==="
head -30 bot.py
echo "=== line count ==="
wc -l bot.py
echo "=== import test ==="
./venv/bin/python -c "import bot" 2>&1
echo "exit: $?"
-36
View File
@@ -1,36 +0,0 @@
=== head ===
#!/usr/bin/env python3
"""
JARVIS v3 PoC — Pipecat bot avec API Hermes
============================================
Phase 1 : bot texte via WebSocket
Version corrigée pour Pipecat 1.4.0 — utilise LLMContext + LLMContextFrame
"""
import json
import os
import subprocess
import sys
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
import uvicorn
from pipecat.frames.frames import (
Frame,
InputTransportMessageFrame,
LLMContextFrame,
LLMFullResponseEndFrame,
LLMFullResponseStartFrame,
LLMTextFrame,
OutputTransportMessageFrame,
OutputTransportMessageUrgentFrame,
StartFrame,
)
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
=== line count ===
354 bot.py
=== import test ===
2026-06-19 17:14:16.253 | INFO | pipecat:<module>:28 - ᓚᘏᗢ Pipecat 1.4.0 (Python 3.13.5 (main, May 5 2026, 21:05:52) [GCC 14.2.0]) ᓚᘏᗢ
exit: 0
-5
View File
@@ -1,5 +0,0 @@
#!/usr/bin/env python3
import pipecat.frames.frames as pf
for name in sorted(dir(pf)):
if 'LLM' in name or 'Context' in name or 'Message' in name:
print(name)
-16
View File
@@ -1,16 +0,0 @@
import pipecat.frames.frames as pf
# LLMContextFrame
import inspect
print("=== LLMContextFrame ===")
print(inspect.getsource(pf.LLMContextFrame))
print()
print("=== LLMMessagesAppendFrame ===")
print(inspect.getsource(pf.LLMMessagesAppendFrame))
print()
print("=== LLMMessagesUpdateFrame ===")
print(inspect.getsource(pf.LLMMessagesUpdateFrame))
print()
# Also look at LLMContext aggregator
from pipecat.processors.aggregators.llm_context import LLMContext
print("=== LLMContext.__init__ ===")
print(inspect.getsource(LLMContext.__init__))
-28
View File
@@ -1,28 +0,0 @@
2026-06-19 17:09:12.173 | INFO | pipecat:<module>:28 - ᓚᘏᗢ Pipecat 1.4.0 (Python 3.13.5 (main, May 5 2026, 21:05:52) [GCC 14.2.0]) ᓚᘏᗢ
FunctionCallFromLLM
InputTransportMessageFrame
LLMAssistantPushAggregationFrame
LLMConfigureOutputFrame
LLMContextAssistantTimestampFrame
LLMContextAssistantTurnFrame
LLMContextFrame
LLMContextSummaryRequestFrame
LLMContextSummaryResultFrame
LLMEnablePromptCachingFrame
LLMFullResponseEndFrame
LLMFullResponseStartFrame
LLMMarkerFrame
LLMMessagesAppendFrame
LLMMessagesTransformFrame
LLMMessagesUpdateFrame
LLMRunFrame
LLMSetToolChoiceFrame
LLMSetToolsFrame
LLMSummarizeContextFrame
LLMTextFrame
LLMThoughtEndFrame
LLMThoughtStartFrame
LLMThoughtTextFrame
LLMUpdateSettingsFrame
OutputTransportMessageFrame
OutputTransportMessageUrgentFrame
-74
View File
@@ -1,74 +0,0 @@
2026-06-19 17:09:30.215 | INFO | pipecat:<module>:28 - ᓚᘏᗢ Pipecat 1.4.0 (Python 3.13.5 (main, May 5 2026, 21:05:52) [GCC 14.2.0]) ᓚᘏᗢ
=== LLMContextFrame ===
@dataclass
class LLMContextFrame(Frame):
"""Frame containing a universal LLM context.
Used as a signal to LLM services to ingest the provided context and
generate a response based on it.
Parameters:
context: The LLM context containing messages, tools, and configuration.
"""
context: LLMContext
=== LLMMessagesAppendFrame ===
@dataclass
class LLMMessagesAppendFrame(DataFrame):
"""Frame containing LLM messages to append to current context.
A frame containing a list of LLM messages that need to be added to the
current context.
Parameters:
messages: List of context messages to append.
run_llm: Whether the context update should be sent to the LLM.
"""
messages: list[LLMContextMessage]
run_llm: bool | None = None
=== LLMMessagesUpdateFrame ===
@dataclass
class LLMMessagesUpdateFrame(DataFrame):
"""Frame containing LLM messages to replace current context.
A frame containing a list of new LLM messages to replace the current
context LLM messages.
Parameters:
messages: List of context messages to replace current context.
run_llm: Whether the context update should be sent to the LLM.
"""
messages: list[LLMContextMessage]
run_llm: bool | None = None
=== LLMContext.__init__ ===
def __init__(
self,
messages: list[LLMContextMessage] | None = None,
tools: ToolsSchema | list[FunctionSchema | DirectFunction] | NotGiven = NOT_GIVEN,
tool_choice: LLMContextToolChoice | NotGiven = NOT_GIVEN,
):
"""Initialize the LLM context.
Args:
messages: Initial list of conversation messages.
tools: Available tools for the LLM to use. May be a ``ToolsSchema``
or a plain list of direct functions and/or ``FunctionSchema``
objects (normalized to a ``ToolsSchema`` internally). Any tool
that carries a handler — a direct function, or a
``FunctionSchema`` with its ``handler`` set — is registered with
the LLM service automatically, so no separate
``register_function`` call is needed.
tool_choice: Tool selection strategy for the LLM.
"""
self._messages: list[LLMContextMessage] = messages if messages else []
self._tools: ToolsSchema | NotGiven = LLMContext._normalize_and_validate_tools(tools)
self._tool_choice: LLMContextToolChoice | NotGiven = tool_choice
-10
View File
@@ -1,10 +0,0 @@
#!/usr/bin/env python3
import os
# Read the raw environ of the hermes gateway process
with open('/proc/1359/environ', 'rb') as f:
data = f.read()
for var in data.split(b'\x00'):
if var:
decoded = var.decode('utf-8', errors='replace')
if any(k in decoded.lower() for k in ['key', 'secret', 'token', 'api_server']):
print(decoded)
View File
-5
View File
@@ -1,5 +0,0 @@
with open('/home/openclaw/workspace/jarvis-pipecat/bot.py') as f:
lines = f.readlines()
for i, line in enumerate(lines, 1):
if 67 <= i <= 100 or 110 <= i <= 175:
print(f'{i}: {line}', end='')
-172
View File
@@ -1,172 +0,0 @@
#!/usr/bin/env python3
"""
JARVIS v3 PoC — Pipecat + Hermes API
=====================================
Bot WebSocket texte : transport → LLM → transport
Sans agrégateur — notre processeur custom gère tout.
"""
import json, os, subprocess, sys
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
import uvicorn
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,
)
PORT = int(os.environ.get("JARVIS_PIPECAT_PORT", "7081"))
# ─── Clé API ─────────────────────────────────────────────────
def _get_hermes_key():
key = os.environ.get("HERMES_API_KEY", "") or os.environ.get("HERMES_ALEXA_KEY", "")
if key: return key
try:
creds = json.load(open("/opt/infisical/admin-credentials.json"))
r = subprocess.run(
["/home/openclaw/.local/bin/infisical", "secrets", "get",
"API_SERVER_KEY", "--domain", "https://secret.dracodev.net",
"--projectId", "192c1eed-6666-41ee-80df-a4636a2da0ad",
"--env", "prod", "--path", "/hermes-claw", "--plain",
"--token", creds["token"]],
capture_output=True, text=True, timeout=15)
return r.stdout.strip()
except Exception as e:
print(f"⚠️ Infisical: {e}"); return ""
_hermes_key = _get_hermes_key()
HERMES_URL = "http://localhost:8642/v1"
SYSTEM_PROMPT = (
"Tu es JARVIS, l'assistant vocal de Bruno. "
"Parle en français, sois concis (3 phrases max), chaleureux et utile. "
"Pas de markdown. Réponds comme si tu parlais à l'oral."
)
# ─── Processeur custom ultra-simple ──────────────────────────
class SimpleLLMRelay(FrameProcessor):
"""
Capture les messages texte du transport et les envoie au LLM via
LLMContextFrame. Les réponses du LLM passent directement au transport.
"""
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:]
print(f"👤 {text[:80]}")
ctx = pframes.LLMContextFrame(context=list(self._messages))
await self.push_frame(ctx)
# ─── Page HTML ───────────────────────────────────────────────
HTML_PAGE = open(os.path.join(os.path.dirname(__file__), "test.html"), "r").read() if os.path.exists("test.html") else """<!DOCTYPE html>
<html lang="fr">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>JARVIS v3</title>
<style>
:root{--bg:#010314;--surface:#060e24;--text:#cdd6f4;--cyan:#06d6f0;--blue:#3b82f6}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,sans-serif;background:var(--bg);color:var(--text);height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px}
h1{color:var(--cyan);margin-bottom:10px;font-size:1.3em}
#status{padding:6px 14px;border-radius:8px;margin:8px 0;font-size:.85em}
.on{background:#10b98122;color:#10b981;border:1px solid #10b981}
.off{background:#ef444422;color:#ef4444;border:1px solid #ef4444}
#chat{width:100%;max-width:600px;flex:1;overflow-y:auto;background:var(--surface);border:1px solid #121e3d;border-radius:12px;padding:16px;margin:10px 0}
.msg{margin:8px 0;padding:8px 12px;border-radius:8px;max-width:85%}
.user{background:var(--blue);margin-left:auto;text-align:right}
.jarvis{background:#1a1a3e;margin-right:auto;border-left:2px solid var(--cyan)}
.sys{text-align:center;color:#4a5a7f;font-style:italic;font-size:.85em}
.row{display:flex;width:100%;max-width:600px;gap:8px}
input{flex:1;padding:12px;border-radius:8px;border:1px solid #121e3d;background:var(--surface);color:var(--text);font-size:1em}
button{padding:12px 24px;border-radius:8px;border:none;background:var(--cyan);color:var(--bg);font-weight:bold;cursor:pointer}
</style></head>
<body><h1>⚡ JARVIS v3 — Pipecat PoC</h1>
<div id="status" class="off">🔴 Déconnecté</div><div id="chat"></div>
<div class="row"><input id="inp" placeholder="Pose ta question..." autofocus><button id="btn">Envoyer</button></div>
<script>
const s=document.getElementById('status'),c=document.getElementById('chat'),i=document.getElementById('inp'),b=document.getElementById('btn');
let ws=null;
function m(t,r){const d=document.createElement('div');d.className='msg '+r;d.textContent=t;c.appendChild(d);c.scrollTop=c.scrollHeight}
function co(){ws=new WebSocket((location.protocol==='https:'?'wss:':'ws:')+'//'+location.host+'/ws');
ws.onopen=()=>{s.textContent='🟢 Connecté';s.className='on'};
ws.onclose=()=>{s.textContent='🔴 Déconnecté';s.className='off';setTimeout(co,2000)};
ws.onmessage=e=>{try{let d=JSON.parse(e.data);m(d.content||d.message||e.data,d.content?'jarvis':'sys')}catch(x){m(e.data,'jarvis')}}}
function se(){let t=i.value.trim();if(!t||!ws||ws.readyState!==WebSocket.OPEN)return;m(t,'user');ws.send(t);i.value=''}
b.onclick=se;i.onkeydown=e=>{if(e.key==='Enter')se()};co()
</script></body></html>"""
# ─── FastAPI ─────────────────────────────────────────────────
app = FastAPI(title="JARVIS v3 PoC", version="0.1.0")
@app.get("/")
async def root():
return HTMLResponse(content=HTML_PAGE)
@app.websocket("/ws")
async def ws_endpoint(websocket: WebSocket):
transport = FastAPIWebsocketTransport(
websocket=websocket,
params=FastAPIWebsocketParams(
add_wav_header=False,
session_timeout=None,
# allowed_origins vide = autorise toutes les origines
),
)
relay = SimpleLLMRelay(SYSTEM_PROMPT)
llm = OpenAILLMService(
api_key=_hermes_key,
base_url=HERMES_URL,
settings=OpenAILLMService.Settings(
model="Hermes Agent", temperature=0.8, max_tokens=300,
),
)
pipeline = Pipeline(processors=[transport.input(), relay, llm, transport.output()])
task = PipelineTask(pipeline)
@transport.event_handler("on_client_connected")
async def on_connect(transport, client):
print(f"✅ Connecté")
await transport.output().send_message(
pframes.OutputTransportMessageFrame(
message="👋 Bonjour Bruno ! Je suis JARVIS v3. Pose-moi une question !"
)
)
@transport.event_handler("on_client_disconnected")
async def on_disconnect(transport, client):
print(f"👋 Déconnecté")
await task.cancel()
runner = PipelineRunner()
await runner.add_workers(task)
try:
await runner.run()
except Exception as e:
print(f"Pipeline error: {e}")
# ─── Main ────────────────────────────────────────────────────
if __name__ == "__main__":
if not _hermes_key:
print("❌ Pas de clé API Hermes"); sys.exit(1)
print(f"🔑 {_hermes_key[:15]}...")
print(f"🌐 {HERMES_URL}")
print(f"🚀 http://0.0.0.0:{PORT}")
print(f" ws://0.0.0.0:{PORT}/ws")
uvicorn.run(app, host="0.0.0.0", port=PORT, log_level="info")
-16
View File
@@ -1,16 +0,0 @@
from pipecat.processors.aggregators.llm_context import OpenAIContextAggregatorPair
import inspect
print("OpenAIContextAggregatorPair:")
for name, method in inspect.getmembers(OpenAIContextAggregatorPair, inspect.isfunction):
if not name.startswith("_"):
sig = inspect.signature(method)
print(f" {name}{sig}")
print()
# Also check what other classes are exported
import pipecat.processors.aggregators.llm_context as m
print("All public symbols:")
for x in dir(m):
if not x.startswith("_"):
print(f" {x}")
-11
View File
@@ -1,11 +0,0 @@
import pipecat.processors.aggregators.llm_context as m
import inspect
print("Has OpenAILLMContext:", hasattr(m, "OpenAILLMContext"))
print("All OpenAI classes:", [x for x in dir(m) if "OpenAI" in x or "Context" in x])
# Try to import from the module's namespace
for name in dir(m):
if name.startswith("OpenAI"):
cls = getattr(m, name)
print(f" {name}: {type(cls)}")
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/env python3
"""Quick test: start bot, hit WS, see result."""
import subprocess, os, json, time, sys, asyncio, threading, signal
os.chdir("/home/openclaw/workspace/jarvis-pipecat")
# Get key
creds = json.load(open("/opt/infisical/admin-credentials.json"))
r = subprocess.run(
["/home/openclaw/.local/bin/infisical", "secrets", "get",
"API_SERVER_KEY", "--domain", "https://secret.dracodev.net",
"--projectId", "192c1eed-6666-41ee-80df-a4636a2da0ad",
"--env", "prod", "--path", "/hermes-claw", "--plain",
"--token", creds["token"]],
capture_output=True, text=True, timeout=15)
os.environ["HERMES_API_KEY"] = r.stdout.strip()
os.environ["PYTHONUNBUFFERED"] = "1"
# Import bot (this triggers the key check print)
import bot
# Start uvicorn in thread
import uvicorn
def run_server():
uvicorn.run(bot.app, host="0.0.0.0", port=7081, log_level="info")
t = threading.Thread(target=run_server, daemon=True)
t.start()
# Wait for server
for i in range(20):
try:
import urllib.request
urllib.request.urlopen("http://localhost:7081/", timeout=1)
break
except:
time.sleep(0.3)
else:
print("FAIL: Server didn't start")
sys.exit(1)
# Test WS
async def test():
import websockets
try:
async with websockets.connect("ws://localhost:7081/ws", open_timeout=5) as ws:
msg = await asyncio.wait_for(ws.recv(), timeout=5)
data = json.loads(msg)
print(f"WELCOME: {data.get('message', msg)[:120]}")
await ws.send("Bonjour! Quelle heure est-il?")
print("SENT question")
resp = await asyncio.wait_for(ws.recv(), timeout=20)
data2 = json.loads(resp)
content = data2.get("content", str(data2))
print(f"REPLY: {content[:300]}")
print("\n✅✅✅ SUCCESS! JARVIS v3 works! ✅✅✅")
return True
except Exception as e:
print(f"WS FAIL: {type(e).__name__}: {e}")
import traceback
traceback.print_exc()
return False
result = asyncio.run(test())
sys.exit(0 if result else 1)
-26
View File
@@ -1,26 +0,0 @@
#!/usr/bin/env python3
"""Starts JARVIS v3 bot with Hermes API key from Infisical."""
import json, subprocess, os, sys
creds = json.load(open("/opt/infisical/admin-credentials.json"))
r = subprocess.run(
["/home/openclaw/.local/bin/infisical", "secrets", "get",
"API_SERVER_KEY", "--domain", "https://secret.dracodev.net",
"--projectId", "192c1eed-6666-41ee-80df-a4636a2da0ad",
"--env", "prod", "--path", "/hermes-claw", "--plain",
"--token", creds["token"]],
capture_output=True, text=True, timeout=20,
)
hermes_key = r.stdout.strip()
if not hermes_key:
print(f"ERROR: {r.stderr}", file=sys.stderr)
sys.exit(1)
print(f"Key: {hermes_key[:15]}...")
os.chdir("/home/openclaw/workspace/jarvis-pipecat")
os.environ["HERMES_API_KEY"] = hermes_key
os.environ["PYTHONUNBUFFERED"] = "1"
# PAS de PIPECAT_ALLOWED_ORIGINS = laisse le défaut (liste vide = tout autorisé)
if "PIPECAT_ALLOWED_ORIGINS" in os.environ:
del os.environ["PIPECAT_ALLOWED_ORIGINS"]
os.execvp("./venv/bin/python", ["./venv/bin/python", "bot.py"])
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash
set -e
pkill -f "bot.py" 2>/dev/null
sleep 1
TOKEN=$(python3 -c "import json; print(json.load(open('/opt/infisical/admin-credentials.json'))['token'])")
HERMES_KEY=$(/home/openclaw/.local/bin/infisical secrets get API_SERVER_KEY --domain https://secret.dracodev.net --projectId 192c1eed-6666-41ee-80df-a4636a2da0ad --env prod --path /hermes-claw --plain --token "$TOKEN" 2>/dev/null)
export HERMES_API_KEY="$HERMES_KEY"
cd /home/openclaw/workspace/jarvis-pipecat
exec ./venv/bin/python bot.py
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env python3
"""Launcher pour JARVIS v3 Phase 1 (direct WebSocket)."""
import json, subprocess, os, sys
creds = json.load(open("/opt/infisical/admin-credentials.json"))
r = subprocess.run(
["/home/openclaw/.local/bin/infisical", "secrets", "get",
"API_SERVER_KEY", "--domain", "https://secret.dracodev.net",
"--projectId", "192c1eed-6666-41ee-80df-a4636a2da0ad",
"--env", "prod", "--path", "/hermes-claw", "--plain",
"--token", creds["token"]],
capture_output=True, text=True, timeout=20,
)
hermes_key = r.stdout.strip()
if not hermes_key:
print(f"ERROR: {r.stderr}", file=sys.stderr); sys.exit(1)
os.chdir("/home/openclaw/workspace/jarvis-pipecat")
os.environ["HERMES_API_KEY"] = hermes_key
os.execvp("./venv/bin/python", ["./venv/bin/python", "bot_direct.py"])
-111
View File
@@ -1,111 +0,0 @@
#!/usr/bin/env python3
"""Script de test complet pour JARVIS v3 Pipecat."""
import subprocess, os, json, time, sys, asyncio
# 1. Récupérer la clé API Hermes depuis Infisical
print("🔑 Récupération clé API Hermes...")
try:
creds = json.load(open("/opt/infisical/admin-credentials.json"))
r = subprocess.run(
["/home/openclaw/.local/bin/infisical", "secrets", "get",
"API_SERVER_KEY", "--domain", "https://secret.dracodev.net",
"--projectId", "192c1eed-6666-41ee-80df-a4636a2da0ad",
"--env", "prod", "--path", "/hermes-claw", "--plain",
"--token", creds["token"]],
capture_output=True, text=True, timeout=20,
env={"PATH": "/usr/local/bin:/usr/bin:/bin"}
)
hermes_key = r.stdout.strip()
if not hermes_key:
print(f"❌ Erreur Infisical: {r.stderr}")
sys.exit(1)
print(f"✅ Clé: {hermes_key[:15]}...")
except Exception as e:
print(f"❌ Erreur: {e}")
sys.exit(1)
# 2. Lancer le bot
print("\n🚀 Lancement du bot Pipecat...")
os.environ["HERMES_API_KEY"] = hermes_key
proc = subprocess.Popen(
["./venv/bin/python", "bot.py"],
cwd="/home/openclaw/workspace/jarvis-pipecat",
env={**os.environ, "HERMES_API_KEY": hermes_key},
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, bufsize=1
)
# 3. Attendre que le serveur démarre
print("⏳ Attente démarrage serveur...")
import select
start = time.time()
while time.time() - start < 15:
ready, _, _ = select.select([proc.stdout], [], [], 0.5)
if ready:
line = proc.stdout.readline()
if line:
print(f" {line.rstrip()}")
if "Uvicorn running" in line or "Application startup complete" in line:
break
# Vérifier si le port écoute
try:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect(("localhost", 7081))
s.close()
print("✅ Port 7081 ouvert!")
break
except:
pass
time.sleep(0.5)
if proc.poll() is not None:
print("❌ Le bot a crashé au démarrage!")
sys.exit(1)
print("✅ Bot démarré!")
# 4. Test HTTP
print("\n🌐 Test HTTP GET /")
import urllib.request
resp = urllib.request.urlopen("http://localhost:7081/", timeout=5)
content = resp.read().decode()
if "JARVIS" in content:
print("✅ Page HTML OK")
else:
print(f"⚠️ Page: {content[:100]}")
# 5. Test WebSocket
print("\n🔌 Test WebSocket...")
try:
import websockets
except ImportError:
subprocess.run(["./venv/bin/pip", "install", "websockets"], check=True)
import websockets
async def ws_test():
async with websockets.connect("ws://localhost:7081/ws") as ws:
# Recevoir message de bienvenue
msg = await asyncio.wait_for(ws.recv(), timeout=5)
print(f"👋 Welcome: {msg[:150]}")
# Envoyer une question
await ws.send("Quelle heure est-il actuellement?")
print("📤 Question envoyée")
# Attendre la réponse
try:
msg = await asyncio.wait_for(ws.recv(), timeout=15)
print(f"🤖 Réponse: {msg[:300]}")
print("\n✅✅✅ TEST RÉUSSI! JARVIS v3 répond via Pipecat! ✅✅✅")
except asyncio.TimeoutError:
print("⚠️ Timeout: pas de réponse du LLM en 15s")
print(" (le LLM peut prendre plus de temps)")
asyncio.run(ws_test())
print("\n📊 Serveur toujours en cours d'exécution...")
print(f" 🌐 http://openclaw1.dev.home:7081/")
print(f" 🔌 ws://openclaw1.dev.home:7081/ws")
print(f" PID: {proc.pid}")
-38
View File
@@ -1,38 +0,0 @@
#!/bin/bash
# ~/workspace/jarvis-pipecat/start.sh
# Lancement JARVIS v3 Pipecat
set -e
cd "$(dirname "$0")"
# Récupération clé API Hermes depuis Infisical
echo "🔐 Récupération clé API Hermes..."
HERMES_API_KEY=$(
./venv/bin/python3 -c "
import json, subprocess
creds = json.load(open('/opt/infisical/admin-credentials.json'))
r = subprocess.run(
['/home/openclaw/.local/bin/infisical', 'secrets', 'get', 'API_SERVER_KEY',
'--domain', 'https://secret.dracodev.net',
'--projectId', '192c1eed-6666-41ee-80df-a4636a2da0ad',
'--env', 'prod', '--path', '/hermes-claw', '--plain'],
env={'INFISICAL_TOKEN': creds['token'], 'PATH': '/usr/local/bin:/usr/bin:/bin'},
capture_output=True, text=True, timeout=15,
)
print(r.stdout.strip())
"
)
if [ -z "$HERMES_API_KEY" ]; then
echo "❌ Impossible de récupérer la clé API Hermes"
exit 1
fi
export HERMES_API_KEY
export JARVIS_PIPECAT_PORT="${JARVIS_PIPECAT_PORT:-7081}"
# Désactiver la vérification d'origine pour le PoC (client websockets n'envoie pas d'Origin)
export PIPECAT_ALLOWED_ORIGINS="http://localhost:7081,http://0.0.0.0:7081"
echo "🔑 Clé: ${HERMES_API_KEY:0:15}..."
echo "🚀 JARVIS v3 Pipecat → http://0.0.0.0:${JARVIS_PIPECAT_PORT}"
exec ./venv/bin/python3 bot.py
-27
View File
@@ -1,27 +0,0 @@
from pipecat.processors.aggregators.llm_context import (
LLMUserContextAggregator,
LLMAssistantContextAggregator,
)
import inspect
print("=== LLMUserContextAggregator ===")
sig = inspect.signature(LLMUserContextAggregator.__init__)
for n, p in sig.parameters.items():
if n == 'self': continue
default = "REQUIRED" if p.default is inspect.Parameter.empty else str(p.default)[:60]
print(f" {n}: {default}")
print()
print("=== LLMAssistantContextAggregator ===")
sig2 = inspect.signature(LLMAssistantContextAggregator.__init__)
for n, p in sig2.parameters.items():
if n == 'self': continue
default = "REQUIRED" if p.default is inspect.Parameter.empty else str(p.default)[:60]
print(f" {n}: {default}")
# Check if they need a context object
print()
print("=== LLMUserContextAggregator base classes ===")
for cls in LLMUserContextAggregator.__mro__:
print(f" {cls.__name__}")
-35
View File
@@ -1,35 +0,0 @@
# Test all imports needed for the bot
from pipecat.frames.frames import OutputTransportMessageFrame
print("✅ OutputTransportMessageFrame")
from pipecat.pipeline.pipeline import Pipeline
print("✅ Pipeline")
from pipecat.pipeline.runner import PipelineRunner
print("✅ PipelineRunner")
from pipecat.pipeline.task import PipelineTask
print("✅ PipelineTask")
from pipecat.processors.aggregators.llm_context import OpenAILLMContext
print("✅ OpenAILLMContext")
from pipecat.services.openai.llm import OpenAILLMService
print("✅ OpenAILLMService")
from pipecat.transports.websocket.fastapi import (
FastAPIWebsocketParams,
FastAPIWebsocketTransport,
)
print("✅ FastAPIWebsocketTransport")
# Test create_context_aggregator
import inspect
if hasattr(OpenAILLMService, 'create_context_aggregator'):
print("✅ create_context_aggregator exists")
else:
print("❌ create_context_aggregator NOT found - checking parent...")
for cls in OpenAILLMService.__mro__:
if hasattr(cls, 'create_context_aggregator') and 'create_context_aggregator' in cls.__dict__:
print(f" Found in {cls.__name__}")
-30
View File
@@ -1,30 +0,0 @@
import pipecat.processors.aggregators.llm_context as ctx_mod
import inspect
# Check OpenAIContextAggregatorPair
print("=== OpenAIContextAggregatorPair ===")
cls = ctx_mod.OpenAIContextAggregatorPair
sig = inspect.signature(cls.__init__)
print(f"__init__ params: {list(sig.parameters.keys())}")
# Check OpenAILLMContextAggregatorPair
print()
print("=== OpenAILLMContextAggregatorPair ===")
cls2 = ctx_mod.OpenAILLMContextAggregatorPair
sig2 = inspect.signature(cls2.__init__)
print(f"__init__ params: {list(sig2.parameters.keys())}")
# Check methods
print()
print("=== OpenAIContextAggregatorPair methods ===")
for name in dir(ctx_mod.OpenAIContextAggregatorPair):
if not name.startswith('_'):
print(f" {name}")
# Check LLMUserContextAggregator
print()
print("=== LLMUserContextAggregator __init__ ===")
cls3 = ctx_mod.LLMUserContextAggregator
sig3 = inspect.signature(cls3.__init__)
print(f"params: {list(sig3.parameters.keys())}")
-136
View File
@@ -1,136 +0,0 @@
#!/usr/bin/env python3
"""Test complet JARVIS v3 Pipecat : démarre le bot, teste WebSocket."""
import subprocess, os, json, time, sys, asyncio, signal
# 1. Kill existing bots
print("🧹 Nettoyage...")
subprocess.run(["pkill", "-f", "bot.py"], capture_output=True)
time.sleep(0.5)
# 2. Get Hermes key
print("🔑 Récupération clé API...")
creds = json.load(open("/opt/infisical/admin-credentials.json"))
r = subprocess.run(
["/home/openclaw/.local/bin/infisical", "secrets", "get",
"API_SERVER_KEY", "--domain", "https://secret.dracodev.net",
"--projectId", "192c1eed-6666-41ee-80df-a4636a2da0ad",
"--env", "prod", "--path", "/hermes-claw", "--plain",
"--token", creds["token"]],
capture_output=True, text=True, timeout=20,
env={"PATH": "/usr/local/bin:/usr/bin:/bin"}
)
hermes_key = r.stdout.strip()
if not hermes_key:
print(f"❌ Erreur Infisical: {r.stderr}")
sys.exit(1)
print(f"✅ Clé: {hermes_key[:12]}...")
# 3. Start bot
print("🚀 Démarrage du bot...")
os.chdir("/home/openclaw/workspace/jarvis-pipecat")
bot_env = os.environ.copy()
bot_env["HERMES_API_KEY"] = hermes_key
bot_env["PYTHONUNBUFFERED"] = "1"
bot_proc = subprocess.Popen(
["./venv/bin/python", "bot.py"],
env=bot_env,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, bufsize=1
)
# 4. Wait for server
print("⏳ Attente démarrage...")
import select, socket
start = time.time()
ready = False
while time.time() - start < 15:
# Check port
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect(("localhost", 7081))
s.close()
ready = True
break
except:
pass
# Check for crash
if bot_proc.poll() is not None:
out = bot_proc.stdout.read() if bot_proc.stdout else ""
print(f"❌ Bot crashed!")
print(f"Output: {out[-500:]}")
sys.exit(1)
time.sleep(0.3)
if not ready:
print("❌ Timeout: serveur pas prêt")
bot_proc.kill()
sys.exit(1)
print("✅ Port 7081 ouvert!")
# Allow extra startup time for pipeline init
time.sleep(2)
# 5. Test WS
print("🔌 Test WebSocket...")
try:
import websockets
except ImportError:
subprocess.run(["./venv/bin/pip", "install", "websockets"], check=True)
import websockets
async def test_ws():
uri = "ws://localhost:7081/ws"
async with websockets.connect(uri, open_timeout=10) as ws:
# Welcome message
msg = await asyncio.wait_for(ws.recv(), timeout=10)
try:
data = json.loads(msg)
print(f"👋 Welcome: {data.get('message', data.get('content', msg))[:120]}")
except:
print(f"👋 Welcome (raw): {msg[:120]}")
# Send question
await ws.send("Bonjour JARVIS, quelle heure est-il actuellement?")
print("📤 Question envoyée")
# Wait for response
for i in range(3):
try:
resp = await asyncio.wait_for(ws.recv(), timeout=30)
try:
data = json.loads(resp)
print(f"🤖 Réponse [{i+1}]: {json.dumps(data, ensure_ascii=False)[:300]}")
if data.get("content"):
print("\n✅✅✅ SUCCÈS! JARVIS v3 répond via Pipecat! ✅✅✅")
return True
except:
print(f"🤖 Raw [{i+1}]: {resp[:200]}")
except asyncio.TimeoutError:
print(f"⏰ Timeout - pas de réponse après 30s (message {i+1})")
break
return False
success = asyncio.run(test_ws())
# 6. Report
print(f"\n{'='*50}")
print(f"🌐 URL: http://openclaw1.dev.home:7081/")
print(f"🔌 WS: ws://openclaw1.dev.home:7081/ws")
print(f"📋 Bot PID: {bot_proc.pid}")
if success:
print("✅ TEST RÉUSSI!")
else:
print("⚠️ Test partiel - vérifie le navigateur")
# Kill bot (clean exit)
bot_proc.send_signal(signal.SIGTERM)
try:
bot_proc.wait(timeout=5)
except:
bot_proc.kill()
print("🛑 Bot arrêté")
-12
View File
@@ -1,12 +0,0 @@
import pipecat.processors.aggregators.llm_context as ctx_mod
print("Module imported OK")
print("Public symbols:", [x for x in dir(ctx_mod) if not x.startswith('_')])
# Try to access classes
for name in ['OpenAILLMContext', 'OpenAILLMContextAggregatorPair', 'LLMContext']:
if hasattr(ctx_mod, name):
cls = getattr(ctx_mod, name)
print(f' {name}: {cls}')
else:
print(f' {name}: NOT FOUND')
-23
View File
@@ -1,23 +0,0 @@
from pipecat.services.openai.llm import OpenAILLMService
import inspect
# Get the create_context_aggregator method
method = getattr(OpenAILLMService, 'create_context_aggregator', None)
if method:
print("Method found!")
print(f"Type: {type(method)}")
sig = inspect.signature(method)
print(f"Signature: {sig}")
# Try to see what params it takes
for name, param in sig.parameters.items():
print(f" {name}: {param.annotation if param.annotation is not inspect.Parameter.empty else 'Any'} = {param.default if param.default is not inspect.Parameter.empty else 'REQUIRED'}")
else:
print("Method NOT found!")
# Check if it's created by a metaclass or __init_subclass__
for cls in OpenAILLMService.__mro__:
if hasattr(cls, 'create_context_aggregator'):
print(f" Found in {cls.__name__}")
m = getattr(cls, 'create_context_aggregator')
print(f" Type: {type(m)}")
-17
View File
@@ -1,17 +0,0 @@
import pipecat.processors.aggregators.llm_context as m
# Access directly from __dict__
user_cls = m.__dict__.get('LLMUserContextAggregator')
asst_cls = m.__dict__.get('LLMAssistantContextAggregator')
print(f"LLMUserContextAggregator: {user_cls}")
print(f"LLMAssistantContextAggregator: {asst_cls}")
if user_cls:
import inspect
sig = inspect.signature(user_cls.__init__)
print(f"User params: {list(sig.parameters.keys())}")
if asst_cls:
sig = inspect.signature(asst_cls.__init__)
print(f"Asst params: {list(sig.parameters.keys())}")
-13
View File
@@ -1,13 +0,0 @@
import pipecat.frames.frames as fm
# Try module attribute access
try:
cls = fm.LLMMessagesFrame
print(f"LLMMessagesFrame via module: {cls}")
except AttributeError as e:
print(f"ERROR via module: {e}")
# Try __dict__ access
for key in sorted(fm.__dict__.keys()):
if 'LLM' in key and 'Frame' in key:
print(f" fm.{key}: {type(fm.__dict__[key]).__name__}")
-8
View File
@@ -1,8 +0,0 @@
import pipecat.processors.aggregators.llm_context as m
try:
Ctx = m.OpenAILLMContext
print(f"OpenAILLMContext: {Ctx}")
except Exception as e:
print(f"ERROR: {type(e).__name__}: {e}")
import traceback
traceback.print_exc()
-6
View File
@@ -1,6 +0,0 @@
import traceback
try:
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
print("SUCCESS:", OpenAILLMContext)
except Exception as e:
traceback.print_exc()
-19
View File
@@ -1,19 +0,0 @@
import traceback, sys
tests = [
("pipecat.pipeline.pipeline", "Pipeline"),
("pipecat.pipeline.runner", "PipelineRunner"),
("pipecat.pipeline.task", "PipelineTask"),
("pipecat.processors.aggregators.llm_context", "OpenAILLMContext"),
("pipecat.services.openai.llm", "OpenAILLMService"),
("pipecat.transports.websocket.fastapi", "FastAPIWebsocketTransport"),
("pipecat.transports.websocket.fastapi", "FastAPIWebsocketParams"),
("pipecat.frames.frames", "OutputTransportMessageFrame"),
]
for mod, cls in tests:
try:
m = __import__(mod, fromlist=[cls])
getattr(m, cls)
print(f" ✅ {mod}.{cls}")
except Exception as e:
print(f" ❌ {mod}.{cls}: {e}")
-15
View File
@@ -1,15 +0,0 @@
import sys
import pipecat.processors.aggregators.llm_context as m
# Try to trigger __getattr__ if it's lazy
print("Module:", m)
print("Module file:", getattr(m, '__file__', 'none'))
# Check if __getattr__ is defined
print("Has __getattr__:", hasattr(m, '__getattr__'))
# Try to access via module __dict__
for key in sorted(m.__dict__.keys()):
if not key.startswith('_'):
print(f" m.{key}: {type(m.__dict__[key]).__name__}")
-19
View File
@@ -1,19 +0,0 @@
from pipecat.services.openai.llm import OpenAILLMService
import inspect
# Check if create_context_aggregator exists
print("Has create_context_aggregator:", hasattr(OpenAILLMService, "create_context_aggregator"))
# Check all methods
methods = [m for m in dir(OpenAILLMService) if not m.startswith('_')]
print("Methods:", methods)
# Check the class signature
sig = inspect.signature(OpenAILLMService.__init__)
print()
print("__init__ params:")
for name, param in sig.parameters.items():
if name not in ('self', 'kwargs'):
default = "REQUIRED" if param.default is inspect.Parameter.empty else str(param.default)[:50]
print(f" {name}: {default}")
-11
View File
@@ -1,11 +0,0 @@
# Try importing LLMContextAggregatorPair
from pipecat.processors.aggregators.llm_context import LLMContextAggregatorPair
print("LLMContextAggregatorPair imported OK!")
import inspect
sig = inspect.signature(LLMContextAggregatorPair.__init__)
print("__init__ params:", list(sig.parameters.keys()))
# Check if it has user() and assistant() methods
print()
print("Methods:", [m for m in dir(LLMContextAggregatorPair) if not m.startswith('_')])
-24
View File
@@ -1,24 +0,0 @@
import pipecat.processors.aggregators.llm_context as m
# Access via module attribute
try:
UserCtx = m.LLMUserContextAggregator
print(f"LLMUserContextAggregator accessed: {UserCtx}")
except AttributeError as e:
print(f"ERROR accessing LLMUserContextAggregator: {e}")
try:
AsstCtx = m.LLMAssistantContextAggregator
print(f"LLMAssistantContextAggregator accessed: {AsstCtx}")
except AttributeError as e:
print(f"ERROR accessing LLMAssistantContextAggregator: {e}")
# Try the module's __getattr__
print()
print("Testing __getattr__...")
try:
x = m.OpenAILLMContext
print(f"OpenAILLMContext: {x}")
except Exception as e:
print(f"OpenAILLMContext ERROR: {type(e).__name__}: {e}")
-15
View File
@@ -1,15 +0,0 @@
# Test 1: Access via module getattr
import pipecat.processors.aggregators.llm_context as m
try:
Ctx = m.OpenAILLMContext
print(f"✅ OpenAILLMContext accessed: {Ctx}")
except Exception as e:
print(f"❌ OpenAILLMContext: {e}")
# Test 2: from import
try:
from pipecat.processors.aggregators.llm_context import OpenAILLMContext
print("✅ from-import works!")
except ImportError as e:
print(f"❌ from-import: {e}")
-34
View File
@@ -1,34 +0,0 @@
import asyncio
import websockets
import json
import sys
async def test():
print("🔌 Connexion WebSocket à ws://localhost:7081/ws...")
try:
async with websockets.connect('ws://localhost:7081/ws', open_timeout=10) as ws:
print("✅ Connecté!")
# Receive welcome message
try:
msg = await asyncio.wait_for(ws.recv(), timeout=15)
print(f"📩 Message de bienvenue: {msg[:300]}")
except asyncio.TimeoutError:
print("⚠️ Pas de message de bienvenue (normal si pas d'event handler)")
# Send a question
print("📤 Envoi: 'Quelle heure est-il ?'")
await ws.send("Quelle heure est-il ?")
# Get response
try:
response = await asyncio.wait_for(ws.recv(), timeout=60)
print(f"📩 Réponse: {response[:500]}")
except asyncio.TimeoutError:
print("❌ Timeout: pas de réponse du LLM en 60s")
except Exception as e:
print(f"❌ Erreur: {e}")
asyncio.run(test())
-50
View File
@@ -1,50 +0,0 @@
import asyncio
import websockets
import json
import time
async def test():
t0 = time.time()
print("🔌 Connexion WebSocket à ws://localhost:7081/ws...")
try:
async with websockets.connect('ws://localhost:7081/ws', open_timeout=10) as ws:
t_connect = time.time() - t0
print(f"✅ Connecté en {t_connect:.1f}s!")
# Receive welcome message
try:
msg = await asyncio.wait_for(ws.recv(), timeout=15)
print(f"📩 Bienvenue: {msg[:200]}")
except asyncio.TimeoutError:
print("⚠️ Pas de message de bienvenue")
# Send a question
t1 = time.time()
print("📤 Envoi: 'Quelle heure est-il ?'")
await ws.send("Quelle heure est-il ?")
# Get response
try:
response = await asyncio.wait_for(ws.recv(), timeout=90)
t_resp = time.time() - t1
print(f"📩 Réponse ({t_resp:.1f}s): {response[:500]}")
except asyncio.TimeoutError:
print("❌ Timeout: pas de réponse du LLM en 90s")
# Send second question
t2 = time.time()
print("📤 Envoi: 'Quel temps fait-il aujourd''hui ?'")
await ws.send("Quel temps fait-il aujourd'hui ?")
try:
response2 = await asyncio.wait_for(ws.recv(), timeout=90)
t_resp2 = time.time() - t2
print(f"📩 Réponse 2 ({t_resp2:.1f}s): {response2[:500]}")
except asyncio.TimeoutError:
print("❌ Timeout: pas de réponse 2 en 90s")
except Exception as e:
print(f"❌ Erreur: {type(e).__name__}: {e}")
asyncio.run(test())