Skip to content

Commit

Permalink
Merge pull request #397 from LlmKira/main
Browse files Browse the repository at this point in the history
Build Docker
  • Loading branch information
sudoskys authored Apr 19, 2024
2 parents 75260bc + 2e05c37 commit f071b2d
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 115 deletions.
38 changes: 24 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ or [one-api](https://github.com/songquanpeng/one-api) independently.
### 🍔 Login Modes

- `Login via url`: Use `/login token$https://provider.com` to Login. The program posts the token to the interface to
retrieve configuration information
retrieve configuration
information, [how to develop this](https://github.com/LlmKira/Openaibot/blob/81eddbff0f136697d5ad6e13ee1a7477b26624ed/app/components/credential.py#L20).
- `Login`: Use `/login https://api.com/v1$key$model` to login

### 🧀 Plugin Previews
Expand Down Expand Up @@ -105,8 +106,18 @@ or [one-api](https://github.com/songquanpeng/one-api) independently.

Refer to the [🧀 Deployment Document](https://llmkira.github.io/Docs/) for more information.

### 📦 One-click Deployment

If you are using a brand-new server, you can use the following shell to automatically install this project.

```shell
curl -sSL https://raw.githubusercontent.com/LLMKira/Openaibot/main/deploy.sh | bash
```

### 📦 Manual Installation

```shell
# Install Telegram Voice dependencies
# Install Voice dependencies
apt install ffmpeg
# Install RabbitMQ
docker pull rabbitmq:3.10-management
Expand All @@ -118,30 +129,26 @@ docker run -d -p 5672:5672 -p 15672:15672 \
rabbitmq:3.10-management
docker ps -l
# Install Project
git clone https://github.com/LlmKira/Openaibot/
cd Openaibot
pip install pdm
pdm install -G bot
cp .env.exp .env && nano .env
# Test
pdm run python3 start_sender.py
pdm run python3 start_receiver.py
# Host
pdm start pm2.json
apt install npm
npm install pm2 -g
pm2 start pm2.json
```

### 🥣 Docker

Build Hub: [sudoskys/llmbot](https://hub.docker.com/repository/docker/sudoskys/llmbot/general)

#### Automatic Docker/Docker-compose Installation

If you are using a brand new server, you can use the following shell to automatically install this project.

This script automatically installs the required services and maps ports using Docker methods. If you have
deployed `redis`, `rabbitmq`, `mongodb`, please modify the `docker-compose.yml` file accordingly.

```shell
curl -sSL https://raw.githubusercontent.com/LLMKira/Openaibot/main/deploy.sh | bash
```
> Note that if you run this project using Docker, you will start Redis, MongoDB, and RabbitMQ. But if you're running
> locally, just RabbitMQ
#### Manual Docker-compose Installation

Expand Down Expand Up @@ -185,8 +192,11 @@ Hooks control the EventMessage in sender and receiver. For example, we have `voi
you can enable it by setting `VOICE_REPLY_ME=true` in `.env`.

```shell
/env VOICE_REPLY_ME=true
/env VOICE_REPLY_ME=yes
# must

/env REECHO_VOICE_KEY=<key in dev.reecho.ai>
# not must
```

use `/env VOICE_REPLY_ME=NONE` to disable this env.
Expand Down
51 changes: 32 additions & 19 deletions app/receiver/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ async def run_pending_task(task: TaskHeader, pending_task: ToolCall):
async def process_function_call(self, message: AbstractIncomingMessage):
"""
定位,解析,运行函数。要求认证,或申请结束/继续指标。
Receive credential, or a list of function calls, attention, message may queue itself for auth.
:param message: message from queue
:return: None
"""
Expand All @@ -242,27 +243,39 @@ async def process_function_call(self, message: AbstractIncomingMessage):
f"[552351] Received A Function Call from {message.body.decode('utf-8')}"
)
task: TaskHeader = TaskHeader.model_validate_json(message.body.decode("utf-8"))
RUN_LIMIT = 6
while task.task_sign.tool_calls_pending and RUN_LIMIT > 0:
# Get Function Call
pending_task: ToolCall = await task.task_sign.get_pending_tool_call(
tool_calls_pending_now=task.task_sign.snapshot_credential,
return_default_if_empty=False,
)
if pending_task:
await self.run_task(task=task, pending_task=pending_task)
if task.task_sign.snapshot_credential:
return logger.debug(
f"Received A Credential {task.task_sign.snapshot_credential}, End Run Function Call Loop!"
)
RUN_LIMIT = 4
for pending_task in task.task_sign.tool_calls_pending:
RUN_LIMIT -= 1
# Get Function Call
pending_task: ToolCall = await task.task_sign.get_pending_tool_call(
tool_calls_pending_now=task.task_sign.snapshot_credential,
return_default_if_empty=True,
if RUN_LIMIT <= 0:
logger.error("Limit Run Times, Stop Run Function Call Loop!")
break
logger.debug(
f"Received A ToolCall {RUN_LIMIT} {len(task.task_sign.tool_calls_pending)}"
)
if not pending_task:
return logger.debug("But No ToolCall")
logger.debug("Received A ToolCall")
try:
await self.run_pending_task(task=task, pending_task=pending_task)
except Exception as e:
await task.task_sign.complete_task(
tool_calls=pending_task, success_or_not=False, run_result=str(e)
)
logger.error(f"Function Call Error {e}")
raise e
finally:
logger.trace("Function Call Finished")
await self.run_task(task=task, pending_task=pending_task)

async def run_task(self, task, pending_task):
try:
await self.run_pending_task(task=task, pending_task=pending_task)
except Exception as e:
await task.task_sign.complete_task(
tool_calls=pending_task, success_or_not=False, run_result=str(e)
)
logger.error(f"Function Call Error {e}")
raise e
finally:
logger.trace("Function Call Finished")

async def on_message(self, message: AbstractIncomingMessage):
"""
Expand Down
117 changes: 42 additions & 75 deletions deploy.sh
Original file line number Diff line number Diff line change
@@ -1,87 +1,54 @@
#!/bin/bash

# Check if the Openaibot directory exists
echo "$(tput setaf 6)Checking the Openaibot directory...$(tput sgr0)"
if [ -d "Openaibot" ]; then
# shellcheck disable=SC2164
pip uninstall llmkira
cd Openaibot && git pull && echo "$(tput setaf 6)Update successfully...$(tput sgr0)"
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml pull
docker-compose -f docker-compose.yml up -d
# Update the Openaibot project
exit 0
else
# Clone the project if not already cloned
git clone https://github.com/LlmKira/Openaibot.git
fi

echo "$(tput setaf 2)Openaibot directory check complete.$(tput sgr0)"
echo "$(tput setaf 6)Type yes will install Docker and the Openaibot project. Do you want to proceed? (y/n):$(tput sgr0) "
read -r choice
if [[ $choice =~ ^([yY][eE][sS]|[yY])$ ]]; then
sleep 1s
echo "Beginning the setup process..."

# Install Voice dependencies
echo "Installing Voice dependencies..."
apt install ffmpeg

# Pull RabbitMQ
echo "Pulling RabbitMQ..."
docker pull rabbitmq:3.10-management

# Check if RabbitMQ container exists
if [ "$(docker ps -a -f name=rabbitmq | grep rabbitmq | wc -l)" -eq 0 ]; then
# Run the RabbitMQ if not exist
echo "Running RabbitMQ..."
docker run -d -p 5672:5672 -p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=8a8a8a \
--hostname myRabbit \
--name rabbitmq \
rabbitmq:3.10-management
else
echo "$(tput setaf 1)Installation cancelled.$(tput sgr0)"
exit 0
echo "RabbitMQ already exists. Using it..."
fi

# Function to handle errors and exit
handle_error() {
echo "$(tput setaf 1)Error occurred during installation. Exiting...$(tput sgr0)"
exit 1
}

# Set error trap
trap 'handle_error' ERR
docker ps -l

# Check if Docker is installed
if ! [ -x "$(command -v docker)" ]; then
# Install Docker
echo "$(tput setaf 6)Installing Docker...$(tput sgr0)"
curl -fsSL https://get.docker.com | bash -s docker
# Clone or update the project
if [ ! -d "Openaibot" ] ; then
echo "Cloning Openaibot..."
git clone https://github.com/LlmKira/Openaibot/
cd Openaibot || exit
else
echo "$(tput setaf 2)Docker already installed.$(tput sgr0)"
echo "Updating Openaibot..."
cd Openaibot || exit
git pull
fi

# Run Docker Compose
echo "$(tput setaf 6)Running Docker Compose...$(tput sgr0)"
# 检查是否安装docker-compose
if ! [ -x "$(command -v docker-compose)" ]; then
# Install Docker
echo "$(tput setaf 6)Installing docker-compose...$(tput sgr0)"
curl -L "https://github.com/docker/compose/releases/download/v2.14.0/docker-compose-linux-$(uname -m)" >/usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
else
echo "$(tput setaf 2)docker-compose already installed.$(tput sgr0)"
fi
# Change directory to the project
cd Openaibot || echo "DO NOT Exist Openaibot Dir" && exit

# Install project dependencies
echo "$(tput setaf 6)Installing project dependencies...$(tput sgr0)"
python3 -m pip install poetry
poetry config virtualenvs.in-project true &&
poetry install --all-extras
python3 -m pip install rich loguru && python3 start_tutorial.py
echo "$(tput setaf 2)Project dependencies installation complete.$(tput sgr0)"
echo "Setting up Python dependencies..."
pip install pdm
pdm install -G bot
cp .env.exp .env && nano .env

# Copy .env.exp to .env if .env doesn't exist
if [ ! -f ".env" ]; then
echo "$(tput setaf 6)Copying .env.example to .env...$(tput sgr0)"
cp .env.exp .env
nano .env
echo "$(tput setaf 2).env file copy complete.$(tput sgr0)"
# Install or update pm2
if ! [ -x "$(command -v pm2)" ]; then
echo "Installing npm and pm2..."
apt install npm
npm install pm2 -g
fi

if [ ! -f "docker-compose.yml" ]; then
echo "$(tput setaf 6)Run docker-compose.yml...$(tput sgr0)"
docker-compose -f docker-compose.yml up -d
fi

echo "$(tput setaf 2)Docker Compose completed with:docker-compose -f docker-compose.yml up -d$(tput sgr0)"

# Remove the error trap
trap - ERR
echo "Starting application with pm2..."
pm2 start pm2.json

echo "$(tput setaf 2)Installation completed successfully.$(tput sgr0)"
echo "Setup complete!"
6 changes: 3 additions & 3 deletions llmkira/extra/plugins/search/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ class SearchTool(BaseTool):
env_prefix: str = "SERPER_"

def require_auth(self, env_map: dict) -> bool:
if "SERPER_API_KEY" in env_map:
return False
return True
if env_map.get("SERPER_API_KEY", None) is None:
return True
return False

@classmethod
def env_help_docs(cls, empty_env: List[str]) -> str:
Expand Down
2 changes: 1 addition & 1 deletion llmkira/kv_manager/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
DEFAULT_INSTRUCTION = (
"[ASSISTANT RULE]"
"SPEAK IN MORE CUTE STYLE, DONT REPEAT, ACT STEP BY STEP, CALL USER MASTER, REPLY IN USER "
"LANGUAGE"
"LANGUAGE, ACT STEP BY STEP"
"[RULE END]"
)

Expand Down
13 changes: 13 additions & 0 deletions llmkira/openai/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ def check_content(cls, v):
raise ValueError("content must be a string or a list of ContentPart")

def add_text(self, text: str):
"""
Add a text to the message
:param text: text
:return: self
"""
self.content.append(ContentPart.create_text(text=text))
return self

Expand All @@ -255,6 +260,14 @@ def add_image(
image_url: Union[str, bytes],
detail: Literal["low", "high", "auto"] = "auto",
):
"""
Add an image to the message
:param image_url: image url or image bytes
:param detail: image detail
:return: self
:raises ValueError: detail must be low, high or auto
:raises ValueError: url must be a http url or `data:image/jpeg;base64,` as base64 image
"""
self.content.append(ContentPart.create_image(url=image_url, detail=detail))
return self

Expand Down
2 changes: 1 addition & 1 deletion llmkira/openai/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def make_url(base_url: str):
def check_vision(self):
if not self.model.startswith(("gpt-4-vision", "gpt-4-turbo", "claude-3")):
logger.info(
"Remove the image content part from the messages, because the model is not supported."
"Try to remove the image content part from the messages, because the model is not supported."
)
for message in self.messages:
if isinstance(message, UserMessage) and isinstance(
Expand Down
2 changes: 1 addition & 1 deletion llmkira/sdk/tools/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def require_auth(self, env_map: dict) -> bool:
"""
Check if authentication is required
"""
return False
return True

@final
def get_os_env(self, env_name):
Expand Down
5 changes: 4 additions & 1 deletion llmkira/task/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ async def format_user_message(self, name: str = None) -> UserMessage:
for file in self.files:
if file.file_name.endswith((".jpg", ".jpeg", ".png")):
file_bytes = await file.download_file()
content_list.append(ContentPart.create_image(file_bytes))
try:
content_list.append(ContentPart.create_image(file_bytes))
except Exception as e:
logger.error(f"Error when create image: {e}")
return UserMessage(
role="user",
name=name,
Expand Down

0 comments on commit f071b2d

Please sign in to comment.