Integrate OpenWebUI bot with AI service
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
# OpenWebUI Bot Integration
|
||||
|
||||
This project integrates the [Open WebUI bot](https://github.com/open-webui/bot) to provide AI responses in OpenWebUI channels.
|
||||
|
||||
## Overview
|
||||
|
||||
The bot connects to OpenWebUI via WebSocket and listens for messages in channels. When a message mentions the AI (using trigger words like `@ai`), the bot processes the message and sends a response back to the channel.
|
||||
|
||||
## Integration with AI Service
|
||||
|
||||
The bot is integrated with the AI service and starts automatically when the service starts (if `BOT_ENABLED=true` in the `.env` file). You don't need to run the bot separately unless you want to test it independently.
|
||||
|
||||
## Configuration
|
||||
|
||||
The bot is configured using the following environment variables in the `.env` file:
|
||||
|
||||
```
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://your-openwebui-url:8080
|
||||
OPENWEBUI_API_KEY=your-openwebui-api-key
|
||||
|
||||
# Model configuration
|
||||
DEFAULT_MODEL=llama3.1
|
||||
|
||||
# Bot configuration
|
||||
BOT_ENABLED=true # Set to 'true' to enable the 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
|
||||
```
|
||||
|
||||
These settings control how the bot behaves:
|
||||
|
||||
- `BOT_SYSTEM_PROMPT`: The system prompt that sets the bot's personality and behavior
|
||||
- `BOT_TEMPERATURE`: Controls the randomness of the responses (higher values = more random)
|
||||
- `BOT_MAX_TOKENS`: Maximum length of the generated responses
|
||||
- `BOT_TOP_P`: Controls the diversity of the responses (higher values = more diverse)
|
||||
- `BOT_TRIGGERS`: Words that will trigger the bot to respond (e.g., `@ai`)
|
||||
- `BOT_RESPOND_TO_ALL`: If true, the bot will respond to all messages, not just those with triggers
|
||||
|
||||
## Bot API Endpoints
|
||||
|
||||
The AI service provides the following endpoints to manage the bot:
|
||||
|
||||
- `GET /bot/status`: Get the status and configuration of the bot
|
||||
- `POST /bot/start`: Start the bot with optional configuration parameters:
|
||||
- `model_id`: ID of the model to use (e.g., "llama3.1")
|
||||
- `system_prompt`: System prompt for the bot
|
||||
- `temperature`: Temperature for response generation (0.0 to 1.0)
|
||||
- `max_tokens`: Maximum number of tokens to generate
|
||||
- `top_p`: Top-p sampling parameter (0.0 to 1.0)
|
||||
- `respond_to_all`: Whether to respond to all messages (true/false)
|
||||
- `POST /bot/stop`: Stop the bot if it's running
|
||||
|
||||
You can also check the bot status and configuration in the configuration endpoint:
|
||||
|
||||
- `GET /config`: Get the current configuration, including detailed bot status and settings
|
||||
|
||||
### Examples
|
||||
|
||||
Start the bot with default settings:
|
||||
```bash
|
||||
curl -X POST http://localhost:5252/bot/start
|
||||
```
|
||||
|
||||
Start the bot with custom settings:
|
||||
```bash
|
||||
curl -X POST http://localhost:5252/bot/start \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model_id": "gemma3",
|
||||
"system_prompt": "You are a friendly and helpful AI assistant.",
|
||||
"temperature": 0.8,
|
||||
"max_tokens": 1024,
|
||||
"top_p": 0.95,
|
||||
"respond_to_all": false
|
||||
}'
|
||||
```
|
||||
|
||||
Get the bot status:
|
||||
```bash
|
||||
curl http://localhost:5252/bot/status
|
||||
```
|
||||
|
||||
Stop the bot:
|
||||
```bash
|
||||
curl -X POST http://localhost:5252/bot/stop
|
||||
```
|
||||
|
||||
## Running the Bot Separately
|
||||
|
||||
If you want to run the bot separately from the AI service, you can use the provided scripts:
|
||||
|
||||
### 1. Using the Original AI Example
|
||||
|
||||
```bash
|
||||
./run_openwebui_bot.py
|
||||
```
|
||||
|
||||
This runs the original AI example from the Open WebUI bot repository.
|
||||
|
||||
### 2. Using Our Custom AI Bot
|
||||
|
||||
```bash
|
||||
./run_custom_bot.py
|
||||
```
|
||||
|
||||
This runs our custom AI bot implementation, which includes additional features like:
|
||||
|
||||
- Responding only to messages that mention the bot (using trigger words like `@ai`)
|
||||
- Adding a robot emoji (🤖) to responses
|
||||
- Better error handling
|
||||
- Longer timeout for API calls
|
||||
|
||||
## How It Works
|
||||
|
||||
The bot uses WebSockets to maintain a persistent connection to OpenWebUI and receive real-time events. When a message is received in a channel, the bot checks if it mentions the AI. If it does, the bot processes the message and sends a response back to the channel.
|
||||
|
||||
The bot uses the OpenAI-compatible API provided by OpenWebUI to generate responses to messages.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the bot is not responding to messages, check the following:
|
||||
|
||||
1. Make sure the OpenWebUI URL and API key are correct in the `.env` file
|
||||
2. Verify that the bot is connected to OpenWebUI by checking the logs
|
||||
3. Make sure you're using one of the trigger words in your messages (e.g., `@ai`)
|
||||
4. Check that the model ID is correct and available in your OpenWebUI instance
|
||||
|
||||
## Differences from Webhook Approach
|
||||
|
||||
This bot uses WebSockets to connect to OpenWebUI, which is different from the webhook approach used in the previous implementation. The main differences are:
|
||||
|
||||
1. **WebSockets**: The bot maintains a persistent connection to OpenWebUI and receives real-time events.
|
||||
2. **Webhooks**: The previous implementation used webhooks, where OpenWebUI sends HTTP requests to your service when messages are posted in channels.
|
||||
|
||||
The WebSocket approach is more efficient for real-time communication and doesn't require your service to be publicly accessible.
|
||||
@@ -1,160 +0,0 @@
|
||||
# Local Testing with Remote Server Resources
|
||||
|
||||
This guide explains how to set up local testing that connects to remote server resources for the chatbot project.
|
||||
|
||||
## Overview
|
||||
|
||||
These scripts allow you to:
|
||||
|
||||
1. Test the connection to the remote Ollama server
|
||||
2. Create SSH tunnels to access remote resources locally
|
||||
3. Run tests against the remote Ollama models from your local machine
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Initial Setup
|
||||
|
||||
Run the setup script to create a virtual environment and install dependencies:
|
||||
|
||||
```bash
|
||||
chmod +x setup_local_test_env.sh
|
||||
./setup_local_test_env.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Create a Python virtual environment
|
||||
- Install required dependencies
|
||||
- Create a `local_test.env` file from the template
|
||||
|
||||
### 2. Configure Environment Variables
|
||||
|
||||
Edit the `local_test.env` file with your server credentials:
|
||||
|
||||
```bash
|
||||
# Remote server configuration
|
||||
SERVER_IP=104.225.217.215
|
||||
SERVER_PORT=22
|
||||
SERVER_USER=root
|
||||
SERVER_PASSWORD=your_password_here # Add your actual password
|
||||
|
||||
# Ollama configuration
|
||||
OLLAMA_API_URL=http://104.225.217.215:11434
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://104.225.217.215:8080
|
||||
OPENWEBUI_API_KEY=your_openwebui_api_key_here
|
||||
```
|
||||
|
||||
### 3. Create SSH Tunnels (Optional)
|
||||
|
||||
If you want to access the remote resources through localhost (recommended for security and to avoid firewall issues), create SSH tunnels:
|
||||
|
||||
```bash
|
||||
chmod +x create_ssh_tunnel.sh
|
||||
./create_ssh_tunnel.sh
|
||||
```
|
||||
|
||||
This will create SSH tunnels for:
|
||||
- Ollama API (localhost:11434 → remote:11434)
|
||||
- OpenWebUI (localhost:8080 → remote:8080)
|
||||
|
||||
If you use SSH tunnels, update your `local_test.env` file to use localhost URLs:
|
||||
|
||||
```bash
|
||||
# Ollama configuration
|
||||
OLLAMA_API_URL=http://localhost:11434
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://localhost:8080
|
||||
```
|
||||
|
||||
### 4. Run Tests
|
||||
|
||||
Activate the virtual environment and run the test script:
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python test_remote_ollama.py
|
||||
```
|
||||
|
||||
#### Test Options
|
||||
|
||||
You can customize the test with command-line arguments:
|
||||
|
||||
```bash
|
||||
# Test with a specific model
|
||||
python test_remote_ollama.py --model llama3.3
|
||||
|
||||
# Test with a custom prompt
|
||||
python test_remote_ollama.py --prompt "Explain quantum computing"
|
||||
|
||||
# Test with a different Ollama URL
|
||||
python test_remote_ollama.py --ollama-url http://your-server-ip:11434
|
||||
|
||||
# Test with a different timeout
|
||||
python test_remote_ollama.py --timeout 600
|
||||
```
|
||||
|
||||
### 5. Stop SSH Tunnels
|
||||
|
||||
When you're done testing, stop the SSH tunnels:
|
||||
|
||||
```bash
|
||||
chmod +x stop_ssh_tunnels.sh
|
||||
./stop_ssh_tunnels.sh
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
If you can't connect to the remote server:
|
||||
|
||||
1. Check if the server is reachable:
|
||||
```bash
|
||||
ping 104.225.217.215
|
||||
```
|
||||
|
||||
2. Verify that Ollama is running on the server:
|
||||
```bash
|
||||
ssh root@104.225.217.215 "curl http://localhost:11434/api/tags"
|
||||
```
|
||||
|
||||
3. Check if there are firewall rules blocking the connection:
|
||||
```bash
|
||||
ssh root@104.225.217.215 "iptables -L"
|
||||
```
|
||||
|
||||
### SSH Tunnel Issues
|
||||
|
||||
If the SSH tunnels aren't working:
|
||||
|
||||
1. Make sure you have SSH access to the server:
|
||||
```bash
|
||||
ssh root@104.225.217.215
|
||||
```
|
||||
|
||||
2. Check if the ports are already in use:
|
||||
```bash
|
||||
netstat -tuln | grep 11434
|
||||
netstat -tuln | grep 8080
|
||||
```
|
||||
|
||||
3. Try creating the tunnels manually:
|
||||
```bash
|
||||
ssh -N -L 11434:localhost:11434 root@104.225.217.215
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Testing with OpenWebUI's RAG Capabilities
|
||||
|
||||
To test document-based question answering using OpenWebUI's knowledge database:
|
||||
|
||||
```bash
|
||||
python test_remote_ollama.py --prompt "What information do you have about our project?" --model llama3.1
|
||||
```
|
||||
|
||||
### Integrating with Your Local Development
|
||||
|
||||
You can use these scripts as a foundation for local development that connects to the remote resources. This allows you to develop and test locally while using the remote server's models and data.
|
||||
@@ -8,7 +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
|
||||
- Team chat functionality
|
||||
|
||||
## Technology Stack
|
||||
|
||||
@@ -26,11 +26,13 @@ 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
|
||||
openwebui-bot/ # OpenWebUI bot integration
|
||||
run_ai_service.py # Script to run the service
|
||||
run_openwebui_bot.py # Script to run the OpenWebUI bot
|
||||
run_custom_bot.py # Script to run a custom OpenWebUI bot
|
||||
deploy_ai_service.sh # Local deployment script
|
||||
remote_deploy.sh # Remote deployment script
|
||||
```
|
||||
@@ -91,6 +93,34 @@ To configure OpenWebUI to use this service as its backend:
|
||||
|
||||
2. Restart OpenWebUI to apply the changes.
|
||||
|
||||
## Team Chat Feature
|
||||
## OpenWebUI Bot Integration
|
||||
|
||||
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.
|
||||
This project includes integration with the [Open WebUI bot](https://github.com/open-webui/bot) to provide AI responses in OpenWebUI channels. See [BOT_README.md](BOT_README.md) for detailed instructions on how to use the bot.
|
||||
|
||||
### Bot Integration
|
||||
|
||||
The bot is integrated with the AI service and starts automatically when the service starts (if `BOT_ENABLED=true` in the `.env` file). The bot connects to OpenWebUI via WebSocket and listens for messages in channels. When a message mentions the AI (using trigger words like `@ai`), the bot processes the message and sends a response back to the channel.
|
||||
|
||||
### Bot API Endpoints
|
||||
|
||||
The AI service provides the following endpoints to manage the bot:
|
||||
|
||||
- `GET /bot/status`: Get the status of the bot (enabled and running)
|
||||
- `POST /bot/start`: Start the bot if it's not already running
|
||||
- `POST /bot/stop`: Stop the bot if it's running
|
||||
|
||||
You can also check the bot status in the configuration endpoint:
|
||||
|
||||
- `GET /config`: Get the current configuration, including bot status
|
||||
|
||||
### Running the Bot Separately
|
||||
|
||||
If you want to run the bot separately from the AI service, you can use the provided scripts:
|
||||
|
||||
```bash
|
||||
# Run the original AI example
|
||||
./run_openwebui_bot.py
|
||||
|
||||
# Or run our custom AI bot implementation
|
||||
./run_custom_bot.py
|
||||
```
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# Document-Based Question Answering with OpenWebUI
|
||||
|
||||
This document explains how to use the document-based question answering (RAG) functionality in the AI service.
|
||||
|
||||
## Overview
|
||||
|
||||
The AI service now supports Retrieval Augmented Generation (RAG) by leveraging OpenWebUI's built-in knowledge database. This allows users to:
|
||||
|
||||
1. Upload documents to OpenWebUI
|
||||
2. Ask questions about those documents
|
||||
3. Receive responses that incorporate information from the documents
|
||||
|
||||
## How It Works
|
||||
|
||||
When RAG is enabled:
|
||||
|
||||
1. The AI service forwards the request to OpenWebUI with `use_knowledge=True`
|
||||
2. OpenWebUI searches its knowledge database for relevant information
|
||||
3. The retrieved information is used to augment the model's response
|
||||
4. The response is returned to the user
|
||||
|
||||
## Using RAG in API Requests
|
||||
|
||||
To enable RAG in your API requests, set the `use_rag` parameter to `true`:
|
||||
|
||||
```json
|
||||
POST /chats/{chat_id}/messages
|
||||
{
|
||||
"message": "What information do you have about project X?",
|
||||
"user_id": "user123",
|
||||
"use_rag": true
|
||||
}
|
||||
```
|
||||
|
||||
## Testing RAG Functionality
|
||||
|
||||
You can test the RAG functionality using the `/test-rag` endpoint:
|
||||
|
||||
```
|
||||
POST /test-rag?query=What information do you have about project X?
|
||||
```
|
||||
|
||||
This will return a response that includes information from documents in OpenWebUI's knowledge database.
|
||||
|
||||
## Uploading Documents to OpenWebUI
|
||||
|
||||
To use RAG effectively, you need to upload documents to OpenWebUI:
|
||||
|
||||
1. Log in to OpenWebUI at your configured URL (default: http://104.225.217.215:8080)
|
||||
2. Navigate to the Knowledge section
|
||||
3. Upload your documents (PDF, TXT, DOCX, etc.)
|
||||
4. OpenWebUI will automatically process and index the documents
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If RAG is not working as expected:
|
||||
|
||||
1. Ensure OpenWebUI is running and accessible
|
||||
2. Check that documents are properly uploaded and indexed in OpenWebUI
|
||||
3. Verify that the `use_rag` parameter is set to `true` in your requests
|
||||
4. Check the logs for any errors related to OpenWebUI API calls
|
||||
|
||||
If there are connection issues with OpenWebUI, the AI service will automatically fall back to using the direct Ollama API without RAG.
|
||||
|
||||
## Configuration
|
||||
|
||||
The following configuration settings affect RAG functionality:
|
||||
|
||||
- `OPENWEBUI_URL`: URL of your OpenWebUI instance
|
||||
- `OPENWEBUI_API_KEY`: API key for OpenWebUI (if required)
|
||||
- `API_TIMEOUT`: Timeout for API requests (in seconds)
|
||||
|
||||
These can be set in your environment variables or in the `.env` file.
|
||||
@@ -1,153 +0,0 @@
|
||||
# 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,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()
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to create an SSH tunnel to the remote server
|
||||
|
||||
# Load environment variables from local_test.env
|
||||
if [ -f "local_test.env" ]; then
|
||||
source local_test.env
|
||||
else
|
||||
echo "Error: local_test.env file not found. Please run setup_local_test_env.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if required variables are set
|
||||
if [ -z "$SERVER_IP" ] || [ -z "$SERVER_PORT" ] || [ -z "$SERVER_USER" ]; then
|
||||
echo "Error: SERVER_IP, SERVER_PORT, or SERVER_USER not set in local_test.env."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if password is set
|
||||
if [ -z "$SERVER_PASSWORD" ]; then
|
||||
echo "Warning: SERVER_PASSWORD not set in local_test.env. You will be prompted for the password."
|
||||
fi
|
||||
|
||||
# Function to create SSH tunnel
|
||||
create_tunnel() {
|
||||
local local_port=$1
|
||||
local remote_port=$2
|
||||
|
||||
echo "Creating SSH tunnel from localhost:$local_port to $SERVER_IP:$remote_port..."
|
||||
|
||||
# Check if the tunnel is already established
|
||||
if netstat -tuln | grep -q ":$local_port "; then
|
||||
echo "Port $local_port is already in use. Tunnel may already be established."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create the SSH tunnel
|
||||
if [ -n "$SERVER_PASSWORD" ]; then
|
||||
# Use sshpass if available
|
||||
if command -v sshpass &> /dev/null; then
|
||||
sshpass -p "$SERVER_PASSWORD" ssh -N -L $local_port:localhost:$remote_port -p $SERVER_PORT $SERVER_USER@$SERVER_IP &
|
||||
else
|
||||
echo "sshpass not installed. You will be prompted for the password."
|
||||
ssh -N -L $local_port:localhost:$remote_port -p $SERVER_PORT $SERVER_USER@$SERVER_IP &
|
||||
fi
|
||||
else
|
||||
ssh -N -L $local_port:localhost:$remote_port -p $SERVER_PORT $SERVER_USER@$SERVER_IP &
|
||||
fi
|
||||
|
||||
# Check if the tunnel was established
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "SSH tunnel established. Local port $local_port is now forwarded to $SERVER_IP:$remote_port."
|
||||
echo "Tunnel process ID: $!"
|
||||
echo $! >> tunnel_pids.txt
|
||||
else
|
||||
echo "Failed to establish SSH tunnel."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Create tunnels for Ollama and OpenWebUI
|
||||
echo "Setting up SSH tunnels to remote server at $SERVER_IP..."
|
||||
|
||||
# Create a file to store tunnel process IDs
|
||||
touch tunnel_pids.txt
|
||||
|
||||
# Create tunnel for Ollama (port 11434)
|
||||
create_tunnel 11434 11434
|
||||
|
||||
# Create tunnel for OpenWebUI (port 8080)
|
||||
create_tunnel 8080 8080
|
||||
|
||||
echo "SSH tunnels established. You can now access:"
|
||||
echo "- Ollama API at http://localhost:11434"
|
||||
echo "- OpenWebUI at http://localhost:8080"
|
||||
echo ""
|
||||
echo "To stop the tunnels, run: ./stop_ssh_tunnels.sh"
|
||||
+2
-53
@@ -34,61 +34,10 @@ if pgrep -f "run_ai_service.py" > /dev/null; then
|
||||
echo "Check ai_service.log for output"
|
||||
echo "To stop the service, run: pkill -f \"run_ai_service.py\""
|
||||
|
||||
# Test the health endpoint
|
||||
echo -e "\nTesting health endpoint..."
|
||||
# Check the health endpoint
|
||||
echo -e "\nChecking health endpoint..."
|
||||
curl -s http://localhost:5252/health
|
||||
echo -e "\n"
|
||||
|
||||
# Test creating a chat and sending a message
|
||||
echo "Testing chat creation and message sending..."
|
||||
if [ -f "$PYTHON_PATH" ]; then
|
||||
# Create a simple test script
|
||||
cat > test_api.py << 'EOF'
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Create a chat
|
||||
response = requests.post(
|
||||
"http://localhost:5252/chats",
|
||||
json={
|
||||
"user_id": "test_user",
|
||||
"title": "Test Chat",
|
||||
"model_id": "llama3.1"
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
chat_id = response.json()["id"]
|
||||
print(f"Chat created with ID: {chat_id}")
|
||||
|
||||
# Send a message with parameters
|
||||
response = requests.post(
|
||||
f"http://localhost:5252/chats/{chat_id}/messages",
|
||||
json={
|
||||
"message": "Hello, AI!",
|
||||
"user_id": "test_user",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 100
|
||||
}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
print("Message sent successfully")
|
||||
print(f"Response: {response.json()['content'][:100]}...")
|
||||
else:
|
||||
print(f"Error sending message: {response.status_code}")
|
||||
print(response.text)
|
||||
else:
|
||||
print(f"Error creating chat: {response.status_code}")
|
||||
print(response.text)
|
||||
EOF
|
||||
|
||||
# Run the test script with the virtual environment's Python
|
||||
$PYTHON_PATH test_api.py
|
||||
rm test_api.py
|
||||
else
|
||||
echo "Skipping API test as virtual environment Python is not available"
|
||||
fi
|
||||
else
|
||||
echo "Failed to start AI Service. Check ai_service.log for errors."
|
||||
exit 1
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Remote server configuration
|
||||
SERVER_IP=104.225.217.215
|
||||
SERVER_PORT=22
|
||||
SERVER_USER=root
|
||||
SERVER_PASSWORD=S5qqENZNqc
|
||||
|
||||
# Ollama configuration
|
||||
OLLAMA_API_URL=http://104.225.217.215:8080/ollama
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://104.225.217.215:8080
|
||||
OPENWEBUI_API_KEY=your_openwebui_api_key_here
|
||||
|
||||
# Model configuration
|
||||
DEFAULT_MODEL=llama3.1
|
||||
|
||||
# API timeout (in seconds)
|
||||
API_TIMEOUT=300
|
||||
@@ -1,18 +0,0 @@
|
||||
# Remote server configuration
|
||||
SERVER_IP=104.225.217.215
|
||||
SERVER_PORT=22
|
||||
SERVER_USER=root
|
||||
SERVER_PASSWORD=your_password_here
|
||||
|
||||
# Ollama configuration
|
||||
OLLAMA_API_URL=http://104.225.217.215:11434
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL=http://104.225.217.215:8080
|
||||
OPENWEBUI_API_KEY=your_openwebui_api_key_here
|
||||
|
||||
# Model configuration
|
||||
DEFAULT_MODEL=llama3.1
|
||||
|
||||
# API timeout (in seconds)
|
||||
API_TIMEOUT=300
|
||||
@@ -1,35 +0,0 @@
|
||||
"""
|
||||
Configuration for local testing with remote server resources.
|
||||
"""
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Try to load environment variables from .env file
|
||||
dotenv_path = os.path.join(os.path.dirname(__file__), 'local_test.env')
|
||||
load_dotenv(dotenv_path=dotenv_path)
|
||||
|
||||
# Configuration class
|
||||
class LocalTestConfig:
|
||||
"""Configuration for local testing with remote server resources."""
|
||||
|
||||
# Remote server configuration
|
||||
SERVER_IP = os.environ.get('SERVER_IP', '104.225.217.215')
|
||||
SERVER_PORT = os.environ.get('SERVER_PORT', '22')
|
||||
SERVER_USER = os.environ.get('SERVER_USER', 'root')
|
||||
|
||||
# Ollama configuration
|
||||
OLLAMA_API_URL = os.environ.get('OLLAMA_API_URL', f'http://{SERVER_IP}:11434')
|
||||
|
||||
# OpenWebUI configuration
|
||||
OPENWEBUI_URL = os.environ.get('OPENWEBUI_URL', f'http://{SERVER_IP}:8080')
|
||||
OPENWEBUI_API_KEY = os.environ.get('OPENWEBUI_API_KEY', '')
|
||||
|
||||
# Model configuration
|
||||
DEFAULT_MODEL = os.environ.get('DEFAULT_MODEL', 'llama3.1')
|
||||
|
||||
# API timeout (in seconds)
|
||||
API_TIMEOUT = int(os.environ.get('API_TIMEOUT', 300)) # 5 minutes
|
||||
|
||||
# Create a singleton instance
|
||||
config = LocalTestConfig()
|
||||
@@ -0,0 +1,171 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Open WebUI
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,30 @@
|
||||
# open-webui/bot
|
||||
|
||||
This repository provides an experimental boilerplate for building bots compatible with the **Open WebUI** "Channels" feature (introduced in version 0.5.0). It serves as a proof of concept to demonstrate bot-building capabilities while highlighting the potential of asynchronous communication enabled by Channels.
|
||||
|
||||
## ⚡ Key Highlights
|
||||
- **Highly Experimental**: This is an early-stage project showcasing basic bot-building functionality. Expect major API changes in the future.
|
||||
- **Extensible Framework**: Designed as a foundation for further development, with plans to enhance APIs, developer tooling, and usability.
|
||||
- **Asynchronous Communication**: Leverages Open WebUI Channels for event-driven workflows.
|
||||
|
||||
## 🛠️ Getting Started with Examples
|
||||
This repository includes an `/examples` folder containing runnable example bots that demonstrate basic functionality.
|
||||
|
||||
To run an example, execute the corresponding module using the `-m` flag in Python. For example, to run the `ai` example:
|
||||
|
||||
```bash
|
||||
python -m examples.ai
|
||||
```
|
||||
|
||||
> **Note**: Ensure that your current working directory (PWD) is the root of this repository when running examples, as this is required for proper execution.
|
||||
|
||||
Replace `ai` in the command above with the specific example you’d like to execute from the `/examples` folder.
|
||||
|
||||
## 🚧 Disclaimer
|
||||
This project is an early-stage proof of concept. **APIs will break** and existing functionality may change as Open WebUI evolves to include native bot support. This repository is not production-ready and primarily serves experimental and exploratory purposes.
|
||||
|
||||
## 🎯 Future Vision
|
||||
We aim to introduce improved APIs, enhanced developer tooling, and seamless native support for bots directly within Open WebUI. The ultimate goal is to make building bots easier, faster, and more intuitive.
|
||||
|
||||
---
|
||||
Contributions, feedback, and experimentation are encouraged. Join us in shaping the future of bot-building on Open WebUI!
|
||||
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
except ImportError:
|
||||
print("dotenv not installed, skipping...")
|
||||
|
||||
|
||||
WEBUI_URL = os.getenv("WEBUI_URL", "http://localhost:8080")
|
||||
TOKEN = os.getenv("TOKEN", "")
|
||||
@@ -0,0 +1,132 @@
|
||||
# WARNING: This might not work in the future. Do NOT use this in production.
|
||||
|
||||
import asyncio
|
||||
import socketio
|
||||
from env import WEBUI_URL, TOKEN
|
||||
from utils import send_message, send_typing
|
||||
|
||||
|
||||
MODEL_ID = "llama3.2:latest"
|
||||
|
||||
# Create an asynchronous Socket.IO client instance
|
||||
sio = socketio.AsyncClient(logger=False, engineio_logger=False)
|
||||
|
||||
|
||||
# Event handlers
|
||||
@sio.event
|
||||
async def connect():
|
||||
print("Connected!")
|
||||
|
||||
|
||||
@sio.event
|
||||
async def disconnect():
|
||||
print("Disconnected from the server!")
|
||||
|
||||
|
||||
import aiohttp
|
||||
import asyncio
|
||||
|
||||
|
||||
async def openai_chat_completion(messages):
|
||||
payload = {
|
||||
"model": MODEL_ID,
|
||||
"messages": messages,
|
||||
"stream": False,
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"{WEBUI_URL}/api/chat/completions",
|
||||
headers={"Authorization": f"Bearer {TOKEN}"},
|
||||
json=payload,
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
return await response.json()
|
||||
else:
|
||||
# Optional: Handle errors or return raw response text
|
||||
return {"error": await response.text(), "status": response.status}
|
||||
|
||||
|
||||
# Define a function to handle channel events
|
||||
def events(user_id):
|
||||
@sio.on("channel-events")
|
||||
async def channel_events(data):
|
||||
if data["user"]["id"] == user_id:
|
||||
# Ignore events from the bot itself
|
||||
return
|
||||
|
||||
if data["data"]["type"] == "message":
|
||||
print(f'{data["user"]["name"]}: {data["data"]["data"]["content"]}')
|
||||
await send_typing(sio, data["channel_id"])
|
||||
|
||||
async def send_typing_until_complete(channel_id, coro):
|
||||
"""
|
||||
Sends typing indicators every second until the provided coroutine completes.
|
||||
"""
|
||||
task = asyncio.create_task(coro) # Begin the provided coroutine task
|
||||
try:
|
||||
# While the task is running, send typing indicators every second
|
||||
while not task.done():
|
||||
await send_typing(sio, channel_id)
|
||||
await asyncio.sleep(1)
|
||||
# Await the actual result of the coroutine
|
||||
return await task
|
||||
except Exception as e:
|
||||
task.cancel()
|
||||
raise e # Propagate any exceptions that occurred in the coroutine
|
||||
|
||||
# OpenAI API coroutine
|
||||
# This uses naive implementation of OpenAI API, that does not utilize the context of the conversation
|
||||
openai_task = openai_chat_completion(
|
||||
[
|
||||
{"role": "system", "content": "You are a friendly AI."},
|
||||
{"role": "user", "content": data["data"]["data"]["content"]},
|
||||
]
|
||||
)
|
||||
|
||||
try:
|
||||
# Run OpenAI coroutine while showing typing indicators
|
||||
response = await send_typing_until_complete(
|
||||
data["channel_id"], openai_task
|
||||
)
|
||||
|
||||
if response.get("choices"):
|
||||
completion = response["choices"][0]["message"]["content"]
|
||||
await send_message(data["channel_id"], completion)
|
||||
else:
|
||||
await send_message(
|
||||
data["channel_id"], "I'm sorry, I don't understand."
|
||||
)
|
||||
except Exception:
|
||||
await send_message(
|
||||
data["channel_id"],
|
||||
"Something went wrong while processing your request.",
|
||||
)
|
||||
|
||||
|
||||
# Define an async function for the main workflow
|
||||
async def main():
|
||||
try:
|
||||
print(f"Connecting to {WEBUI_URL}...")
|
||||
await sio.connect(
|
||||
WEBUI_URL, socketio_path="/ws/socket.io", transports=["websocket"]
|
||||
)
|
||||
print("Connection established!")
|
||||
except Exception as e:
|
||||
print(f"Failed to connect: {e}")
|
||||
return
|
||||
|
||||
# Callback function for user-join
|
||||
async def join_callback(data):
|
||||
events(data["id"]) # Attach the event handlers dynamically
|
||||
|
||||
# Authenticate with the server
|
||||
await sio.emit("user-join", {"auth": {"token": TOKEN}}, callback=join_callback)
|
||||
|
||||
# Wait indefinitely to keep the connection open
|
||||
await sio.wait()
|
||||
|
||||
|
||||
# Actually run the async `main` function using `asyncio`
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,247 @@
|
||||
# Custom AI bot for our specific needs
|
||||
|
||||
import asyncio
|
||||
import socketio
|
||||
import os
|
||||
import sys
|
||||
from env import WEBUI_URL, TOKEN
|
||||
from utils import send_message, send_typing
|
||||
import aiohttp
|
||||
|
||||
# Get configuration from environment variables
|
||||
MODEL_ID = os.getenv("MODEL_ID", "llama3.1")
|
||||
SYSTEM_PROMPT = os.getenv("SYSTEM_PROMPT", "You are a helpful AI assistant.")
|
||||
TEMPERATURE = float(os.getenv("TEMPERATURE", "0.7"))
|
||||
MAX_TOKENS = int(os.getenv("MAX_TOKENS", "2048"))
|
||||
TOP_P = float(os.getenv("TOP_P", "0.9"))
|
||||
TRIGGERS = os.getenv("TRIGGERS", "@ai,@bot,@assistant,@chatbot").split(",")
|
||||
RESPOND_TO_ALL = os.getenv("RESPOND_TO_ALL", "false").lower() == "true"
|
||||
|
||||
# Create an asynchronous Socket.IO client instance
|
||||
sio = socketio.AsyncClient(logger=False, engineio_logger=False)
|
||||
|
||||
# Event handlers
|
||||
@sio.event
|
||||
async def connect():
|
||||
print("Connected to OpenWebUI!")
|
||||
|
||||
@sio.event
|
||||
async def disconnect():
|
||||
print("Disconnected from OpenWebUI!")
|
||||
|
||||
# Function to call the OpenAI-compatible API
|
||||
async def openai_chat_completion(messages):
|
||||
payload = {
|
||||
"model": MODEL_ID,
|
||||
"messages": messages,
|
||||
"stream": False,
|
||||
"temperature": TEMPERATURE,
|
||||
"max_tokens": MAX_TOKENS,
|
||||
"top_p": TOP_P
|
||||
}
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.post(
|
||||
f"{WEBUI_URL}/api/chat/completions",
|
||||
headers={"Authorization": f"Bearer {TOKEN}"},
|
||||
json=payload,
|
||||
timeout=300 # 5-minute timeout
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
return await response.json()
|
||||
else:
|
||||
# Handle errors or return raw response text
|
||||
error_text = await response.text()
|
||||
print(f"API error: {response.status} - {error_text}")
|
||||
return {"error": error_text, "status": response.status}
|
||||
except aiohttp.ClientError as e:
|
||||
print(f"HTTP request error: {str(e)}")
|
||||
return {"error": f"HTTP request error: {str(e)}", "status": 500}
|
||||
except Exception as e:
|
||||
print(f"Unexpected error in openai_chat_completion: {str(e)}")
|
||||
return {"error": f"Unexpected error: {str(e)}", "status": 500}
|
||||
|
||||
# Helper function to send typing indicators while waiting for a response
|
||||
async def send_typing_until_complete(channel_id, coro):
|
||||
"""
|
||||
Sends typing indicators every second until the provided coroutine completes.
|
||||
"""
|
||||
task = asyncio.create_task(coro) # Begin the provided coroutine task
|
||||
try:
|
||||
# While the task is running, send typing indicators every second
|
||||
while not task.done():
|
||||
await send_typing(sio, channel_id)
|
||||
await asyncio.sleep(1)
|
||||
# Await the actual result of the coroutine
|
||||
return await task
|
||||
except Exception as e:
|
||||
task.cancel()
|
||||
raise e # Propagate any exceptions that occurred in the coroutine
|
||||
|
||||
# Define a function to handle channel events
|
||||
def events(user_id):
|
||||
# Use the configured triggers and respond_to_all setting
|
||||
global TRIGGERS, RESPOND_TO_ALL
|
||||
|
||||
@sio.on("channel-events")
|
||||
async def channel_events(data):
|
||||
if data["user"]["id"] == user_id:
|
||||
# Ignore events from the bot itself
|
||||
return
|
||||
|
||||
if data["data"]["type"] == "message":
|
||||
message_content = data["data"]["data"]["content"]
|
||||
channel_id = data["channel_id"]
|
||||
sender_name = data["user"]["name"]
|
||||
|
||||
print(f"{sender_name}: {message_content}")
|
||||
|
||||
# Check if we should respond
|
||||
should_respond = RESPOND_TO_ALL
|
||||
message_lower = message_content.lower()
|
||||
|
||||
if not should_respond:
|
||||
# Check if the message mentions the bot
|
||||
for trigger in TRIGGERS:
|
||||
trigger_lower = trigger.lower()
|
||||
if trigger_lower in message_lower:
|
||||
should_respond = True
|
||||
break
|
||||
|
||||
if not should_respond:
|
||||
# Skip messages that don't mention the bot
|
||||
return
|
||||
|
||||
# Remove the trigger from the message
|
||||
processed_message = message_content
|
||||
|
||||
# Only try to remove triggers if we're not responding to all messages
|
||||
if not RESPOND_TO_ALL:
|
||||
for trigger in TRIGGERS:
|
||||
trigger_lower = trigger.lower()
|
||||
if trigger_lower in message_lower:
|
||||
# Find the trigger in the message
|
||||
index = message_lower.find(trigger_lower)
|
||||
if index != -1:
|
||||
# Remove the trigger
|
||||
processed_message = processed_message[:index] + processed_message[index + len(trigger):].strip()
|
||||
|
||||
# If the message is empty after removing the trigger, use a default prompt
|
||||
if not processed_message.strip():
|
||||
processed_message = "Hello, how can I help you?"
|
||||
break
|
||||
|
||||
# Show typing indicator
|
||||
await send_typing(sio, channel_id)
|
||||
|
||||
try:
|
||||
# Prepare the messages for the API
|
||||
messages = [
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": processed_message}
|
||||
]
|
||||
|
||||
# Call the API while showing typing indicators
|
||||
response = await send_typing_until_complete(
|
||||
channel_id, openai_chat_completion(messages)
|
||||
)
|
||||
|
||||
# Process the response
|
||||
if response.get("choices"):
|
||||
completion = response["choices"][0]["message"]["content"]
|
||||
# Add a robot emoji to the response
|
||||
formatted_response = f"🤖 {completion}"
|
||||
await send_message(channel_id, formatted_response)
|
||||
else:
|
||||
error_message = response.get("error", "I'm sorry, I couldn't generate a response.")
|
||||
await send_message(channel_id, f"🤖 Error: {error_message}")
|
||||
except Exception as e:
|
||||
print(f"Error generating response: {str(e)}")
|
||||
await send_message(
|
||||
channel_id,
|
||||
"🤖 Something went wrong while processing your request."
|
||||
)
|
||||
|
||||
# Define an async function for the main workflow
|
||||
async def main():
|
||||
max_retries = 3
|
||||
retry_delay = 5 # seconds
|
||||
|
||||
for attempt in range(1, max_retries + 1):
|
||||
try:
|
||||
print(f"Connecting to {WEBUI_URL}... (Attempt {attempt}/{max_retries})")
|
||||
await sio.connect(
|
||||
WEBUI_URL, socketio_path="/ws/socket.io", transports=["websocket"]
|
||||
)
|
||||
print("Connection established!")
|
||||
break # Connection successful, exit the retry loop
|
||||
except Exception as e:
|
||||
print(f"Failed to connect: {e}")
|
||||
if attempt < max_retries:
|
||||
print(f"Retrying in {retry_delay} seconds...")
|
||||
await asyncio.sleep(retry_delay)
|
||||
else:
|
||||
print("Maximum connection attempts reached. Exiting.")
|
||||
return
|
||||
|
||||
try:
|
||||
# Callback function for user-join
|
||||
async def join_callback(data):
|
||||
try:
|
||||
bot_id = data["id"]
|
||||
print(f"Bot connected with ID: {bot_id}")
|
||||
events(bot_id) # Attach the event handlers dynamically
|
||||
except Exception as e:
|
||||
print(f"Error in join_callback: {str(e)}")
|
||||
|
||||
# Authenticate with the server
|
||||
print("Authenticating with the server...")
|
||||
await sio.emit("user-join", {"auth": {"token": TOKEN}}, callback=join_callback)
|
||||
print("Authentication request sent")
|
||||
|
||||
# Wait indefinitely to keep the connection open
|
||||
print("Waiting for events...")
|
||||
await sio.wait()
|
||||
except Exception as e:
|
||||
print(f"Error in main loop: {str(e)}")
|
||||
|
||||
# Actually run the async `main` function using `asyncio`
|
||||
async def shutdown():
|
||||
"""Gracefully shut down the bot."""
|
||||
print("\nShutting down bot...")
|
||||
if sio.connected:
|
||||
print("Disconnecting from OpenWebUI...")
|
||||
await sio.disconnect()
|
||||
print("Bot shutdown complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting custom AI bot...")
|
||||
print(f"OpenWebUI URL: {WEBUI_URL}")
|
||||
print(f"Model: {MODEL_ID}")
|
||||
print(f"System prompt: {SYSTEM_PROMPT[:50]}..." if len(SYSTEM_PROMPT) > 50 else f"System prompt: {SYSTEM_PROMPT}")
|
||||
print(f"Temperature: {TEMPERATURE}")
|
||||
print(f"Max tokens: {MAX_TOKENS}")
|
||||
print(f"Top-p: {TOP_P}")
|
||||
print(f"Triggers: {TRIGGERS}")
|
||||
print(f"Respond to all: {RESPOND_TO_ALL}")
|
||||
print("Press Ctrl+C to stop")
|
||||
|
||||
try:
|
||||
# Run the main function
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print("\nBot stopped by user")
|
||||
# Run the shutdown function
|
||||
try:
|
||||
asyncio.run(shutdown())
|
||||
except Exception as e:
|
||||
print(f"Error during shutdown: {str(e)}")
|
||||
except Exception as e:
|
||||
print(f"Error running bot: {str(e)}")
|
||||
# Try to shut down gracefully
|
||||
try:
|
||||
asyncio.run(shutdown())
|
||||
except Exception as shutdown_error:
|
||||
print(f"Error during shutdown: {str(shutdown_error)}")
|
||||
@@ -0,0 +1,100 @@
|
||||
# WARNING: This might not work in the future. Do NOT use this in production.
|
||||
|
||||
import asyncio
|
||||
import socketio
|
||||
from smolagents import ToolCallingAgent, LiteLLMModel, DuckDuckGoSearchTool
|
||||
|
||||
|
||||
from env import WEBUI_URL, TOKEN
|
||||
from utils import send_message, send_typing
|
||||
|
||||
search_tool = DuckDuckGoSearchTool()
|
||||
|
||||
MODEL_ID = "gpt-4o"
|
||||
|
||||
model = LiteLLMModel(
|
||||
model_id=f"openai/{MODEL_ID}", api_base=f"{WEBUI_URL}/api/", api_key=TOKEN
|
||||
)
|
||||
agent = ToolCallingAgent(tools=[search_tool], model=model)
|
||||
|
||||
# Create an asynchronous Socket.IO client instance
|
||||
sio = socketio.AsyncClient(logger=False, engineio_logger=False)
|
||||
|
||||
|
||||
# Event handlers
|
||||
@sio.event
|
||||
async def connect():
|
||||
print("Connected!")
|
||||
|
||||
|
||||
@sio.event
|
||||
async def disconnect():
|
||||
print("Disconnected from the server!")
|
||||
|
||||
|
||||
# Define a function to handle channel events
|
||||
def events(user_id):
|
||||
@sio.on("channel-events")
|
||||
async def channel_events(data):
|
||||
if data["user"]["id"] == user_id:
|
||||
# Ignore events from the bot itself
|
||||
return
|
||||
|
||||
if data["data"]["type"] == "message":
|
||||
print(f'{data["user"]["name"]}: {data["data"]["data"]["content"]}')
|
||||
|
||||
# Send typing events every second while processing the input
|
||||
async def simulate_typing(channel_id):
|
||||
try:
|
||||
while not processing_event.is_set():
|
||||
await send_typing(sio, channel_id)
|
||||
await asyncio.sleep(1)
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# Create an asyncio.Event to manage typing simulation
|
||||
processing_event = asyncio.Event()
|
||||
typing_task = asyncio.create_task(simulate_typing(data["channel_id"]))
|
||||
|
||||
try:
|
||||
# Run the blocking agent.run in a non-blocking way using asyncio
|
||||
loop = asyncio.get_running_loop()
|
||||
output = await loop.run_in_executor(
|
||||
None, agent.run, data["data"]["data"]["content"]
|
||||
)
|
||||
finally:
|
||||
# Signal that typing simulation should stop
|
||||
processing_event.set()
|
||||
# Wait for the typing task to finish
|
||||
await typing_task
|
||||
|
||||
# Send the generated output as a message
|
||||
await send_message(data["channel_id"], f"{output}")
|
||||
|
||||
|
||||
# Define an async function for the main workflow
|
||||
async def main():
|
||||
try:
|
||||
print(f"Connecting to {WEBUI_URL}...")
|
||||
await sio.connect(
|
||||
WEBUI_URL, socketio_path="/ws/socket.io", transports=["websocket"]
|
||||
)
|
||||
print("Connection established!")
|
||||
except Exception as e:
|
||||
print(f"Failed to connect: {e}")
|
||||
return
|
||||
|
||||
# Callback function for user-join
|
||||
async def join_callback(data):
|
||||
events(data["id"]) # Attach the event handlers dynamically
|
||||
|
||||
# Authenticate with the server
|
||||
await sio.emit("user-join", {"auth": {"token": TOKEN}}, callback=join_callback)
|
||||
|
||||
# Wait indefinitely to keep the connection open
|
||||
await sio.wait()
|
||||
|
||||
|
||||
# Actually run the async `main` function using `asyncio`
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,103 @@
|
||||
# WARNING: This might not work in the future. Do NOT use this in production.
|
||||
|
||||
import asyncio
|
||||
import socketio
|
||||
from smolagents import CodeAgent, LiteLLMModel, DuckDuckGoSearchTool
|
||||
|
||||
|
||||
from env import WEBUI_URL, TOKEN
|
||||
from utils import send_message, send_typing
|
||||
|
||||
# search_tool = DuckDuckGoSearchTool()
|
||||
|
||||
MODEL_ID = "llama3.2:latest"
|
||||
|
||||
model = LiteLLMModel(
|
||||
model_id=f"openai/{MODEL_ID}", api_base=f"{WEBUI_URL}/api/", api_key=TOKEN
|
||||
)
|
||||
agent = CodeAgent(
|
||||
tools=[], model=model, additional_authorized_imports=["requests", "bs4"]
|
||||
)
|
||||
|
||||
|
||||
# Create an asynchronous Socket.IO client instance
|
||||
sio = socketio.AsyncClient(logger=False, engineio_logger=False)
|
||||
|
||||
|
||||
# Event handlers
|
||||
@sio.event
|
||||
async def connect():
|
||||
print("Connected!")
|
||||
|
||||
|
||||
@sio.event
|
||||
async def disconnect():
|
||||
print("Disconnected from the server!")
|
||||
|
||||
|
||||
# Define a function to handle channel events
|
||||
def events(user_id):
|
||||
@sio.on("channel-events")
|
||||
async def channel_events(data):
|
||||
if data["user"]["id"] == user_id:
|
||||
# Ignore events from the bot itself
|
||||
return
|
||||
|
||||
if data["data"]["type"] == "message":
|
||||
print(f'{data["user"]["name"]}: {data["data"]["data"]["content"]}')
|
||||
|
||||
# Send typing events every second while processing the input
|
||||
async def simulate_typing(channel_id):
|
||||
try:
|
||||
while not processing_event.is_set():
|
||||
await send_typing(sio, channel_id)
|
||||
await asyncio.sleep(1)
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# Create an asyncio.Event to manage typing simulation
|
||||
processing_event = asyncio.Event()
|
||||
typing_task = asyncio.create_task(simulate_typing(data["channel_id"]))
|
||||
|
||||
try:
|
||||
# Run the blocking agent.run in a non-blocking way using asyncio
|
||||
loop = asyncio.get_running_loop()
|
||||
output = await loop.run_in_executor(
|
||||
None, agent.run, data["data"]["data"]["content"]
|
||||
)
|
||||
finally:
|
||||
# Signal that typing simulation should stop
|
||||
processing_event.set()
|
||||
# Wait for the typing task to finish
|
||||
await typing_task
|
||||
|
||||
# Send the generated output as a message
|
||||
await send_message(data["channel_id"], f"{output}")
|
||||
|
||||
|
||||
# Define an async function for the main workflow
|
||||
async def main():
|
||||
try:
|
||||
print(f"Connecting to {WEBUI_URL}...")
|
||||
await sio.connect(
|
||||
WEBUI_URL, socketio_path="/ws/socket.io", transports=["websocket"]
|
||||
)
|
||||
print("Connection established!")
|
||||
except Exception as e:
|
||||
print(f"Failed to connect: {e}")
|
||||
return
|
||||
|
||||
# Callback function for user-join
|
||||
async def join_callback(data):
|
||||
events(data["id"]) # Attach the event handlers dynamically
|
||||
|
||||
# Authenticate with the server
|
||||
await sio.emit("user-join", {"auth": {"token": TOKEN}}, callback=join_callback)
|
||||
|
||||
# Wait indefinitely to keep the connection open
|
||||
await sio.wait()
|
||||
|
||||
|
||||
# Actually run the async `main` function using `asyncio`
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,61 @@
|
||||
import asyncio
|
||||
import socketio
|
||||
from env import WEBUI_URL, TOKEN
|
||||
from utils import send_message, send_typing
|
||||
|
||||
# Create an asynchronous Socket.IO client instance
|
||||
sio = socketio.AsyncClient(logger=False, engineio_logger=False)
|
||||
|
||||
|
||||
# Event handlers
|
||||
@sio.event
|
||||
async def connect():
|
||||
print("Connected!")
|
||||
|
||||
|
||||
@sio.event
|
||||
async def disconnect():
|
||||
print("Disconnected from the server!")
|
||||
|
||||
|
||||
# Define a function to handle channel events
|
||||
def events(user_id):
|
||||
@sio.on("channel-events")
|
||||
async def channel_events(data):
|
||||
if data["user"]["id"] == user_id:
|
||||
# Ignore events from the bot itself
|
||||
return
|
||||
|
||||
if data["data"]["type"] == "message":
|
||||
print(f'{data["user"]["name"]}: {data["data"]["data"]["content"]}')
|
||||
await send_typing(sio, data["channel_id"])
|
||||
await asyncio.sleep(1) # Simulate a delay
|
||||
await send_message(data["channel_id"], "Pong!")
|
||||
|
||||
|
||||
# Define an async function for the main workflow
|
||||
async def main():
|
||||
try:
|
||||
print(f"Connecting to {WEBUI_URL}...")
|
||||
await sio.connect(
|
||||
WEBUI_URL, socketio_path="/ws/socket.io", transports=["websocket"]
|
||||
)
|
||||
print("Connection established!")
|
||||
except Exception as e:
|
||||
print(f"Failed to connect: {e}")
|
||||
return
|
||||
|
||||
# Callback function for user-join
|
||||
async def join_callback(data):
|
||||
events(data["id"]) # Attach the event handlers dynamically
|
||||
|
||||
# Authenticate with the server
|
||||
await sio.emit("user-join", {"auth": {"token": TOKEN}}, callback=join_callback)
|
||||
|
||||
# Wait indefinitely to keep the connection open
|
||||
await sio.wait()
|
||||
|
||||
|
||||
# Actually run the async `main` function using `asyncio`
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,33 @@
|
||||
import aiohttp
|
||||
import socketio
|
||||
from env import WEBUI_URL, TOKEN
|
||||
|
||||
|
||||
async def send_message(channel_id: str, message: str):
|
||||
url = f"{WEBUI_URL}/api/v1/channels/{channel_id}/messages/post"
|
||||
headers = {"Authorization": f"Bearer {TOKEN}"}
|
||||
data = {"content": str(message)}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, headers=headers, json=data) as response:
|
||||
if response.status != 200:
|
||||
# Raise an exception if the request fails
|
||||
raise aiohttp.ClientResponseError(
|
||||
request_info=response.request_info,
|
||||
history=response.history,
|
||||
status=response.status,
|
||||
message=await response.text(),
|
||||
headers=response.headers,
|
||||
)
|
||||
# Return response JSON if successful
|
||||
return await response.json()
|
||||
|
||||
|
||||
async def send_typing(sio: socketio.AsyncClient, channel_id: str):
|
||||
await sio.emit(
|
||||
"channel-events",
|
||||
{
|
||||
"channel_id": channel_id,
|
||||
"data": {"type": "typing", "data": {"typing": True}},
|
||||
},
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
# Core dependencies
|
||||
flask==2.3.3
|
||||
fastapi==0.103.1
|
||||
uvicorn==0.23.2
|
||||
flask-sqlalchemy==3.0.5
|
||||
python-dotenv==1.0.0
|
||||
pydantic==2.3.0
|
||||
|
||||
# For future implementation
|
||||
pinecone-client==2.2.2
|
||||
@@ -9,3 +9,8 @@ langchain-text-splitters==0.3.8
|
||||
|
||||
# Utilities
|
||||
requests==2.32.3
|
||||
|
||||
# WebSocket and async support
|
||||
python-socketio==5.10.0
|
||||
aiohttp==3.9.1
|
||||
websockets==12.0
|
||||
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to run the custom OpenWebUI AI bot.
|
||||
|
||||
This script runs our custom AI bot implementation.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
# 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__))
|
||||
openwebui_bot_dir = os.path.join(current_dir, 'openwebui-bot')
|
||||
sys.path.insert(0, openwebui_bot_dir)
|
||||
print(f"Adding OpenWebUI bot directory to Python path: {openwebui_bot_dir}")
|
||||
|
||||
# Import the main function from our custom AI example
|
||||
from examples.custom_ai import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting custom OpenWebUI AI bot...")
|
||||
print("Press Ctrl+C to stop")
|
||||
|
||||
try:
|
||||
# Run the main function
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print("\nBot stopped by user")
|
||||
# No need to call shutdown here as it's handled in the main function
|
||||
except Exception as e:
|
||||
print(f"Error running bot: {str(e)}")
|
||||
print("Check that the OpenWebUI server is running and accessible.")
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to run the AI service locally for testing
|
||||
# This script activates the virtual environment and runs the service with uvicorn
|
||||
|
||||
# Check if the virtual environment exists
|
||||
if [ ! -d "venv" ]; then
|
||||
echo "Virtual environment not found. Creating one..."
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
else
|
||||
source venv/bin/activate
|
||||
fi
|
||||
|
||||
# Create a directory for logs
|
||||
mkdir -p logs
|
||||
|
||||
# Set environment variables for local testing
|
||||
export API_HOST=0.0.0.0
|
||||
export API_PORT=5252
|
||||
export PUBLIC_URL=http://localhost:5252 # For local testing
|
||||
export PYTHONPATH=$PYTHONPATH:$(pwd)
|
||||
|
||||
# Check if .env file exists
|
||||
if [ ! -f "ai_service/.env" ]; then
|
||||
echo "Warning: .env file not found in ai_service directory."
|
||||
echo "Creating a basic .env file from .env.example..."
|
||||
|
||||
if [ -f "ai_service/.env.example" ]; then
|
||||
cp ai_service/.env.example ai_service/.env
|
||||
echo "Created .env file from .env.example. Please edit it with your actual values."
|
||||
else
|
||||
echo "Error: .env.example file not found. Please create a .env file manually."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run the service with uvicorn
|
||||
echo "Starting AI service on http://localhost:5252"
|
||||
echo "Press Ctrl+C to stop the service"
|
||||
uvicorn ai_service.api:app --host $API_HOST --port $API_PORT --reload
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to run the OpenWebUI AI bot.
|
||||
|
||||
This script runs the AI bot example from the openwebui-bot repository.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
# 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__))
|
||||
openwebui_bot_dir = os.path.join(current_dir, 'openwebui-bot')
|
||||
sys.path.insert(0, openwebui_bot_dir)
|
||||
print(f"Adding OpenWebUI bot directory to Python path: {openwebui_bot_dir}")
|
||||
|
||||
# Import the main function from the AI example
|
||||
from examples.ai import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting OpenWebUI AI bot...")
|
||||
print("Press Ctrl+C to stop")
|
||||
|
||||
try:
|
||||
# Run the main function
|
||||
asyncio.run(main())
|
||||
except KeyboardInterrupt:
|
||||
print("\nBot stopped by user")
|
||||
# No need to call shutdown here as it's handled in the main function
|
||||
except Exception as e:
|
||||
print(f"Error running bot: {str(e)}")
|
||||
print("Check that the OpenWebUI server is running and accessible.")
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to run the RAG test in a Python virtual environment
|
||||
|
||||
# Make the script executable
|
||||
chmod +x test_rag.py
|
||||
|
||||
# Check if virtual environment is activated
|
||||
if [[ -z "$VIRTUAL_ENV" ]]; then
|
||||
echo "Virtual environment is not activated."
|
||||
echo "Please activate your virtual environment first with:"
|
||||
echo "source /path/to/your/venv/bin/activate"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Running RAG tests against remote server..."
|
||||
python test_rag.py --remote --verbose
|
||||
|
||||
# If you want to test against a different server, uncomment and modify this line:
|
||||
# python test_rag.py --api-url "http://your-server-url:port" --verbose
|
||||
|
||||
# If you want to test with a specific query, uncomment and modify this line:
|
||||
# python test_rag.py --remote --query "What information do you have about project X?" --verbose
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to set up the local test environment
|
||||
|
||||
# Check if Python is installed
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "Python 3 is not installed. Please install Python 3 and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create a virtual environment if it doesn't exist
|
||||
if [ ! -d "venv" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv venv
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create virtual environment. Please check your Python installation."
|
||||
exit 1
|
||||
fi
|
||||
echo "Virtual environment created successfully."
|
||||
else
|
||||
echo "Virtual environment already exists."
|
||||
fi
|
||||
|
||||
# Activate the virtual environment
|
||||
echo "Activating virtual environment..."
|
||||
source venv/bin/activate
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to activate virtual environment."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install dependencies
|
||||
echo "Installing dependencies..."
|
||||
pip install --upgrade pip
|
||||
pip install requests python-dotenv argparse
|
||||
|
||||
# Create local_test.env file if it doesn't exist
|
||||
if [ ! -f "local_test.env" ]; then
|
||||
echo "Creating local_test.env file from template..."
|
||||
cp local_test.env.example local_test.env
|
||||
echo "Please edit local_test.env with your server credentials."
|
||||
else
|
||||
echo "local_test.env file already exists."
|
||||
fi
|
||||
|
||||
# Make the test script executable
|
||||
chmod +x test_remote_ollama.py
|
||||
|
||||
echo "Setup complete!"
|
||||
echo "To run the test script:"
|
||||
echo "1. Activate the virtual environment: source venv/bin/activate"
|
||||
echo "2. Run the test script: python test_remote_ollama.py"
|
||||
echo "3. To specify a different model or prompt: python test_remote_ollama.py --model llama3.3 --prompt \"Tell me about AI\""
|
||||
echo "4. To specify a different Ollama URL: python test_remote_ollama.py --ollama-url http://your-server-ip:11434"
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script to stop SSH tunnels
|
||||
|
||||
# Check if the tunnel_pids.txt file exists
|
||||
if [ ! -f "tunnel_pids.txt" ]; then
|
||||
echo "No tunnel PIDs file found. No tunnels to stop."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Read the PIDs from the file and kill each process
|
||||
while read pid; do
|
||||
if [ -n "$pid" ]; then
|
||||
echo "Stopping tunnel with PID $pid..."
|
||||
kill $pid 2>/dev/null || true
|
||||
fi
|
||||
done < tunnel_pids.txt
|
||||
|
||||
# Remove the PIDs file
|
||||
rm tunnel_pids.txt
|
||||
|
||||
# Check for any remaining SSH tunnels
|
||||
remaining_tunnels=$(ps aux | grep "ssh -N -L" | grep -v grep | wc -l)
|
||||
if [ $remaining_tunnels -gt 0 ]; then
|
||||
echo "Warning: There are still $remaining_tunnels SSH tunnels running."
|
||||
echo "You may need to kill them manually:"
|
||||
ps aux | grep "ssh -N -L" | grep -v grep
|
||||
else
|
||||
echo "All SSH tunnels have been stopped."
|
||||
fi
|
||||
@@ -1,214 +0,0 @@
|
||||
"""
|
||||
Test script for the AI service API.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
|
||||
def test_health(api_url):
|
||||
"""Test the health endpoint."""
|
||||
print(f"Testing health endpoint at: {api_url}/health")
|
||||
|
||||
try:
|
||||
response = requests.get(f"{api_url}/health", timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
print("Health check successful!")
|
||||
print(f"Response: {response.json()}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"ERROR: Health check failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_config(api_url):
|
||||
"""Test the config endpoint."""
|
||||
print(f"Testing config endpoint at: {api_url}/config")
|
||||
|
||||
try:
|
||||
response = requests.get(f"{api_url}/config", timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
print("Config check successful!")
|
||||
print("Configuration:")
|
||||
config = response.json()
|
||||
for key, value in config.items():
|
||||
print(f" {key}: {value}")
|
||||
|
||||
return True, config
|
||||
except Exception as e:
|
||||
print(f"ERROR: Config check failed: {str(e)}")
|
||||
return False, None
|
||||
|
||||
def test_ollama_connection(api_url):
|
||||
"""Test the Ollama connection."""
|
||||
print(f"Testing Ollama connection at: {api_url}/test-ollama")
|
||||
|
||||
try:
|
||||
response = requests.get(f"{api_url}/test-ollama", timeout=60)
|
||||
response.raise_for_status()
|
||||
|
||||
print("Ollama connection test successful!")
|
||||
result = response.json()
|
||||
print(f"Status: {result.get('status')}")
|
||||
print(f"Message: {result.get('message')}")
|
||||
print(f"Ollama URL: {result.get('ollama_url')}")
|
||||
|
||||
if 'models' in result:
|
||||
print("Available models:")
|
||||
for model in result.get('models', {}).get('models', []):
|
||||
print(f" - {model.get('name')}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"ERROR: Ollama connection test failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_chat_completion(api_url, model_id, prompt):
|
||||
"""Test the chat completion endpoint."""
|
||||
print(f"Testing chat completion at: {api_url}/test-ollama-direct")
|
||||
print(f"Model: {model_id}")
|
||||
print(f"Prompt: {prompt}")
|
||||
|
||||
try:
|
||||
# First, create a chat
|
||||
chat_response = requests.post(
|
||||
f"{api_url}/chats",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"user_id": "test_user",
|
||||
"title": "Test Chat",
|
||||
"model_id": model_id
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if chat_response.status_code != 200:
|
||||
print(f"ERROR: Failed to create chat: {chat_response.status_code}")
|
||||
print(chat_response.text)
|
||||
# Try direct Ollama test instead
|
||||
print("Trying direct Ollama test...")
|
||||
response = requests.post(
|
||||
f"{api_url}/test-ollama-direct",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"model": model_id,
|
||||
"prompt": prompt
|
||||
},
|
||||
timeout=120
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
print("\nResponse:")
|
||||
print(result.get('response', 'No response'))
|
||||
|
||||
return True
|
||||
|
||||
chat_id = chat_response.json().get("id")
|
||||
print(f"Chat created with ID: {chat_id}")
|
||||
|
||||
# Send a message to the chat
|
||||
message_response = requests.post(
|
||||
f"{api_url}/chats/{chat_id}/messages",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"message": prompt,
|
||||
"user_id": "test_user",
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 500
|
||||
},
|
||||
timeout=120
|
||||
)
|
||||
|
||||
message_response.raise_for_status()
|
||||
|
||||
result = message_response.json()
|
||||
print("\nResponse:")
|
||||
print(result.get('content', 'No content in response'))
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"ERROR: Chat completion test failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_rag_completion(api_url, prompt):
|
||||
"""Test the RAG completion endpoint."""
|
||||
print(f"Testing RAG completion at: {api_url}/ask-rag")
|
||||
print(f"Prompt: {prompt}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/ask-rag",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={"query": prompt},
|
||||
timeout=120
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
print("\nResponse:")
|
||||
print(result.get('response', 'No response'))
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"ERROR: RAG completion test failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Test the AI service API')
|
||||
parser.add_argument('--api-url', type=str, default='http://localhost:5252', help='AI service API URL')
|
||||
parser.add_argument('--model', type=str, default='llama3.1', help='Model to use for testing')
|
||||
parser.add_argument('--prompt', type=str, default='What is the capital of France?', help='Prompt to use for testing')
|
||||
parser.add_argument('--rag-prompt', type=str, default='What information do you have in your knowledge base?', help='Prompt to use for RAG testing')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== AI Service API Test ===")
|
||||
print(f"API URL: {args.api_url}")
|
||||
print(f"Model: {args.model}")
|
||||
print()
|
||||
|
||||
# Test health endpoint
|
||||
print("=== Testing Health Endpoint ===")
|
||||
health_success = test_health(args.api_url)
|
||||
print()
|
||||
|
||||
# Test config endpoint
|
||||
print("=== Testing Config Endpoint ===")
|
||||
config_success, config = test_config(args.api_url)
|
||||
print()
|
||||
|
||||
# Test Ollama connection
|
||||
print("=== Testing Ollama Connection ===")
|
||||
ollama_success = test_ollama_connection(args.api_url)
|
||||
print()
|
||||
|
||||
# Test chat completion
|
||||
if ollama_success:
|
||||
print("=== Testing Chat Completion ===")
|
||||
chat_success = test_chat_completion(args.api_url, args.model, args.prompt)
|
||||
print()
|
||||
|
||||
# Test RAG completion
|
||||
print("=== Testing RAG Completion ===")
|
||||
rag_success = test_rag_completion(args.api_url, args.rag_prompt)
|
||||
print()
|
||||
else:
|
||||
chat_success = False
|
||||
rag_success = False
|
||||
|
||||
# Print summary
|
||||
print("=== Test Summary ===")
|
||||
print(f"Health Endpoint: {'SUCCESS' if health_success else 'FAILED'}")
|
||||
print(f"Config Endpoint: {'SUCCESS' if config_success else 'FAILED'}")
|
||||
print(f"Ollama Connection: {'SUCCESS' if ollama_success else 'FAILED'}")
|
||||
print(f"Chat Completion: {'SUCCESS' if chat_success else 'FAILED'}")
|
||||
print(f"RAG Completion: {'SUCCESS' if rag_success else 'FAILED'}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,76 +0,0 @@
|
||||
"""
|
||||
Test script for the direct Ollama endpoint.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
|
||||
def test_direct_ollama(api_url, model_id, prompt):
|
||||
"""Test the direct Ollama endpoint."""
|
||||
print(f"Testing direct Ollama endpoint at: {api_url}/test-ollama-direct")
|
||||
print(f"Model: {model_id}")
|
||||
print(f"Prompt: {prompt}")
|
||||
|
||||
try:
|
||||
start_time = time.time()
|
||||
|
||||
response = requests.post(
|
||||
f"{api_url}/test-ollama-direct",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"model": model_id,
|
||||
"prompt": prompt
|
||||
},
|
||||
timeout=300 # 5 minutes timeout
|
||||
)
|
||||
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
print(f"\nResponse (took {elapsed_time:.2f} seconds):")
|
||||
|
||||
# Handle different response formats
|
||||
content = None
|
||||
if 'response' in result and isinstance(result['response'], dict) and 'message' in result['response']:
|
||||
content = result['response']['message'].get('content')
|
||||
elif 'response' in result and isinstance(result['response'], str):
|
||||
content = result['response']
|
||||
else:
|
||||
content = str(result)
|
||||
|
||||
print(content or 'No content in response')
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"ERROR: Direct Ollama test failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Test the direct Ollama endpoint')
|
||||
parser.add_argument('--api-url', type=str, default='http://localhost:5252', help='AI service API URL')
|
||||
parser.add_argument('--model', type=str, default='llama3.1', help='Model to use for testing')
|
||||
parser.add_argument('--prompt', type=str, default='What is the capital of France?', help='Prompt to use for testing')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== Direct Ollama Test ===")
|
||||
print(f"API URL: {args.api_url}")
|
||||
print(f"Model: {args.model}")
|
||||
print(f"Prompt: {args.prompt}")
|
||||
print()
|
||||
|
||||
# Test direct Ollama endpoint
|
||||
success = test_direct_ollama(args.api_url, args.model, args.prompt)
|
||||
|
||||
# Print summary
|
||||
print("\n=== Test Summary ===")
|
||||
print(f"Direct Ollama Test: {'SUCCESS' if success else 'FAILED'}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,73 +0,0 @@
|
||||
"""
|
||||
Test script for Ollama integration.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Add the parent directory to the path so we can import ai_service
|
||||
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
|
||||
|
||||
from ai_service.config import config
|
||||
from ai_service.models.model_service import model_service
|
||||
|
||||
def test_available_models():
|
||||
"""Test getting available models."""
|
||||
models = model_service.get_available_models()
|
||||
print("Available models:")
|
||||
for model in models:
|
||||
print(f"- {model['name']} ({model['id']}): {model['description']}")
|
||||
print()
|
||||
|
||||
def test_generate_response():
|
||||
"""Test generating a response."""
|
||||
model_id = "llama3.1" # Use a specific model instead of config.DEFAULT_MODEL
|
||||
prompt = "What is the capital of France?"
|
||||
|
||||
print(f"Testing model: {model_id}")
|
||||
print(f"Prompt: {prompt}")
|
||||
|
||||
response = model_service.generate_response(
|
||||
model_id=model_id,
|
||||
prompt=prompt,
|
||||
use_rag=False
|
||||
)
|
||||
|
||||
print("Response:")
|
||||
print(response)
|
||||
print()
|
||||
|
||||
def test_rag_response():
|
||||
"""Test generating a response with RAG."""
|
||||
model_id = "llama3.1" # Use a specific model instead of config.DEFAULT_MODEL
|
||||
prompt = "Tell me about the documents in the knowledge base."
|
||||
|
||||
print(f"Testing RAG with model: {model_id}")
|
||||
print(f"Prompt: {prompt}")
|
||||
|
||||
response = model_service.generate_response(
|
||||
model_id=model_id,
|
||||
prompt=prompt,
|
||||
use_rag=True
|
||||
)
|
||||
|
||||
print("Response with RAG:")
|
||||
print(response)
|
||||
print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing Ollama integration")
|
||||
print(f"OpenWebUI URL: {config.OPENWEBUI_URL}")
|
||||
|
||||
# Override the Ollama API URL to use OpenWebUI
|
||||
model_service.ollama_api_url = f"{config.OPENWEBUI_URL}/ollama"
|
||||
print(f"Using Ollama API URL: {model_service.ollama_api_url}")
|
||||
|
||||
print(f"Default model: {config.DEFAULT_MODEL}")
|
||||
print()
|
||||
|
||||
test_available_models()
|
||||
test_generate_response()
|
||||
test_rag_response()
|
||||
@@ -1,104 +0,0 @@
|
||||
"""
|
||||
Test script for the Ollama API with custom prompts.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
|
||||
class OllamaRequest:
|
||||
"""Request model for Ollama API."""
|
||||
def __init__(self, model, prompt, system_prompt=None):
|
||||
self.model = model
|
||||
self.prompt = prompt
|
||||
self.system_prompt = system_prompt or "You are a helpful assistant."
|
||||
|
||||
def to_json(self):
|
||||
"""Convert to JSON for the API request."""
|
||||
messages = []
|
||||
|
||||
# Add system message
|
||||
messages.append({"role": "system", "content": self.system_prompt})
|
||||
|
||||
# Add user message
|
||||
messages.append({"role": "user", "content": self.prompt})
|
||||
|
||||
return {
|
||||
"model": self.model,
|
||||
"messages": messages,
|
||||
"stream": False
|
||||
}
|
||||
|
||||
def test_ollama_api(api_url, model, prompt, system_prompt=None):
|
||||
"""Test the Ollama API directly."""
|
||||
print(f"Testing Ollama API at: {api_url}")
|
||||
print(f"Model: {model}")
|
||||
print(f"Prompt: {prompt}")
|
||||
if system_prompt:
|
||||
print(f"System prompt: {system_prompt}")
|
||||
|
||||
# Create the request
|
||||
request = OllamaRequest(model, prompt, system_prompt)
|
||||
request_json = request.to_json()
|
||||
|
||||
try:
|
||||
start_time = time.time()
|
||||
|
||||
# Make the API call
|
||||
response = requests.post(
|
||||
api_url,
|
||||
headers={"Content-Type": "application/json"},
|
||||
json=request_json,
|
||||
timeout=300 # 5 minutes timeout
|
||||
)
|
||||
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
# Parse the response
|
||||
result = response.json()
|
||||
|
||||
print(f"\nResponse (took {elapsed_time:.2f} seconds):")
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
# Extract the content
|
||||
if 'message' in result:
|
||||
content = result['message'].get('content', 'No content in response')
|
||||
print("\nContent:")
|
||||
print(content)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"ERROR: Ollama API test failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Test the Ollama API with custom prompts')
|
||||
parser.add_argument('--api-url', type=str, default='http://localhost:11434/api/chat', help='Ollama API URL')
|
||||
parser.add_argument('--model', type=str, default='llama3.1', help='Model to use for testing')
|
||||
parser.add_argument('--prompt', type=str, default='What is the capital of France?', help='Prompt to use for testing')
|
||||
parser.add_argument('--system-prompt', type=str, default=None, help='System prompt to use for testing')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== Ollama API Test ===")
|
||||
print(f"API URL: {args.api_url}")
|
||||
print(f"Model: {args.model}")
|
||||
print(f"Prompt: {args.prompt}")
|
||||
if args.system_prompt:
|
||||
print(f"System prompt: {args.system_prompt}")
|
||||
print()
|
||||
|
||||
# Test Ollama API
|
||||
success = test_ollama_api(args.api_url, args.model, args.prompt, args.system_prompt)
|
||||
|
||||
# Print summary
|
||||
print("\n=== Test Summary ===")
|
||||
print(f"Ollama API Test: {'SUCCESS' if success else 'FAILED'}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
-309
@@ -1,309 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Test script for RAG functionality in the AI service.
|
||||
This script tests the document-based question answering capabilities
|
||||
by making requests to the API endpoints.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
import requests
|
||||
from typing import Dict, Any, Optional
|
||||
from pprint import pprint
|
||||
|
||||
# Default configuration
|
||||
DEFAULT_API_URL = "http://localhost:5252" # Local development server
|
||||
DEFAULT_REMOTE_API_URL = "http://157.157.221.29:5252" # Remote server
|
||||
DEFAULT_MODEL = "llama3.1"
|
||||
|
||||
class RAGTester:
|
||||
"""Test the RAG functionality of the AI service."""
|
||||
|
||||
def __init__(self, api_url: str, verbose: bool = False):
|
||||
"""
|
||||
Initialize the RAG tester.
|
||||
|
||||
Args:
|
||||
api_url: URL of the AI service API.
|
||||
verbose: Whether to print verbose output.
|
||||
"""
|
||||
self.api_url = api_url
|
||||
self.verbose = verbose
|
||||
self.session = requests.Session()
|
||||
|
||||
# Print configuration
|
||||
print(f"Testing RAG functionality against API at: {self.api_url}")
|
||||
|
||||
def _log(self, message: str):
|
||||
"""Log a message if verbose mode is enabled."""
|
||||
if self.verbose:
|
||||
print(f"[DEBUG] {message}")
|
||||
|
||||
def check_server_health(self) -> bool:
|
||||
"""
|
||||
Check if the server is healthy.
|
||||
|
||||
Returns:
|
||||
True if the server is healthy, False otherwise.
|
||||
"""
|
||||
try:
|
||||
response = self.session.get(f"{self.api_url}/health", timeout=10)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if result.get("status") == "healthy":
|
||||
print("✅ Server is healthy")
|
||||
return True
|
||||
else:
|
||||
print("❌ Server health check failed")
|
||||
print(f"Response: {result}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error checking server health: {str(e)}")
|
||||
return False
|
||||
|
||||
def check_config(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Check the server configuration.
|
||||
|
||||
Returns:
|
||||
Server configuration.
|
||||
"""
|
||||
try:
|
||||
response = self.session.get(f"{self.api_url}/config", timeout=10)
|
||||
response.raise_for_status()
|
||||
config = response.json()
|
||||
|
||||
print("✅ Server configuration:")
|
||||
print(f" - API Host: {config.get('api_host')}")
|
||||
print(f" - API Port: {config.get('api_port')}")
|
||||
print(f" - OpenWebUI URL: {config.get('openwebui_url')}")
|
||||
print(f" - Ollama API URL: {config.get('ollama_api_url')}")
|
||||
print(f" - Default Model: {config.get('default_model')}")
|
||||
print(f" - API Timeout: {config.get('api_timeout')} seconds")
|
||||
print(f" - Available Models: {', '.join(config.get('available_models', []))}")
|
||||
|
||||
return config
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error checking server configuration: {str(e)}")
|
||||
return {}
|
||||
|
||||
def test_ollama_connection(self) -> bool:
|
||||
"""
|
||||
Test the connection to Ollama.
|
||||
|
||||
Returns:
|
||||
True if the connection is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
response = self.session.get(f"{self.api_url}/test-ollama", timeout=30)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if result.get("status") == "success":
|
||||
print("✅ Successfully connected to Ollama API")
|
||||
print(f" - Ollama URL: {result.get('ollama_url')}")
|
||||
print(f" - Available models: {len(result.get('models', {}).get('models', []))}")
|
||||
return True
|
||||
else:
|
||||
print("❌ Failed to connect to Ollama API")
|
||||
print(f" - Error: {result.get('message')}")
|
||||
print(f" - Ollama URL: {result.get('ollama_url')}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error testing Ollama connection: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_chat_completion(self, model_id: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Test basic chat completion without RAG.
|
||||
|
||||
Args:
|
||||
model_id: Optional model ID to use.
|
||||
|
||||
Returns:
|
||||
True if the test is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
response = self.session.post(f"{self.api_url}/test-chat", timeout=60)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if result.get("status") == "success":
|
||||
print("✅ Successfully tested chat completion")
|
||||
print(f" - Model: {result.get('model')}")
|
||||
print(f" - Response: {result.get('response')[:100]}...") # First 100 chars
|
||||
return True
|
||||
else:
|
||||
print("❌ Failed to test chat completion")
|
||||
print(f" - Error: {result.get('message')}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error testing chat completion: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_rag_completion(self, query: str = "What information do you have in your knowledge database?") -> bool:
|
||||
"""
|
||||
Test RAG completion with a query.
|
||||
|
||||
Args:
|
||||
query: Query to test with RAG.
|
||||
|
||||
Returns:
|
||||
True if the test is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
response = self.session.post(
|
||||
f"{self.api_url}/test-rag",
|
||||
params={"query": query},
|
||||
timeout=120 # Longer timeout for RAG
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if result.get("status") == "success":
|
||||
print("✅ Successfully tested RAG completion")
|
||||
print(f" - Model: {result.get('model')}")
|
||||
print(f" - Query: {result.get('query')}")
|
||||
print(f" - OpenWebUI URL: {result.get('openwebui_url')}")
|
||||
print(f" - Response: {result.get('response')[:150]}...") # First 150 chars
|
||||
|
||||
# Print full response in verbose mode
|
||||
if self.verbose:
|
||||
print("\nFull response:")
|
||||
print(result.get('response'))
|
||||
|
||||
return True
|
||||
else:
|
||||
print("❌ Failed to test RAG completion")
|
||||
print(f" - Error: {result.get('message')}")
|
||||
print(f" - OpenWebUI URL: {result.get('openwebui_url')}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error testing RAG completion: {str(e)}")
|
||||
return False
|
||||
|
||||
def create_chat_and_test_rag(self, query: str, user_id: str = "test_user") -> bool:
|
||||
"""
|
||||
Create a chat and test RAG with a message.
|
||||
|
||||
Args:
|
||||
query: Query to test with RAG.
|
||||
user_id: User ID for the chat.
|
||||
|
||||
Returns:
|
||||
True if the test is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
# Create a chat
|
||||
create_response = self.session.post(
|
||||
f"{self.api_url}/chats",
|
||||
json={
|
||||
"user_id": user_id,
|
||||
"title": "RAG Test Chat",
|
||||
"model_id": DEFAULT_MODEL,
|
||||
"is_team_chat": False
|
||||
},
|
||||
timeout=30
|
||||
)
|
||||
create_response.raise_for_status()
|
||||
chat = create_response.json()
|
||||
chat_id = chat.get("id")
|
||||
|
||||
print(f"✅ Created chat with ID: {chat_id}")
|
||||
|
||||
# Send a message with RAG enabled
|
||||
message_response = self.session.post(
|
||||
f"{self.api_url}/chats/{chat_id}/messages",
|
||||
json={
|
||||
"message": query,
|
||||
"user_id": user_id,
|
||||
"use_rag": True,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 1000
|
||||
},
|
||||
timeout=120 # Longer timeout for RAG
|
||||
)
|
||||
message_response.raise_for_status()
|
||||
message = message_response.json()
|
||||
|
||||
print("✅ Successfully sent message with RAG")
|
||||
print(f" - Message ID: {message.get('id')}")
|
||||
print(f" - Response: {message.get('content')[:150]}...") # First 150 chars
|
||||
|
||||
# Print full response in verbose mode
|
||||
if self.verbose:
|
||||
print("\nFull response:")
|
||||
print(message.get('content'))
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error testing chat with RAG: {str(e)}")
|
||||
return False
|
||||
|
||||
def run_all_tests(self, query: str = "What information do you have in your knowledge database?"):
|
||||
"""
|
||||
Run all tests.
|
||||
|
||||
Args:
|
||||
query: Query to test with RAG.
|
||||
"""
|
||||
print("\n=== Running RAG Functionality Tests ===\n")
|
||||
|
||||
# Check server health
|
||||
if not self.check_server_health():
|
||||
print("❌ Server health check failed. Aborting tests.")
|
||||
return
|
||||
|
||||
# Check configuration
|
||||
config = self.check_config()
|
||||
if not config:
|
||||
print("❌ Failed to get server configuration. Continuing with tests...")
|
||||
|
||||
# Test Ollama connection
|
||||
if not self.test_ollama_connection():
|
||||
print("⚠️ Ollama connection test failed. Some tests may fail.")
|
||||
|
||||
# Test basic chat completion
|
||||
if not self.test_chat_completion():
|
||||
print("⚠️ Basic chat completion test failed. RAG tests may also fail.")
|
||||
|
||||
# Test RAG completion
|
||||
print("\n--- Testing RAG Completion ---\n")
|
||||
self.test_rag_completion(query)
|
||||
|
||||
# Test chat with RAG
|
||||
print("\n--- Testing Chat with RAG ---\n")
|
||||
self.create_chat_and_test_rag(query)
|
||||
|
||||
print("\n=== Tests Completed ===\n")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to run the tests."""
|
||||
parser = argparse.ArgumentParser(description="Test RAG functionality in the AI service")
|
||||
parser.add_argument("--api-url", default=DEFAULT_API_URL, help=f"URL of the AI service API (default: {DEFAULT_API_URL})")
|
||||
parser.add_argument("--remote", action="store_true", help=f"Use the remote API URL ({DEFAULT_REMOTE_API_URL})")
|
||||
parser.add_argument("--query", default="What information do you have in your knowledge database?", help="Query to test with RAG")
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Use remote URL if specified
|
||||
api_url = DEFAULT_REMOTE_API_URL if args.remote else args.api_url
|
||||
|
||||
# Create and run the tester
|
||||
tester = RAGTester(api_url=api_url, verbose=args.verbose)
|
||||
tester.run_all_tests(query=args.query)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,165 +0,0 @@
|
||||
"""
|
||||
Test script for connecting to a remote Ollama server.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
|
||||
# Import local test configuration
|
||||
from local_test_config import config
|
||||
|
||||
def test_ollama_connection():
|
||||
"""Test the connection to the Ollama API."""
|
||||
print(f"Testing connection to Ollama API at: {config.OLLAMA_API_URL}")
|
||||
|
||||
try:
|
||||
# Try to connect to Ollama API via OpenWebUI
|
||||
# OpenWebUI exposes Ollama models through its own API
|
||||
response = requests.get(f"{config.OPENWEBUI_URL}/api/models", timeout=config.API_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
|
||||
# Print the models from OpenWebUI
|
||||
print("Connection successful!")
|
||||
print("Available models:")
|
||||
models_data = response.json()
|
||||
if isinstance(models_data, list):
|
||||
for model in models_data:
|
||||
print(f"- {model.get('id')}")
|
||||
else:
|
||||
print(f"Unexpected response format: {models_data}")
|
||||
|
||||
return True
|
||||
except requests.exceptions.Timeout as e:
|
||||
print(f"ERROR: Timeout connecting to Ollama API: {str(e)}. The request exceeded the {config.API_TIMEOUT} second timeout.")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f"ERROR: Connection error to Ollama API: {str(e)}. Please check if Ollama is running at {config.OLLAMA_API_URL}.")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"ERROR: Error connecting to Ollama API: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_generate_response(model_id=None, prompt=None):
|
||||
"""Test generating a response from the model."""
|
||||
if model_id is None:
|
||||
model_id = config.DEFAULT_MODEL
|
||||
|
||||
if prompt is None:
|
||||
prompt = "What is the capital of France?"
|
||||
|
||||
print(f"Testing model: {model_id}")
|
||||
print(f"Prompt: {prompt}")
|
||||
|
||||
try:
|
||||
# Prepare the request for OpenWebUI's chat completions API
|
||||
request_json = {
|
||||
"model": model_id,
|
||||
"messages": [
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
"stream": False,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 500
|
||||
}
|
||||
|
||||
# Make the API call to OpenWebUI's chat completions API
|
||||
print(f"Sending request to OpenWebUI API at: {config.OPENWEBUI_URL}/api/chat/completions")
|
||||
|
||||
response = requests.post(
|
||||
f"{config.OPENWEBUI_URL}/api/chat/completions",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json=request_json,
|
||||
timeout=config.API_TIMEOUT
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
# Parse the response
|
||||
response_data = response.json()
|
||||
|
||||
# Print the response (OpenWebUI uses OpenAI-compatible format)
|
||||
print("\nResponse:")
|
||||
content = response_data.get('choices', [{}])[0].get('message', {}).get('content', 'No content in response')
|
||||
print(content)
|
||||
|
||||
return True
|
||||
except requests.exceptions.Timeout as e:
|
||||
print(f"ERROR: Timeout generating response: {str(e)}. The request exceeded the {config.API_TIMEOUT} second timeout.")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f"ERROR: Connection error to Ollama API: {str(e)}. Please check if Ollama is running at {config.OLLAMA_API_URL}.")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"ERROR: Error generating response: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_openwebui_connection():
|
||||
"""Test the connection to OpenWebUI."""
|
||||
print(f"Testing connection to OpenWebUI at: {config.OPENWEBUI_URL}")
|
||||
|
||||
try:
|
||||
# Try to connect to OpenWebUI
|
||||
response = requests.get(f"{config.OPENWEBUI_URL}/api/health", timeout=config.API_TIMEOUT)
|
||||
response.raise_for_status()
|
||||
|
||||
# Print the response
|
||||
print("Connection successful!")
|
||||
print(f"OpenWebUI status: {response.text}")
|
||||
|
||||
return True
|
||||
except requests.exceptions.Timeout as e:
|
||||
print(f"ERROR: Timeout connecting to OpenWebUI: {str(e)}. The request exceeded the {config.API_TIMEOUT} second timeout.")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f"ERROR: Connection error to OpenWebUI: {str(e)}. Please check if OpenWebUI is running at {config.OPENWEBUI_URL}.")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"ERROR: Error connecting to OpenWebUI: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Test remote Ollama and OpenWebUI connections')
|
||||
parser.add_argument('--model', type=str, default=config.DEFAULT_MODEL, help='Model to use for testing')
|
||||
parser.add_argument('--prompt', type=str, default="What is the capital of France?", help='Prompt to use for testing')
|
||||
parser.add_argument('--ollama-url', type=str, default=config.OLLAMA_API_URL, help='Ollama API URL')
|
||||
parser.add_argument('--openwebui-url', type=str, default=config.OPENWEBUI_URL, help='OpenWebUI URL')
|
||||
parser.add_argument('--timeout', type=int, default=config.API_TIMEOUT, help='API timeout in seconds')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Update configuration with command-line arguments
|
||||
config.OLLAMA_API_URL = args.ollama_url
|
||||
config.OPENWEBUI_URL = args.openwebui_url
|
||||
config.API_TIMEOUT = args.timeout
|
||||
|
||||
print("=== Remote Connection Test ===")
|
||||
print(f"Ollama API URL: {config.OLLAMA_API_URL}")
|
||||
print(f"OpenWebUI URL: {config.OPENWEBUI_URL}")
|
||||
print(f"API Timeout: {config.API_TIMEOUT} seconds")
|
||||
print()
|
||||
|
||||
# Test Ollama connection
|
||||
print("=== Testing Ollama Connection ===")
|
||||
ollama_success = test_ollama_connection()
|
||||
print()
|
||||
|
||||
# Test OpenWebUI connection
|
||||
print("=== Testing OpenWebUI Connection ===")
|
||||
openwebui_success = test_openwebui_connection()
|
||||
print()
|
||||
|
||||
# Test generating a response
|
||||
if ollama_success:
|
||||
print("=== Testing Model Response ===")
|
||||
test_generate_response(args.model, args.prompt)
|
||||
|
||||
print("\n=== Test Summary ===")
|
||||
print(f"Ollama Connection: {'SUCCESS' if ollama_success else 'FAILED'}")
|
||||
print(f"OpenWebUI Connection: {'SUCCESS' if openwebui_success else 'FAILED'}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,2 +0,0 @@
|
||||
requests>=2.28.0
|
||||
pydantic>=2.0.0
|
||||
@@ -1,91 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple test script for team chat functionality.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Simple test for team chat functionality')
|
||||
parser.add_argument('--api-url', type=str, default='http://localhost:5252', help='AI service API URL')
|
||||
parser.add_argument('--model', type=str, default='llama3.1', help='Model to use for testing')
|
||||
parser.add_argument('--user-id', type=str, default='test_user', help='User ID for testing')
|
||||
parser.add_argument('--chat-id', type=str, help='Existing chat ID to use (optional)')
|
||||
parser.add_argument('--message', type=str, default='Hello team!', help='Message to send')
|
||||
parser.add_argument('--with-mention', action='store_true', help='Add @ai mention to message')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"API URL: {args.api_url}")
|
||||
print(f"Model: {args.model}")
|
||||
print(f"User ID: {args.user_id}")
|
||||
|
||||
# Create a chat if no chat ID is provided
|
||||
chat_id = args.chat_id
|
||||
if not chat_id:
|
||||
print("\nCreating a new team chat...")
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{args.api_url}/chats",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"user_id": args.user_id,
|
||||
"title": "Simple Team Chat Test",
|
||||
"model_id": args.model,
|
||||
"is_team_chat": True
|
||||
},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
chat = response.json()
|
||||
chat_id = chat.get("id")
|
||||
|
||||
print(f"Created team chat with ID: {chat_id}")
|
||||
print(f"OpenWebUI Channel ID: {chat.get('openwebui_channel_id', 'Not available')}")
|
||||
print(f"Team Members: {chat.get('team_members', [])}")
|
||||
except Exception as e:
|
||||
print(f"Error creating team chat: {str(e)}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"\nUsing existing chat ID: {chat_id}")
|
||||
|
||||
# Prepare message
|
||||
message = args.message
|
||||
if args.with_mention:
|
||||
message = f"@ai {message}"
|
||||
print(f"\nSending message WITH @ai mention: {message}")
|
||||
else:
|
||||
print(f"\nSending message WITHOUT @ai mention: {message}")
|
||||
|
||||
# Send message
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{args.api_url}/chats/{chat_id}/messages",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"message": message,
|
||||
"user_id": args.user_id
|
||||
},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
print("\nMessage sent successfully!")
|
||||
print(f"AI Response: {result.get('content', 'No content in response')}")
|
||||
|
||||
print("\nNote: When using the API directly, the AI will always respond")
|
||||
print("The @ai mention functionality only applies to messages sent through OpenWebUI channels")
|
||||
print(f"\nYou can check the OpenWebUI interface at: http://104.225.217.215:8080/")
|
||||
print(f"Chat ID: {chat_id}")
|
||||
except Exception as e:
|
||||
print(f"Error sending message: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,266 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for team chat mentions functionality.
|
||||
This script tests the AI response to @ai mentions in team chats.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
import sys
|
||||
from typing import Dict, Any, List, Tuple, Optional
|
||||
|
||||
def test_health(api_url: str) -> bool:
|
||||
"""Test the health endpoint."""
|
||||
print(f"Testing health endpoint at: {api_url}/health")
|
||||
|
||||
try:
|
||||
response = requests.get(f"{api_url}/health", timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
print("✅ Health check successful!")
|
||||
print(f"Response: {response.json()}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Health check failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_create_team_chat(api_url: str, user_id: str, title: str, model_id: str) -> Tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Test creating a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
user_id: User ID
|
||||
title: Chat title
|
||||
model_id: Model ID
|
||||
|
||||
Returns:
|
||||
Tuple of (success, chat_id)
|
||||
"""
|
||||
print(f"Creating team chat with title: {title}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"user_id": user_id,
|
||||
"title": title,
|
||||
"model_id": model_id,
|
||||
"is_team_chat": True
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
chat = response.json()
|
||||
chat_id = chat.get("id")
|
||||
|
||||
print(f"✅ Team chat created successfully with ID: {chat_id}")
|
||||
print(f"OpenWebUI Channel ID: {chat.get('openwebui_channel_id', 'Not available')}")
|
||||
print(f"Team Members: {chat.get('team_members', [])}")
|
||||
|
||||
return True, chat_id
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to create team chat: {str(e)}")
|
||||
return False, None
|
||||
|
||||
def test_add_team_member(api_url: str, chat_id: str, user_id: str) -> bool:
|
||||
"""
|
||||
Test adding a member to a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
chat_id: Chat ID
|
||||
user_id: User ID to add
|
||||
|
||||
Returns:
|
||||
Success status
|
||||
"""
|
||||
print(f"Adding user {user_id} to team chat {chat_id}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats/{chat_id}/members/{user_id}",
|
||||
timeout=30
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
print(f"✅ User {user_id} added to team chat successfully")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to add user to team chat: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_send_team_message_with_mention(api_url: str, chat_id: str, user_id: str, message: str, mention: str) -> bool:
|
||||
"""
|
||||
Test sending a message with an @ai mention to a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
chat_id: Chat ID
|
||||
user_id: User ID
|
||||
message: Message content
|
||||
mention: Mention to use (@ai, @bot, etc.)
|
||||
|
||||
Returns:
|
||||
Success status
|
||||
"""
|
||||
full_message = f"{mention} {message}"
|
||||
print(f"Sending message to team chat {chat_id}: {full_message}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats/{chat_id}/messages",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"message": full_message,
|
||||
"user_id": user_id,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 500
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
print(f"✅ Message with mention sent successfully")
|
||||
print(f"AI Response: {result.get('content', 'No content in response')[:100]}...")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to send message to team chat: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_send_team_message_without_mention(api_url: str, chat_id: str, user_id: str, message: str) -> bool:
|
||||
"""
|
||||
Test sending a message without an @ai mention to a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
chat_id: Chat ID
|
||||
user_id: User ID
|
||||
message: Message content
|
||||
|
||||
Returns:
|
||||
Success status
|
||||
"""
|
||||
print(f"Sending message without mention to team chat {chat_id}: {message}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats/{chat_id}/messages",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"message": message,
|
||||
"user_id": user_id,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 500
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
print(f"✅ Message without mention sent successfully")
|
||||
|
||||
# Note: When using the API directly, the AI will always respond
|
||||
# The AI_RESPOND_TO_ALL setting only applies to messages received through the webhook
|
||||
print(f"Note: AI responded because we're using the API directly: {result.get('content', 'No content in response')[:100]}...")
|
||||
print("The @ai mention functionality only applies to messages sent through OpenWebUI channels")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to send message to team chat: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Test team chat mentions functionality')
|
||||
parser.add_argument('--api-url', type=str, default='http://localhost:5252', help='AI service API URL')
|
||||
parser.add_argument('--model', type=str, default='llama3.1', help='Model to use for testing')
|
||||
parser.add_argument('--user-id', type=str, default='test_user', help='User ID for testing')
|
||||
parser.add_argument('--second-user-id', type=str, default='test_user2', help='Second user ID for testing team membership')
|
||||
parser.add_argument('--mention', type=str, default='@ai', help='Mention to use (@ai, @bot, etc.)')
|
||||
parser.add_argument('--message', type=str, default='Can you help me with a project?', help='Message to send to the team chat')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== Team Chat Mentions Functionality Test ===")
|
||||
print(f"API URL: {args.api_url}")
|
||||
print(f"Model: {args.model}")
|
||||
print(f"User ID: {args.user_id}")
|
||||
print(f"Mention: {args.mention}")
|
||||
print()
|
||||
|
||||
# Test health endpoint
|
||||
print("=== Testing Health Endpoint ===")
|
||||
health_success = test_health(args.api_url)
|
||||
print()
|
||||
|
||||
if not health_success:
|
||||
print("❌ Health check failed. Aborting tests.")
|
||||
sys.exit(1)
|
||||
|
||||
# Test creating a team chat
|
||||
print("=== Creating Team Chat ===")
|
||||
chat_success, chat_id = test_create_team_chat(
|
||||
args.api_url,
|
||||
args.user_id,
|
||||
f"Mention Test Chat {time.strftime('%Y-%m-%d %H:%M:%S')}",
|
||||
args.model
|
||||
)
|
||||
print()
|
||||
|
||||
if not chat_success or not chat_id:
|
||||
print("❌ Failed to create team chat. Aborting tests.")
|
||||
sys.exit(1)
|
||||
|
||||
# Test adding a team member
|
||||
print("=== Adding Team Member ===")
|
||||
member_success = test_add_team_member(args.api_url, chat_id, args.second_user_id)
|
||||
print()
|
||||
|
||||
# Test sending a message without a mention
|
||||
print("=== Sending Message WITHOUT Mention ===")
|
||||
no_mention_success = test_send_team_message_without_mention(
|
||||
args.api_url,
|
||||
chat_id,
|
||||
args.user_id,
|
||||
f"This is a message without a mention: {args.message}"
|
||||
)
|
||||
print()
|
||||
|
||||
# Test sending a message with a mention
|
||||
print("=== Sending Message WITH Mention ===")
|
||||
mention_success = test_send_team_message_with_mention(
|
||||
args.api_url,
|
||||
chat_id,
|
||||
args.user_id,
|
||||
args.message,
|
||||
args.mention
|
||||
)
|
||||
print()
|
||||
|
||||
# Print summary
|
||||
print("=== Test Summary ===")
|
||||
print(f"Health Endpoint: {'✅ SUCCESS' if health_success else '❌ FAILED'}")
|
||||
print(f"Create Team Chat: {'✅ SUCCESS' if chat_success else '❌ FAILED'}")
|
||||
print(f"Add Team Member: {'✅ SUCCESS' if member_success else '❌ FAILED'}")
|
||||
print(f"Send Message WITHOUT Mention: {'✅ SUCCESS' if no_mention_success else '❌ FAILED'}")
|
||||
print(f"Send Message WITH Mention: {'✅ SUCCESS' if mention_success else '❌ FAILED'}")
|
||||
|
||||
if chat_success and chat_id:
|
||||
print(f"\nCreated team chat ID: {chat_id}")
|
||||
print("You can now check the OpenWebUI interface to see if the channel was created")
|
||||
print("and if the AI responded to the message with the mention.")
|
||||
print(f"OpenWebUI URL: http://104.225.217.215:8080/")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,243 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for team chats functionality.
|
||||
This script tests the team chat features of the AI service, including:
|
||||
- Creating team chats
|
||||
- Adding members to team chats
|
||||
- Sending messages to team chats
|
||||
- Verifying integration with OpenWebUI channels
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
import sys
|
||||
from typing import Dict, Any, List, Tuple, Optional
|
||||
|
||||
def test_health(api_url: str) -> bool:
|
||||
"""Test the health endpoint."""
|
||||
print(f"Testing health endpoint at: {api_url}/health")
|
||||
|
||||
try:
|
||||
response = requests.get(f"{api_url}/health", timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
print("✅ Health check successful!")
|
||||
print(f"Response: {response.json()}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Health check failed: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_create_team_chat(api_url: str, user_id: str, title: str, model_id: str) -> Tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Test creating a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
user_id: User ID
|
||||
title: Chat title
|
||||
model_id: Model ID
|
||||
|
||||
Returns:
|
||||
Tuple of (success, chat_id)
|
||||
"""
|
||||
print(f"Creating team chat with title: {title}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"user_id": user_id,
|
||||
"title": title,
|
||||
"model_id": model_id,
|
||||
"is_team_chat": True
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
chat = response.json()
|
||||
chat_id = chat.get("id")
|
||||
|
||||
print(f"✅ Team chat created successfully with ID: {chat_id}")
|
||||
print(f"OpenWebUI Channel ID: {chat.get('openwebui_channel_id', 'Not available')}")
|
||||
print(f"Team Members: {chat.get('team_members', [])}")
|
||||
|
||||
return True, chat_id
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to create team chat: {str(e)}")
|
||||
return False, None
|
||||
|
||||
def test_add_team_member(api_url: str, chat_id: str, user_id: str) -> bool:
|
||||
"""
|
||||
Test adding a member to a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
chat_id: Chat ID
|
||||
user_id: User ID to add
|
||||
|
||||
Returns:
|
||||
Success status
|
||||
"""
|
||||
print(f"Adding user {user_id} to team chat {chat_id}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats/{chat_id}/members/{user_id}",
|
||||
timeout=30
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
print(f"✅ User {user_id} added to team chat successfully")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to add user to team chat: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_send_team_message(api_url: str, chat_id: str, user_id: str, message: str) -> bool:
|
||||
"""
|
||||
Test sending a message to a team chat.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
chat_id: Chat ID
|
||||
user_id: User ID
|
||||
message: Message content
|
||||
|
||||
Returns:
|
||||
Success status
|
||||
"""
|
||||
print(f"Sending message to team chat {chat_id}: {message}")
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{api_url}/chats/{chat_id}/messages",
|
||||
headers={"Content-Type": "application/json"},
|
||||
json={
|
||||
"message": message,
|
||||
"user_id": user_id,
|
||||
"temperature": 0.7,
|
||||
"max_tokens": 500
|
||||
},
|
||||
timeout=120
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
print(f"✅ Message sent successfully")
|
||||
print(f"AI Response: {result.get('content', 'No content in response')[:100]}...")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to send message to team chat: {str(e)}")
|
||||
return False
|
||||
|
||||
def test_get_user_chats(api_url: str, user_id: str) -> Tuple[bool, List[Dict[str, Any]]]:
|
||||
"""
|
||||
Test getting all chats for a user.
|
||||
|
||||
Args:
|
||||
api_url: API URL
|
||||
user_id: User ID
|
||||
|
||||
Returns:
|
||||
Tuple of (success, chats)
|
||||
"""
|
||||
print(f"Getting chats for user {user_id}")
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
f"{api_url}/chats/user/{user_id}",
|
||||
timeout=30
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
chats = response.json()
|
||||
team_chats = [chat for chat in chats if chat.get("is_team_chat", False)]
|
||||
|
||||
print(f"✅ Found {len(chats)} chats for user {user_id}, {len(team_chats)} are team chats")
|
||||
|
||||
return True, chats
|
||||
except Exception as e:
|
||||
print(f"❌ ERROR: Failed to get user chats: {str(e)}")
|
||||
return False, []
|
||||
|
||||
def main():
|
||||
"""Main function."""
|
||||
parser = argparse.ArgumentParser(description='Test team chats functionality')
|
||||
parser.add_argument('--api-url', type=str, default='http://localhost:5252', help='AI service API URL')
|
||||
parser.add_argument('--model', type=str, default='llama3.1', help='Model to use for testing')
|
||||
parser.add_argument('--user-id', type=str, default='test_user', help='User ID for testing')
|
||||
parser.add_argument('--second-user-id', type=str, default='test_user2', help='Second user ID for testing team membership')
|
||||
parser.add_argument('--message', type=str, default='Hello team! Can you help me with a project?', help='Message to send to the team chat')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=== Team Chats Functionality Test ===")
|
||||
print(f"API URL: {args.api_url}")
|
||||
print(f"Model: {args.model}")
|
||||
print(f"User ID: {args.user_id}")
|
||||
print()
|
||||
|
||||
# Test health endpoint
|
||||
print("=== Testing Health Endpoint ===")
|
||||
health_success = test_health(args.api_url)
|
||||
print()
|
||||
|
||||
if not health_success:
|
||||
print("❌ Health check failed. Aborting tests.")
|
||||
sys.exit(1)
|
||||
|
||||
# Test creating a team chat
|
||||
print("=== Creating Team Chat ===")
|
||||
chat_success, chat_id = test_create_team_chat(
|
||||
args.api_url,
|
||||
args.user_id,
|
||||
f"Team Chat Test {time.strftime('%Y-%m-%d %H:%M:%S')}",
|
||||
args.model
|
||||
)
|
||||
print()
|
||||
|
||||
if not chat_success or not chat_id:
|
||||
print("❌ Failed to create team chat. Aborting tests.")
|
||||
sys.exit(1)
|
||||
|
||||
# Test adding a team member
|
||||
print("=== Adding Team Member ===")
|
||||
member_success = test_add_team_member(args.api_url, chat_id, args.second_user_id)
|
||||
print()
|
||||
|
||||
# Test sending a message to the team chat
|
||||
print("=== Sending Message to Team Chat ===")
|
||||
message_success = test_send_team_message(args.api_url, chat_id, args.user_id, args.message)
|
||||
print()
|
||||
|
||||
# Test getting user chats
|
||||
print("=== Getting User Chats ===")
|
||||
chats_success, chats = test_get_user_chats(args.api_url, args.user_id)
|
||||
print()
|
||||
|
||||
# Print summary
|
||||
print("=== Test Summary ===")
|
||||
print(f"Health Endpoint: {'✅ SUCCESS' if health_success else '❌ FAILED'}")
|
||||
print(f"Create Team Chat: {'✅ SUCCESS' if chat_success else '❌ FAILED'}")
|
||||
print(f"Add Team Member: {'✅ SUCCESS' if member_success else '❌ FAILED'}")
|
||||
print(f"Send Team Message: {'✅ SUCCESS' if message_success else '❌ FAILED'}")
|
||||
print(f"Get User Chats: {'✅ SUCCESS' if chats_success else '❌ FAILED'}")
|
||||
|
||||
if chat_success and chat_id:
|
||||
print(f"\nCreated team chat ID: {chat_id}")
|
||||
print("You can now check the OpenWebUI interface to see if the channel was created")
|
||||
print("and if the message was sent to the channel.")
|
||||
print(f"OpenWebUI URL: http://104.225.217.215:8080/")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user