Latest fixxes
This commit is contained in:
@@ -8,6 +8,7 @@ A backend service for OpenWebUI that provides OpenWebUI-compatible API endpoints
|
||||
- Ollama API proxy
|
||||
- Chat functionality with model switching
|
||||
- Support for multiple LLM models (gemma3, llama3.3, llama3.1, mistral, deepseek)
|
||||
- Team chat integration with OpenWebUI channels
|
||||
|
||||
## Technology Stack
|
||||
|
||||
@@ -25,6 +26,7 @@ ai_service/
|
||||
├── embeddings/ # Document processing for RAG
|
||||
│ └── document_service.py
|
||||
├── openwebui_api.py # OpenWebUI-compatible API endpoints
|
||||
├── openwebui_channels.py # OpenWebUI channels integration
|
||||
├── config.py # Configuration settings
|
||||
├── api.py # FastAPI application
|
||||
└── deploy.sh # Deployment script
|
||||
@@ -88,3 +90,7 @@ To configure OpenWebUI to use this service as its backend:
|
||||
```
|
||||
|
||||
2. Restart OpenWebUI to apply the changes.
|
||||
|
||||
## Team Chat Feature
|
||||
|
||||
The service now integrates with OpenWebUI's channels feature to provide team chat functionality. See [TEAM_CHAT_GUIDE.md](TEAM_CHAT_GUIDE.md) for detailed instructions on how to use team chats.
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
# Team Chat Guide
|
||||
|
||||
This guide explains how to use the team chat feature with OpenWebUI channels.
|
||||
|
||||
## Overview
|
||||
|
||||
The chatbot now integrates with OpenWebUI's channels feature to provide team chat functionality. When you create a team chat in the chatbot, it automatically creates a corresponding channel in OpenWebUI, allowing multiple users to participate in the same conversation.
|
||||
|
||||
## Using Team Chats
|
||||
|
||||
### AI Responses in Team Chats
|
||||
|
||||
When you send a message in a team chat, the AI will respond both in your local chat and in the OpenWebUI channel. The AI's responses in OpenWebUI channels:
|
||||
|
||||
- Are prefixed with a robot emoji (🤖) to clearly identify them as AI responses
|
||||
- Appear with a special "ai-assistant" user ID to distinguish them from human users
|
||||
- Are visible to all members of the channel in real-time
|
||||
|
||||
This allows all team members to see the conversation with the AI and collaborate effectively.
|
||||
|
||||
### How the AI Bot is Triggered
|
||||
|
||||
The AI bot can be triggered to respond in two ways:
|
||||
|
||||
1. **Through the API**: When you send a message using the `/chats/{chat_id}/messages` endpoint, the AI automatically processes it and responds.
|
||||
|
||||
2. **Directly in OpenWebUI**: When someone mentions the AI in an OpenWebUI channel that's linked to a team chat:
|
||||
- The message is sent to your service through a webhook
|
||||
- If it contains an AI mention (like `@ai`, `@bot`, etc.), the AI processes it
|
||||
- The AI sends its response back to the OpenWebUI channel
|
||||
|
||||
By default, the AI only responds when explicitly mentioned with one of these triggers:
|
||||
- `@ai`
|
||||
- `@bot`
|
||||
- `@assistant`
|
||||
- `@chatbot`
|
||||
|
||||
You can customize these triggers or make the AI respond to all messages by changing the settings in your `.env` file:
|
||||
|
||||
```
|
||||
# To customize the triggers that activate the AI
|
||||
AI_TRIGGERS=@ai,@bot,@assistant,@chatbot
|
||||
|
||||
# To make the AI respond to all messages (not just mentions)
|
||||
AI_RESPOND_TO_ALL=false # Change to 'true' to respond to everything
|
||||
```
|
||||
|
||||
This mention-based approach ensures the AI only joins the conversation when explicitly invited, making team chats more focused and preventing the AI from responding to every message.
|
||||
|
||||
### Creating a Team Chat
|
||||
|
||||
You can create a team chat in two ways:
|
||||
|
||||
1. **Through the API**:
|
||||
```
|
||||
POST /chats
|
||||
{
|
||||
"user_id": "your-user-id",
|
||||
"title": "Team Chat Name",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true
|
||||
}
|
||||
```
|
||||
|
||||
2. **Through OpenWebUI**:
|
||||
- Log in to OpenWebUI at http://104.225.217.215:8080/
|
||||
- Navigate to the Channels section (look for a "#" or group icon in the sidebar)
|
||||
- Click "Create Channel" or "+" button
|
||||
- Give your channel a name and description
|
||||
- Choose whether it should be private or public
|
||||
- Click "Create"
|
||||
|
||||
### Adding Members to a Team Chat
|
||||
|
||||
1. **Through the API**:
|
||||
```
|
||||
POST /chats/{chat_id}/members/{user_id}
|
||||
```
|
||||
|
||||
2. **Through OpenWebUI**:
|
||||
- Open the channel in OpenWebUI
|
||||
- Look for a "Members" or "Invite" option
|
||||
- Add users by their username or email
|
||||
|
||||
### Sending Messages in a Team Chat
|
||||
|
||||
1. **Through the API**:
|
||||
```
|
||||
POST /chats/{chat_id}/messages
|
||||
{
|
||||
"message": "Your message",
|
||||
"user_id": "your-user-id"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Through OpenWebUI**:
|
||||
- Open the channel in OpenWebUI
|
||||
- Type your message in the input box
|
||||
- Press Enter to send
|
||||
|
||||
### Viewing Team Chats
|
||||
|
||||
1. **Through the API**:
|
||||
```
|
||||
GET /chats/user/{user_id}
|
||||
```
|
||||
This will return all chats for the user, including team chats where they are a member.
|
||||
|
||||
2. **Through OpenWebUI**:
|
||||
- Log in to OpenWebUI
|
||||
- Navigate to the Channels section
|
||||
- You'll see all channels you're a member of
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
The team chat feature works by:
|
||||
|
||||
1. Creating an OpenWebUI channel when a team chat is created
|
||||
2. Adding members to both the local team chat and the OpenWebUI channel
|
||||
3. Sending messages to both the local chat and the OpenWebUI channel
|
||||
4. Deleting the OpenWebUI channel when the team chat is deleted
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter issues with team chats:
|
||||
|
||||
1. **Channel not appearing in OpenWebUI**:
|
||||
- Check if the OpenWebUI server is running
|
||||
- Verify that the OpenWebUI URL and API key are correctly configured in the `.env` file
|
||||
|
||||
2. **Cannot add members to a team chat**:
|
||||
- Ensure the user exists in OpenWebUI
|
||||
- Check if the team chat was properly created with `is_team_chat: true`
|
||||
|
||||
3. **Messages not appearing in OpenWebUI channel**:
|
||||
- Check the logs for any errors when sending messages
|
||||
- Verify that the OpenWebUI channel ID is correctly stored in the chat data
|
||||
|
||||
## API Reference
|
||||
|
||||
### Team Chat Endpoints
|
||||
|
||||
- `POST /chats` - Create a new chat (set `is_team_chat: true` for team chats)
|
||||
- `GET /chats/user/{user_id}` - Get all chats for a user (includes team chats)
|
||||
- `POST /chats/{chat_id}/members/{user_id}` - Add a user to a team chat
|
||||
- `DELETE /chats/{chat_id}/members/{user_id}` - Remove a user from a team chat
|
||||
- `DELETE /chats/{chat_id}` - Delete a chat (also deletes the OpenWebUI channel)
|
||||
|
||||
### OpenWebUI Channel Endpoints
|
||||
|
||||
- `GET /channels` - Get all OpenWebUI channels
|
||||
- `GET /channels/{channel_id}` - Get an OpenWebUI channel by ID
|
||||
- `POST /channels` - Create a new OpenWebUI channel
|
||||
@@ -1,6 +1,7 @@
|
||||
# API configuration
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=5251
|
||||
PUBLIC_URL=http://your-public-url:5251 # Public URL for webhooks, needed for OpenWebUI to send channel messages
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://104.225.217.215:8080
|
||||
@@ -13,3 +14,7 @@ DEFAULT_MODEL=llama3.1
|
||||
# Document processing
|
||||
CHUNK_SIZE=1000
|
||||
CHUNK_OVERLAP=200
|
||||
|
||||
# AI bot configuration
|
||||
AI_TRIGGERS=@ai,@bot,@assistant,@chatbot # Comma-separated list of triggers that will make the AI respond
|
||||
AI_RESPOND_TO_ALL=false # Set to 'true' to make the AI respond to all messages, 'false' to only respond to mentions
|
||||
|
||||
@@ -13,6 +13,7 @@ from datetime import datetime, timezone
|
||||
from ai_service.models.model_service import model_service
|
||||
from ai_service.models.chat_service import chat_service
|
||||
from ai_service.openwebui_api import router as openwebui_router
|
||||
from ai_service.openwebui_channels import openwebui_channels
|
||||
from ai_service.config import config
|
||||
|
||||
# Create FastAPI app
|
||||
@@ -37,6 +38,27 @@ app.include_router(openwebui_router, prefix="/api")
|
||||
# Include Ollama proxy routes
|
||||
app.include_router(openwebui_router, prefix="/ollama")
|
||||
|
||||
# Register webhook for channel messages on startup
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""
|
||||
Register webhook for channel messages on startup.
|
||||
"""
|
||||
# Get the public URL of this service
|
||||
service_url = f"http://{config.API_HOST}:{config.API_PORT}"
|
||||
if config.PUBLIC_URL:
|
||||
service_url = config.PUBLIC_URL
|
||||
|
||||
# Register webhook
|
||||
webhook_url = f"{service_url}/webhooks/channel-message"
|
||||
print(f"Registering webhook for channel messages: {webhook_url}")
|
||||
|
||||
success = openwebui_channels.register_webhook(webhook_url)
|
||||
if success:
|
||||
print("Successfully registered webhook for channel messages")
|
||||
else:
|
||||
print("Failed to register webhook for channel messages")
|
||||
|
||||
# Define API models for health check
|
||||
class HealthResponse(BaseModel):
|
||||
"""Response model for health check."""
|
||||
@@ -450,3 +472,133 @@ async def delete_chat(chat_id: str):
|
||||
raise HTTPException(status_code=404, detail="Chat not found")
|
||||
|
||||
return {"status": "success", "message": "Chat deleted"}
|
||||
|
||||
# OpenWebUI Channels endpoints
|
||||
@app.get("/channels")
|
||||
async def get_openwebui_channels():
|
||||
"""
|
||||
Get all OpenWebUI channels.
|
||||
|
||||
Returns:
|
||||
List of channels.
|
||||
"""
|
||||
channels = openwebui_channels.get_channels()
|
||||
return channels
|
||||
|
||||
@app.get("/channels/{channel_id}")
|
||||
async def get_openwebui_channel(channel_id: str):
|
||||
"""
|
||||
Get an OpenWebUI channel by ID.
|
||||
|
||||
Args:
|
||||
channel_id: Channel ID.
|
||||
|
||||
Returns:
|
||||
Channel information.
|
||||
"""
|
||||
channel = openwebui_channels.get_channel(channel_id)
|
||||
if not channel:
|
||||
raise HTTPException(status_code=404, detail="Channel not found")
|
||||
|
||||
return channel
|
||||
|
||||
@app.post("/channels")
|
||||
async def create_openwebui_channel(name: str, description: str = "", is_private: bool = False):
|
||||
"""
|
||||
Create a new OpenWebUI channel.
|
||||
|
||||
Args:
|
||||
name: Channel name.
|
||||
description: Channel description.
|
||||
is_private: Whether the channel is private.
|
||||
|
||||
Returns:
|
||||
Created channel.
|
||||
"""
|
||||
channel = openwebui_channels.create_channel(name, description, is_private)
|
||||
if not channel:
|
||||
raise HTTPException(status_code=400, detail="Failed to create channel")
|
||||
|
||||
return channel
|
||||
|
||||
# Webhook endpoint for OpenWebUI channel messages
|
||||
class ChannelMessageWebhook(BaseModel):
|
||||
"""Model for channel message webhook."""
|
||||
channel_id: str = Field(..., description="Channel ID")
|
||||
message: str = Field(..., description="Message content")
|
||||
user_id: str = Field(..., description="User ID")
|
||||
timestamp: Optional[str] = Field(None, description="Message timestamp")
|
||||
|
||||
@app.post("/webhooks/channel-message")
|
||||
async def channel_message_webhook(request: ChannelMessageWebhook):
|
||||
"""
|
||||
Webhook endpoint for receiving messages from OpenWebUI channels.
|
||||
|
||||
This endpoint is called by OpenWebUI when a message is sent in a channel.
|
||||
The AI service will process the message and respond in the channel.
|
||||
|
||||
Args:
|
||||
request: Channel message webhook request.
|
||||
|
||||
Returns:
|
||||
Processing status.
|
||||
"""
|
||||
try:
|
||||
print(f"Received channel message webhook: {request.channel_id}, {request.user_id}, {request.message}")
|
||||
|
||||
# Find the chat associated with this OpenWebUI channel
|
||||
chat_id = None
|
||||
for cid, chat in chat_service.chats.items():
|
||||
if chat.get('is_team_chat') and chat.get('openwebui_channel_id') == request.channel_id:
|
||||
chat_id = cid
|
||||
break
|
||||
|
||||
if not chat_id:
|
||||
print(f"No chat found for OpenWebUI channel {request.channel_id}")
|
||||
return {"status": "error", "message": "No chat found for this channel"}
|
||||
|
||||
# Skip messages from the AI assistant to avoid loops
|
||||
if request.user_id == "ai-assistant":
|
||||
return {"status": "skipped", "message": "Skipping AI assistant message"}
|
||||
|
||||
# Check if we should respond to all messages or only to mentions
|
||||
if not config.AI_RESPOND_TO_ALL:
|
||||
# Check if the message mentions the AI using configured triggers
|
||||
message_lower = request.message.lower()
|
||||
|
||||
is_triggered = False
|
||||
for trigger in config.AI_TRIGGERS:
|
||||
if trigger.lower() in message_lower:
|
||||
is_triggered = True
|
||||
break
|
||||
|
||||
# If no trigger is found, skip processing
|
||||
if not is_triggered:
|
||||
print(f"No AI mention found in message, skipping: {request.message[:50]}...")
|
||||
return {"status": "skipped", "message": "No AI mention found in message"}
|
||||
|
||||
# Extract the actual message content (remove the trigger)
|
||||
# This is a simple approach - for more complex cases, you might want more sophisticated parsing
|
||||
processed_message = request.message
|
||||
message_lower = request.message.lower()
|
||||
|
||||
# Only try to remove triggers if we're not responding to all messages
|
||||
if not config.AI_RESPOND_TO_ALL:
|
||||
for trigger in config.AI_TRIGGERS:
|
||||
if trigger.lower() in message_lower:
|
||||
# Remove the trigger from the message
|
||||
processed_message = request.message.replace(trigger, "").strip()
|
||||
break
|
||||
|
||||
# Process the message and generate a response
|
||||
response = chat_service.get_chat_response(
|
||||
chat_id=chat_id,
|
||||
message=processed_message,
|
||||
user_id=request.user_id
|
||||
)
|
||||
|
||||
return {"status": "success", "message": "Message processed", "response": response}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error processing channel message webhook: {str(e)}")
|
||||
return {"status": "error", "message": f"Error processing message: {str(e)}"}
|
||||
|
||||
+13
-5
@@ -3,12 +3,15 @@ Configuration settings for the AI service.
|
||||
"""
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
import os.path
|
||||
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
|
||||
load_dotenv(dotenv_path=dotenv_path)
|
||||
|
||||
# Try to load environment variables from .env file
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
dotenv_path = os.path.join(os.path.dirname(__file__), '.env')
|
||||
load_dotenv(dotenv_path=dotenv_path)
|
||||
except ImportError:
|
||||
print("Warning: python-dotenv not installed. Using environment variables directly.")
|
||||
|
||||
class Config:
|
||||
"""Base configuration."""
|
||||
@@ -16,6 +19,7 @@ class Config:
|
||||
# API configuration
|
||||
API_HOST = os.environ.get('API_HOST', '0.0.0.0')
|
||||
API_PORT = int(os.environ.get('API_PORT', 5252))
|
||||
PUBLIC_URL = os.environ.get('PUBLIC_URL', '') # Public URL for webhooks
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL = os.environ.get('OPENWEBUI_URL', 'http://104.225.217.215:8080')
|
||||
@@ -30,5 +34,9 @@ class Config:
|
||||
CHUNK_SIZE = int(os.environ.get('CHUNK_SIZE', 1000))
|
||||
CHUNK_OVERLAP = int(os.environ.get('CHUNK_OVERLAP', 200))
|
||||
|
||||
# AI bot configuration
|
||||
AI_TRIGGERS = os.environ.get('AI_TRIGGERS', '@ai,@bot,@assistant,@chatbot').split(',')
|
||||
AI_RESPOND_TO_ALL = os.environ.get('AI_RESPOND_TO_ALL', 'false').lower() == 'true'
|
||||
|
||||
|
||||
config = Config()
|
||||
|
||||
@@ -11,6 +11,7 @@ from typing import List, Dict, Any, Optional
|
||||
from ai_service.config import config
|
||||
from ai_service.models.model_service import model_service
|
||||
from ai_service.models.model_parameters import ModelParameters
|
||||
from ai_service.openwebui_channels import openwebui_channels
|
||||
|
||||
class ChatService:
|
||||
"""Service for chat functionality."""
|
||||
@@ -61,17 +62,40 @@ class ChatService:
|
||||
# Generate a unique ID for the chat
|
||||
chat_id = str(uuid.uuid4())
|
||||
|
||||
# Default title if none provided
|
||||
chat_title = title or f"Chat {len(self.chats) + 1}"
|
||||
|
||||
# For team chats, create an OpenWebUI channel
|
||||
openwebui_channel_id = None
|
||||
if is_team_chat:
|
||||
try:
|
||||
# Create a channel in OpenWebUI
|
||||
channel_response = openwebui_channels.create_channel(
|
||||
name=chat_title,
|
||||
description=f"Team chat created by {user_id}",
|
||||
is_private=True # Team chats are private by default
|
||||
)
|
||||
|
||||
if channel_response:
|
||||
openwebui_channel_id = channel_response.get('id')
|
||||
print(f"Created OpenWebUI channel with ID: {openwebui_channel_id}")
|
||||
else:
|
||||
print("Failed to create OpenWebUI channel, continuing with local team chat only")
|
||||
except Exception as e:
|
||||
print(f"Error creating OpenWebUI channel: {str(e)}")
|
||||
|
||||
# Create chat data
|
||||
self.chats[chat_id] = {
|
||||
'id': chat_id,
|
||||
'title': title or f"Chat {len(self.chats) + 1}",
|
||||
'title': chat_title,
|
||||
'user_id': user_id,
|
||||
'model_id': model_id or config.DEFAULT_MODEL,
|
||||
'is_team_chat': is_team_chat,
|
||||
'created_at': datetime.utcnow().isoformat(),
|
||||
'updated_at': datetime.utcnow().isoformat(),
|
||||
'created_at': datetime.now().isoformat(),
|
||||
'updated_at': datetime.now().isoformat(),
|
||||
'messages': [],
|
||||
'team_members': [user_id] if is_team_chat else []
|
||||
'team_members': [user_id] if is_team_chat else [],
|
||||
'openwebui_channel_id': openwebui_channel_id
|
||||
}
|
||||
|
||||
# Save chats to file
|
||||
@@ -96,20 +120,47 @@ class ChatService:
|
||||
if chat_id not in self.chats:
|
||||
raise ValueError(f"Chat with ID {chat_id} not found")
|
||||
|
||||
chat = self.chats[chat_id]
|
||||
|
||||
# Create message data
|
||||
message = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'content': content,
|
||||
'user_id': user_id if is_user_message else None,
|
||||
'is_user_message': is_user_message,
|
||||
'timestamp': datetime.utcnow().isoformat()
|
||||
'timestamp': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
# Add message to chat
|
||||
self.chats[chat_id]['messages'].append(message)
|
||||
chat['messages'].append(message)
|
||||
|
||||
# Update chat timestamp
|
||||
self.chats[chat_id]['updated_at'] = datetime.utcnow().isoformat()
|
||||
chat['updated_at'] = datetime.now().isoformat()
|
||||
|
||||
# If this is a team chat with an OpenWebUI channel, send the message there too
|
||||
if chat['is_team_chat'] and 'openwebui_channel_id' in chat and chat['openwebui_channel_id']:
|
||||
try:
|
||||
# For AI responses, use a special AI user ID
|
||||
# This ensures the AI's messages are properly distinguished in OpenWebUI
|
||||
sender_id = "ai-assistant" if not is_user_message else user_id
|
||||
|
||||
# Format the message for OpenWebUI
|
||||
if is_user_message:
|
||||
# For user messages, use the regular format
|
||||
formatted_content = content
|
||||
else:
|
||||
# For AI responses, add a special prefix to make it clear it's from the AI
|
||||
# This helps visually distinguish AI responses in the channel
|
||||
formatted_content = f"🤖 {content}"
|
||||
|
||||
# Send message to OpenWebUI channel
|
||||
openwebui_channels.send_message(
|
||||
channel_id=chat['openwebui_channel_id'],
|
||||
message=formatted_content,
|
||||
user_id=sender_id
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Error sending message to OpenWebUI channel: {str(e)}")
|
||||
|
||||
# Save chats to file
|
||||
self._save_chats()
|
||||
@@ -140,7 +191,7 @@ class ChatService:
|
||||
"""
|
||||
user_chats = []
|
||||
|
||||
for chat_id, chat in self.chats.items():
|
||||
for _, chat in self.chats.items():
|
||||
# Include private chats owned by the user
|
||||
if chat['user_id'] == user_id and not chat['is_team_chat']:
|
||||
user_chats.append(chat)
|
||||
@@ -173,6 +224,21 @@ class ChatService:
|
||||
if not chat['is_team_chat']:
|
||||
return False
|
||||
|
||||
# Add to OpenWebUI channel if available
|
||||
if 'openwebui_channel_id' in chat and chat['openwebui_channel_id']:
|
||||
try:
|
||||
# Add member to OpenWebUI channel
|
||||
openwebui_success = openwebui_channels.add_member(
|
||||
channel_id=chat['openwebui_channel_id'],
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
if not openwebui_success:
|
||||
print(f"Warning: Failed to add user {user_id} to OpenWebUI channel {chat['openwebui_channel_id']}")
|
||||
except Exception as e:
|
||||
print(f"Error adding member to OpenWebUI channel: {str(e)}")
|
||||
|
||||
# Add to local team members list
|
||||
if user_id not in chat['team_members']:
|
||||
chat['team_members'].append(user_id)
|
||||
self._save_chats()
|
||||
@@ -198,6 +264,21 @@ class ChatService:
|
||||
if not chat['is_team_chat']:
|
||||
return False
|
||||
|
||||
# Remove from OpenWebUI channel if available
|
||||
if 'openwebui_channel_id' in chat and chat['openwebui_channel_id']:
|
||||
try:
|
||||
# Remove member from OpenWebUI channel
|
||||
openwebui_success = openwebui_channels.remove_member(
|
||||
channel_id=chat['openwebui_channel_id'],
|
||||
user_id=user_id
|
||||
)
|
||||
|
||||
if not openwebui_success:
|
||||
print(f"Warning: Failed to remove user {user_id} from OpenWebUI channel {chat['openwebui_channel_id']}")
|
||||
except Exception as e:
|
||||
print(f"Error removing member from OpenWebUI channel: {str(e)}")
|
||||
|
||||
# Remove from local team members list
|
||||
if user_id in chat['team_members']:
|
||||
chat['team_members'].remove(user_id)
|
||||
self._save_chats()
|
||||
@@ -217,6 +298,22 @@ class ChatService:
|
||||
if chat_id not in self.chats:
|
||||
return False
|
||||
|
||||
chat = self.chats[chat_id]
|
||||
|
||||
# Delete OpenWebUI channel if this is a team chat
|
||||
if chat['is_team_chat'] and 'openwebui_channel_id' in chat and chat['openwebui_channel_id']:
|
||||
try:
|
||||
# Delete the OpenWebUI channel
|
||||
openwebui_success = openwebui_channels.delete_channel(
|
||||
channel_id=chat['openwebui_channel_id']
|
||||
)
|
||||
|
||||
if not openwebui_success:
|
||||
print(f"Warning: Failed to delete OpenWebUI channel {chat['openwebui_channel_id']}")
|
||||
except Exception as e:
|
||||
print(f"Error deleting OpenWebUI channel: {str(e)}")
|
||||
|
||||
# Delete the chat from local storage
|
||||
del self.chats[chat_id]
|
||||
self._save_chats()
|
||||
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
"""
|
||||
OpenWebUI channels integration for team chats.
|
||||
|
||||
This module provides functions to interact with OpenWebUI channels API
|
||||
for creating and managing team chats through OpenWebUI's channels feature.
|
||||
It also includes functionality to listen for and respond to messages in OpenWebUI channels.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional
|
||||
|
||||
from ai_service.config import config
|
||||
|
||||
class OpenWebUIChannels:
|
||||
"""Class for interacting with OpenWebUI channels."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the OpenWebUI channels integration."""
|
||||
self.openwebui_url = config.OPENWEBUI_URL
|
||||
self.openwebui_api_key = config.OPENWEBUI_API_KEY
|
||||
self.headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# Add API key if available
|
||||
if self.openwebui_api_key:
|
||||
self.headers["Authorization"] = f"Bearer {self.openwebui_api_key}"
|
||||
|
||||
def create_channel(self, name: str, description: str = "", is_private: bool = False) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Create a new channel in OpenWebUI.
|
||||
|
||||
Args:
|
||||
name: Name of the channel.
|
||||
description: Description of the channel.
|
||||
is_private: Whether the channel is private.
|
||||
|
||||
Returns:
|
||||
Channel data if creation was successful, None otherwise.
|
||||
"""
|
||||
try:
|
||||
# Prepare channel data
|
||||
channel_data = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"is_private": is_private
|
||||
}
|
||||
|
||||
# Make API request to create channel
|
||||
response = requests.post(
|
||||
f"{self.openwebui_url}/api/channels",
|
||||
headers=self.headers,
|
||||
json=channel_data,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code == 200 or response.status_code == 201:
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Error creating channel: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating channel: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_channel(self, channel_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Get a channel by ID.
|
||||
|
||||
Args:
|
||||
channel_id: ID of the channel.
|
||||
|
||||
Returns:
|
||||
Channel data if found, None otherwise.
|
||||
"""
|
||||
try:
|
||||
# Make API request to get channel
|
||||
response = requests.get(
|
||||
f"{self.openwebui_url}/api/channels/{channel_id}",
|
||||
headers=self.headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Error getting channel: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting channel: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_channels(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get all channels.
|
||||
|
||||
Returns:
|
||||
List of channel data.
|
||||
"""
|
||||
try:
|
||||
# Make API request to get channels
|
||||
response = requests.get(
|
||||
f"{self.openwebui_url}/api/channels",
|
||||
headers=self.headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Error getting channels: {response.status_code} - {response.text}")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting channels: {str(e)}")
|
||||
return []
|
||||
|
||||
def add_member(self, channel_id: str, user_id: str) -> bool:
|
||||
"""
|
||||
Add a user to a channel.
|
||||
|
||||
Args:
|
||||
channel_id: ID of the channel.
|
||||
user_id: ID of the user to add.
|
||||
|
||||
Returns:
|
||||
True if addition was successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# Prepare member data
|
||||
member_data = {
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
# Make API request to add member
|
||||
response = requests.post(
|
||||
f"{self.openwebui_url}/api/channels/{channel_id}/members",
|
||||
headers=self.headers,
|
||||
json=member_data,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
return response.status_code == 200 or response.status_code == 201
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error adding member to channel: {str(e)}")
|
||||
return False
|
||||
|
||||
def remove_member(self, channel_id: str, user_id: str) -> bool:
|
||||
"""
|
||||
Remove a user from a channel.
|
||||
|
||||
Args:
|
||||
channel_id: ID of the channel.
|
||||
user_id: ID of the user to remove.
|
||||
|
||||
Returns:
|
||||
True if removal was successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# Make API request to remove member
|
||||
response = requests.delete(
|
||||
f"{self.openwebui_url}/api/channels/{channel_id}/members/{user_id}",
|
||||
headers=self.headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
return response.status_code == 200 or response.status_code == 204
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error removing member from channel: {str(e)}")
|
||||
return False
|
||||
|
||||
def delete_channel(self, channel_id: str) -> bool:
|
||||
"""
|
||||
Delete a channel.
|
||||
|
||||
Args:
|
||||
channel_id: ID of the channel.
|
||||
|
||||
Returns:
|
||||
True if deletion was successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# Make API request to delete channel
|
||||
response = requests.delete(
|
||||
f"{self.openwebui_url}/api/channels/{channel_id}",
|
||||
headers=self.headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
return response.status_code == 200 or response.status_code == 204
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error deleting channel: {str(e)}")
|
||||
return False
|
||||
|
||||
def register_webhook(self, webhook_url: str) -> bool:
|
||||
"""
|
||||
Register a webhook to receive channel messages.
|
||||
|
||||
Args:
|
||||
webhook_url: URL of the webhook endpoint.
|
||||
|
||||
Returns:
|
||||
True if registration was successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# Prepare webhook data
|
||||
webhook_data = {
|
||||
"url": webhook_url,
|
||||
"events": ["channel_message"]
|
||||
}
|
||||
|
||||
# Make API request to register webhook
|
||||
response = requests.post(
|
||||
f"{self.openwebui_url}/api/webhooks",
|
||||
headers=self.headers,
|
||||
json=webhook_data,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code == 200 or response.status_code == 201:
|
||||
print(f"Successfully registered webhook: {webhook_url}")
|
||||
return True
|
||||
else:
|
||||
print(f"Error registering webhook: {response.status_code} - {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error registering webhook: {str(e)}")
|
||||
return False
|
||||
|
||||
def send_message(self, channel_id: str, message: str, user_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Send a message to a channel.
|
||||
|
||||
Args:
|
||||
channel_id: ID of the channel.
|
||||
message: Message content.
|
||||
user_id: ID of the user sending the message.
|
||||
|
||||
Returns:
|
||||
Message data if sending was successful, None otherwise.
|
||||
"""
|
||||
try:
|
||||
# Prepare message data
|
||||
message_data = {
|
||||
"content": message,
|
||||
"user_id": user_id
|
||||
}
|
||||
|
||||
# Make API request to send message
|
||||
response = requests.post(
|
||||
f"{self.openwebui_url}/api/channels/{channel_id}/messages",
|
||||
headers=self.headers,
|
||||
json=message_data,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code == 200 or response.status_code == 201:
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Error sending message to channel: {response.status_code} - {response.text}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error sending message to channel: {str(e)}")
|
||||
return None
|
||||
|
||||
# Create a singleton instance
|
||||
openwebui_channels = OpenWebUIChannels()
|
||||
Reference in New Issue
Block a user