Skip to content

Commit

Permalink
feat: add project
Browse files Browse the repository at this point in the history
  • Loading branch information
kense-lab committed Jan 3, 2024
1 parent 565e5a3 commit 68b99ba
Show file tree
Hide file tree
Showing 113 changed files with 6,919 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
__pycache__/
.idea/
venv/
.venv
.env
volumes/
logs
.vscode
.DS_Store
51 changes: 51 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# app
APP_NAME=open-assistant-api
APP_DEBUG=True
APP_ENV=local
APP_SERVER_HOST=0.0.0.0
APP_SERVER_PORT=8086
APP_SERVER_WORKERS=1
APP_API_PREFIX=/api

LOG_LEVEL=DEBUG

# database
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=open_assistant
DB_USER=root
DB_PASSWORD=123456
DB_POOL_SIZE=1

# redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
REDIS_PASSWORD=123456

# file service
FILE_SERVICE_MODULE=app.services.file.impl.oss_file.OSSFileService

# s3 storage
S3_ENDPOINT=http://minio:9000
S3_BUCKET_NAME=oas
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_REGION=us-east-1

# celery
CELERY_BROKER_URL=redis://:[email protected]:6379/1

# llm
OPENAI_API_BASE=
OPENAI_API_KEY=
LLM_MAX_STEP=25

# tool
TOOL_WORKER_NUM=10
TOOL_WORKER_EXECUTION_TIMEOUT=180

# web search tool
BING_SEARCH_URL=https://api.bing.microsoft.com/v7.0/search
BING_SUBSCRIPTION_KEY=xxxx
WEB_SEARCH_NUM_RESULTS=5
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
__pycache__/
.idea/
venv/
.venv
.env
volumes/
logs
.vscode
.DS_Store
28 changes: 28 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
FROM python:3.10-slim AS base

LABEL maintainer="[email protected]"

COPY docker/sources.list /etc/apt/sources.list
RUN apt-get update \
&& apt-get install -y --no-install-recommends bash curl wget vim libmagic-dev \
&& apt-get autoremove \
&& rm -rf /var/lib/apt/lists/*

RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ --no-cache-dir poetry \
&& poetry config virtualenvs.create false

COPY poetry.lock /env/poetry.lock
COPY pyproject.toml /env/pyproject.toml

RUN cd /env && poetry install --no-dev

EXPOSE 8086

WORKDIR /app

COPY . /app

COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.PHONY: all format lint

all: help

help:
@echo "make"

@echo " format"
@echo " Apply black formatting to code."
@echo " lint"
@echo " Lint code with ruff, and check if black formatter should be applied."

format:
poetry run black .
poetry run ruff . --fix

lint:
poetry run black . --check
poetry run ruff .
111 changes: 110 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,110 @@
# open-assistant-api
<div align="center">

# Open Assistant Api

_✨ 开箱即用的 AI 智能助手 API ✨_

</div>

## 简介

Open Assistant API 是一个开源自托管的 AI 智能助手 API,兼容 OpenAI 官方接口,
可以直接使用 OpenAI 官方的 [Client](https://github.com/openai/openai-python) 构建 LLM 应用。

支持 [One API](https://github.com/songquanpeng/one-api) 可以用其接入更多商业和私有模型。

## 使用

以下是使用了 OpenAI 官方的 Python `openai` 库的使用示例:

```python
import openai

client = openai.OpenAI(
base_url="http://127.0.0.1:8086/api/v1",
api_key="xxx"
)

assistant = client.beta.assistants.create(
name="demo",
instructions="You are a helpful assistant.",
model="gpt-4-1106-preview"
)
```

## 为什么选择 Open Assistant API

| 功能 | Open Assistant API | OpenAI Assistant API |
|------------------|--------------------|----------------------|
| 生态策略 | 开源 | 闭源 |
| RAG 引擎 | 简单实现 | 支持 |
| 联网搜索 | 支持 | 不支持 |
| 自定义 Functions | 支持 | 支持 |
| 内置 Tool | 支持扩展 | 不支持扩展 |
| Code Interpreter | 待开发 | 支持 |
| LLM 支持 | 支持更多的 LLM | 仅 GPT |
| 本地部署 | 支持 | 不支持 |

- **LLM 支持**: 相较于 OpenAI 官方版本,可以通过接入 One API 来支持更多的模型。
- **Tool**: 目前支持联网搜索;可以较容易扩展更多的 Tool。
- **RAG 引擎**: 目前支持的文件类型有 txt、pdf、html、markdown。我们提供了一个初步的实现。
- **生态策略**: 开源,你可以将服务部署在本地,可以对已有功能进行扩展。

## 快速上手

启动 Open Assistant API 最简单方法是运行 docker-compose.yml 文件。 运行之前确保机器上安装了 Docker 和 Docker Compose。

### 配置

进入项目根目录,打开 `docker-compose.yml`,填写 openai api_key 和 bing search key (非必填)。

```sh
# openai api_key
OPENAI_API_KEY=<openai_api_key>

# bing search key (非必填)
BING_SUBSCRIPTION_KEY=<bing_subscription_key>
```

### 运行

#### 使用 Docker Compose 运行:

```sh
docker compose up -d
```

### 访问 API

Api Base URL: http://127.0.0.1:8086/api/v1

接口文档地址: http://127.0.0.1:8086/docs

### 完整使用示例

此示例中使用 OpenAI 官方的 client 库创建并运行了一个 AI 助手,包含了 web_search 和 retrieval 两个内置 tool 和一个自定义 function。
运行之前需要运行 `pip install openai` 安装 Python `openai` 库。

```sh
# !pip install openai
python tests/e2e/index.py
```

## 特别感谢

我们主要参考和依赖了以下项目:

- [OpenOpenAI](https://github.com/transitive-bullshit/OpenOpenAI): Node 实现的 Assistant API
- [One API](https://github.com/songquanpeng/one-api): 多模型管理工具
- [OpenAI-Python](https://github.com/openai/openai-python): OpenAI Python Client
- [OpenAI API](https://github.com/openai/openai-openapi): OpenAI 接口定义
- [LangChain](https://github.com/langchain-ai/langchain): LLM 应用开发库
- [OpenGPTs](https://github.com/langchain-ai/opengpts): LangChain GPTs

## 参与贡献

请阅读我们的[贡献文档](./docs/CONTRIBUTING.md),了解如何参与贡献。

## 开源协议

本仓库遵循 MIT 开源协议。有关详细信息,请参阅 [LICENSE](./LICENSE) 文件。
72 changes: 72 additions & 0 deletions alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = migrations

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d-%%(minute).2d_%%(rev)s

# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =

# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false

# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false

# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Empty file added app/__init__.py
Empty file.
Empty file added app/api/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions app/api/deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from sqlmodel import Session

from app.providers import database


def get_session():
with Session(database.engine) as session:
yield session
11 changes: 11 additions & 0 deletions app/api/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from fastapi import APIRouter
from app.api.v1 import assistant, assistant_file, thread, message, files, runs

api_router = APIRouter(prefix="/v1")

api_router.include_router(assistant.router, prefix="/assistants", tags=["assistants"])
api_router.include_router(assistant_file.router, prefix="/assistants", tags=["assistants"])
api_router.include_router(thread.router, prefix="/threads", tags=["threads"])
api_router.include_router(message.router, prefix="/threads", tags=["messages"])
api_router.include_router(runs.router, prefix="/threads", tags=["runs"])
api_router.include_router(files.router, prefix="/files", tags=["files"])
Empty file added app/api/v1/__init__.py
Empty file.
50 changes: 50 additions & 0 deletions app/api/v1/assistant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from fastapi import APIRouter, Depends
from sqlmodel import Session, select

from app.api.deps import get_session
from app.models.assistant import Assistant, AssistantUpdate
from app.libs.paginate import cursor_page, CommonPage
from app.schemas.common import DeleteResponse
from app.services.assistant.assistant import AssistantService

router = APIRouter()


@router.get("", response_model=CommonPage[Assistant])
def list_assistants(*, session: Session = Depends(get_session)):
"""
Returns a list of assistants.
"""
return cursor_page(select(Assistant), session)


@router.post("", response_model=Assistant)
def create_assistant(*, session: Session = Depends(get_session), body: Assistant) -> Assistant:
"""
Create an assistant with a model and instructions.
"""
return AssistantService.create_assistant(session=session, body=body)


@router.get("/{assistant_id}", response_model=Assistant)
def get_assistant(*, session: Session = Depends(get_session), assistant_id: str) -> Assistant:
"""
Retrieves an assistant.
"""
return AssistantService.get_assistant(session=session, assistant_id=assistant_id)


@router.post("/{assistant_id}", response_model=Assistant)
def modify_assistant(*, session: Session = Depends(get_session), assistant_id: str, body: AssistantUpdate) -> Assistant:
"""
Modifies an assistant.
"""
return AssistantService.modify_assistant(session=session, assistant_id=assistant_id, body=body)


@router.delete("/{assistant_id}", response_model=DeleteResponse)
def delete_assistant(*, session: Session = Depends(get_session), assistant_id: str) -> DeleteResponse:
"""
Delete an assistant.
"""
return AssistantService.delete_assistant(session=session, assistant_id=assistant_id)
Loading

0 comments on commit 68b99ba

Please sign in to comment.