#ChatInterface
🤗Gradio <> LangChain

Gradio recently added a ChatInterface to make it easier to build chat applications

They helpfully put together an example of integrating it with LangChain - check it out below!

www.gradio.app/guides/chati...
December 2, 2024 at 7:20 PM
We just released Gradio 5.9.0, with a much more beautiful and capable ChatInterface. This is a big release, let us know what you think!
December 13, 2024 at 9:21 PM
If you’ve ever wanted to build an AI chat tool, this article shows you how using LangChain and Gradio.

Worth checking out
www.gradio.app/guides/chati...
Chatinterface Examples
A Step-by-Step Gradio Tutorial
www.gradio.app
December 2, 2024 at 7:27 PM
Und wie hoch war der Anteil des LLM im Vergleich zum Anteil konventioneller Software?

Wurde das nur über das Chatinterface ohne Plugins gemacht oder wurde über MCP oder CLI am Ende doch konventionelle Software verwendet?
September 10, 2026 at 10:42 AM
Aber zumindest in Österreich gehostet (das Chatinterface, der Bot ist was Microsoft Azure oder halt OpenAI USA).

Next: Telfs Chatbot. Das schaut wieder nach AI Concierge aus, der KI-Chatbot-Krebs, der sich durch ganz Österreich frisst. Vor dem Berufsinfomat Debakel war übringes nohc eine ÖVP nahe
October 16, 2025 at 7:45 PM
Voice-AI in Banken erfordert völlig neue Architekturen, da LLM-basierte Ansätze an Echtzeit-Latenz scheitern. #Aktuell #Strategie #IT‑Architektur #LLM #Prompt #Sprachmodell
https://www.it-finanzmagazin.de/voice-ai-wird-in-banken-scheitern-wenn-man-sie-wie-chatgpt-baut-241755/
Voice-AI wird in Banken scheitern - wenn man sie wie ChatGPT baut
Für textbasierte Copilots funktioniert die Verbindung von Sprachmodellen mit einem Chatinterface gut - für Voice-Systeme in Echtzeit nicht.
www.it-finanzmagazin.de
March 23, 2026 at 3:17 PM
OpenAI verrast gebruikers en onthult directe spraakfunctie: ‘Je praat nu gewoon in ChatGPT zelf’ aiwereld.nl/nieuws/opena...
OpenAI onthult directe spraakfunctie: ‘Je praat nu gewoon in ChatGPT zelf’
OpenAI introduceert vandaag ChatGPT Voice integratie in de normale chatinterface, waardoor gebruikers meteen kunnen praten, luisteren en live beelden bekijken. De functie rolt nu uit naar alle…
aiwereld.nl
November 27, 2025 at 5:41 PM
Creating a Website Chat Widget with Gradio Part III
`gr.State` is **convenient when everything can be handled solely within the Gradio UI** , but when that’s not the case, managing data yourself is ultimately simpler. Even for functions called directly from `ChatInterface`, it’s possible to add more arguments. Therefore, the basic approach should be to add arguments and exchange IDs between the frontend and backend. * * * You’re bumping into a very real, very common backend problem: “How do I log every message, but still know which lines belong to which user/session, when everything goes through one shared `chat()` function?” I’ll walk through the idea carefully and tie it to your current setup: 1. Why one log file is okay (as long as you add a session id). 2. Why `gr.State` feels confusing with your custom widget. 3. A simple, practical pattern that works well **with your widget** : * Generate a `conversation_id` in JavaScript * Send it to Python on every request * Log it with each turn 4. How this compares to the “Gradio-native” `gr.State` pattern, so your intuition about it makes sense. Throughout I’ll relate to what Gradio supports officially (ChatInterface, session state, JS client) so you can see you’re not fighting the framework. (Gradio) * * * ## 1. One log file is fine if every line has a conversation_id Background: * Your `app.py` runs inside **one** Gradio demo (or a small pool), but it serves **many users** in parallel. * Gradio calls your `chat(message, history, ...)` function once for each incoming message. ((note Subtitles)) * If you log each turn to `/data/chat_logs.jsonl`, you’ll indeed get messages from different users interleaved in time. That interleaving is normal. The way to make it not “a huge unreadable mess” is to include **conversation_id** on every log line: {"timestamp": "...", "conversation_id": "abc123", "user_text": "Hi", ...} {"timestamp": "...", "conversation_id": "def456", "user_text": "Hello", ...} {"timestamp": "...", "conversation_id": "abc123", "user_text": "Tell me more", ...} Later, you can: * filter or group by `conversation_id` in any tool (Python, pandas, `jq`, etc.), and * reconstruct each conversation independently. So the core requirement is not “multiple log files” but “a stable id for each browser/chat session”. * * * ## 2. Why `gr.State` feels tricky in your setup Gradio has **session state** that persists across submits within one browser tab. (Gradio) * With the **built-in UI** (Gradio page), you can attach a `gr.State` to a Chatbot and store a UUID there. * The official “Chatbot Specific Events” guide shows exactly this: they store a `uuid` per chat session and reuse it in the handler. (Gradio) That example looks roughly like: from uuid import uuid4 import gradio as gr def clear(): return uuid4() def chat_fn(message, history, uuid): # use uuid here ... with gr.Blocks() as demo: uuid_state = gr.State(uuid4) chatbot = gr.Chatbot(type="messages") chatbot.clear(clear, outputs=[uuid_state]) gr.ChatInterface( chat_fn, chatbot=chatbot, additional_inputs=[uuid_state], type="messages", ) Here: * `uuid_state` is per-session; each browser/tab gets its own UUID. * Gradio’s built-in UI handles the wiring: when the user sends a message, it calls `chat_fn(message, history, uuid_state_value)` automatically. (Gradio) In your situation: * You are **not** using the built-in UI. * Your front-end is your **own HTML + JS widget** that calls the Space via `@gradio/client`: const result = await client.predict("/chat", { message: { text: userMessage, files: [] }, }); When you add `gr.State` or extra inputs in Python, your function signature changes: def chat(message, history, uuid): ... and Gradio’s `/chat` endpoint now expects that extra argument. (Gradio) For the **built-in UI** , Gradio injects that for you. For your widget, you must explicitly send it in the JS **payload** ; otherwise the arguments don’t match and you get errors (this is exactly what people hit in GitHub issues when they see “predict() got an unexpected keyword argument X” or payload length mismatches). (GitHub) So: * `gr.State` is powerful, but you need to carefully mirror whatever **extra inputs** your Python function wants on the JS side. * Since you already control the JS payload, it’s actually simpler to **let JS generate the conversation_id and send it in** directly. That’s why `gr.State` felt confusing: it’s Gradio’s way to persist things inside the Gradio UI, but you’ve now brought your own UI. * * * ## 3. Simple, robust pattern for your widget: generate UUID in JS, log it in Python You were already thinking along these lines with `uuid4` in Python. The missing piece is: the **stable value** should live on the client (browser), and be passed into Python on every request. ### 3.1. Step 1 – Extend your Python chat to accept conversation_id Let’s extend your base app minimally. **Python (`app.py`):** import os import json import uuid from datetime import datetime import gradio as gr LOG_PATH = "/data/chat_logs.jsonl" os.makedirs(os.path.dirname(LOG_PATH), exist_ok=True) def log_turn(conversation_id, message, history, response_lines): # 1) Extract user text (since you're using multimodal=True) if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) # 2) Build a log record with a conversation_id record = { "timestamp": datetime.utcnow().isoformat(), "conversation_id": conversation_id, "user_text": user_text, "response": response_lines, "history": history, } # 3) Append as JSONL try: with open(LOG_PATH, "a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n") except Exception as e: print(f"[log_turn] failed: {e}") def chat(message, history, conversation_id): # Fallback: if conversation_id somehow missing/empty, generate one if not conversation_id: conversation_id = str(uuid.uuid4()) # Your current simple logic if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] # Log this turn log_turn(conversation_id, message, history, response_lines) return response_lines # Additional (hidden) input so ChatInterface exposes 'conversation_id' conversation_id_input = gr.Textbox( label="conversation_id", visible=False, value="", ) demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", additional_inputs=[conversation_id_input], # extra arg to fn ) if __name__ == "__main__": demo.launch() What this does: * `chat()` now takes three args: `message, history, conversation_id`. Gradio’s docs say: `ChatInterface(fn, ...)` will pass standard inputs (`message`, `history`) and then any extra `additional_inputs` you list, in order. (Gradio) * `conversation_id_input` is a hidden textbox; its `label` becomes the key in the API payload (`conversation_id`), which matches how `@gradio/client` expects to receive arguments by name. (Gradio) * Every log record includes that `conversation_id`. So Python is now ready to receive an id from the front-end. * * * ### 3.2. Step 2 – Generate a UUID once in JavaScript On the widget side, you already have something like (simplified): const client = await Client.connect("https://your-space.hf.space"); async function sendMessage() { const result = await client.predict("/chat", { message: { text: userMessage, files: [] }, }); } We extend it only slightly: <script type="module"> import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; async function initChatWidget() { const client = await Client.connect("https://your-space.hf.space"); // 1. Create a conversationId for this browser widget. // Option A: new conversation each page load: const conversationId = crypto.randomUUID(); // Option B (optional): persist across reloads: // let conversationId = localStorage.getItem("my_chat_conversation_id"); // if (!conversationId) { // conversationId = crypto.randomUUID(); // localStorage.setItem("my_chat_conversation_id", conversationId); // } // ... your existing DOM setup ... async function sendMessage() { const userMessage = chatInput.value.trim(); if (!userMessage) return; appendMessage(userMessage, "user"); chatInput.value = ""; try { const result = await client.predict("/chat", { // required by ChatInterface with multimodal=True message: { text: userMessage, files: [] }, // this extra field must match the label of the extra input conversation_id: conversationId, }); const lines = result.data[0]; // list of strings from Python const botMessage = Array.isArray(lines) ? lines.join("\n") : String(lines); appendMessage(botMessage, "bot"); } catch (error) { console.error("Error:", error); appendMessage("Sorry, there was an error.", "bot"); } } // ... event listeners, initial greeting, etc. ... } initChatWidget(); </script> Key details: * `conversationId` is created **once** when the widget is initialized. * Every call to `sendMessage()` reuses the same id. * Another user on another browser gets a different id. * The payload keys (`message`, `conversation_id`) match the names of the inputs on the backend: * `message` → the main input (defined by ChatInterface’s `type="messages", multimodal=True`). (Gradio) * `conversation_id` → the additional hidden `Textbox(label="conversation_id")`. * The Gradio JS client just forwards that payload; internally it maps the keys to `fn` arguments in the same order the inputs are declared. (Gradio) Now each line in `/data/chat_logs.jsonl` will look like: {"timestamp":"2025-11-17T08:00:00Z", "conversation_id":"c6e0c7b9-...", "user_text":"Hi", "response":["Hello from your Gradio Space!","You said: Hi"], "history":[...] } and for another user: {"timestamp":"2025-11-17T08:01:00Z", "conversation_id":"f1a3e8d2-...", "user_text":"Hello", "response":["Hello from your Gradio Space!","You said: Hello"], "history":[...] } Same file, but easy to separate by `conversation_id`. * * * ## 4. How this compares to using `gr.State` Your instinct to use `gr.State()` is good; it’s exactly what the official “Chatbot Specific Events” example does to store a UUID per chat session in the **Gradio UI**. (Gradio) But because your **front-end is not Gradio** , Gradio’s usual session-state magic isn’t being used. Instead: * The JS client (`@gradio/client`) sees your app as a plain HTTP API with named inputs. (Gradio) * It doesn’t know anything about Gradio’s UI session state unless you explicitly treat that state as just another input or output. In principle you _could_ : * Define a `gr.State` for `conversation_id` in Python, * Have `chat()` return the updated `conversation_id` as an extra output, * Have your widget read `result.data[1]` and send that value back on the next call, etc. But that’s more wiring and it doesn’t buy you much over simply generating the UUID directly in JS. So for your specific setup (custom widget + Gradio backend): * **Standard, simple pattern** : * Generate `conversation_id` in the client. * Send it to the server on every `predict()`. * Use it in logs. * **Gradio-native`gr.State` pattern**: * Great when using the Gradio UI directly, or when you don’t have your own front-end. Both are valid; the first is simpler for you right now. * * * ### Practical bottom line 1. Yes, you should log for each prompt/response pair inside `chat()`. 2. Yes, a single log file is fine — **as long as you log a`conversation_id`** per line. 3. The cleanest way for your custom widget is: * create a UUID in JavaScript once per widget instance, * pass it as `conversation_id` in every `client.predict("/chat", {...})`, * accept it as an extra `conversation_id` argument in Python and log it.
discuss.huggingface.co
November 17, 2025 at 2:06 AM
Creating a Website Chat Widget with Gradio Part II
> How do I pre-seed the chat with a prompt such as “Hello, welcome to the chat, how can I help?” It seems best to support it **on both** the Gradio side (Python + JS) and the custom frontend JS side. * * * You’re right that with your current setup the Space just “waits” for the first user message. That’s expected: `ChatInterface` only calls your `chat()` function when a message comes in. There are **two different places** you can add a welcome message: 1. On the **backend (Python / Gradio)** – pre-seed the conversation history. 2. On the **frontend (your custom widget JS)** – pre-seed what the user sees in the website chat box. Because your widget is custom HTML/JS using `@gradio/client`, you usually want **both** : * Python: so your model “knows” there is an initial assistant message in the history. * JS: so the welcome bubble actually appears in your web page. Below I’ll show both, starting from a corrected version of your code. * * * ## 1. Corrected base Python code (what you already have) Your snippet with proper indentation, quotes, and `__name__`: import gradio as gr def chat(message, history): if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] return response_lines demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", # matches the "/chat" path used by the JS client ) if __name__ == "__main__": demo.launch() * `multimodal=True` → `message` is a dict like `{"text": "...", "files": ...]}`. ([gradio.app) * `api_name="chat"` → the HTTP endpoint is `/chat`, which your widget calls via `client.predict("/chat", ...)`. (gradio.app) This is the base we will extend. * * * ## 2. Pre-seed on the backend (Gradio side) Gradio’s `Chatbot` component lets you set an initial history using the `value` parameter. With `type="messages"`, the history is a list of dictionaries: {"role": "assistant" or "user", "content": "text here"} This is described in the Chatbot/messages docs. (gradio.app) You can create a `Chatbot` with a starting assistant message and pass it into `ChatInterface` via the `chatbot=` argument. ### 2.1. Python with pre-seeded welcome message import gradio as gr def chat(message, history): if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] return response_lines # Initial chat history in "messages" format initial_history = [ { "role": "assistant", "content": "Hello, welcome to the chat, how can I help?", } ] # Chatbot component with initial value chatbot = gr.Chatbot( value=initial_history, type="messages", ) demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", chatbot=chatbot, # use our pre-seeded chatbot ) if __name__ == "__main__": demo.launch() What this achieves: * If someone opens the **Gradio UI** (e.g. on the Space itself), they see the greeting already in the chat window. * On the backend, when the first user message arrives, the `history` parameter passed into `chat(message, history)` already includes this assistant message. So if later you build an LLM prompt from `history`, that greeting will be part of the conversation context. (gradio.app) However, this **does not automatically show** the greeting inside your custom website widget, because that UI is controlled by your own JavaScript. * * * ## 3. Pre-seed in your custom widget (front-end side) Your widget uses `@gradio/client` to call the `/chat` endpoint and then appends DOM elements for each message. (gradio.app) To show a welcome message as soon as the widget appears, you just call your own `appendMessage` once during initialization. Assume your JS looks roughly like this (based on the official guide): import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; async function initChatWidget() { const client = await Client.connect("https://your-username-your-space.hf.space"); const chatToggle = document.getElementById("chat-toggle"); const chatContainer = document.getElementById("chat-container"); const closeChat = document.getElementById("close-chat"); const chatInput = document.getElementById("chat-input"); const sendButton = document.getElementById("send-message"); const messagesContainer = document.getElementById("chat-messages"); chatToggle.addEventListener("click", () => { chatContainer.classList.remove("hidden"); }); closeChat.addEventListener("click", () => { chatContainer.classList.add("hidden"); }); function appendMessage(text, sender) { const div = document.createElement("div"); div.className = `message ${sender}-message`; div.textContent = text; // or marked.parse(text) if you're using marked messagesContainer.appendChild(div); messagesContainer.scrollTop = messagesContainer.scrollHeight; } async function sendMessage() { const userMessage = chatInput.value.trim(); if (!userMessage) return; appendMessage(userMessage, "user"); chatInput.value = ""; try { const result = await client.predict("/chat", { message: { text: userMessage, files: [] }, }); const lines = result.data[0]; // list of strings from Python const botMessage = Array.isArray(lines) ? lines.join("\n") : String(lines); appendMessage(botMessage, "bot"); } catch (error) { console.error("Error:", error); appendMessage("Sorry, there was an error.", "bot"); } } sendButton.addEventListener("click", sendMessage); chatInput.addEventListener("keydown", (e) => { if (e.key === "Enter") sendMessage(); }); // PRE-SEED: show a bot greeting as soon as the widget is ready appendMessage("Hello, welcome to the chat, how can I help?", "bot"); } initChatWidget(); That final `appendMessage(...)` is the crucial part: * It runs once when `initChatWidget()` finishes. * It directly adds a bot bubble to your widget UI. * It does **not** call the backend; it’s purely visual. So the sequence is: 1. Page loads → `initChatWidget()` runs. 2. `appendMessage("Hello, welcome...", "bot")` creates the greeting bubble. 3. User types something → `sendMessage()` calls your Gradio backend via `client.predict("/chat", ...)`. 4. Python `chat()` returns a list of strings → you join them and display them. * * * ## 4. Which parts you actually need Given your description (“my custom website widget is not using Gradio’s built-in UI”): * If you only care about the **widget UX** , the **minimum** change is step 3 (the single `appendMessage` call). * If you also plan to use `history` on the backend for your LLM, or if people might open the Space directly, add step 2 (the `Chatbot(value=...)`) so the server-side history and Space UI are consistent with the widget greeting. Conceptually: * **Backend seeding (Chatbot value)** = initial assistant message in the conversation history the model sees. (gradio.app) * **Frontend seeding (appendMessage)** = initial bubble the user sees in your website widget. Both are independent; you can enable either or both depending on your needs.
discuss.huggingface.co
November 15, 2025 at 11:59 PM
Creating a Website Chat Widget with Gradio Part II
> How do I pre-seed the chat with a prompt such as “Hello, welcome to the chat, how can I help?” It seems best to support it **on both** the Gradio side (Python + JS) and the custom frontend JS side. * * * You’re right that with your current setup the Space just “waits” for the first user message. That’s expected: `ChatInterface` only calls your `chat()` function when a message comes in. There are **two different places** you can add a welcome message: 1. On the **backend (Python / Gradio)** – pre-seed the conversation history. 2. On the **frontend (your custom widget JS)** – pre-seed what the user sees in the website chat box. Because your widget is custom HTML/JS using `@gradio/client`, you usually want **both** : * Python: so your model “knows” there is an initial assistant message in the history. * JS: so the welcome bubble actually appears in your web page. Below I’ll show both, starting from a corrected version of your code. * * * ## 1. Corrected base Python code (what you already have) Your snippet with proper indentation, quotes, and `__name__`: import gradio as gr def chat(message, history): if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] return response_lines demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", # matches the "/chat" path used by the JS client ) if __name__ == "__main__": demo.launch() * `multimodal=True` → `message` is a dict like `{"text": "...", "files": ...]}`. ([gradio.app) * `api_name="chat"` → the HTTP endpoint is `/chat`, which your widget calls via `client.predict("/chat", ...)`. (gradio.app) This is the base we will extend. * * * ## 2. Pre-seed on the backend (Gradio side) Gradio’s `Chatbot` component lets you set an initial history using the `value` parameter. With `type="messages"`, the history is a list of dictionaries: {"role": "assistant" or "user", "content": "text here"} This is described in the Chatbot/messages docs. (gradio.app) You can create a `Chatbot` with a starting assistant message and pass it into `ChatInterface` via the `chatbot=` argument. ### 2.1. Python with pre-seeded welcome message import gradio as gr def chat(message, history): if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] return response_lines # Initial chat history in "messages" format initial_history = [ { "role": "assistant", "content": "Hello, welcome to the chat, how can I help?", } ] # Chatbot component with initial value chatbot = gr.Chatbot( value=initial_history, type="messages", ) demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", chatbot=chatbot, # use our pre-seeded chatbot ) if __name__ == "__main__": demo.launch() What this achieves: * If someone opens the **Gradio UI** (e.g. on the Space itself), they see the greeting already in the chat window. * On the backend, when the first user message arrives, the `history` parameter passed into `chat(message, history)` already includes this assistant message. So if later you build an LLM prompt from `history`, that greeting will be part of the conversation context. (gradio.app) However, this **does not automatically show** the greeting inside your custom website widget, because that UI is controlled by your own JavaScript. * * * ## 3. Pre-seed in your custom widget (front-end side) Your widget uses `@gradio/client` to call the `/chat` endpoint and then appends DOM elements for each message. (gradio.app) To show a welcome message as soon as the widget appears, you just call your own `appendMessage` once during initialization. Assume your JS looks roughly like this (based on the official guide): import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; async function initChatWidget() { const client = await Client.connect("https://your-username-your-space.hf.space"); const chatToggle = document.getElementById("chat-toggle"); const chatContainer = document.getElementById("chat-container"); const closeChat = document.getElementById("close-chat"); const chatInput = document.getElementById("chat-input"); const sendButton = document.getElementById("send-message"); const messagesContainer = document.getElementById("chat-messages"); chatToggle.addEventListener("click", () => { chatContainer.classList.remove("hidden"); }); closeChat.addEventListener("click", () => { chatContainer.classList.add("hidden"); }); function appendMessage(text, sender) { const div = document.createElement("div"); div.className = `message ${sender}-message`; div.textContent = text; // or marked.parse(text) if you're using marked messagesContainer.appendChild(div); messagesContainer.scrollTop = messagesContainer.scrollHeight; } async function sendMessage() { const userMessage = chatInput.value.trim(); if (!userMessage) return; appendMessage(userMessage, "user"); chatInput.value = ""; try { const result = await client.predict("/chat", { message: { text: userMessage, files: [] }, }); const lines = result.data[0]; // list of strings from Python const botMessage = Array.isArray(lines) ? lines.join("\n") : String(lines); appendMessage(botMessage, "bot"); } catch (error) { console.error("Error:", error); appendMessage("Sorry, there was an error.", "bot"); } } sendButton.addEventListener("click", sendMessage); chatInput.addEventListener("keydown", (e) => { if (e.key === "Enter") sendMessage(); }); // PRE-SEED: show a bot greeting as soon as the widget is ready appendMessage("Hello, welcome to the chat, how can I help?", "bot"); } initChatWidget(); That final `appendMessage(...)` is the crucial part: * It runs once when `initChatWidget()` finishes. * It directly adds a bot bubble to your widget UI. * It does **not** call the backend; it’s purely visual. So the sequence is: 1. Page loads → `initChatWidget()` runs. 2. `appendMessage("Hello, welcome...", "bot")` creates the greeting bubble. 3. User types something → `sendMessage()` calls your Gradio backend via `client.predict("/chat", ...)`. 4. Python `chat()` returns a list of strings → you join them and display them. * * * ## 4. Which parts you actually need Given your description (“my custom website widget is not using Gradio’s built-in UI”): * If you only care about the **widget UX** , the **minimum** change is step 3 (the single `appendMessage` call). * If you also plan to use `history` on the backend for your LLM, or if people might open the Space directly, add step 2 (the `Chatbot(value=...)`) so the server-side history and Space UI are consistent with the widget greeting. Conceptually: * **Backend seeding (Chatbot value)** = initial assistant message in the conversation history the model sees. (gradio.app) * **Frontend seeding (appendMessage)** = initial bubble the user sees in your website widget. Both are independent; you can enable either or both depending on your needs.
discuss.huggingface.co
November 15, 2025 at 5:52 PM
Creating a Website Chat Widget with Gradio Part II
> How do I pre-seed the chat with a prompt such as “Hello, welcome to the chat, how can I help?” It seems best to support it **on both** the Gradio side (Python + JS) and the custom frontend JS side. * * * You’re right that with your current setup the Space just “waits” for the first user message. That’s expected: `ChatInterface` only calls your `chat()` function when a message comes in. There are **two different places** you can add a welcome message: 1. On the **backend (Python / Gradio)** – pre-seed the conversation history. 2. On the **frontend (your custom widget JS)** – pre-seed what the user sees in the website chat box. Because your widget is custom HTML/JS using `@gradio/client`, you usually want **both** : * Python: so your model “knows” there is an initial assistant message in the history. * JS: so the welcome bubble actually appears in your web page. Below I’ll show both, starting from a corrected version of your code. * * * ## 1. Corrected base Python code (what you already have) Your snippet with proper indentation, quotes, and `__name__`: import gradio as gr def chat(message, history): if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] return response_lines demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", # matches the "/chat" path used by the JS client ) if __name__ == "__main__": demo.launch() * `multimodal=True` → `message` is a dict like `{"text": "...", "files": ...]}`. ([gradio.app) * `api_name="chat"` → the HTTP endpoint is `/chat`, which your widget calls via `client.predict("/chat", ...)`. (gradio.app) This is the base we will extend. * * * ## 2. Pre-seed on the backend (Gradio side) Gradio’s `Chatbot` component lets you set an initial history using the `value` parameter. With `type="messages"`, the history is a list of dictionaries: {"role": "assistant" or "user", "content": "text here"} This is described in the Chatbot/messages docs. (gradio.app) You can create a `Chatbot` with a starting assistant message and pass it into `ChatInterface` via the `chatbot=` argument. ### 2.1. Python with pre-seeded welcome message import gradio as gr def chat(message, history): if isinstance(message, dict): user_text = message.get("text", "") else: user_text = str(message) response_lines = [ "Hello from your Gradio Space!", f"You said: {user_text}", ] return response_lines # Initial chat history in "messages" format initial_history = [ { "role": "assistant", "content": "Hello, welcome to the chat, how can I help?", } ] # Chatbot component with initial value chatbot = gr.Chatbot( value=initial_history, type="messages", ) demo = gr.ChatInterface( fn=chat, type="messages", multimodal=True, title="Widget Demo Bot", api_name="chat", chatbot=chatbot, # use our pre-seeded chatbot ) if __name__ == "__main__": demo.launch() What this achieves: * If someone opens the **Gradio UI** (e.g. on the Space itself), they see the greeting already in the chat window. * On the backend, when the first user message arrives, the `history` parameter passed into `chat(message, history)` already includes this assistant message. So if later you build an LLM prompt from `history`, that greeting will be part of the conversation context. (gradio.app) However, this **does not automatically show** the greeting inside your custom website widget, because that UI is controlled by your own JavaScript. * * * ## 3. Pre-seed in your custom widget (front-end side) Your widget uses `@gradio/client` to call the `/chat` endpoint and then appends DOM elements for each message. (gradio.app) To show a welcome message as soon as the widget appears, you just call your own `appendMessage` once during initialization. Assume your JS looks roughly like this (based on the official guide): import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; async function initChatWidget() { const client = await Client.connect("https://your-username-your-space.hf.space"); const chatToggle = document.getElementById("chat-toggle"); const chatContainer = document.getElementById("chat-container"); const closeChat = document.getElementById("close-chat"); const chatInput = document.getElementById("chat-input"); const sendButton = document.getElementById("send-message"); const messagesContainer = document.getElementById("chat-messages"); chatToggle.addEventListener("click", () => { chatContainer.classList.remove("hidden"); }); closeChat.addEventListener("click", () => { chatContainer.classList.add("hidden"); }); function appendMessage(text, sender) { const div = document.createElement("div"); div.className = `message ${sender}-message`; div.textContent = text; // or marked.parse(text) if you're using marked messagesContainer.appendChild(div); messagesContainer.scrollTop = messagesContainer.scrollHeight; } async function sendMessage() { const userMessage = chatInput.value.trim(); if (!userMessage) return; appendMessage(userMessage, "user"); chatInput.value = ""; try { const result = await client.predict("/chat", { message: { text: userMessage, files: [] }, }); const lines = result.data[0]; // list of strings from Python const botMessage = Array.isArray(lines) ? lines.join("\n") : String(lines); appendMessage(botMessage, "bot"); } catch (error) { console.error("Error:", error); appendMessage("Sorry, there was an error.", "bot"); } } sendButton.addEventListener("click", sendMessage); chatInput.addEventListener("keydown", (e) => { if (e.key === "Enter") sendMessage(); }); // PRE-SEED: show a bot greeting as soon as the widget is ready appendMessage("Hello, welcome to the chat, how can I help?", "bot"); } initChatWidget(); That final `appendMessage(...)` is the crucial part: * It runs once when `initChatWidget()` finishes. * It directly adds a bot bubble to your widget UI. * It does **not** call the backend; it’s purely visual. So the sequence is: 1. Page loads → `initChatWidget()` runs. 2. `appendMessage("Hello, welcome...", "bot")` creates the greeting bubble. 3. User types something → `sendMessage()` calls your Gradio backend via `client.predict("/chat", ...)`. 4. Python `chat()` returns a list of strings → you join them and display them. * * * ## 4. Which parts you actually need Given your description (“my custom website widget is not using Gradio’s built-in UI”): * If you only care about the **widget UX** , the **minimum** change is step 3 (the single `appendMessage` call). * If you also plan to use `history` on the backend for your LLM, or if people might open the Space directly, add step 2 (the `Chatbot(value=...)`) so the server-side history and Space UI are consistent with the widget greeting. Conceptually: * **Backend seeding (Chatbot value)** = initial assistant message in the conversation history the model sees. (gradio.app) * **Frontend seeding (appendMessage)** = initial bubble the user sees in your website widget. Both are independent; you can enable either or both depending on your needs.
discuss.huggingface.co
November 15, 2025 at 3:52 PM
Gradio Chatbot Placeholder Not Rendered
It’s a feature that was added in Gradio 5, so it might still be buggy. You might want to file an issue. There seems to be an alternative… GitHub ### gradio-app/gradio Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work! - gradio-app/gradio github.com/gradio-app/gradio #### Make placeholder a variable in ChatInterface class opened 02:14AM - 17 Jun 24 UTC closed 02:27PM - 19 Jun 24 UTC ThomasCosyn Here is the code with the modification. ` class ChatInterface(Blocks): …""" ChatInterface is Gradio's high-level abstraction for creating chatbot UIs, and allows you to create a web-based demo around a chatbot model in a few lines of code. Only one parameter is required: fn, which takes a function that governs the response of the chatbot based on the user input and chat history. Additional parameters can be used to control the appearance and behavior of the demo. Example: import gradio as gr def echo(message, history): return message demo = gr.ChatInterface(fn=echo, examples=["hello", "hola", "merhaba"], title="Echo Bot") demo.launch() Demos: chatinterface_multimodal, chatinterface_random_response, chatinterface_streaming_echo Guides: creating-a-chatbot-fast, sharing-your-app """ def __init__( self, fn: Callable, *, multimodal: bool = False, chatbot: Chatbot | None = None, textbox: Textbox | MultimodalTextbox | None = None, additional_inputs: str | Component | list[str | Component] | None = None, additional_inputs_accordion_name: str | None = None, additional_inputs_accordion: str | Accordion | None = None, examples: list[str] | list[dict[str, str | list]] | list[list] | None = None, cache_examples: bool | Literal["lazy"] | None = None, examples_per_page: int = 10, title: str | None = None, description: str | None = None, theme: Theme | str | None = None, css: str | None = None, js: str | None = None, head: str | None = None, analytics_enabled: bool | None = None, submit_btn: str | None | Button = "Submit", stop_btn: str | None | Button = "Stop", retry_btn: str | None | Button = "🔄 Retry", undo_btn: str | None | Button = "↩️ Undo", clear_btn: str | None | Button = "🗑️ Clear", autofocus: bool = True, concurrency_limit: int | None | Literal["default"] = "default", fill_height: bool = True, delete_cache: tuple[int, int] | None = None, placeholder: str = "Type a message...", ): """ Parameters: fn: The function to wrap the chat interface around. Should accept two parameters: a string input message and list of two-element lists of the form [[user_message, bot_message], ...] representing the chat history, and return a string response. See the Chatbot documentation for more information on the chat history format. multimodal: If True, the chat interface will use a gr.MultimodalTextbox component for the input, which allows for the uploading of multimedia files. If False, the chat interface will use a gr.Textbox component for the input. chatbot: An instance of the gr.Chatbot component to use for the chat interface, if you would like to customize the chatbot properties. If not provided, a default gr.Chatbot component will be created. textbox: An instance of the gr.Textbox or gr.MultimodalTextbox component to use for the chat interface, if you would like to customize the textbox properties. If not provided, a default gr.Textbox or gr.MultimodalTextbox component will be created. additional_inputs: An instance or list of instances of gradio components (or their string shortcuts) to use as additional inputs to the chatbot. If components are not already rendered in a surrounding Blocks, then the components will be displayed under the chatbot, in an accordion. additional_inputs_accordion_name: Deprecated. Will be removed in a future version of Gradio. Use the `additional_inputs_accordion` parameter instead. additional_inputs_accordion: If a string is provided, this is the label of the `gr.Accordion` to use to contain additional inputs. A `gr.Accordion` object can be provided as well to configure other properties of the container holding the additional inputs. Defaults to a `gr.Accordion(label="Additional Inputs", open=False)`. This parameter is only used if `additional_inputs` is provided. examples: Sample inputs for the function; if provided, appear below the chatbot and can be clicked to populate the chatbot input. Should be a list of strings if `multimodal` is False, and a list of dictionaries (with keys `text` and `files`) if `multimodal` is True. cache_examples: If True, caches examples in the server for fast runtime in examples. The default option in HuggingFace Spaces is True. The default option elsewhere is False. examples_per_page: If examples are provided, how many to display per page. title: a title for the interface; if provided, appears above chatbot in large font. Also used as the tab title when opened in a browser window. description: a description for the interface; if provided, appears above the chatbot and beneath the title in regular font. Accepts Markdown and HTML content. theme: Theme to use, loaded from gradio.themes. css: Custom css as a string or path to a css file. This css will be included in the demo webpage. js: Custom js as a string or path to a js file. The custom js should be in the form of a single js function. This function will automatically be executed when the page loads. For more flexibility, use the head parameter to insert js inside <script> tags. head: Custom html to insert into the head of the demo webpage. This can be used to add custom meta tags, multiple scripts, stylesheets, etc. to the page. analytics_enabled: Whether to allow basic telemetry. If None, will use GRADIO_ANALYTICS_ENABLED environment variable if defined, or default to True. submit_btn: Text to display on the submit button. If None, no button will be displayed. If a Button object, that button will be used. stop_btn: Text to display on the stop button, which replaces the submit_btn when the submit_btn or retry_btn is clicked and response is streaming. Clicking on the stop_btn will halt the chatbot response. If set to None, stop button functionality does not appear in the chatbot. If a Button object, that button will be used as the stop button. retry_btn: Text to display on the retry button. If None, no button will be displayed. If a Button object, that button will be used. undo_btn: Text to display on the delete last button. If None, no button will be displayed. If a Button object, that button will be used. clear_btn: Text to display on the clear button. If None, no button will be displayed. If a Button object, that button will be used. autofocus: If True, autofocuses to the textbox when the page loads. concurrency_limit: If set, this is the maximum number of chatbot submissions that can be running simultaneously. Can be set to None to mean no limit (any number of chatbot submissions can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `.queue()`, which is 1 by default). fill_height: If True, the chat interface will expand to the height of window. delete_cache: A tuple corresponding [frequency, age] both expressed in number of seconds. Every `frequency` seconds, the temporary files created by this Blocks instance will be deleted if more than `age` seconds have passed since the file was created. For example, setting this to (86400, 86400) will delete temporary files every day. The cache will be deleted entirely when the server restarts. If None, no cache deletion will occur. placeholder: Placeholder text to display in the textbox. Defaults to "Type a message...". """ super().__init__( analytics_enabled=analytics_enabled, mode="chat_interface", css=css, title=title or "Gradio", theme=theme, js=js, head=head, fill_height=fill_height, delete_cache=delete_cache, ) self.multimodal = multimodal self.concurrency_limit = concurrency_limit self.fn = fn self.is_async = inspect.iscoroutinefunction( self.fn ) or inspect.isasyncgenfunction(self.fn) self.is_generator = inspect.isgeneratorfunction( self.fn ) or inspect.isasyncgenfunction(self.fn) self.buttons: list[Button | None] = [] self.examples = examples self.cache_examples = cache_examples if additional_inputs: if not isinstance(additional_inputs, list): additional_inputs = [additional_inputs] self.additional_inputs = [ get_component_instance(i) for i in additional_inputs # type: ignore ] else: self.additional_inputs = [] if additional_inputs_accordion_name is not None: print( "The `additional_inputs_accordion_name` parameter is deprecated and will be removed in a future version of Gradio. Use the `additional_inputs_accordion` parameter instead." ) self.additional_inputs_accordion_params = { "label": additional_inputs_accordion_name } if additional_inputs_accordion is None: self.additional_inputs_accordion_params = { "label": "Additional Inputs", "open": False, } elif isinstance(additional_inputs_accordion, str): self.additional_inputs_accordion_params = { "label": additional_inputs_accordion } elif isinstance(additional_inputs_accordion, Accordion): self.additional_inputs_accordion_params = ( additional_inputs_accordion.recover_kwargs( additional_inputs_accordion.get_config() ) ) else: raise ValueError( f"The `additional_inputs_accordion` parameter must be a string or gr.Accordion, not {type(additional_inputs_accordion)}" ) with self: if title: Markdown( f"<h1 style='text-align: center; margin-bottom: 1rem'>{self.title}</h1>" ) if description: Markdown(description) if chatbot: self.chatbot = chatbot.render() else: self.chatbot = Chatbot( label="Chatbot", scale=1, height=200 if fill_height else None ) with Row(): for btn in [retry_btn, undo_btn, clear_btn]: if btn is not None: if isinstance(btn, Button): btn.render() elif isinstance(btn, str): btn = Button( btn, variant="secondary", size="sm", min_width=60 ) else: raise ValueError( f"All the _btn parameters must be a gr.Button, string, or None, not {type(btn)}" ) self.buttons.append(btn) # type: ignore with Group(): with Row(): if textbox: if self.multimodal: submit_btn = None else: textbox.container = False textbox.show_label = False textbox_ = textbox.render() if not isinstance(textbox_, (Textbox, MultimodalTextbox)): raise TypeError( f"Expected a gr.Textbox or gr.MultimodalTextbox component, but got {type(textbox_)}" ) self.textbox = textbox_ elif self.multimodal: submit_btn = None self.textbox = MultimodalTextbox( show_label=False, label="Message", placeholder=placeholder, scale=7, autofocus=autofocus, ) else: self.textbox = Textbox( container=False, show_label=False, label="Message", placeholder=placeholder, scale=7, autofocus=autofocus, ) ... ` Works fine for me !
discuss.huggingface.co
February 28, 2025 at 6:26 AM
Was wäre Retrieval-Augmented Generation ohne zuverlässige und klar separierte Quellennachweise?
Hier ein Beispiel für ein Gradio-ChatInterface, bei dem ein LLM von einer Datenbank unterstützt antwortet und anschließend die verwendeten Textstellen angezeigt werden:

huggingface.co/spaces/AFisc...
December 26, 2023 at 1:11 PM
@hllizi aber aber aber... ich mag es doch gerade, weil es *kein* Chatinterface ist.
December 9, 2025 at 8:01 AM
De "Lister Page" of categoriepagina is dood. Straks gaat de klantreis alleen nog maar via de AI chatinterface en direct naar het product. Of misschien koopt de AI agent direct op je shop. Check de hele sessie over AI en productdata: https://www.pimvendors.com/ai
March 26, 2025 at 3:04 PM