Integrate OpenWebUI bot with AI service
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
# API configuration
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=5252
|
||||
PUBLIC_URL=http://your-public-url:5252 # Public URL for webhooks, needed for OpenWebUI to send channel messages
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://104.225.217.215:8080
|
||||
@@ -15,6 +14,12 @@ DEFAULT_MODEL=llama3.1
|
||||
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
|
||||
# Bot configuration
|
||||
BOT_ENABLED=true # Set to 'true' to enable the OpenWebUI bot, 'false' to disable it
|
||||
BOT_SYSTEM_PROMPT="You are a helpful AI assistant." # System prompt for the bot
|
||||
BOT_TEMPERATURE=0.7 # Temperature for response generation (0.0 to 1.0)
|
||||
BOT_MAX_TOKENS=2048 # Maximum number of tokens to generate
|
||||
BOT_TOP_P=0.9 # Top-p sampling parameter (0.0 to 1.0)
|
||||
BOT_TRIGGERS=@ai,@bot,@assistant,@chatbot # Comma-separated list of triggers that will make the bot respond
|
||||
BOT_RESPOND_TO_ALL=false # Set to 'true' to make the bot respond to all messages, 'false' to only respond to mentions
|
||||
|
||||
|
||||
+150
-367
@@ -13,13 +13,14 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
import asyncio
|
||||
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
|
||||
from ai_service import bot_manager
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(
|
||||
@@ -43,26 +44,64 @@ 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
|
||||
# API startup event
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""
|
||||
Register webhook for channel messages on startup.
|
||||
Startup event for the API.
|
||||
"""
|
||||
# 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
|
||||
print("=" * 50)
|
||||
print(f"Starting AI Service API on {config.API_HOST}:{config.API_PORT}")
|
||||
print(f"OpenWebUI URL: {config.OPENWEBUI_URL}")
|
||||
print(f"Default model: {config.DEFAULT_MODEL}")
|
||||
|
||||
# Register webhook
|
||||
webhook_url = f"{service_url}/webhooks/channel-message"
|
||||
print(f"Registering webhook for channel messages: {webhook_url}")
|
||||
# Start the OpenWebUI bot if enabled
|
||||
if config.BOT_ENABLED:
|
||||
print("=" * 50)
|
||||
print("Starting OpenWebUI bot...")
|
||||
|
||||
success = openwebui_channels.register_webhook(webhook_url)
|
||||
if success:
|
||||
print("Successfully registered webhook for channel messages")
|
||||
# Start the bot with configuration
|
||||
success = await bot_manager.start_bot(
|
||||
openwebui_url=config.OPENWEBUI_URL,
|
||||
api_key=config.OPENWEBUI_API_KEY,
|
||||
model_id=config.DEFAULT_MODEL,
|
||||
system_prompt=config.BOT_SYSTEM_PROMPT,
|
||||
temperature=config.BOT_TEMPERATURE,
|
||||
max_tokens=config.BOT_MAX_TOKENS,
|
||||
top_p=config.BOT_TOP_P,
|
||||
triggers=config.BOT_TRIGGERS,
|
||||
respond_to_all=config.BOT_RESPOND_TO_ALL
|
||||
)
|
||||
|
||||
if success:
|
||||
print("Bot started successfully!")
|
||||
else:
|
||||
print("Failed to start bot. Check the logs for details.")
|
||||
print("=" * 50)
|
||||
else:
|
||||
print("Failed to register webhook for channel messages")
|
||||
print("OpenWebUI bot is disabled. Set BOT_ENABLED=true in .env to enable it.")
|
||||
|
||||
print("=" * 50)
|
||||
|
||||
# API shutdown event
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
"""
|
||||
Shutdown event for the API.
|
||||
"""
|
||||
print("=" * 50)
|
||||
print("Shutting down AI Service API...")
|
||||
|
||||
# Stop the OpenWebUI bot if it's running
|
||||
if bot_manager.is_bot_running():
|
||||
print("Stopping OpenWebUI bot...")
|
||||
success = await bot_manager.stop_bot()
|
||||
if success:
|
||||
print("Bot stopped successfully!")
|
||||
else:
|
||||
print("Failed to stop bot. Check the logs for details.")
|
||||
|
||||
print("=" * 50)
|
||||
|
||||
# Define API models for health check
|
||||
class HealthResponse(BaseModel):
|
||||
@@ -137,172 +176,6 @@ async def health_check():
|
||||
"""
|
||||
return {"status": "healthy"}
|
||||
|
||||
@app.get("/test-ollama")
|
||||
async def test_ollama_connection():
|
||||
"""
|
||||
Test the connection to the Ollama API.
|
||||
|
||||
Returns:
|
||||
Connection status and available models from Ollama.
|
||||
"""
|
||||
import requests
|
||||
|
||||
try:
|
||||
# Try to connect to Ollama API
|
||||
response = requests.get(f"{config.OLLAMA_API_URL}/api/tags", timeout=config.API_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
|
||||
# Return the models from Ollama
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Successfully connected to Ollama API",
|
||||
"ollama_url": config.OLLAMA_API_URL,
|
||||
"models": response.json()
|
||||
}
|
||||
except requests.exceptions.Timeout as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Timeout connecting to Ollama API: {str(e)}. The request exceeded the {config.API_TIMEOUT} second timeout.",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Connection error to Ollama API: {str(e)}. Please check if Ollama is running at {config.OLLAMA_API_URL}.",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Failed to connect to Ollama API: {str(e)}",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
|
||||
@app.post("/test-chat")
|
||||
async def test_chat_completion():
|
||||
"""
|
||||
Test the chat completion with a simple prompt.
|
||||
|
||||
Returns:
|
||||
Model response.
|
||||
"""
|
||||
try:
|
||||
# Use the model service directly
|
||||
response = model_service.generate_response(
|
||||
model_id=config.DEFAULT_MODEL,
|
||||
prompt="Hello, how are you?",
|
||||
context=[],
|
||||
use_rag=False
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"model": config.DEFAULT_MODEL,
|
||||
"response": response,
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Failed to get chat completion: {str(e)}",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
|
||||
@app.post("/test-rag")
|
||||
async def test_rag_completion(query: str = "What information do you have in your knowledge database?"):
|
||||
"""
|
||||
Test the RAG (Retrieval Augmented Generation) functionality with a query.
|
||||
|
||||
This endpoint tests the integration with OpenWebUI's knowledge database.
|
||||
|
||||
Args:
|
||||
query: The question to ask about documents in the knowledge database.
|
||||
|
||||
Returns:
|
||||
Model response using RAG.
|
||||
"""
|
||||
try:
|
||||
# Use the model service directly with RAG enabled
|
||||
response = model_service.generate_response(
|
||||
model_id=config.DEFAULT_MODEL,
|
||||
prompt=query,
|
||||
context=[],
|
||||
use_rag=True # Enable RAG
|
||||
)
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"model": config.DEFAULT_MODEL,
|
||||
"query": query,
|
||||
"use_rag": True,
|
||||
"response": response,
|
||||
"openwebui_url": config.OPENWEBUI_URL
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Failed to get RAG completion: {str(e)}",
|
||||
"openwebui_url": config.OPENWEBUI_URL
|
||||
}
|
||||
|
||||
@app.post("/test-ollama-direct")
|
||||
async def test_ollama_direct():
|
||||
"""
|
||||
Test the Ollama API directly with a simple chat request.
|
||||
|
||||
Returns:
|
||||
Raw Ollama API response.
|
||||
"""
|
||||
import requests
|
||||
|
||||
try:
|
||||
# Prepare a simple chat request
|
||||
request_json = {
|
||||
"model": config.DEFAULT_MODEL,
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hello, how are you?"}
|
||||
],
|
||||
"stream": False
|
||||
}
|
||||
|
||||
# Make the API call to Ollama
|
||||
print(f"Sending direct request to Ollama API at: {config.OLLAMA_API_URL}/api/chat")
|
||||
response = requests.post(
|
||||
f"{config.OLLAMA_API_URL}/api/chat",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json=request_json,
|
||||
timeout=config.API_TIMEOUT
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"ollama_url": config.OLLAMA_API_URL,
|
||||
"request": request_json,
|
||||
"response": result
|
||||
}
|
||||
except requests.exceptions.Timeout as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Timeout connecting to Ollama API: {str(e)}. The request exceeded the {config.API_TIMEOUT} second timeout.",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Connection error to Ollama API: {str(e)}. Please check if Ollama is running at {config.OLLAMA_API_URL}.",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Failed to connect to Ollama API: {str(e)}",
|
||||
"ollama_url": config.OLLAMA_API_URL
|
||||
}
|
||||
|
||||
@app.get("/config")
|
||||
async def get_config():
|
||||
"""
|
||||
@@ -318,38 +191,114 @@ async def get_config():
|
||||
"ollama_api_url": config.OLLAMA_API_URL,
|
||||
"default_model": config.DEFAULT_MODEL,
|
||||
"api_timeout": config.API_TIMEOUT,
|
||||
"bot": {
|
||||
"enabled": config.BOT_ENABLED,
|
||||
"running": bot_manager.is_bot_running(),
|
||||
"model_id": config.DEFAULT_MODEL,
|
||||
"system_prompt": config.BOT_SYSTEM_PROMPT[:50] + "..." if len(config.BOT_SYSTEM_PROMPT) > 50 else config.BOT_SYSTEM_PROMPT,
|
||||
"temperature": config.BOT_TEMPERATURE,
|
||||
"max_tokens": config.BOT_MAX_TOKENS,
|
||||
"top_p": config.BOT_TOP_P,
|
||||
"triggers": config.BOT_TRIGGERS,
|
||||
"respond_to_all": config.BOT_RESPOND_TO_ALL
|
||||
},
|
||||
"available_models": list(model_service.AVAILABLE_MODELS.keys())
|
||||
}
|
||||
|
||||
@app.get("/test-webhook")
|
||||
async def test_webhook():
|
||||
@app.get("/bot/status")
|
||||
async def get_bot_status():
|
||||
"""
|
||||
Test the webhook registration.
|
||||
Get the status and configuration of the OpenWebUI bot.
|
||||
|
||||
Returns:
|
||||
Webhook registration status.
|
||||
Bot status and configuration.
|
||||
"""
|
||||
# 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
|
||||
|
||||
# Webhook URL
|
||||
webhook_url = f"{service_url}/webhooks/channel-message"
|
||||
|
||||
# Try to register the webhook again
|
||||
success = openwebui_channels.register_webhook(webhook_url)
|
||||
|
||||
# Get all registered webhooks
|
||||
webhooks = openwebui_channels.get_webhooks()
|
||||
|
||||
return {
|
||||
"webhook_url": webhook_url,
|
||||
"registration_success": success,
|
||||
"registered_webhooks": webhooks
|
||||
"enabled": config.BOT_ENABLED,
|
||||
"running": bot_manager.is_bot_running(),
|
||||
"config": {
|
||||
"model_id": config.DEFAULT_MODEL,
|
||||
"system_prompt": config.BOT_SYSTEM_PROMPT[:50] + "..." if len(config.BOT_SYSTEM_PROMPT) > 50 else config.BOT_SYSTEM_PROMPT,
|
||||
"temperature": config.BOT_TEMPERATURE,
|
||||
"max_tokens": config.BOT_MAX_TOKENS,
|
||||
"top_p": config.BOT_TOP_P,
|
||||
"triggers": config.BOT_TRIGGERS,
|
||||
"respond_to_all": config.BOT_RESPOND_TO_ALL
|
||||
}
|
||||
}
|
||||
|
||||
@app.post("/bot/start")
|
||||
async def start_bot(
|
||||
model_id: str = None,
|
||||
system_prompt: str = None,
|
||||
temperature: float = None,
|
||||
max_tokens: int = None,
|
||||
top_p: float = None,
|
||||
respond_to_all: bool = None
|
||||
):
|
||||
"""
|
||||
Start the OpenWebUI bot with optional configuration.
|
||||
|
||||
Args:
|
||||
model_id: ID of the model to use (default: config.DEFAULT_MODEL)
|
||||
system_prompt: System prompt for the bot (default: config.BOT_SYSTEM_PROMPT)
|
||||
temperature: Temperature for response generation (default: config.BOT_TEMPERATURE)
|
||||
max_tokens: Maximum number of tokens to generate (default: config.BOT_MAX_TOKENS)
|
||||
top_p: Top-p sampling parameter (default: config.BOT_TOP_P)
|
||||
respond_to_all: Whether to respond to all messages (default: config.BOT_RESPOND_TO_ALL)
|
||||
|
||||
Returns:
|
||||
Start status.
|
||||
"""
|
||||
if bot_manager.is_bot_running():
|
||||
return {"status": "already_running", "message": "Bot is already running"}
|
||||
|
||||
# Use provided values or defaults from config
|
||||
success = await bot_manager.start_bot(
|
||||
openwebui_url=config.OPENWEBUI_URL,
|
||||
api_key=config.OPENWEBUI_API_KEY,
|
||||
model_id=model_id or config.DEFAULT_MODEL,
|
||||
system_prompt=system_prompt or config.BOT_SYSTEM_PROMPT,
|
||||
temperature=temperature if temperature is not None else config.BOT_TEMPERATURE,
|
||||
max_tokens=max_tokens if max_tokens is not None else config.BOT_MAX_TOKENS,
|
||||
top_p=top_p if top_p is not None else config.BOT_TOP_P,
|
||||
triggers=config.BOT_TRIGGERS,
|
||||
respond_to_all=respond_to_all if respond_to_all is not None else config.BOT_RESPOND_TO_ALL
|
||||
)
|
||||
|
||||
if success:
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Bot started successfully",
|
||||
"config": {
|
||||
"model_id": model_id or config.DEFAULT_MODEL,
|
||||
"system_prompt": (system_prompt or config.BOT_SYSTEM_PROMPT)[:50] + "..." if len(system_prompt or config.BOT_SYSTEM_PROMPT) > 50 else (system_prompt or config.BOT_SYSTEM_PROMPT),
|
||||
"temperature": temperature if temperature is not None else config.BOT_TEMPERATURE,
|
||||
"max_tokens": max_tokens if max_tokens is not None else config.BOT_MAX_TOKENS,
|
||||
"top_p": top_p if top_p is not None else config.BOT_TOP_P,
|
||||
"respond_to_all": respond_to_all if respond_to_all is not None else config.BOT_RESPOND_TO_ALL
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to start bot"}
|
||||
|
||||
@app.post("/bot/stop")
|
||||
async def stop_bot():
|
||||
"""
|
||||
Stop the OpenWebUI bot.
|
||||
|
||||
Returns:
|
||||
Stop status.
|
||||
"""
|
||||
if not bot_manager.is_bot_running():
|
||||
return {"status": "not_running", "message": "Bot is not running"}
|
||||
|
||||
success = await bot_manager.stop_bot()
|
||||
|
||||
if success:
|
||||
return {"status": "success", "message": "Bot stopped successfully"}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to stop bot"}
|
||||
|
||||
# Model endpoints
|
||||
@app.get("/models", response_model=List[ModelInfo])
|
||||
@@ -543,170 +492,4 @@ async def delete_chat(chat_id: str):
|
||||
|
||||
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("=" * 50)
|
||||
print("WEBHOOK RECEIVED")
|
||||
print(f"Channel ID: {request.channel_id}")
|
||||
print(f"User ID: {request.user_id}")
|
||||
print(f"Message: {request.message}")
|
||||
print(f"Timestamp: {request.timestamp}")
|
||||
print("=" * 50)
|
||||
|
||||
# Find the chat associated with this OpenWebUI channel
|
||||
print(f"Looking for chat with OpenWebUI channel ID: {request.channel_id}")
|
||||
print(f"Number of chats in system: {len(chat_service.chats)}")
|
||||
|
||||
# Debug: Print all chats and their channel IDs
|
||||
print("All chats in the system:")
|
||||
for cid, chat in chat_service.chats.items():
|
||||
is_team = chat.get('is_team_chat', False)
|
||||
channel_id = chat.get('openwebui_channel_id', 'None')
|
||||
print(f" Chat ID: {cid}, Is Team Chat: {is_team}, OpenWebUI Channel ID: {channel_id}")
|
||||
|
||||
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
|
||||
print(f"Found matching chat: {cid}")
|
||||
break
|
||||
|
||||
if not chat_id:
|
||||
print(f"No chat found for OpenWebUI channel {request.channel_id}")
|
||||
|
||||
# If no chat exists for this channel, create one
|
||||
print("Creating a new team chat for this channel...")
|
||||
try:
|
||||
new_chat_id = chat_service.create_chat(
|
||||
user_id=request.user_id,
|
||||
title=f"Channel Chat {request.channel_id}",
|
||||
model_id=config.DEFAULT_MODEL,
|
||||
is_team_chat=True
|
||||
)
|
||||
|
||||
# Manually set the OpenWebUI channel ID for this chat
|
||||
chat_service.chats[new_chat_id]['openwebui_channel_id'] = request.channel_id
|
||||
chat_service._save_chats()
|
||||
|
||||
print(f"Created new chat with ID {new_chat_id} for channel {request.channel_id}")
|
||||
|
||||
# Use this new chat
|
||||
chat_id = new_chat_id
|
||||
except Exception as e:
|
||||
print(f"Error creating new chat for channel: {str(e)}")
|
||||
return {"status": "error", "message": "No chat found for this channel and failed to create one"}
|
||||
|
||||
# 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)}"}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Bot manager for the AI service.
|
||||
|
||||
This module provides functionality to manage the OpenWebUI bot.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Add the openwebui-bot directory to the Python path
|
||||
# Use a path relative to the current file's directory to ensure it works in any environment
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
root_dir = os.path.dirname(current_dir)
|
||||
openwebui_bot_dir = os.path.join(root_dir, 'openwebui-bot')
|
||||
sys.path.insert(0, openwebui_bot_dir)
|
||||
|
||||
# Log the path for debugging
|
||||
logger.info(f"Adding OpenWebUI bot directory to Python path: {openwebui_bot_dir}")
|
||||
|
||||
# Import the bot modules
|
||||
try:
|
||||
from env import WEBUI_URL, TOKEN
|
||||
from examples.custom_ai import main as custom_bot_main
|
||||
except ImportError as e:
|
||||
print(f"Error importing OpenWebUI bot modules: {str(e)}")
|
||||
print("Make sure the openwebui-bot directory exists and contains the required files.")
|
||||
WEBUI_URL = None
|
||||
TOKEN = None
|
||||
custom_bot_main = None
|
||||
|
||||
# Global variable to store the bot task
|
||||
bot_task = None
|
||||
|
||||
async def start_bot(
|
||||
openwebui_url: str,
|
||||
api_key: str,
|
||||
model_id: str,
|
||||
system_prompt: str = None,
|
||||
temperature: float = None,
|
||||
max_tokens: int = None,
|
||||
top_p: float = None,
|
||||
triggers: list = None,
|
||||
respond_to_all: bool = None
|
||||
) -> bool:
|
||||
"""
|
||||
Start the OpenWebUI bot.
|
||||
|
||||
Args:
|
||||
openwebui_url: URL of the OpenWebUI instance.
|
||||
api_key: API key for authentication.
|
||||
model_id: ID of the model to use.
|
||||
system_prompt: System prompt for the bot.
|
||||
temperature: Temperature for response generation.
|
||||
max_tokens: Maximum number of tokens to generate.
|
||||
top_p: Top-p sampling parameter.
|
||||
triggers: List of trigger words that will make the bot respond.
|
||||
respond_to_all: Whether to respond to all messages or only to mentions.
|
||||
|
||||
Returns:
|
||||
True if the bot was started successfully, False otherwise.
|
||||
"""
|
||||
global bot_task
|
||||
|
||||
# Check if the bot is already running
|
||||
if bot_task is not None and not bot_task.done():
|
||||
logger.warning("Bot is already running!")
|
||||
return True
|
||||
|
||||
# Check if the bot modules were imported successfully
|
||||
if custom_bot_main is None:
|
||||
logger.error("OpenWebUI bot modules not found. Bot cannot be started.")
|
||||
return False
|
||||
|
||||
# Set environment variables for the bot
|
||||
os.environ["WEBUI_URL"] = openwebui_url
|
||||
os.environ["TOKEN"] = api_key
|
||||
os.environ["MODEL_ID"] = model_id
|
||||
|
||||
# Set optional parameters if provided
|
||||
if system_prompt:
|
||||
os.environ["SYSTEM_PROMPT"] = system_prompt
|
||||
if temperature is not None:
|
||||
os.environ["TEMPERATURE"] = str(temperature)
|
||||
if max_tokens is not None:
|
||||
os.environ["MAX_TOKENS"] = str(max_tokens)
|
||||
if top_p is not None:
|
||||
os.environ["TOP_P"] = str(top_p)
|
||||
if triggers:
|
||||
os.environ["TRIGGERS"] = ",".join(triggers)
|
||||
if respond_to_all is not None:
|
||||
os.environ["RESPOND_TO_ALL"] = str(respond_to_all).lower()
|
||||
|
||||
try:
|
||||
# Start the bot in a background task
|
||||
logger.info("Starting OpenWebUI bot...")
|
||||
logger.info(f"OpenWebUI URL: {openwebui_url}")
|
||||
logger.info(f"Model: {model_id}")
|
||||
|
||||
if system_prompt:
|
||||
logger.info(f"System prompt: {system_prompt[:50]}...")
|
||||
if temperature is not None:
|
||||
logger.info(f"Temperature: {temperature}")
|
||||
if max_tokens is not None:
|
||||
logger.info(f"Max tokens: {max_tokens}")
|
||||
if top_p is not None:
|
||||
logger.info(f"Top-p: {top_p}")
|
||||
if triggers:
|
||||
logger.info(f"Triggers: {triggers}")
|
||||
if respond_to_all is not None:
|
||||
logger.info(f"Respond to all: {respond_to_all}")
|
||||
|
||||
# Create a task for the bot
|
||||
bot_task = asyncio.create_task(custom_bot_main())
|
||||
|
||||
logger.info("Bot started successfully!")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error starting bot: {str(e)}")
|
||||
return False
|
||||
|
||||
async def stop_bot() -> bool:
|
||||
"""
|
||||
Stop the OpenWebUI bot.
|
||||
|
||||
Returns:
|
||||
True if the bot was stopped successfully, False otherwise.
|
||||
"""
|
||||
global bot_task
|
||||
|
||||
# Check if the bot is running
|
||||
if bot_task is None or bot_task.done():
|
||||
logger.warning("Bot is not running!")
|
||||
return True
|
||||
|
||||
try:
|
||||
# Cancel the bot task
|
||||
logger.info("Stopping OpenWebUI bot...")
|
||||
bot_task.cancel()
|
||||
|
||||
try:
|
||||
# Wait for the task to be cancelled
|
||||
await bot_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
logger.info("Bot stopped successfully!")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Error stopping bot: {str(e)}")
|
||||
return False
|
||||
|
||||
def is_bot_running() -> bool:
|
||||
"""
|
||||
Check if the bot is running.
|
||||
|
||||
Returns:
|
||||
True if the bot is running, False otherwise.
|
||||
"""
|
||||
global bot_task
|
||||
return bot_task is not None and not bot_task.done()
|
||||
@@ -34,9 +34,13 @@ 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'
|
||||
|
||||
# Bot configuration
|
||||
BOT_ENABLED = os.environ.get('BOT_ENABLED', 'true').lower() == 'true'
|
||||
BOT_SYSTEM_PROMPT = os.environ.get('BOT_SYSTEM_PROMPT', 'You are a helpful AI assistant.')
|
||||
BOT_TEMPERATURE = float(os.environ.get('BOT_TEMPERATURE', '0.7'))
|
||||
BOT_MAX_TOKENS = int(os.environ.get('BOT_MAX_TOKENS', '2048'))
|
||||
BOT_TOP_P = float(os.environ.get('BOT_TOP_P', '0.9'))
|
||||
BOT_TRIGGERS = os.environ.get('BOT_TRIGGERS', '@ai,@bot,@assistant,@chatbot').split(',')
|
||||
BOT_RESPOND_TO_ALL = os.environ.get('BOT_RESPOND_TO_ALL', 'false').lower() == 'true'
|
||||
|
||||
config = Config()
|
||||
|
||||
@@ -225,5 +225,194 @@
|
||||
],
|
||||
"team_members": [],
|
||||
"openwebui_channel_id": null
|
||||
},
|
||||
"0967f62c-c5b9-4d40-b64e-8758dc42b124": {
|
||||
"id": "0967f62c-c5b9-4d40-b64e-8758dc42b124",
|
||||
"title": "Mention Test Chat 2025-05-19 16:31:41",
|
||||
"user_id": "test_user",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true,
|
||||
"created_at": "2025-05-19T16:31:41.897128",
|
||||
"updated_at": "2025-05-19T16:31:42.669052",
|
||||
"messages": [
|
||||
{
|
||||
"id": "b6097881-e33e-4e52-897f-27c4f3ded33e",
|
||||
"content": "This is a message without a mention: Can you help me with a project?",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T16:31:42.077261"
|
||||
},
|
||||
{
|
||||
"id": "490c6646-f4c5-4f78-8982-2ad91977056f",
|
||||
"content": "Connection error to Ollama API: HTTPConnectionPool(host='104.225.217.215', port=11434): Max retries exceeded with url: /api/chat (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x10f679110>: Failed to establish a new connection: [Errno 61] Connection refused')). Please check if Ollama is running at http://104.225.217.215:11434.",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T16:31:42.346450"
|
||||
},
|
||||
{
|
||||
"id": "ade9f283-35b8-467a-bf45-98a327e30c99",
|
||||
"content": "@ai Can you help me with a project?",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T16:31:42.373840"
|
||||
},
|
||||
{
|
||||
"id": "8e971524-ad12-4237-b1d1-ea9142c89887",
|
||||
"content": "Connection error to Ollama API: HTTPConnectionPool(host='104.225.217.215', port=11434): Max retries exceeded with url: /api/chat (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x10f65fd50>: Failed to establish a new connection: [Errno 61] Connection refused')). Please check if Ollama is running at http://104.225.217.215:11434.",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T16:31:42.669036"
|
||||
}
|
||||
],
|
||||
"team_members": [
|
||||
"test_user",
|
||||
"test_user2"
|
||||
],
|
||||
"openwebui_channel_id": null
|
||||
},
|
||||
"5b6a2e66-035f-4810-b46f-9b035da6baa7": {
|
||||
"id": "5b6a2e66-035f-4810-b46f-9b035da6baa7",
|
||||
"title": "Mention Test Chat 2025-05-19 16:36:11",
|
||||
"user_id": "test_user",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true,
|
||||
"created_at": "2025-05-19T16:36:11.848997",
|
||||
"updated_at": "2025-05-19T16:36:12.626528",
|
||||
"messages": [
|
||||
{
|
||||
"id": "2f27e470-1406-4863-86ea-c6dc83d0a114",
|
||||
"content": "This is a message without a mention: Can you help me with a project?",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T16:36:11.983300"
|
||||
},
|
||||
{
|
||||
"id": "606353ef-079e-40b0-ade9-247aa07b487c",
|
||||
"content": "Connection error to Ollama API: HTTPConnectionPool(host='104.225.217.215', port=11434): Max retries exceeded with url: /api/chat (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x10f855890>: Failed to establish a new connection: [Errno 61] Connection refused')). Please check if Ollama is running at http://104.225.217.215:11434.",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T16:36:12.280914"
|
||||
},
|
||||
{
|
||||
"id": "4e2bdf6b-052c-4fa1-8c41-b180fcb6d9f3",
|
||||
"content": "@ai Can you help me with a project?",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T16:36:12.333088"
|
||||
},
|
||||
{
|
||||
"id": "4af0d91d-4b76-4ed1-9305-5ef999cfb3a6",
|
||||
"content": "Connection error to Ollama API: HTTPConnectionPool(host='104.225.217.215', port=11434): Max retries exceeded with url: /api/chat (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x10f857f10>: Failed to establish a new connection: [Errno 61] Connection refused')). Please check if Ollama is running at http://104.225.217.215:11434.",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T16:36:12.626493"
|
||||
}
|
||||
],
|
||||
"team_members": [
|
||||
"test_user",
|
||||
"test_user2"
|
||||
],
|
||||
"openwebui_channel_id": null
|
||||
},
|
||||
"7e3d4d9a-3b53-4169-bdb5-49853d2ee147": {
|
||||
"id": "7e3d4d9a-3b53-4169-bdb5-49853d2ee147",
|
||||
"title": "Webhook Test Chat 2025-05-19 17:31:02",
|
||||
"user_id": "test_user",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true,
|
||||
"created_at": "2025-05-19T17:31:02.808376",
|
||||
"updated_at": "2025-05-19T17:31:02.809151",
|
||||
"messages": [],
|
||||
"team_members": [
|
||||
"test_user"
|
||||
],
|
||||
"openwebui_channel_id": null
|
||||
},
|
||||
"db1531f0-23b8-44be-be0b-4468469ddfd1": {
|
||||
"id": "db1531f0-23b8-44be-be0b-4468469ddfd1",
|
||||
"title": "Channel mock-channel-123",
|
||||
"user_id": "test_user",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true,
|
||||
"created_at": "2025-05-19T17:31:05.523198",
|
||||
"updated_at": "2025-05-19T17:31:08.008697",
|
||||
"messages": [
|
||||
{
|
||||
"id": "f1a41e2c-67d5-4713-9690-31de7957e880",
|
||||
"content": "Can you help me with a project?",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T17:31:07.159077"
|
||||
},
|
||||
{
|
||||
"id": "97e3ab10-31f8-4c4f-af0f-828e7d9b1583",
|
||||
"content": "Connection error to Ollama API: HTTPConnectionPool(host='104.225.217.215', port=11434): Max retries exceeded with url: /api/chat (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x109592410>: Failed to establish a new connection: [Errno 61] Connection refused')). Please check if Ollama is running at http://104.225.217.215:11434.",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T17:31:08.008671"
|
||||
}
|
||||
],
|
||||
"team_members": [
|
||||
"test_user"
|
||||
],
|
||||
"openwebui_channel_id": "mock-channel-123"
|
||||
},
|
||||
"cadd55b8-93f9-469a-99ca-3569fddf2a89": {
|
||||
"id": "cadd55b8-93f9-469a-99ca-3569fddf2a89",
|
||||
"title": "Test Team Chat",
|
||||
"user_id": "test_user",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true,
|
||||
"created_at": "2025-05-19T18:05:04.943296",
|
||||
"updated_at": "2025-05-19T18:05:57.781112",
|
||||
"messages": [
|
||||
{
|
||||
"id": "38bf0784-ff2f-45ed-88b0-b7bbe4fe73da",
|
||||
"content": "Hello, AI!",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T18:05:18.975944"
|
||||
},
|
||||
{
|
||||
"id": "5b4b2c32-551a-413e-81f1-8d762f38309e",
|
||||
"content": "How can I assist you today? Do you have any questions or need help with something specific?",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T18:05:57.781076"
|
||||
}
|
||||
],
|
||||
"team_members": [
|
||||
"test_user"
|
||||
],
|
||||
"openwebui_channel_id": null
|
||||
},
|
||||
"a8ecdf7f-3d84-495b-85e3-846acffcfda6": {
|
||||
"id": "a8ecdf7f-3d84-495b-85e3-846acffcfda6",
|
||||
"title": "Channel test-channel-123",
|
||||
"user_id": "test_user",
|
||||
"model_id": "llama3.1",
|
||||
"is_team_chat": true,
|
||||
"created_at": "2025-05-19T18:06:19.115818",
|
||||
"updated_at": "2025-05-19T18:07:05.899175",
|
||||
"messages": [
|
||||
{
|
||||
"id": "6b2021b9-fea3-4b5b-b48e-f338dbf53338",
|
||||
"content": "Hello",
|
||||
"user_id": "test_user",
|
||||
"is_user_message": true,
|
||||
"timestamp": "2025-05-19T18:06:19.647395"
|
||||
},
|
||||
{
|
||||
"id": "6770337d-fc5d-4ecc-9a0d-e272a02bd31c",
|
||||
"content": "How can I assist you today? Do you have any questions or topics you'd like to discuss? I'm here to help with anything from answering general knowledge questions to providing guidance on a specific problem. Just let me know what's on your mind!",
|
||||
"user_id": null,
|
||||
"is_user_message": false,
|
||||
"timestamp": "2025-05-19T18:07:05.899147"
|
||||
}
|
||||
],
|
||||
"team_members": [
|
||||
"test_user"
|
||||
],
|
||||
"openwebui_channel_id": "test-channel-123"
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ 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."""
|
||||
@@ -65,34 +64,11 @@ class ChatService:
|
||||
# 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
|
||||
# For team chats
|
||||
if is_team_chat:
|
||||
try:
|
||||
print("=" * 50)
|
||||
print(f"Creating team chat with title: {chat_title}")
|
||||
print(f"OpenWebUI URL: {openwebui_channels.openwebui_url}")
|
||||
print(f"OpenWebUI API Key: {openwebui_channels.openwebui_api_key[:5]}..." if openwebui_channels.openwebui_api_key else "No API key")
|
||||
|
||||
# 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
|
||||
)
|
||||
|
||||
print(f"Channel response: {json.dumps(channel_response, indent=2) if channel_response else 'None'}")
|
||||
|
||||
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")
|
||||
print("=" * 50)
|
||||
except Exception as e:
|
||||
print("=" * 50)
|
||||
print(f"Error creating OpenWebUI channel: {str(e)}")
|
||||
print("=" * 50)
|
||||
print("=" * 50)
|
||||
print(f"Creating team chat with title: {chat_title}")
|
||||
print("=" * 50)
|
||||
|
||||
# Create chat data
|
||||
self.chats[chat_id] = {
|
||||
@@ -104,8 +80,7 @@ class ChatService:
|
||||
'created_at': datetime.now().isoformat(),
|
||||
'updated_at': datetime.now().isoformat(),
|
||||
'messages': [],
|
||||
'team_members': [user_id] if is_team_chat else [],
|
||||
'openwebui_channel_id': openwebui_channel_id
|
||||
'team_members': [user_id] if is_team_chat else []
|
||||
}
|
||||
|
||||
# Save chats to file
|
||||
@@ -147,30 +122,18 @@ class ChatService:
|
||||
# Update chat timestamp
|
||||
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
|
||||
# If this is a team chat, log the message
|
||||
if chat['is_team_chat']:
|
||||
# For AI responses, use a special AI user ID
|
||||
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}"
|
||||
# Format the message for logging
|
||||
if is_user_message:
|
||||
formatted_content = content
|
||||
else:
|
||||
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)}")
|
||||
print(f"Team chat message in {chat_id} from {sender_id}: {formatted_content[:100]}...")
|
||||
|
||||
# Save chats to file
|
||||
self._save_chats()
|
||||
@@ -234,19 +197,8 @@ 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)}")
|
||||
# Log the team member addition
|
||||
print(f"Adding user {user_id} to team chat {chat_id}")
|
||||
|
||||
# Add to local team members list
|
||||
if user_id not in chat['team_members']:
|
||||
@@ -274,19 +226,8 @@ 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)}")
|
||||
# Log the team member removal
|
||||
print(f"Removing user {user_id} from team chat {chat_id}")
|
||||
|
||||
# Remove from local team members list
|
||||
if user_id in chat['team_members']:
|
||||
@@ -310,18 +251,9 @@ class ChatService:
|
||||
|
||||
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)}")
|
||||
# Log the chat deletion
|
||||
if chat['is_team_chat']:
|
||||
print(f"Deleting team chat {chat_id}")
|
||||
|
||||
# Delete the chat from local storage
|
||||
del self.chats[chat_id]
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
"""
|
||||
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
|
||||
|
||||
def get_webhooks(self) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get all registered webhooks.
|
||||
|
||||
Returns:
|
||||
List of webhook data.
|
||||
"""
|
||||
try:
|
||||
# Make API request to get webhooks
|
||||
response = requests.get(
|
||||
f"{self.openwebui_url}/api/webhooks",
|
||||
headers=self.headers,
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Error getting webhooks: {response.status_code} - {response.text}")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting webhooks: {str(e)}")
|
||||
return []
|
||||
|
||||
# Create a singleton instance
|
||||
openwebui_channels = OpenWebUIChannels()
|
||||
Reference in New Issue
Block a user