Sanchit Verma
commited on
Commit
·
e2e7692
1
Parent(s):
1856369
Update app configuration and README for OpenRouter and Ollama support
Browse files- Update `.env` to include OpenRouter and Ollama configurations
- Modify `.gitignore` to ignore environment files
- Enhance `README.md` with detailed installation, configuration, and usage instructions
- Improve `app.py` to handle message validation and error responses
.env
CHANGED
@@ -1,4 +1,14 @@
|
|
1 |
-
|
2 |
-
|
3 |
USE_OLLAMA=false
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Choose one provider only
|
2 |
+
USE_OPENROUTER=true
|
3 |
USE_OLLAMA=false
|
4 |
+
|
5 |
+
# OpenRouter settings
|
6 |
+
OPENROUTER_API_KEY=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
|
7 |
+
OPENROUTER_MODEL=meta-llama/llama-3.3-8b-instruct:free
|
8 |
+
|
9 |
+
# Ollama (if used)
|
10 |
+
OLLAMA_MODEL=llama3
|
11 |
+
|
12 |
+
# OpenAI fallback (if used)
|
13 |
+
OPENAI_API_KEY=
|
14 |
+
OPENAI_MODEL=gpt-4o
|
.gitignore
CHANGED
@@ -9,6 +9,11 @@ wheels/
|
|
9 |
# Python bytecode and cache
|
10 |
__pycache__/
|
11 |
*.py[cod]
|
|
|
|
|
|
|
|
|
|
|
12 |
*$py.class
|
13 |
|
14 |
# Distribution / packaging
|
@@ -34,6 +39,9 @@ share/python-wheels/
|
|
34 |
MANIFEST
|
35 |
/.python-version
|
36 |
|
|
|
|
|
|
|
37 |
# PyInstaller
|
38 |
*.manifest
|
39 |
*.spec
|
|
|
9 |
# Python bytecode and cache
|
10 |
__pycache__/
|
11 |
*.py[cod]
|
12 |
+
|
13 |
+
# Environment files
|
14 |
+
.env
|
15 |
+
.env.local
|
16 |
+
.env.*.local
|
17 |
*$py.class
|
18 |
|
19 |
# Distribution / packaging
|
|
|
39 |
MANIFEST
|
40 |
/.python-version
|
41 |
|
42 |
+
# Gradio
|
43 |
+
.gradio/
|
44 |
+
|
45 |
# PyInstaller
|
46 |
*.manifest
|
47 |
*.spec
|
README.md
CHANGED
@@ -1,20 +1,68 @@
|
|
1 |
# 🤖 LLMates – Chat with Custom AI Personas
|
2 |
|
3 |
-
LLMates is a minimal, modular chatbot
|
4 |
-
It supports OpenAI (e.g. GPT-4o), or free alternatives like **Ollama** (LLaMA3, Mistral) or Hugging Face.
|
5 |
|
6 |
-
##
|
7 |
-
- Python Tutor
|
8 |
-
- Regex Helper
|
9 |
-
- Motivational Coach
|
10 |
-
- Startup Advisor
|
11 |
|
12 |
-
|
13 |
-
-
|
14 |
-
-
|
15 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
-
## 🧪 Run Locally
|
18 |
```bash
|
19 |
-
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# 🤖 LLMates – Chat with Custom AI Personas
|
2 |
|
3 |
+
LLMates is a minimal, modular chatbot application that allows you to switch between different AI assistant personas powered by language models. The application supports both cloud-based (OpenAI) and local (Ollama) model backends.
|
|
|
4 |
|
5 |
+
## 🚀 Features
|
|
|
|
|
|
|
|
|
6 |
|
7 |
+
- Multiple AI personas with distinct personalities and expertise
|
8 |
+
- Support for OpenAI models (including GPT-4o) and local models via Ollama
|
9 |
+
- Simple and intuitive Gradio-based web interface
|
10 |
+
- Easy configuration through environment variables
|
11 |
+
|
12 |
+
## 💡 Available Personas
|
13 |
+
|
14 |
+
- **Python Tutor**: Get help with Python programming concepts and debugging
|
15 |
+
- **Regex Helper**: Expert assistance with regular expressions
|
16 |
+
- **Motivational Coach**: Encouraging and inspiring conversations
|
17 |
+
- **Startup Advisor**: Practical advice for startups and entrepreneurship
|
18 |
+
|
19 |
+
## 🛠️ Installation
|
20 |
+
|
21 |
+
1. Clone the repository:
|
22 |
+
```bash
|
23 |
+
git clone https://github.com/yourusername/llmates.git
|
24 |
+
cd llmates
|
25 |
+
```
|
26 |
+
|
27 |
+
2. Install the required dependencies:
|
28 |
+
```bash
|
29 |
+
pip install -r requirements.txt
|
30 |
+
```
|
31 |
+
|
32 |
+
3. Create a `.env` file and configure your settings (see Configuration section below)
|
33 |
+
|
34 |
+
## ⚙️ Configuration
|
35 |
+
|
36 |
+
Copy the example `.env` file and update it with your settings:
|
37 |
+
|
38 |
+
```env
|
39 |
+
# OpenAI Configuration (required if not using Ollama)
|
40 |
+
OPENAI_API_KEY=your_openai_api_key
|
41 |
+
OPENAI_MODEL=gpt-4o # or any other OpenAI model
|
42 |
+
|
43 |
+
# Ollama Configuration (set to true to use local models)
|
44 |
+
USE_OLLAMA=false
|
45 |
+
OLLAMA_MODEL=llama3 # or any other Ollama model
|
46 |
+
|
47 |
+
# Application Settings
|
48 |
+
DEFAULT_PERSONA="Python Tutor"
|
49 |
+
TEMPERATURE=0.7
|
50 |
+
MAX_TURNS=10
|
51 |
+
```
|
52 |
+
|
53 |
+
## 🚀 Running the Application
|
54 |
+
|
55 |
+
Start the application with:
|
56 |
|
|
|
57 |
```bash
|
58 |
+
python app.py
|
59 |
+
```
|
60 |
+
|
61 |
+
The application will start a local web server, and you can access it in your browser at `http://localhost:7860`.
|
62 |
+
|
63 |
+
## 🛠️ Tech Stack
|
64 |
+
|
65 |
+
- **UI**: Gradio
|
66 |
+
- **Backend**: OpenAI API / Ollama
|
67 |
+
- **Language**: Python 3.8+
|
68 |
+
- **Configuration**: Environment variables via python-dotenv
|
app.py
CHANGED
@@ -4,23 +4,72 @@ from utils import generate_response
|
|
4 |
from config import DEFAULT_PERSONA
|
5 |
|
6 |
|
7 |
-
def
|
8 |
-
|
|
|
9 |
|
|
|
|
|
|
|
|
|
10 |
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
gr.Markdown("## 🤖 LLMates: Persona-based Chat Assistant")
|
13 |
|
14 |
persona = gr.Dropdown(
|
15 |
choices=list(PERSONAS.keys()), value=DEFAULT_PERSONA, label="Choose Persona"
|
16 |
)
|
17 |
-
chatbox = gr.Chatbot(label="LLMates")
|
18 |
msg = gr.Textbox(label="Type your message...")
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
|
22 |
-
|
23 |
|
24 |
-
|
|
|
25 |
|
26 |
-
|
|
|
|
|
|
4 |
from config import DEFAULT_PERSONA
|
5 |
|
6 |
|
7 |
+
def handle_message(persona, user_input, history):
|
8 |
+
"""
|
9 |
+
Handle a new message in the chat.
|
10 |
|
11 |
+
Args:
|
12 |
+
persona (str): The selected persona
|
13 |
+
user_input (str): The user's message
|
14 |
+
history (list): Chat history in messages format
|
15 |
|
16 |
+
Returns:
|
17 |
+
list: Updated chat history with new messages
|
18 |
+
"""
|
19 |
+
# Validate inputs
|
20 |
+
if not persona or persona not in PERSONAS:
|
21 |
+
return history + [
|
22 |
+
{"role": "assistant", "content": "Please select a valid persona"}
|
23 |
+
]
|
24 |
+
|
25 |
+
if not user_input.strip():
|
26 |
+
return history + [
|
27 |
+
{"role": "assistant", "content": "Please enter a message"}
|
28 |
+
]
|
29 |
+
|
30 |
+
try:
|
31 |
+
# Get response from model
|
32 |
+
return generate_response(PERSONAS[persona], user_input, history)
|
33 |
+
except Exception as e:
|
34 |
+
# Return error message in proper format
|
35 |
+
return history + [
|
36 |
+
{"role": "assistant", "content": f"Error: {str(e)}"}
|
37 |
+
]
|
38 |
+
|
39 |
+
with gr.Blocks() as demo:
|
40 |
gr.Markdown("## 🤖 LLMates: Persona-based Chat Assistant")
|
41 |
|
42 |
persona = gr.Dropdown(
|
43 |
choices=list(PERSONAS.keys()), value=DEFAULT_PERSONA, label="Choose Persona"
|
44 |
)
|
45 |
+
chatbox = gr.Chatbot(label="LLMates", type="messages")
|
46 |
msg = gr.Textbox(label="Type your message...")
|
47 |
+
chat_history = gr.State([])
|
48 |
+
|
49 |
+
def user_submit(user_input, history, persona):
|
50 |
+
if not user_input.strip():
|
51 |
+
return ([], history, "") # Return empty chatbox update but keep history and clear input
|
52 |
+
|
53 |
+
if not persona or persona not in PERSONAS:
|
54 |
+
return ([], history + [{"role": "assistant", "content": "Please select a valid persona"}], "")
|
55 |
+
|
56 |
+
try:
|
57 |
+
# Get response from model
|
58 |
+
new_history = generate_response(PERSONAS[persona], user_input, history)
|
59 |
+
return (new_history, new_history, "") # Clear input after successful submission
|
60 |
+
except Exception as e:
|
61 |
+
return ([], history + [{"role": "assistant", "content": f"Error: {str(e)}"}], "")
|
62 |
+
|
63 |
+
def clear_history():
|
64 |
+
"""Clear the chat history and reset message input when persona changes"""
|
65 |
+
return ([], [], "") # Clear chatbox, history state, and message input
|
66 |
|
67 |
+
# Clear chat history and reset message input when persona changes
|
68 |
+
persona.change(clear_history, outputs=[chatbox, chat_history, msg])
|
69 |
|
70 |
+
# Handle message submission
|
71 |
+
msg.submit(user_submit, [msg, chat_history, persona], [chatbox, chat_history, msg])
|
72 |
|
73 |
+
# main function
|
74 |
+
if __name__ == "__main__":
|
75 |
+
demo.launch()
|
config.py
CHANGED
@@ -3,13 +3,20 @@ from dotenv import load_dotenv
|
|
3 |
|
4 |
load_dotenv()
|
5 |
|
6 |
-
#
|
7 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
8 |
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
|
|
|
|
|
9 |
USE_OLLAMA = os.getenv("USE_OLLAMA", "false").lower() == "true"
|
10 |
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3")
|
11 |
|
|
|
|
|
|
|
|
|
|
|
12 |
# UI + LLM behavior config
|
13 |
-
DEFAULT_PERSONA = "
|
14 |
TEMPERATURE = 0.7
|
15 |
MAX_TURNS = 10
|
|
|
3 |
|
4 |
load_dotenv()
|
5 |
|
6 |
+
# OpenAI configuration
|
7 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
8 |
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o")
|
9 |
+
|
10 |
+
# Ollama configuration
|
11 |
USE_OLLAMA = os.getenv("USE_OLLAMA", "false").lower() == "true"
|
12 |
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3")
|
13 |
|
14 |
+
# OpenRouter configuration
|
15 |
+
USE_OPENROUTER = os.getenv("USE_OPENROUTER", "false").lower() == "true"
|
16 |
+
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "")
|
17 |
+
OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "openai/gpt-4")
|
18 |
+
|
19 |
# UI + LLM behavior config
|
20 |
+
DEFAULT_PERSONA = "Startup Advisor"
|
21 |
TEMPERATURE = 0.7
|
22 |
MAX_TURNS = 10
|
utils.py
CHANGED
@@ -1,73 +1,88 @@
|
|
1 |
-
from config import OPENAI_API_KEY, OPENAI_MODEL, USE_OLLAMA, OLLAMA_MODEL
|
2 |
import requests
|
3 |
import openai
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
openai.api_key = OPENAI_API_KEY
|
6 |
|
7 |
|
8 |
def query_openai(messages):
|
9 |
-
"""Query the OpenAI API with the given messages.
|
10 |
-
|
11 |
-
Args:
|
12 |
-
messages (list): A list of message dictionaries, where each dictionary contains
|
13 |
-
'role' and 'content' keys representing the conversation history.
|
14 |
-
|
15 |
-
Returns:
|
16 |
-
str: The assistant's response as a string, or an error message if the API call fails.
|
17 |
-
"""
|
18 |
try:
|
19 |
-
response = openai.ChatCompletion.create(
|
|
|
|
|
20 |
return response["choices"][0]["message"]["content"]
|
21 |
except Exception as e:
|
22 |
return f"⚠️ OpenAI Error: {e}"
|
23 |
|
24 |
|
25 |
-
def
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
Args:
|
29 |
-
prompt (str): The input prompt to send to the Ollama model.
|
30 |
|
31 |
-
|
32 |
-
str: The model's response as a string, or an error message if the API call fails.
|
33 |
-
"""
|
34 |
try:
|
35 |
-
|
36 |
"http://localhost:11434/api/generate",
|
37 |
json={"model": OLLAMA_MODEL, "prompt": prompt},
|
38 |
)
|
39 |
-
return
|
40 |
except Exception as e:
|
41 |
return f"⚠️ Ollama Error: {e}"
|
42 |
|
43 |
|
44 |
-
def generate_response(
|
45 |
-
|
46 |
-
|
47 |
-
Args:
|
48 |
-
persona (str): The system prompt or persona that defines the assistant's behavior.
|
49 |
-
user_input (str): The latest user input message.
|
50 |
-
history (list): A list of tuples representing the conversation history,
|
51 |
-
where each tuple is (user_message, bot_response).
|
52 |
-
|
53 |
-
Returns:
|
54 |
-
tuple: A tuple containing:
|
55 |
-
- Updated conversation history including the new exchange
|
56 |
-
- The same history (for compatibility with some interfaces)
|
57 |
-
"""
|
58 |
if USE_OLLAMA:
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
62 |
full_prompt += f"User: {user_input}\nBot:"
|
|
|
63 |
reply = query_ollama(full_prompt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
else:
|
65 |
-
messages = [{"role": "system", "content": persona}]
|
66 |
-
for u, b in history:
|
67 |
-
messages.append({"role": "user", "content": u})
|
68 |
-
messages.append({"role": "assistant", "content": b})
|
69 |
-
messages.append({"role": "user", "content": user_input})
|
70 |
reply = query_openai(messages)
|
71 |
|
72 |
-
history
|
73 |
-
return history
|
|
|
|
|
|
|
|
|
|
1 |
import requests
|
2 |
import openai
|
3 |
+
from config import (
|
4 |
+
OPENAI_API_KEY,
|
5 |
+
OPENAI_MODEL,
|
6 |
+
USE_OLLAMA,
|
7 |
+
OLLAMA_MODEL,
|
8 |
+
USE_OPENROUTER,
|
9 |
+
OPENROUTER_API_KEY,
|
10 |
+
OPENROUTER_MODEL,
|
11 |
+
TEMPERATURE,
|
12 |
+
)
|
13 |
|
14 |
openai.api_key = OPENAI_API_KEY
|
15 |
|
16 |
|
17 |
def query_openai(messages):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
try:
|
19 |
+
response = openai.ChatCompletion.create(
|
20 |
+
model=OPENAI_MODEL, messages=messages, temperature=TEMPERATURE
|
21 |
+
)
|
22 |
return response["choices"][0]["message"]["content"]
|
23 |
except Exception as e:
|
24 |
return f"⚠️ OpenAI Error: {e}"
|
25 |
|
26 |
|
27 |
+
def query_openrouter(messages):
|
28 |
+
headers = {
|
29 |
+
"Authorization": f"Bearer {OPENROUTER_API_KEY}",
|
30 |
+
"Content-Type": "application/json",
|
31 |
+
}
|
32 |
+
payload = {
|
33 |
+
"model": OPENROUTER_MODEL,
|
34 |
+
"messages": messages,
|
35 |
+
"temperature": TEMPERATURE,
|
36 |
+
}
|
37 |
+
try:
|
38 |
+
response = requests.post(
|
39 |
+
"https://openrouter.ai/api/v1/chat/completions",
|
40 |
+
headers=headers,
|
41 |
+
json=payload,
|
42 |
+
)
|
43 |
+
return response.json()["choices"][0]["message"]["content"]
|
44 |
+
except Exception as e:
|
45 |
+
return f"⚠️ OpenRouter Error: {e}"
|
46 |
|
|
|
|
|
47 |
|
48 |
+
def query_ollama(prompt):
|
|
|
|
|
49 |
try:
|
50 |
+
response = requests.post(
|
51 |
"http://localhost:11434/api/generate",
|
52 |
json={"model": OLLAMA_MODEL, "prompt": prompt},
|
53 |
)
|
54 |
+
return response.json()["response"]
|
55 |
except Exception as e:
|
56 |
return f"⚠️ Ollama Error: {e}"
|
57 |
|
58 |
|
59 |
+
def generate_response(persona_prompt, user_input, history):
|
60 |
+
# Handle Ollama case
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
if USE_OLLAMA:
|
62 |
+
# Convert history to Ollama format
|
63 |
+
full_prompt = f"{persona_prompt}\n\n"
|
64 |
+
for msg in history:
|
65 |
+
full_prompt += f"{msg['role'].capitalize()}: {msg['content']}\n"
|
66 |
full_prompt += f"User: {user_input}\nBot:"
|
67 |
+
|
68 |
reply = query_ollama(full_prompt)
|
69 |
+
return history + [
|
70 |
+
{"role": "user", "content": user_input},
|
71 |
+
{"role": "assistant", "content": reply}
|
72 |
+
]
|
73 |
+
|
74 |
+
# Handle OpenAI/OpenRouter case
|
75 |
+
messages = [{"role": "system", "content": persona_prompt}]
|
76 |
+
messages.extend(history)
|
77 |
+
messages.append({"role": "user", "content": user_input})
|
78 |
+
|
79 |
+
if USE_OPENROUTER:
|
80 |
+
reply = query_openrouter(messages)
|
81 |
else:
|
|
|
|
|
|
|
|
|
|
|
82 |
reply = query_openai(messages)
|
83 |
|
84 |
+
# Return the complete history with the new messages
|
85 |
+
return history + [
|
86 |
+
{"role": "user", "content": user_input},
|
87 |
+
{"role": "assistant", "content": reply}
|
88 |
+
]
|