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

Files changed (6) hide show
  1. .env +13 -3
  2. .gitignore +8 -0
  3. README.md +62 -14
  4. app.py +58 -9
  5. config.py +9 -2
  6. utils.py +59 -44
.env CHANGED
@@ -1,4 +1,14 @@
1
- OPENAI_API_KEY=sk-xxxxx
2
- OPENAI_MODEL=gpt-4o
3
  USE_OLLAMA=false
4
- OLLAMA_MODEL=llama3
 
 
 
 
 
 
 
 
 
 
 
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 app where you can switch between assistant personas powered by LLMs.
4
- It supports OpenAI (e.g. GPT-4o), or free alternatives like **Ollama** (LLaMA3, Mistral) or Hugging Face.
5
 
6
- ## 💡 Personas
7
- - Python Tutor
8
- - Regex Helper
9
- - Motivational Coach
10
- - Startup Advisor
11
 
12
- ## ⚙️ Stack
13
- - Gradio UI
14
- - OpenAI / Ollama / HF model backend
15
- - Modular Python
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- ## 🧪 Run Locally
18
  ```bash
19
- pip install -r requirements.txt
20
- python app.py
 
 
 
 
 
 
 
 
 
 
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 chat_fn(persona, user_input, history):
8
- return generate_response(PERSONAS[persona], user_input, history)
 
9
 
 
 
 
 
10
 
11
- with gr.Blocks() as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- state = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- def user_submit(user_input, history):
22
- return chat_fn(persona.value, user_input, history)
23
 
24
- msg.submit(user_submit, [msg, state], [chatbox, state])
 
25
 
26
- app.launch()
 
 
 
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
- # Model and key
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 = "Python Tutor"
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(model=OPENAI_MODEL, messages=messages)
 
 
20
  return response["choices"][0]["message"]["content"]
21
  except Exception as e:
22
  return f"⚠️ OpenAI Error: {e}"
23
 
24
 
25
- def query_ollama(prompt):
26
- """Query a local Ollama instance with the given prompt.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- Args:
29
- prompt (str): The input prompt to send to the Ollama model.
30
 
31
- Returns:
32
- str: The model's response as a string, or an error message if the API call fails.
33
- """
34
  try:
35
- res = requests.post(
36
  "http://localhost:11434/api/generate",
37
  json={"model": OLLAMA_MODEL, "prompt": prompt},
38
  )
39
- return res.json()["response"]
40
  except Exception as e:
41
  return f"⚠️ Ollama Error: {e}"
42
 
43
 
44
- def generate_response(persona, user_input, history):
45
- """Generate a response using either OpenAI or Ollama based on configuration.
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
- full_prompt = f"{persona}\n\n"
60
- for u, b in history:
61
- full_prompt += f"User: {u}\nBot: {b}\n"
 
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.append((user_input, reply))
73
- return history, 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
+ ]