Update Ollama integration for Matrix bot
- Added Ollama client integration to handle chat completions. - Modified the `chatbot` function to use the Ollama client for chat interactions. - Removed the `dalle` function as it is not supported by Ollama. - Updated the `reset_chat` function to handle resetting the chat history with Ollama. - Added new settings for Ollama base URL and model in `settings.py`. - Updated documentation in `README.md` to reflect the changes.
This commit is contained in:
parent
89d76525ec
commit
451ab831b8
28
README.md
28
README.md
@ -27,9 +27,8 @@ A Matrix bot that helps manage TODOs, track expenses, monitor bank accounts, sav
|
|||||||
- Auto-saves any shared URL to an org-mode file
|
- Auto-saves any shared URL to an org-mode file
|
||||||
|
|
||||||
- **AI Integration**:
|
- **AI Integration**:
|
||||||
- Chat with GPT-4 (continues conversation)
|
- Chat with Llama 3.2 via Ollama (continues conversation)
|
||||||
- `!reset` - Reset chat history
|
- `!reset` - Reset chat history
|
||||||
- `!dalle` - Generate images using DALL-E
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@ -38,7 +37,15 @@ A Matrix bot that helps manage TODOs, track expenses, monitor bank accounts, sav
|
|||||||
poetry install
|
poetry install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Create a `.env` file with the following variables:
|
2. Install and run Ollama:
|
||||||
|
- Follow the instructions at [Ollama's website](https://ollama.ai/) to install Ollama
|
||||||
|
- Pull the Llama 3.2 model:
|
||||||
|
```bash
|
||||||
|
ollama pull llama3.2:latest
|
||||||
|
```
|
||||||
|
- Start the Ollama server (it typically runs on port 11434)
|
||||||
|
|
||||||
|
3. Create a `.env` file with the following variables:
|
||||||
```
|
```
|
||||||
# Matrix Configuration
|
# Matrix Configuration
|
||||||
MATRIX_URL=
|
MATRIX_URL=
|
||||||
@ -54,11 +61,12 @@ ORG_CAPTURE_FILENAME=
|
|||||||
ORG_PLAN_FILENAME=
|
ORG_PLAN_FILENAME=
|
||||||
ORG_LINKS_FILENAME=
|
ORG_LINKS_FILENAME=
|
||||||
|
|
||||||
# API Keys
|
# Ollama Configuration
|
||||||
OPEN_AI_API_KEY=
|
OLLAMA_BASE_URL=http://localhost:11434
|
||||||
|
OLLAMA_MODEL=llama3.2:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Run the bot:
|
4. Run the bot:
|
||||||
```bash
|
```bash
|
||||||
python bot.py
|
python bot.py
|
||||||
```
|
```
|
||||||
@ -68,12 +76,13 @@ python bot.py
|
|||||||
- Python 3.9+
|
- Python 3.9+
|
||||||
- Poetry for dependency management
|
- Poetry for dependency management
|
||||||
- Matrix server access
|
- Matrix server access
|
||||||
- Optional: Bank accounts with BROU and Itau for banking features
|
- Optional: Bank accounts with Bank of America for banking features
|
||||||
- Optional: OpenAI API key for AI features
|
- Ollama installed and running with the llama3.2:latest model
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
- `bot.py`: Main bot implementation with command handlers
|
- `bot.py`: Main bot implementation with command handlers
|
||||||
|
- `ollama_client.py`: Ollama API client for AI features
|
||||||
- `bofa.py`: Bank of America data processing
|
- `bofa.py`: Bank of America data processing
|
||||||
- `org.py`: Org-mode file management
|
- `org.py`: Org-mode file management
|
||||||
- `settings.py`: Environment configuration
|
- `settings.py`: Environment configuration
|
||||||
@ -83,6 +92,5 @@ python bot.py
|
|||||||
Key dependencies include:
|
Key dependencies include:
|
||||||
- `simplematrixbotlib`: Matrix bot framework
|
- `simplematrixbotlib`: Matrix bot framework
|
||||||
- `orgparse`: Org-mode file parsing
|
- `orgparse`: Org-mode file parsing
|
||||||
- `openai`: GPT-4 and DALL-E integration
|
- `requests`: API interactions with Ollama
|
||||||
- `pyexcel-ods3`: Spreadsheet processing
|
- `pyexcel-ods3`: Spreadsheet processing
|
||||||
- `requests`: API interactions
|
|
||||||
|
57
bot.py
57
bot.py
@ -1,13 +1,11 @@
|
|||||||
"""A Matrix bot that manages TODOs, expenses, and AI interactions."""
|
"""A Matrix bot that manages TODOs, expenses, and AI interactions."""
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import openai
|
|
||||||
import simplematrixbotlib as botlib
|
import simplematrixbotlib as botlib
|
||||||
import validators
|
import validators
|
||||||
import wget
|
|
||||||
|
|
||||||
from bofa import BofaData
|
from bofa import BofaData
|
||||||
|
from ollama_client import OllamaClient
|
||||||
from org import OrgData
|
from org import OrgData
|
||||||
from settings import (
|
from settings import (
|
||||||
MATRIX_PASSWORD,
|
MATRIX_PASSWORD,
|
||||||
@ -15,10 +13,12 @@ from settings import (
|
|||||||
MATRIX_USER,
|
MATRIX_USER,
|
||||||
MATRIX_USERNAME,
|
MATRIX_USERNAME,
|
||||||
MATRIX_USERNAMES,
|
MATRIX_USERNAMES,
|
||||||
OPEN_AI_API_KEY,
|
OLLAMA_BASE_URL, # New setting for Ollama
|
||||||
|
OLLAMA_MODEL, # New setting for Ollama model
|
||||||
)
|
)
|
||||||
|
|
||||||
openai.api_key = OPEN_AI_API_KEY
|
# Initialize the Ollama client
|
||||||
|
ollama = OllamaClient(base_url=OLLAMA_BASE_URL, model=OLLAMA_MODEL)
|
||||||
|
|
||||||
creds = botlib.Creds(MATRIX_URL, MATRIX_USER, MATRIX_PASSWORD)
|
creds = botlib.Creds(MATRIX_URL, MATRIX_USER, MATRIX_PASSWORD)
|
||||||
bot = botlib.Bot(creds)
|
bot = botlib.Bot(creds)
|
||||||
@ -144,12 +144,12 @@ async def save_link(room, message):
|
|||||||
|
|
||||||
|
|
||||||
@bot.listener.on_message_event
|
@bot.listener.on_message_event
|
||||||
async def chatgpt(room, message):
|
async def chatbot(room, message):
|
||||||
"""Start a conversation with ChatGPT.
|
"""Start a conversation with the Ollama model.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
user: !chatgpt Hello!
|
user: Any message
|
||||||
bot: [prints chatgpt response]
|
bot: [prints Ollama model response]
|
||||||
"""
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, PREFIX)
|
match = botlib.MessageMatch(room, message, bot, PREFIX)
|
||||||
message_content = message.body
|
message_content = message.body
|
||||||
@ -160,7 +160,7 @@ async def chatgpt(room, message):
|
|||||||
personal_conversation = CONVERSATION[user]
|
personal_conversation = CONVERSATION[user]
|
||||||
room_id = room.room_id
|
room_id = room.room_id
|
||||||
|
|
||||||
print(f"Room: {room_id}, User: {user}, Message: chatgpt")
|
print(f"Room: {room_id}, User: {user}, Message: chat with Ollama")
|
||||||
|
|
||||||
def format_message(message):
|
def format_message(message):
|
||||||
return {"role": "user", "content": message}
|
return {"role": "user", "content": message}
|
||||||
@ -168,9 +168,10 @@ async def chatgpt(room, message):
|
|||||||
personal_conversation.append(format_message(message_content))
|
personal_conversation.append(format_message(message_content))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
completion = openai.ChatCompletion.create(model="gpt-4o", messages=personal_conversation)
|
# Using Ollama instead of OpenAI
|
||||||
response = completion.choices[0].message.content
|
completion = ollama.chat_completion(personal_conversation)
|
||||||
personal_conversation.append(completion.choices[0].message)
|
response = completion["choices"][0]["message"]["content"]
|
||||||
|
personal_conversation.append(completion["choices"][0]["message"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error: {e}")
|
print(f"Error: {e}")
|
||||||
response = "There was a problem with your prompt"
|
response = "There was a problem with your prompt"
|
||||||
@ -180,8 +181,8 @@ async def chatgpt(room, message):
|
|||||||
|
|
||||||
|
|
||||||
@bot.listener.on_message_event
|
@bot.listener.on_message_event
|
||||||
async def reset_chatgpt(room, message):
|
async def reset_chat(room, message):
|
||||||
"""Reset the ChatGPT conversation history.
|
"""Reset the chat conversation history.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
user: !reset
|
user: !reset
|
||||||
@ -200,12 +201,12 @@ async def reset_chatgpt(room, message):
|
|||||||
|
|
||||||
|
|
||||||
@bot.listener.on_message_event
|
@bot.listener.on_message_event
|
||||||
async def dall_e(room, message):
|
async def dalle(room, message):
|
||||||
"""Generate an image using DALL-E.
|
"""Generate an image (feature not available with Ollama).
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
user: !dalle A sunny caribbean beach
|
user: !dalle A sunny caribbean beach
|
||||||
bot: returns an image
|
bot: returns an error message
|
||||||
"""
|
"""
|
||||||
match = botlib.MessageMatch(room, message, bot, PREFIX)
|
match = botlib.MessageMatch(room, message, bot, PREFIX)
|
||||||
if match.is_not_from_this_bot() and match.prefix() and match.command("dalle"):
|
if match.is_not_from_this_bot() and match.prefix() and match.command("dalle"):
|
||||||
@ -213,22 +214,12 @@ async def dall_e(room, message):
|
|||||||
|
|
||||||
if user in MATRIX_USERNAMES:
|
if user in MATRIX_USERNAMES:
|
||||||
room_id = room.room_id
|
room_id = room.room_id
|
||||||
message = " ".join(message.body.split(" ")[1:]).strip()
|
|
||||||
|
|
||||||
print(f"Room: {room_id}, User: {user}, Message: dalle")
|
return await bot.api.send_text_message(
|
||||||
await bot.api.send_text_message(room_id, "Generating image...")
|
room_id,
|
||||||
|
"Image generation is not available with the Ollama integration. "
|
||||||
try:
|
"Please consider using a dedicated image generation service.",
|
||||||
image = openai.Image.create(prompt=message)
|
)
|
||||||
image_url = image["data"][0]["url"]
|
|
||||||
image_filename = wget.download(image_url)
|
|
||||||
|
|
||||||
await bot.api.send_image_message(room_id, image_filename)
|
|
||||||
os.remove(image_filename)
|
|
||||||
return None
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error sending image: {e}")
|
|
||||||
return await bot.api.send_text_message(room_id, f"Error sending image: {e}")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
64
ollama_client.py
Normal file
64
ollama_client.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""Ollama integration for Matrix bot."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
HTTP_OK = 200
|
||||||
|
|
||||||
|
|
||||||
|
class OllamaClient:
|
||||||
|
"""Client for interacting with Ollama API."""
|
||||||
|
|
||||||
|
def __init__(self, base_url: str = "http://localhost:11434", model: str = "llama3.2:latest"):
|
||||||
|
"""Initialize the Ollama client.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base_url: Base URL for the Ollama API
|
||||||
|
model: Model name to use for completions
|
||||||
|
"""
|
||||||
|
self.base_url = base_url
|
||||||
|
self.model = model
|
||||||
|
self.api_url = f"{base_url}/api"
|
||||||
|
|
||||||
|
def chat_completion(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
|
||||||
|
"""Create a chat completion using Ollama.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
messages: List of message dictionaries with 'role' and 'content' keys
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing the model's response
|
||||||
|
"""
|
||||||
|
# Convert OpenAI-style messages to Ollama format
|
||||||
|
# Ollama expects a simpler format with just the messages array
|
||||||
|
payload = {
|
||||||
|
"model": self.model,
|
||||||
|
"messages": messages,
|
||||||
|
"stream": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
f"{self.api_url}/chat",
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
data=json.dumps(payload),
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != HTTP_OK:
|
||||||
|
raise Exception(f"Error from Ollama API: {response.text}")
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
# Convert Ollama response to a format similar to OpenAI for easier integration
|
||||||
|
return {
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"message": {
|
||||||
|
"role": "assistant",
|
||||||
|
"content": result["message"]["content"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"model": self.model,
|
||||||
|
}
|
@ -19,4 +19,5 @@ ORG_CAPTURE_FILENAME = f"{ORG_LOCATION}/{os.environ.get('ORG_CAPTURE_FILENAME')}
|
|||||||
ORG_PLAN_FILENAME = f"{ORG_LOCATION}/{os.environ.get('ORG_PLAN_FILENAME')}"
|
ORG_PLAN_FILENAME = f"{ORG_LOCATION}/{os.environ.get('ORG_PLAN_FILENAME')}"
|
||||||
ORG_LINKS_FILENAME = f"{ORG_LOCATION}/{os.environ.get('ORG_LINKS_FILENAME')}"
|
ORG_LINKS_FILENAME = f"{ORG_LOCATION}/{os.environ.get('ORG_LINKS_FILENAME')}"
|
||||||
|
|
||||||
OPEN_AI_API_KEY = os.environ.get("OPEN_AI_API_KEY")
|
OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434")
|
||||||
|
OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3.2:latest")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user