Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# LLM Provider Configuration Examples
# Copy this file to .env and fill in your actual API keys

# Choose which LLM provider to use: openai, ollama, or gemini
LLM_PROVIDER=openai

# OpenAI Configuration (required for OpenAI provider)
OPENAI_API_KEY=your_openai_api_key_here
OPENAI_MODEL=gpt-5-mini

# Ollama Configuration (for local models)
OLLAMA_HOST=http://localhost:11434
OLLAMA_MODEL=gemma3:4b

# Google Gemini Configuration (required for Google provider)
GEMINI_API_KEY=your_gemini_api_key_here
GEMINI_MODEL=gemini-2.0-flash

# Optional: Use Google Vertex AI instead of the direct API
# GOOGLE_USE_VERTEXAI=true
# GOOGLE_PROJECT_ID=your-gcp-project-id
# GOOGLE_LOCATION=us-central1
133 changes: 112 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,110 @@
# Reflex Chat App

A user-friendly, highly customizable Python web app designed to demonstrate LLMs in a ChatGPT format.
**This is a fork of the original [Reflex Chat](https://github.com/reflex-dev/reflex-chat) with enhanced multi-provider support.**

A user-friendly, highly customizable Python web app designed to demonstrate LLMs in a ChatGPT format with support for multiple LLM providers. This fork serves as an excellent example for developers who want to learn how to implement and integrate different LLM providers in their applications.

## What's New in This Fork

- **Added Ollama support** - Run local models with real-time streaming
- **Added Google Gemini support** - Including Vertex AI integration with streaming
- **Enhanced provider architecture** - Clean, extensible factory pattern for adding new providers
- **Unified streaming** - All providers now support real-time response streaming
- **uv support** - Lightning-fast dependency management

## Learning Resource

This fork is designed to help developers understand:

- How to implement multiple LLM provider integrations
- Async streaming patterns for real-time chat
- Clean architecture patterns for provider management
- Environment-based configuration for different services
- Error handling and fallback mechanisms

<div align="center">
<img src="./docs/demo.gif" alt="icon"/>
</div>

# Getting Started

You'll need a valid OpenAI subscription - save your API key under the environment variable `OPENAI_API_KEY`:
## Choose Your LLM Provider

The app supports three LLM providers. Configure the one you want to use:

### Option 1: OpenAI

You'll need a valid OpenAI subscription. Copy `.env.example` to `.env` and configure:

```bash
export OPENAI_API_KEY="YOUR_OPENAI_API_KEY" # replace me!
cp .env.example .env
```

Then edit `.env` with your OpenAI credentials:

```env
LLM_PROVIDER=openai
OPENAI_API_KEY=your_openai_api_key_here
OPENAI_MODEL=gpt-4o # or gpt-4o-mini, gpt-5-mini, etc.
```

### Option 2: Ollama (Local Models)

Run models locally using Ollama:

1. [Install Ollama](https://ollama.ai/download)
2. Start Ollama server: `ollama serve`
3. Pull a model: `ollama pull gemma3:4b`
4. Configure your `.env`:

```env
LLM_PROVIDER=ollama
OLLAMA_HOST=http://localhost:11434 # optional, defaults to this
OLLAMA_MODEL=gemma3:4b # required: must be a model you have installed
```

### Option 3: Google Gemini

Use Google's Gemini models. Configure your `.env`:

```env
LLM_PROVIDER=gemini
GEMINI_API_KEY=your_gemini_api_key_here
GEMINI_MODEL=gemini-2.0-flash # or gemini-1.5-flash, gemini-2.0-pro, etc.
```

For Vertex AI (Enterprise Google Cloud):

```env
LLM_PROVIDER=gemini
GEMINI_API_KEY=your_gemini_api_key_here
GOOGLE_USE_VERTEXAI=true
GOOGLE_PROJECT_ID=your-gcp-project-id
GOOGLE_LOCATION=us-central1 # optional
GEMINI_MODEL=gemini-2.0-flash
```

### 🧬 1. Clone the Repo

```bash
git clone https://github.com/reflex-dev/reflex-chat.git
cd reflex-chat
```

### 📦 2. Install Reflex
### 📦 2. Install Dependencies

To get started with Reflex, you'll need Python 3.10+. You can install dependencies using either **pip** (traditional) or **uv** (recommended for faster installs).

To get started with Reflex, you'll need:
#### Using uv

- Python 3.10+
- Pip dependencies: `reflex`, `openai`
2. **Install and sync dependencies**:
```bash
uv sync
```

Install `pip` dependencies with the provided `requirements.txt`:
#### Using pip

Install all dependencies with the provided `requirements.txt`:

```bash
pip install -r requirements.txt
Expand All @@ -37,27 +114,41 @@ pip install -r requirements.txt

Initialize and run the app:

```
```bash
reflex init
reflex run
```

# Features
#### Model Specification:

- 100% Python-based, including the UI, using Reflex
- Create and delete chat sessions
- The application is fully customizable and no knowledge of web dev is required to use it.
- See https://reflex.dev/docs/styling/overview for more details
- Easily swap out any LLM
- Responsive design for various devices
- **OpenAI**: `OPENAI_MODEL` (your chosen model, e.g., gpt-5-mini, gpt-4o, etc.)
- **Ollama**: `OLLAMA_MODEL` (your local model, e.g., gemma3:4b, llama3.2, etc.)
- Note: Ollama requests use `num_ctx=4096` by default for optimal context window
- **Google**: `GEMINI_MODEL` (your chosen model, e.g., gemini-2.0-flash, gemini-2.5-pro, etc.)

## Environment Variables Reference

# Contributing
| Variable | Required | Default | Description |
| --------------------- | ------------- | ------------------------ | ------------------------------------ |
| `LLM_PROVIDER` | No | `openai` | Choose: `openai`, `ollama`, `gemini` |
| `OPENAI_API_KEY` | For OpenAI | - | Your OpenAI API key |
| `OPENAI_MODEL` | For OpenAI | - | OpenAI model to use |
| `OLLAMA_HOST` | No | `http://localhost:11434` | Ollama server URL |
| `OLLAMA_MODEL` | For Ollama | - | Ollama model to use |
| `GEMINI_API_KEY` | For Gemini | - | Your Google API key |
| `GEMINI_MODEL` | For Gemini | - | Gemini Model to use |
| `GOOGLE_USE_VERTEXAI` | No | `false` | Use Vertex AI instead |
| `GOOGLE_PROJECT_ID` | For Vertex AI | - | GCP project ID |
| `GOOGLE_LOCATION` | No | `us-central1` | GCP region |

We welcome contributions to improve and extend the LLM Web UI.
If you'd like to contribute, please do the following:
# Features

- Fork the repository and make your changes.
- Once you're ready, submit a pull request for review.
- **100% Python-based**, including the UI, using Reflex
- **Create and delete chat sessions**
- **Real-time streaming** responses for all LLM providers (OpenAI, Ollama, Gemini)
- **Multiple LLM support**: OpenAI, Ollama (local), Google Gemini with Vertex AI
- **Fully customizable** - no web dev knowledge required
- See https://reflex.dev/docs/styling/overview for more details

# License

Expand Down
2 changes: 2 additions & 0 deletions chat/components/navbar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import reflex as rx
from chat.state import State
from chat.components.provider_selector import provider_status_bar


def sidebar_chat(chat: str) -> rx.Component:
Expand Down Expand Up @@ -98,6 +99,7 @@ def navbar():
variant="soft",
margin_inline_end="auto",
),
provider_status_bar(),
modal(
rx.icon_button("message-square-plus"),
),
Expand Down
171 changes: 171 additions & 0 deletions chat/components/provider_selector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import reflex as rx
from chat.state import State


def provider_selector() -> rx.Component:
"""Create a provider selection component."""
return rx.card(
rx.vstack(
rx.heading("LLM Provider Settings", size="4", weight="bold"),
rx.text(
"Choose your LLM provider and model",
size="2",
color=rx.color("mauve", 10),
),
# Provider Selection
rx.hstack(
rx.text("Provider:", weight="medium"),
rx.select(
State.available_providers,
placeholder="Select provider",
value=State.current_provider,
on_change=State.set_provider,
width="200px",
),
rx.spinner(
size="2",
loading=State.processing,
),
align="center",
spacing="3",
),
# Model Selection
rx.hstack(
rx.text("Model:", weight="medium"),
rx.select(
State.available_models,
placeholder="Select model",
value=State.current_model,
on_change=State.set_model,
width="250px",
),
rx.cond(
State.available_models.length() == 0,
rx.tooltip(
rx.icon("info", size=16),
content="No models available. Check your provider configuration.",
),
),
align="center",
spacing="3",
),
# Error Display
rx.cond(
State.provider_error != "",
rx.callout(
rx.icon("triangle_alert", size=4),
rx.text(State.provider_error),
color_scheme="red",
variant="soft",
),
),
# Status Indicator
rx.hstack(
rx.cond(
State._provider_instance != None,
rx.hstack(
rx.icon("check-circle", size=4, color=rx.color("green", 10)),
rx.text(
"Provider ready", size="2", color=rx.color("green", 10)
),
),
rx.hstack(
rx.icon("x-circle", size=4, color=rx.color("red", 10)),
rx.text(
"Provider not configured",
size="2",
color=rx.color("red", 10),
),
),
),
rx.spacer(),
rx.button(
"Refresh Models",
size="2",
variant="outline",
on_click=State.update_available_models,
loading=State.processing,
),
align="center",
width="100%",
),
# Configuration Help
rx.divider(),
rx.accordion.root(
rx.accordion.item(
header="Configuration Help",
content=rx.vstack(
rx.text("Environment variables needed:", weight="medium"),
rx.code_block(
"""# OpenAI
OPENAI_API_KEY=your_openai_api_key
OPENAI_MODEL=gpt-5-mini

# Ollama (local models)
OLLAMA_HOST=http://localhost:11434
OLLAMA_MODEL=gemma3:4b

# Google Gemini
GEMINI_API_KEY=your_GEMINI_API_KEY
GEMINI_MODEL=gemini-2.0-flash

# Provider Selection
LLM_PROVIDER=openai # or ollama, gemini
""",
language="bash",
width="100%",
),
rx.text(
"Note: Models are dynamically fetched when possible. If no models appear, ensure your MODEL environment variable is set.",
size="1",
),
align="start",
spacing="2",
),
value="config-help",
),
collapsible=True,
width="100%",
),
spacing="4",
align="stretch",
width="100%",
),
padding="20px",
radius="12px",
background_color=rx.color("mauve", 1),
border=f"1px solid {rx.color('mauve', 3)}",
width="100%",
max_width="600px",
)


def provider_status_bar() -> rx.Component:
"""Create a compact provider status bar for the navbar."""
return rx.hstack(
rx.icon("bot", size=4, color=rx.color("accent", 10)),
rx.text(
f"{State.current_provider}: {State.current_model}",
size="2",
weight="medium",
color=rx.color("mauve", 11),
),
rx.cond(
State.provider_error != "",
rx.tooltip(
rx.icon("triangle_alert", size=3, color=rx.color("red", 10)),
content=State.provider_error,
),
),
rx.cond(
State.processing,
rx.spinner(size="2", color=rx.color("accent", 10)),
),
align="center",
spacing="2",
padding_x="12px",
padding_y="6px",
radius="8px",
background_color=rx.color("mauve", 2),
border=f"1px solid {rx.color('mauve', 4)}",
)
4 changes: 4 additions & 0 deletions chat/llm_providers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .base import LLMProvider, Message
from .factory import LLMProviderFactory

__all__ = ["LLMProvider", "Message", "LLMProviderFactory"]
Loading