| import os |
| import json |
| import httpx |
| import gradio as gr |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| LETTA_BASE_URL = (os.getenv("LETTA_BASE_URL")) |
| LETTA_API_KEY = (os.getenv("LETTA_API_KEY")) |
| LETTA_AGENT_ID = (os.getenv("LETTA_AGENT_ID", "default")) |
| |
| DEFAULT_SESSION = (os.getenv("LETTA_SESSION_ID", "")) |
|
|
|
|
| def _config_error() -> str | None: |
| if not LETTA_BASE_URL: |
| return "Missing LETTA_BASE_URL (e.g., https://cloud.letta.ai/api/v1)" |
| if not LETTA_BASE_URL.startswith("https://"): |
| return "LETTA_BASE_URL must start with https://" |
| if not LETTA_API_KEY: |
| return "Missing LETTA_API_KEY" |
| return None |
|
|
|
|
| def _stream_letta(messages, max_tokens, temperature, top_p, session_id: str): |
| """ |
| Streams tokens from Letta Cloud (OpenAI-compatible SSE). |
| """ |
| headers = { |
| "Authorization": f"Bearer {LETTA_API_KEY}", |
| "Content-Type": "application/json", |
| } |
|
|
| |
| payload = { |
| "messages": messages, |
| "stream": True, |
| "max_tokens": int(max_tokens), |
| "temperature": float(temperature), |
| "top_p": float(top_p), |
| |
| "extra": { |
| "agent_id": LETTA_AGENT_ID, |
| "session_id": session_id or DEFAULT_SESSION |
| } |
| } |
|
|
| |
| if LETTA_MODEL: |
| payload["model"] = LETTA_MODEL |
|
|
| endpoint = f"{LETTA_BASE_URL}/chat/completions" |
|
|
| |
| with httpx.stream("POST", endpoint, headers=headers, json=payload, timeout=120) as resp: |
| if resp.status_code >= 400: |
| |
| yield f"Letta error {resp.status_code}: {resp.text}" |
| return |
|
|
| partial = "" |
| for raw in resp.iter_lines(): |
| if not raw: |
| continue |
| line = raw.decode("utf-8", errors="ignore") |
| if not line.startswith("data: "): |
| continue |
| data = line[6:].strip() |
| if data == "[DONE]": |
| break |
| try: |
| chunk = json.loads(data) |
| |
| delta = chunk["choices"][0]["delta"] |
| token = delta.get("content", "") |
| if token: |
| partial += token |
| yield partial |
| except Exception: |
| |
| continue |
|
|
|
|
| def respond( |
| message, |
| history: list[dict[str, str]], |
| system_message, |
| max_tokens, |
| temperature, |
| top_p, |
| _hf_token: gr.OAuthToken, |
| session_id="", |
| ): |
| |
| err = _config_error() |
| if err: |
| yield f"⚠️ Configuration error: {err}" |
| return |
|
|
| |
| msgs = [] |
| if system_message: |
| msgs.append({"role": "system", "content": system_message}) |
| if history: |
| msgs.extend(history) |
| msgs.append({"role": "user", "content": message or ""}) |
|
|
| try: |
| for chunk in _stream_letta(msgs, max_tokens, temperature, top_p, session_id=session_id or DEFAULT_SESSION): |
| yield chunk |
| except httpx.ConnectError as e: |
| yield f"❌ Network error reaching Letta: {e}" |
| except Exception as e: |
| yield f"❌ Unexpected error: {e}" |
|
|
|
|
| |
| chat = gr.ChatInterface( |
| respond, |
| type="messages", |
| additional_inputs=[ |
| gr.Textbox(value="You are a concise, helpful assistant.", label="System message"), |
| gr.Slider(1, 4096, value=512, step=1, label="Max new tokens"), |
| gr.Slider(0.0, 2.0, value=0.7, step=0.1, label="Temperature"), |
| gr.Slider(0.1, 1.0, value=0.95, step=0.05, label="Top-p"), |
| gr.Textbox(value=DEFAULT_SESSION, label="Session ID (memory)"), |
| ], |
| ) |
|
|
| with gr.Blocks(theme=gr.themes.Soft()) as demo: |
| with gr.Sidebar(): |
| gr.Markdown("### Letta Cloud Chat") |
| gr.Markdown("Uses OpenAI-compatible streaming via Letta Cloud.") |
| chat.render() |
|
|
| if __name__ == "__main__": |
| demo.queue().launch(server_name="0.0.0.0", server_port=7860) |
|
|