diff --git a/.env b/.env index 8e75a8b..abe61a2 100644 --- a/.env +++ b/.env @@ -23,7 +23,7 @@ EXTRA_ROUTE_PREFIX= # ratelimit-string format [count] [per|/] [n (optional)] [second|minute|hour|day|month|year] :ref:`ratelimit-string`: https://limits.readthedocs.io/en/stable/quickstart.html#rate-limit-string-notation ROUTE_RATE_LIMIT={"/healthz": "60/2minutes", "/v1/chat/completions": "15/minute;200/hour"} -# `GLOBAL_RATE_LIMIT`: 所有`RATE_LIMIT`没有指定的路由. 不填默认无限制 +# `GLOBAL_RATE_LIMIT`: 所有`ROUTE_RATE_LIMIT`没有指定的路由. 不填默认无限制 GLOBAL_RATE_LIMIT=30/minute #`RATE_LIMIT_STRATEGY` Options: (fixed-window, fixed-window-elastic-expiry, moving-window) :ref: https://limits.readthedocs.io/en/latest/strategies.html @@ -32,7 +32,7 @@ RATE_LIMIT_STRATEGY=moving-window # TPM: 返回的token速率限制 -TOKEN_RATE_LIMIT=40/second +TOKEN_RATE_LIMIT=50/second TIMEOUT=300 diff --git a/.env.example b/.env.example index c80fa5d..4e7b3b2 100644 --- a/.env.example +++ b/.env.example @@ -29,7 +29,7 @@ ROUTE_RATE_LIMIT='{ "/localai/v1/chat/completions": "2/second" }' -# `GLOBAL_RATE_LIMIT`: 所有`RATE_LIMIT`没有指定的路由. 不填默认无限制 +# `GLOBAL_RATE_LIMIT`: 所有`ROUTE_RATE_LIMIT`没有指定的路由. 不填默认无限制 GLOBAL_RATE_LIMIT=2/5seconds #`RATE_LIMIT_STRATEGY` Options: (fixed-window, fixed-window-elastic-expiry, moving-window) ref: https://limits.readthedocs.io/en/latest/strategies.html diff --git a/.github/images/startup.png b/.github/images/startup.png new file mode 100644 index 0000000..5c820c0 Binary files /dev/null and b/.github/images/startup.png differ diff --git a/.github/workflows/issue-close.yml b/.github/workflows/issue-close.yml new file mode 100644 index 0000000..593aead --- /dev/null +++ b/.github/workflows/issue-close.yml @@ -0,0 +1,22 @@ +name: Close inactive issues +on: + schedule: + - cron: '0 9 * * *' + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + days-before-issue-stale: 30 + days-before-issue-close: 7 + stale-issue-label: stale + stale-issue-message: This issue is stale because it has been open for 30 days with no activity. + close-issue-message: This issue was closed because it has been inactive for 7 days since being marked as stale. + days-before-pr-stale: -1 + days-before-pr-close: -1 + repo-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/Examples/chat.py b/Examples/chat.py index 642ac8d..fcf1d9a 100644 --- a/Examples/chat.py +++ b/Examples/chat.py @@ -1,5 +1,3 @@ -import time - import openai from rich import print from sparrow import yaml_load diff --git a/README.md b/README.md index 5a2db97..370d0dd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@


- AI forward + OpenAI forward

@@ -37,17 +37,20 @@

-[功能](#功能) | -[部署指南](#部署指南) | -[应用](#应用) | -[配置选项](#配置选项) | +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/beidongjiedeguang/openai-forward) + +[特点](#特点) | +[部署指南](deploy.md) | +[使用](#使用) | +[配置](#配置) | [对话日志](#对话日志) -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/beidongjiedeguang/openai-forward)
-本项目是大模型与用户层之间的一道转发服务,可用于搭建反向代理,用户速率限制,token速率限制,自定义API KEY 等. +OpenAI-Forward是大模型与用户层之间的一道转发服务, +用于对请求模型的速率限制,模型返回的Token速率限制,自定义API KEY 等。 + @@ -56,38 +59,73 @@ ### 特点 -AI-Forward支持以下功能: +OpenAI-Forward支持以下功能: - **万能代理**: 几乎可以转发任何请求 - **用户速率限制**: 提供请求速率限制(**RPM**)与流式返回的Token速率限制(**TPM**) -- **自定义秘钥**: 支持用户使用自定义生成的秘钥代替原始api key使用。 +- **自定义秘钥**: 支持用户使用自定义生成的秘钥代替原始api key使用。 - 流式响应的对话日志 - 可同时转发多个目标服务至不同路由 - 失败请求自动重试 -- 一分钟内完成安装与部署 +- 一分钟内完成安装与部署; 一键部署至云端 - ... - 由本项目搭建的代理服务地址: > https://api.openai-forward.com -> https://render.openai-forward.com +> https://render.openai-forward.com + +注:本项目中提供的所有代理服务仅供学习使用,请勿用作其它用途。 + -注:这里提供的代理地址服务仅限于学生/研究人员使用,若要长期使用请参考部署文档自行搭建。 ## 部署指南 -见 👉 [部署文档](deploy.md) - + 👉 [部署文档](deploy.md) -## 使用方式 +## 使用 +**安装** +```bash +pip install openai-forward +``` +**运行** +```bash +aifd run +``` +如果一切正常将会看到下面的启动信息 -### 反向代理应用: +```bash +❯ aifd run +╭────── 🤗 openai-forward is ready to serve! ───────╮ +│ │ +│ base url https://api.openai.com │ +│ route prefix / │ +│ api keys False │ +│ forward keys False │ +│ Log chat False │ +╰────────────────────────────────────────────────────╯ +╭──────────── ⏱️ Rate Limit configuration ───────────╮ +│ │ +│ strategy moving-window │ +│ /healthz 60/2minutes │ +│ /v1/chat/completions 15/minute;200/hour │ +│ global_rate_limit 30/minute │ +│ token_rate_limit 50/second │ +│ token_interval_time 0.0200s │ +╰────────────────────────────────────────────────────╯ +INFO: Started server process [33811] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) +``` + +### 代理OpenAI API: +这也是`aifd run`的默认选项 #### 在第三方应用中使用 @@ -97,8 +135,6 @@ AI-Forward支持以下功能: 基于开源项目[ChatGPT-Next-Web](https://github.com/Yidadaa/ChatGPT-Next-Web)搭建自己的chatgpt服务 替换docker启动命令中的 `BASE_URL`为自己搭建的代理服务地址 - - ```bash docker run -d \ -p 3000:3000 \ @@ -123,7 +159,6 @@ docker run -d \ openai.api_key = "sk-******" ``` - **JS/TS** ```diff @@ -162,14 +197,23 @@ curl --location 'https://api.openai-forward.com/v1/images/generations' \ -### 与众大模型服务结合使用 +### 代理本地模型 与 [LocalAI](https://github.com/go-skynet/LocalAI), -[api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm), -[claude-to-chatgpt](https://github.com/jtsang4/claude-to-chatgpt) 等 -一起使用,赋予这些服务接口的RPM,TPM,日志等能力。 -(待补充...) +[api-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm)等 +一起使用,赋予这些服务接口的RPM限制,TPM限制,日志等能力。 + +以LocalAI为例: +假设部署的LocalAI服务运行在 `http://localhost:8080`, +那么接下来只需修改.env配置中`OPENAI_BASE_URL=http://localhost:8080` 就可以完成对LocalAI的代理。 + +(待补充) +### 代理其它云端模型 + +例如可通过 [claude-to-chatgpt](https://github.com/jtsang4/claude-to-chatgpt) +将claude的api格式对齐为openai的格式,然后使用`openai-forward`进行代理。 +(待补充) ## 配置 @@ -182,19 +226,18 @@ curl --location 'https://api.openai-forward.com/v1/images/generations' \ **`aifd run`参数配置项** -| 配置项 | 说明 | 默认值 | -|-----------------------|-----------------------|:----------------------:| -| --port | 服务端口号 | 8000 | -| --workers | 工作进程数 | 1 | -| --log_chat | 同 LOG_CHAT | `False` | +| 配置项 | 说明 | 默认值 | +|------------|------------|:-------:| +| --port | 服务端口号 | 8000 | +| --workers | 工作进程数 | 1 | +| --log_chat | 同 LOG_CHAT | `False` | ### 环境变量配置项 支持从运行目录下的`.env`文件中读取 -配置示例见根目录下的 [.env.example](.env.example) - +配置示例见根目录下的 [.env.example](.env.example) | 环境变量 | 说明 | 默认值 | |---------------------|-----------------------------------------------------------------------------------------------------------------------------------|:----------------------:| @@ -204,12 +247,17 @@ curl --location 'https://api.openai-forward.com/v1/images/generations' \ | FORWARD_KEY | 允许调用方使用该key代替openai api key,支持多个forward key, 以逗号分隔; 如果设置了OPENAI_API_KEY,而没有设置FORWARD_KEY, 则客户端调用时无需提供密钥, 此时出于安全考虑不建议FORWARD_KEY置空 | 无 | | EXTRA_BASE_URL | 额外转发服务地址 | 无 | | EXTRA_ROUTE_PREFIX | 额外转发服务路由前缀 | 无 | +| ROUTE_RATE_LIMIT | 指定路由的请求速率限制(区分用户) | 无 | +| GLOBAL_RATE_LIMIT | 所有`RATE_LIMIT`没有指定的路由. 不填默认无限制 | 无 | +| RATE_LIMIT_STRATEGY | 速率限制策略(fixed-window, fixed-window-elastic-expiry, moving-window) | 无 | +| TOKEN_RATE_LIMIT | 对每一份流式返回的token速率限制 (这里的token并不严格等于gpt中定义的token,而是SSE的chunk) | 无 | +| PROXY | http代理 | 无 | | LOG_CHAT | 是否记录聊天内容 | `false` | 更多见 [.env.example](.env.example) 中的说明。(待完善) - ### 自定义秘钥 +
Click for more details @@ -236,7 +284,6 @@ FORWARD_KEY=fk-****** # 这里fk-token由我们自己定义 支持转发不同地址的服务至同一端口的不同路由下 用例见 `.env.example` - ### 对话日志 默认不记录对话日志,若要开启需设置环境变量`LOG_CHAT=true` @@ -298,4 +345,4 @@ aifd convert ## License -AI-Forward is licensed under the [MIT](https://opensource.org/license/mit/) license. +OpenAI-Forward is licensed under the [MIT](https://opensource.org/license/mit/) license. diff --git a/deploy.md b/deploy.md index 9d9e97f..c322fce 100644 --- a/deploy.md +++ b/deploy.md @@ -6,6 +6,11 @@
+一键部署至render +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/beidongjiedeguang/openai-forward) + + + [pip部署](#pip部署) | [docker部署](#docker部署) | [render一键部署](#render-一键部署) | @@ -16,12 +21,12 @@
本文档中提供以下几种部署方式 -**有海外vps方案** +**本地部署** 1. [pip 安装部署](deploy.md#pip部署) 2. [Docker部署](deploy.md#docker部署) -**无vps免费部署方案** +**一键免费云平台部署** 1. [Render一键部署](deploy.md#render-一键部署) 2. [Railway部署](deploy.md#Railway-一键部署) @@ -43,7 +48,7 @@ pip install openai-forward **运行服务** ```bash -openai-forward run # 或者使用别名 aifd run +aifd run ``` 服务就搭建完成了。 配置见[配置](README.md#配置选项) @@ -64,12 +69,15 @@ http://{ip}:{port}/v1/chat/completions ### 开启SSL (以https访问域名) 首先准备好一个域名, 如本项目中使用的域名为`api.openai-forward.com` -常用方式是使用nginx(不习惯用命令行配置的话可以考虑用 [Nginx Proxy Manager](https://github.com/NginxProxyManager/nginx-proxy-manager)) 代理转发 openai-forward 服务端口(默认8000)。 -需要注意的是,若要使用流式转发,在nginx配置中需要添加取消代理缓存的配置: - ```bash - proxy_cache off; - proxy_buffering off; +常用方式是使用nginx(不习惯用命令行配置的话可以考虑用 [Nginx Proxy Manager](https://github.com/NginxProxyManager/nginx-proxy-manager), 它可方便设置Let's Encrypt证书自动申请和自动续期) 代理转发 openai-forward 服务端口(默认8000)。 +需要注意的是,若要使用流式转发,在nginx配置中需要添加关闭代理缓存的配置, 即在Nginx Proxy Manager的 Custom Nginx Configuration中写入: +```bash +proxy_buffering off; ``` + + +**Q**: 使用Nginx 或 Nginx Proxy Manager可以直接对任何api进行转发,为什么要用这个库? +**A**: `openai-forward`的转发代理功能只是一项基础功能,它的日志记录、token速率限制、自定义秘钥等功能都是nginx无法直接做到的。 @@ -96,7 +104,7 @@ git clone https://github.com/beidongjiedeguang/openai-forward.git --depth=1 cd openai-forward pip install -e . -openai-forward run # 或使用别名 aifd run +aifd run ``` 启用SSL同上. @@ -111,8 +119,8 @@ openai-forward run # 或使用别名 aifd run Render应该算是所有部署中最简易的一种, 并且它生成的域名国内可以直接访问! 1. 点击一键部署按钮 - 如果提示需要绑定卡,则可先fork本仓库 -->到Render的Dashboard上 New Web Services --> Connect 到刚刚fork到仓库 后面步骤均默认即可 -2. 填写环境变量,如默认的OPENAI_API_KEY 等,也可以不填 + 也可先fork本仓库 -->到Render的Dashboard上 New Web Services --> Connect 到刚刚fork到仓库 后面步骤均默认即可 +2. 填写环境变量,`openai-forward`所有配置都可以通过环境变量设置,可以根据自己需要填写。 然后等待部署完成即可。 Render的免费计划: 每月750小时免费实例时间(意味着单个实例可以不间断运行)、100G带宽流量、500分钟构建时长. @@ -145,13 +153,14 @@ Render的免费计划: 每月750小时免费实例时间(意味着单个实例 -⚠️下面两种部署方式仅提供简单的转发服务,没有任何额外功能。 +⚠️下面两种部署方式仅提供简单的转发服务,没有任何额外功能。 +适合只有简单需求的用户(国内访问openai api服务)。 ## Vercel 一键部署 [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fbeidongjiedeguang%2Fopenai-forward&project-name=openai-forward&repository-name=openai-forward&framework=other) -因python的部署方式在vercel上存在诸多限制,因此现在将Vercel部署方式切换为直接转发。 +因python的部署方式在vercel上存在诸多限制,因此现在将Vercel部署方式切换为直接代理转发,而没有其它功能。 1. 点击按钮即可一键免费部署 也可先fork本仓库,再手动在vercel操作界面import项目 @@ -171,7 +180,7 @@ Render的免费计划: 每月750小时免费实例时间(意味着单个实例 * Pages部署: fork本仓库,在[cloudflare](https://dash.cloudflare.com/)上创建应用程序时选择Pages, 然后选择连接到Git, 选择刚刚fork的仓库即可完成部署。 * Workers部署: 在[cloudflare](https://dash.cloudflare.com/)上创建应用程序时选择Workers, 部署好示例代码后,点击快速修改(quick edit)复制[_worker.js](_worker.js) 至代码编辑器即可完成服务部署。 -绑定自定义域名: cloudflare自动分配的域名国内也无法访问,所以也需要绑定自定义域名. (**目前Pages部署时自动分配的域名国内可以直接访问**) +绑定自定义域名: **目前Pages部署时cloudflare自动分配的域名国内可以直接访问**, 而Workers部署时分配的则绑定自定义域名国内才可访问. 绑定自定义域名需要将域名默认nameserver(域名服务器)绑定到cloudflare提供的nameserver,大体上过程是: ```mermaid @@ -184,7 +193,7 @@ stateDiagram-v2 去注册域名机构更改默认nameserver为cloudflare提供的nameserver --> 在cloudflare的worker/page中添加域名: 域名服务器更改验证成功 在cloudflare的worker/page中添加域名 --> 成功 ``` -这种部署方式轻便简洁,支持流式转发. 对于没有vps的用户还是十分推荐的。不过目前[_worker.js](_worker.js)这个简单脚本仅提供转发服务, 不支持额外功能。 +这种部署方式轻便简洁,支持流式转发. 不过目前[_worker.js](_worker.js)这个简单脚本仅提供转发服务, 不支持额外功能。 > https://cloudflare.worker.openai-forward.com > https://cloudflare.page.openai-forward.com diff --git a/openai_forward/__init__.py b/openai_forward/__init__.py index ae0f19e..dc133d4 100644 --- a/openai_forward/__init__.py +++ b/openai_forward/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.5.0" +__version__ = "0.5.1" from dotenv import load_dotenv diff --git a/openai_forward/app.py b/openai_forward/app.py index eb20900..9edba0d 100644 --- a/openai_forward/app.py +++ b/openai_forward/app.py @@ -2,7 +2,7 @@ from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded -from .forwarding import get_fwd_anything_objs, get_fwd_openai_style_objs +from .forwarding import fwd_anything_objs, fwd_openai_objs from .forwarding.settings import ( RATE_LIMIT_STRATEGY, dynamic_rate_limit, @@ -34,5 +34,5 @@ def healthz(request: Request): methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"], ) -[add_route(obj) for obj in get_fwd_openai_style_objs()] -[add_route(obj) for obj in get_fwd_anything_objs()] +[add_route(obj) for obj in fwd_openai_objs()] +[add_route(obj) for obj in fwd_anything_objs()] diff --git a/openai_forward/forwarding/__init__.py b/openai_forward/forwarding/__init__.py index c26b0d5..ff07014 100644 --- a/openai_forward/forwarding/__init__.py +++ b/openai_forward/forwarding/__init__.py @@ -1,2 +1,2 @@ -from .extra import AnyForwarding, get_fwd_anything_objs -from .openai import OpenaiForwarding, get_fwd_openai_style_objs +from .extra import AnyForwarding, fwd_anything_objs +from .openai import OpenaiForwarding, fwd_openai_objs diff --git a/openai_forward/forwarding/extra.py b/openai_forward/forwarding/extra.py index ace8c97..912de74 100644 --- a/openai_forward/forwarding/extra.py +++ b/openai_forward/forwarding/extra.py @@ -12,7 +12,7 @@ def __init__(self, base_url: str, route_prefix: str, proxy=None): ) -def get_fwd_anything_objs(): +def fwd_anything_objs(): """ Generate extra forwarding objects. diff --git a/openai_forward/forwarding/openai.py b/openai_forward/forwarding/openai.py index 9a9885c..a76cab2 100644 --- a/openai_forward/forwarding/openai.py +++ b/openai_forward/forwarding/openai.py @@ -16,13 +16,11 @@ def __init__(self, base_url: str, route_prefix: str, proxy=None): self.client = httpx.AsyncClient( base_url=self.BASE_URL, proxies=proxy, http1=True, http2=False ) - self.token_counts = 0 - self.token_limit_dict = {'time': time.time(), 'count': 0} -def get_fwd_openai_style_objs(): +def fwd_openai_objs(): """ - Generate OPENAI route style forwarding objects. + Generate OPENAI api style forwarding objects. Returns: fwd_objs (list): A list of OpenaiForwarding objects.