Spaces:
Running
Running
Commit
·
e832043
1
Parent(s):
4fa815b
loading the langgraph agents issue
Browse files
examples/voice_agent_webrtc_langgraph/pipeline.py
CHANGED
|
@@ -81,72 +81,88 @@ ice_servers = (
|
|
| 81 |
|
| 82 |
@app.get("/assistants")
|
| 83 |
async def list_assistants(request: Request):
|
| 84 |
-
"""
|
| 85 |
-
|
|
|
|
| 86 |
"""
|
| 87 |
import requests
|
| 88 |
|
| 89 |
base_url = os.getenv("LANGGRAPH_BASE_URL", "http://127.0.0.1:2024").rstrip("/")
|
| 90 |
|
| 91 |
-
# Build auth header: prefer inbound request Authorization, else env token
|
| 92 |
inbound_auth = request.headers.get("authorization")
|
| 93 |
token = os.getenv("LANGGRAPH_AUTH_TOKEN") or os.getenv("AUTH0_ACCESS_TOKEN") or os.getenv("AUTH_BEARER_TOKEN")
|
| 94 |
headers = {"Authorization": inbound_auth} if inbound_auth else ({"Authorization": f"Bearer {token}"} if token else None)
|
| 95 |
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
try:
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
json
|
| 101 |
-
|
| 102 |
-
"
|
| 103 |
-
|
| 104 |
-
"sort_by": "assistant_id",
|
| 105 |
-
"sort_order": "asc",
|
| 106 |
-
"select": ["assistant_id"],
|
| 107 |
-
},
|
| 108 |
-
timeout=10,
|
| 109 |
-
headers=headers,
|
| 110 |
-
)
|
| 111 |
-
search_resp.raise_for_status()
|
| 112 |
except Exception as exc: # noqa: BLE001
|
| 113 |
-
logger.
|
| 114 |
-
return JSONResponse(status_code=502, content={"error": "assistants_search_failed"})
|
| 115 |
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
ids = search_resp.json() or []
|
| 119 |
-
if isinstance(ids, dict): # some servers may wrap the list
|
| 120 |
-
ids = ids.get("items") or ids.get("results") or []
|
| 121 |
-
except Exception: # noqa: BLE001
|
| 122 |
-
ids = []
|
| 123 |
-
|
| 124 |
-
for entry in ids:
|
| 125 |
-
assistant_id = None
|
| 126 |
-
if isinstance(entry, dict):
|
| 127 |
-
assistant_id = entry.get("assistant_id") or entry.get("id")
|
| 128 |
-
elif isinstance(entry, str):
|
| 129 |
-
assistant_id = entry
|
| 130 |
-
if not assistant_id:
|
| 131 |
-
continue
|
| 132 |
-
# Fetch details for each assistant id (best-effort)
|
| 133 |
-
detail = {"assistant_id": assistant_id}
|
| 134 |
try:
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
display_name = (
|
| 151 |
detail.get("name")
|
| 152 |
or md.get("display_name")
|
|
@@ -155,9 +171,25 @@ async def list_assistants(request: Request):
|
|
| 155 |
or detail.get("assistant_id")
|
| 156 |
)
|
| 157 |
detail["display_name"] = display_name
|
| 158 |
-
|
| 159 |
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
async def run_bot(webrtc_connection, ws: WebSocket, assistant_override: str | None = None):
|
| 163 |
"""Run the voice agent bot with WebRTC connection and WebSocket.
|
|
|
|
| 81 |
|
| 82 |
@app.get("/assistants")
|
| 83 |
async def list_assistants(request: Request):
|
| 84 |
+
"""Return a list of assistants from LangGraph, with robust fallbacks.
|
| 85 |
+
|
| 86 |
+
Output: List of {assistant_id, graph_id?, name?, description?, display_name}.
|
| 87 |
"""
|
| 88 |
import requests
|
| 89 |
|
| 90 |
base_url = os.getenv("LANGGRAPH_BASE_URL", "http://127.0.0.1:2024").rstrip("/")
|
| 91 |
|
|
|
|
| 92 |
inbound_auth = request.headers.get("authorization")
|
| 93 |
token = os.getenv("LANGGRAPH_AUTH_TOKEN") or os.getenv("AUTH0_ACCESS_TOKEN") or os.getenv("AUTH_BEARER_TOKEN")
|
| 94 |
headers = {"Authorization": inbound_auth} if inbound_auth else ({"Authorization": f"Bearer {token}"} if token else None)
|
| 95 |
|
| 96 |
+
def normalize_entries(raw_items: list) -> list[dict]:
|
| 97 |
+
results: list[dict] = []
|
| 98 |
+
for entry in raw_items:
|
| 99 |
+
assistant_id = None
|
| 100 |
+
if isinstance(entry, dict):
|
| 101 |
+
assistant_id = entry.get("assistant_id") or entry.get("id") or entry.get("name")
|
| 102 |
+
elif isinstance(entry, str):
|
| 103 |
+
assistant_id = entry
|
| 104 |
+
if not assistant_id:
|
| 105 |
+
continue
|
| 106 |
+
results.append({"assistant_id": assistant_id, **(entry if isinstance(entry, dict) else {})})
|
| 107 |
+
return results
|
| 108 |
+
|
| 109 |
+
# Try GET /assistants first (newer servers)
|
| 110 |
+
items: list[dict] = []
|
| 111 |
try:
|
| 112 |
+
get_resp = requests.get(f"{base_url}/assistants", params={"limit": 100}, timeout=8, headers=headers)
|
| 113 |
+
if get_resp.ok:
|
| 114 |
+
data = get_resp.json() or []
|
| 115 |
+
if isinstance(data, dict):
|
| 116 |
+
data = data.get("items") or data.get("results") or data.get("assistants") or []
|
| 117 |
+
items = normalize_entries(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
except Exception as exc: # noqa: BLE001
|
| 119 |
+
logger.warning(f"GET /assistants failed: {exc}")
|
|
|
|
| 120 |
|
| 121 |
+
# Fallback: POST /assistants/search (older servers)
|
| 122 |
+
if not items:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
try:
|
| 124 |
+
search_resp = requests.post(
|
| 125 |
+
f"{base_url}/assistants/search",
|
| 126 |
+
json={
|
| 127 |
+
"metadata": {},
|
| 128 |
+
"limit": 100,
|
| 129 |
+
"offset": 0,
|
| 130 |
+
"sort_by": "assistant_id",
|
| 131 |
+
"sort_order": "asc",
|
| 132 |
+
"select": ["assistant_id"],
|
| 133 |
+
},
|
| 134 |
+
timeout=10,
|
| 135 |
+
headers=headers,
|
| 136 |
+
)
|
| 137 |
+
if search_resp.ok:
|
| 138 |
+
data = search_resp.json() or []
|
| 139 |
+
if isinstance(data, dict):
|
| 140 |
+
data = data.get("items") or data.get("results") or []
|
| 141 |
+
items = normalize_entries(data)
|
| 142 |
+
except Exception as exc: # noqa: BLE001
|
| 143 |
+
logger.warning(f"POST /assistants/search failed: {exc}")
|
| 144 |
+
|
| 145 |
+
# Best-effort: enrich with details when possible
|
| 146 |
+
enriched: list[dict] = []
|
| 147 |
+
for item in items:
|
| 148 |
+
detail = dict(item)
|
| 149 |
+
assistant_id = detail.get("assistant_id")
|
| 150 |
+
if assistant_id:
|
| 151 |
+
try:
|
| 152 |
+
detail_resp = requests.get(f"{base_url}/assistants/{assistant_id}", timeout=5, headers=headers)
|
| 153 |
+
if detail_resp.ok:
|
| 154 |
+
d = detail_resp.json() or {}
|
| 155 |
+
detail.update(
|
| 156 |
+
{
|
| 157 |
+
"graph_id": d.get("graph_id"),
|
| 158 |
+
"name": d.get("name"),
|
| 159 |
+
"description": d.get("description"),
|
| 160 |
+
"metadata": d.get("metadata") or {},
|
| 161 |
+
}
|
| 162 |
+
)
|
| 163 |
+
except Exception:
|
| 164 |
+
pass
|
| 165 |
+
md = (detail.get("metadata") or {}) if isinstance(detail.get("metadata"), dict) else {}
|
| 166 |
display_name = (
|
| 167 |
detail.get("name")
|
| 168 |
or md.get("display_name")
|
|
|
|
| 171 |
or detail.get("assistant_id")
|
| 172 |
)
|
| 173 |
detail["display_name"] = display_name
|
| 174 |
+
enriched.append(detail)
|
| 175 |
|
| 176 |
+
# Final fallback: read local graphs from agents/langgraph.json
|
| 177 |
+
if not enriched:
|
| 178 |
+
try:
|
| 179 |
+
config_path = Path(__file__).parent / "agents" / "langgraph.json"
|
| 180 |
+
with open(config_path, encoding="utf-8") as f:
|
| 181 |
+
cfg = json.load(f) or {}
|
| 182 |
+
graphs = (cfg.get("graphs") or {}) if isinstance(cfg, dict) else {}
|
| 183 |
+
for graph_id in graphs.keys():
|
| 184 |
+
enriched.append({
|
| 185 |
+
"assistant_id": graph_id,
|
| 186 |
+
"graph_id": graph_id,
|
| 187 |
+
"display_name": graph_id,
|
| 188 |
+
})
|
| 189 |
+
except Exception as exc: # noqa: BLE001
|
| 190 |
+
logger.error(f"Failed to read local agents/langgraph.json: {exc}")
|
| 191 |
+
|
| 192 |
+
return enriched
|
| 193 |
|
| 194 |
async def run_bot(webrtc_connection, ws: WebSocket, assistant_override: str | None = None):
|
| 195 |
"""Run the voice agent bot with WebRTC connection and WebSocket.
|