Guest User

Corti API WebSocket

a guest
Apr 17th, 2025
390
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.13 KB | None | 0 0
  1. import os
  2. import json
  3. import time
  4. import uuid
  5. import requests
  6. import websocket
  7. from urllib.parse import quote
  8. from datetime import datetime, timezone
  9. from dotenv import load_dotenv
  10.  
  11. # Load environment variables from .env file
  12. load_dotenv()
  13. CLIENT_ID = os.getenv("CLIENT_ID")
  14. CLIENT_SECRET = os.getenv("CLIENT_SECRET")
  15. TENANT_NAME = os.getenv("TENANT_NAME")
  16. ENVIRONMENT = os.getenv("ENVIRONMENT")
  17.  
  18. def get_access_token():
  19.     print("πŸ” Step 1 β€” Authenticating via Keycloak...")
  20.     url = f"https://keycloak.{ENVIRONMENT}.corti.app/realms/{TENANT_NAME}/protocol/openid-connect/token"
  21.     payload = {
  22.         "grant_type": "client_credentials",
  23.         "client_id": CLIENT_ID,
  24.         "client_secret": CLIENT_SECRET,
  25.         "scope": "openid"
  26.     }
  27.     headers = {"Content-Type": "application/x-www-form-urlencoded"}
  28.     response = requests.post(url, data=payload, headers=headers)
  29.     if response.ok:
  30.         print("βœ… Authenticated")
  31.         return response.json()["access_token"]
  32.     raise Exception(f"Authentication failed: {response.status_code} - {response.text}")
  33.  
  34. def create_interaction(token):
  35.     print("πŸ”— Step 2 β€” Creating interaction session...")
  36.     url = f"https://api.{ENVIRONMENT}.corti.app/v2/interactions"
  37.     headers = {
  38.         "Authorization": f"Bearer {token}",
  39.         "Content-Type": "application/json",
  40.         "Tenant-Name": TENANT_NAME
  41.     }
  42.     now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
  43.     payload = {
  44.         "assignedUserId": str(uuid.uuid4()),
  45.         "encounter": {
  46.             "identifier": str(uuid.uuid4()),
  47.             "status": "planned",
  48.             "type": "first_consultation",
  49.             "period": {
  50.                 "startedAt": now,
  51.                 "startedAtTzoffset": "+00:00",
  52.                 "endedAt": now,
  53.                 "endedAtTzoffset": "+00:00"
  54.             },
  55.             "title": "Consultation"
  56.         }
  57.     }
  58.     response = requests.post(url, json=payload, headers=headers)
  59.     if response.ok:
  60.         print("βœ… Interaction session created")
  61.         return response.json()
  62.     raise Exception(f"Interaction creation failed: {response.status_code} - {response.text}")
  63.  
  64. def stream_audio(ws, path):
  65.     print("🎧 Step 4 β€” Streaming audio...")
  66.     chunk_size = 32000
  67.     with open(path, "rb") as f:
  68.         while chunk := f.read(chunk_size):
  69.             ws.send(chunk, opcode=websocket.ABNF.OPCODE_BINARY)
  70.             time.sleep(0.5)
  71.     ws.send(json.dumps({"type": "end"}))
  72.     print("βœ… Audio stream completed")
  73.  
  74. def connect_and_stream_audio(ws_url, audio_path):
  75.     print("🌐 Step 3 β€” Connecting to WebSocket and sending config...")
  76.     transcript_lines = []
  77.  
  78.     def on_open(ws):
  79.         config = {
  80.             "type": "config",
  81.             "configuration": {
  82.                 "transcription": {
  83.                     "primaryLanguage": "en",
  84.                     "isDiarization": False,
  85.                     "isMultichannel": False,
  86.                     "participants": [{"channel": 0, "role": "multiple"}]
  87.                 },
  88.                 "mode": {"type": "facts", "outputLocale": "en"}
  89.             }
  90.         }
  91.         ws.send(json.dumps(config))
  92.         print("βœ… Configuration sent")
  93.  
  94.     def on_message(ws, message):
  95.         msg = json.loads(message)
  96.         msg_type = msg.get("type").upper()
  97.  
  98.         if msg_type == "CONFIG_ACCEPTED":
  99.             print("🟒 Config accepted by server")
  100.             stream_audio(ws, audio_path)
  101.  
  102.         elif msg_type == "TRANSCRIPT":
  103.             print("πŸ“„Step 5 -Transcript received:")
  104.             content = " ".join([x["transcript"] for x in msg["data"]])
  105.             if content:
  106.                 transcript_lines.append(content)
  107.  
  108.         elif msg_type == "FACTS":
  109.             print("πŸ“„Step 5 - Facts received:")
  110.             print(json.dumps(msg, indent=2))
  111.  
  112.         elif msg_type == "ENDED":
  113.             print("πŸ“Œ Step 6 β€” Session ended by server")
  114.             if transcript_lines:
  115.                 print("\nπŸ“ Final Transcript:")
  116.                 for line in transcript_lines:
  117.                     print(line)
  118.             else:
  119.                 print("⚠️ No transcript received.")
  120.             ws.close()
  121.  
  122.     def on_close(ws, code, reason):
  123.         print("βœ… WebSocket closed")
  124.  
  125.     websocket.enableTrace(False)
  126.     ws = websocket.WebSocketApp(
  127.         ws_url,
  128.         on_open=on_open,
  129.         on_message=on_message,
  130.         on_error=lambda ws, err: print(f"[ERROR] {err}"),
  131.         on_close=on_close
  132.     )
  133.     ws.run_forever()
  134.  
  135. def main(audio_path):
  136.     if not os.path.isfile(audio_path):
  137.         raise FileNotFoundError(f"Missing file: {audio_path}")
  138.  
  139.     token = get_access_token()
  140.     interaction = create_interaction(token)
  141.  
  142.     print("\nπŸ“¦ Final interaction response:")
  143.     print(json.dumps(interaction, indent=2))
  144.  
  145.     ws_url = interaction.get("websocketUrl")
  146.     if not ws_url:
  147.         raise ValueError("Missing WebSocket URL in response")
  148.  
  149.     ws_url += f"&token={quote(f'Bearer {token}')}"
  150.     connect_and_stream_audio(ws_url, audio_path)
  151.  
  152. if __name__ == "__main__":
  153.     audio_file_path = "PATH_TO_FILE.wav"
  154.     main(audio_file_path)
  155.  
Advertisement
Add Comment
Please, Sign In to add comment