📖 Docs: Update the documentation, add an English README.
Browse files- README.md +53 -52
- README_CN.md +190 -0
- test/parse_markdown.py +159 -0
- test/translate_md.py +50 -0
README.md
CHANGED
@@ -1,71 +1,72 @@
|
|
1 |
# uni-api
|
2 |
|
3 |
<p align="center">
|
4 |
-
|
5 |
<img src="https://img.shields.io/badge/Join Telegram Group-blue?&logo=telegram">
|
6 |
</a>
|
7 |
-
|
8 |
<img src="https://img.shields.io/docker/pulls/yym68686/uni-api?color=blue" alt="docker pull">
|
9 |
</a>
|
10 |
</p>
|
11 |
|
|
|
12 |
|
13 |
## Introduction
|
14 |
|
15 |
-
|
16 |
|
17 |
## Features
|
18 |
|
19 |
-
-
|
20 |
-
-
|
21 |
-
-
|
22 |
-
-
|
23 |
-
-
|
24 |
-
-
|
25 |
-
1.
|
26 |
-
2.
|
27 |
-
3.
|
28 |
-
4.
|
29 |
-
-
|
30 |
-
-
|
31 |
-
-
|
32 |
|
33 |
## Configuration
|
34 |
|
35 |
-
|
36 |
|
37 |
```yaml
|
38 |
providers:
|
39 |
-
- provider: provider_name #
|
40 |
-
base_url: https://api.your.com/v1/chat/completions #
|
41 |
-
api: sk-YgS6GTi0b4bEabc4C #
|
42 |
-
model: #
|
43 |
-
- gpt-4o #
|
44 |
-
- claude-3-5-sonnet-20240620: claude-3-5-sonnet #
|
45 |
- dall-e-3
|
46 |
|
47 |
- provider: anthropic
|
48 |
base_url: https://api.anthropic.com/v1/messages
|
49 |
-
api: #
|
50 |
- sk-ant-api03-bNnAOJyA-xQw_twAA
|
51 |
- sk-ant-api02-bNnxxxx
|
52 |
model:
|
53 |
-
- claude-3-5-sonnet-20240620: claude-3-5-sonnet #
|
54 |
-
tools: true #
|
55 |
|
56 |
- provider: gemini
|
57 |
-
base_url: https://generativelanguage.googleapis.com/v1beta # base_url
|
58 |
api: AIzaSyAN2k6IRdgw
|
59 |
model:
|
60 |
- gemini-1.5-pro
|
61 |
-
- gemini-1.5-flash-exp-0827: gemini-1.5-flash #
|
62 |
-
- gemini-1.5-flash-exp-0827 #
|
63 |
tools: true
|
64 |
|
65 |
- provider: vertex
|
66 |
-
project_id: gen-lang-client-xxxxxxxxxxxxxx #
|
67 |
-
private_key: "-----BEGIN PRIVATE KEY-----\nxxxxx\n-----END PRIVATE" #
|
68 |
-
client_email: [email protected] #
|
69 |
model:
|
70 |
- gemini-1.5-pro
|
71 |
- gemini-1.5-flash
|
@@ -74,7 +75,7 @@ providers:
|
|
74 |
- claude-3-sonnet@20240229: claude-3-sonnet
|
75 |
- claude-3-haiku@20240307: claude-3-haiku
|
76 |
tools: true
|
77 |
-
notes: https://xxxxx.com/ #
|
78 |
|
79 |
- provider: other-provider
|
80 |
base_url: https://api.xxx.com/v1/messages
|
@@ -82,40 +83,40 @@ providers:
|
|
82 |
model:
|
83 |
- causallm-35b-beta2ep-q6k: causallm-35b
|
84 |
tools: false
|
85 |
-
engine: openrouter #
|
86 |
|
87 |
api_keys:
|
88 |
-
- api: sk-KjjI60Yf0JFWtfgRmXqFWyGtWUd9GZnmi3KlvowmRWpWpQRo # API Key
|
89 |
-
model: #
|
90 |
-
- gpt-4o #
|
91 |
-
- claude-3-5-sonnet #
|
92 |
-
- gemini/* #
|
93 |
role: admin
|
94 |
|
95 |
- api: sk-pkhf60Yf0JGyJygRmXqFQyTgWUd9GZnmi3KlvowmRWpWqrhy
|
96 |
model:
|
97 |
-
- anthropic/claude-3-5-sonnet #
|
98 |
preferences:
|
99 |
-
USE_ROUND_ROBIN: true #
|
100 |
-
AUTO_RETRY: true #
|
101 |
-
RATE_LIMIT: 2/min #
|
102 |
|
103 |
-
#
|
104 |
- api: sk-KjjI60Yf0JFWtxxxxxxxxxxxxxxwmRWpWpQRo
|
105 |
model:
|
106 |
-
- gcp1/*: 5 #
|
107 |
-
- gcp2/*: 3 #
|
108 |
-
- gcp3/*: 2 #
|
109 |
|
110 |
preferences:
|
111 |
-
USE_ROUND_ROBIN: true #
|
112 |
AUTO_RETRY: true
|
113 |
```
|
114 |
|
115 |
-
##
|
116 |
|
117 |
-
- CONFIG_URL:
|
118 |
-
- TIMEOUT:
|
119 |
|
120 |
## Docker Local Deployment
|
121 |
|
@@ -142,7 +143,7 @@ services:
|
|
142 |
- ./api.yaml:/home/api.yaml
|
143 |
```
|
144 |
|
145 |
-
CONFIG_URL
|
146 |
|
147 |
Run Docker Compose container in the background
|
148 |
|
|
|
1 |
# uni-api
|
2 |
|
3 |
<p align="center">
|
4 |
+
<a href="https://t.me/uni_api">
|
5 |
<img src="https://img.shields.io/badge/Join Telegram Group-blue?&logo=telegram">
|
6 |
</a>
|
7 |
+
<a href="https://hub.docker.com/repository/docker/yym68686/uni-api">
|
8 |
<img src="https://img.shields.io/docker/pulls/yym68686/uni-api?color=blue" alt="docker pull">
|
9 |
</a>
|
10 |
</p>
|
11 |
|
12 |
+
[English](./README.md) | [Chinese](./README_CN.md)
|
13 |
|
14 |
## Introduction
|
15 |
|
16 |
+
If used personally, one/new-api is too complex and has many commercial features that individuals do not need. If you don't want a complicated frontend interface and want to support more models, you can try uni-api. This is a project that manages large model APIs uniformly. It allows you to call multiple backend services through a unified API interface, converting them uniformly to OpenAI format and supporting load balancing. The currently supported backend services include: OpenAI, Anthropic, Gemini, Vertex, DeepBricks, OpenRouter, etc.
|
17 |
|
18 |
## Features
|
19 |
|
20 |
+
- No front-end, purely configuration file to set up API channels. You can run your own API site just by writing a file. The documentation has detailed configuration guidelines, friendly for beginners.
|
21 |
+
- Unified management of multiple backend services, supporting OpenAI, Deepseek, DeepBricks, OpenRouter, and other API providers in the OpenAI format. Supports OpenAI Dalle-3 image generation.
|
22 |
+
- Supports Anthropic, Gemini, Vertex API simultaneously. Vertex supports both Claude and Gemini API.
|
23 |
+
- Support native tool use function calls for OpenAI, Anthropic, Gemini, Vertex.
|
24 |
+
- Supports OpenAI, Anthropic, Gemini, Vertex native image recognition API.
|
25 |
+
- Supports four types of load balancing.
|
26 |
+
1. Supports channel-level weighted load balancing, which can allocate requests based on different channel weights. By default, it is not enabled and requires channel weight configuration.
|
27 |
+
2. Supports Vertex region-level load balancing, supports Vertex high concurrency, and can increase Gemini and Claude concurrency by up to (number of APIs * number of regions) times. Automatically enabled without additional configuration.
|
28 |
+
3. Except for Vertex region-level load balancing, all APIs support channel-level sequential load balancing, enhancing the immersive translation experience. Automatically enabled without additional configuration.
|
29 |
+
4. Support automatic API key-level polling load balancing for multiple API Keys in a single channel.
|
30 |
+
- Supports automatic retry. When an API channel response fails, automatically retry the next API channel.
|
31 |
+
- Supports fine-grained access control. Supports using wildcards to set specific models available for API key channels.
|
32 |
+
- Supports rate limiting, can set the maximum number of requests per minute, can be set as an integer, such as 2/min, 2 times per minute, 5/hour, 5 times per hour, 10/day, 10 times per day, 10/month, 10 times per month, 10/year, 10 times per year. Default is 60/min.
|
33 |
|
34 |
## Configuration
|
35 |
|
36 |
+
Using the api.yaml configuration file, you can configure multiple models, and each model can configure multiple backend services, supporting load balancing. Below is an example of the api.yaml configuration file:
|
37 |
|
38 |
```yaml
|
39 |
providers:
|
40 |
+
- provider: provider_name # Service provider name, such as openai, anthropic, gemini, openrouter, deepbricks, arbitrary name, required
|
41 |
+
base_url: https://api.your.com/v1/chat/completions # Backend service API address, required
|
42 |
+
api: sk-YgS6GTi0b4bEabc4C # Provider's API Key, required
|
43 |
+
model: # At least one model
|
44 |
+
- gpt-4o # Usable model name, required
|
45 |
+
- claude-3-5-sonnet-20240620: claude-3-5-sonnet # Rename model, claude-3-5-sonnet-20240620 is the provider's model name, claude-3-5-sonnet is the renamed name, can use a short name instead of the original complex name, optional
|
46 |
- dall-e-3
|
47 |
|
48 |
- provider: anthropic
|
49 |
base_url: https://api.anthropic.com/v1/messages
|
50 |
+
api: # Supports multiple API Keys, multiple keys automatically enable polling load balancing, at least one key, required
|
51 |
- sk-ant-api03-bNnAOJyA-xQw_twAA
|
52 |
- sk-ant-api02-bNnxxxx
|
53 |
model:
|
54 |
+
- claude-3-5-sonnet-20240620: claude-3-5-sonnet # Rename model, claude-3-5-sonnet-20240620 is the provider's model name, claude-3-5-sonnet is the renamed name, can use a short name instead of the original complex name, optional
|
55 |
+
tools: true # Whether to support tools, such as generating code, generating documents, etc., default is true, optional
|
56 |
|
57 |
- provider: gemini
|
58 |
+
base_url: https://generativelanguage.googleapis.com/v1beta # base_url supports v1beta/v1, only for Gemini models, required
|
59 |
api: AIzaSyAN2k6IRdgw
|
60 |
model:
|
61 |
- gemini-1.5-pro
|
62 |
+
- gemini-1.5-flash-exp-0827: gemini-1.5-flash # After renaming, the original model name gemini-1.5-flash-exp-0827 cannot be used, if you want to use the original name, you can add the original name in the model, just add the following line to use the original name
|
63 |
+
- gemini-1.5-flash-exp-0827 # Add this line, both gemini-1.5-flash-exp-0827 and gemini-1.5-flash can be requested
|
64 |
tools: true
|
65 |
|
66 |
- provider: vertex
|
67 |
+
project_id: gen-lang-client-xxxxxxxxxxxxxx # Description: Your Google Cloud project ID. Format: String, usually composed of lowercase letters, numbers, and hyphens. How to obtain: You can find your project ID in the project selector of the Google Cloud Console.
|
68 |
+
private_key: "-----BEGIN PRIVATE KEY-----\nxxxxx\n-----END PRIVATE" # Description: The private key of the Google Cloud Vertex AI service account. Format: A JSON formatted string containing the private key information of the service account. How to obtain: Create a service account in the Google Cloud Console, generate a JSON formatted key file, and then set its content as the value of this environment variable.
|
69 |
+
client_email: [email protected] # Description: The email address of the Google Cloud Vertex AI service account. Format: Usually a string like "[email protected]". How to obtain: Generated when creating the service account, you can also view the service account details in the "IAM & Admin" section of the Google Cloud Console.
|
70 |
model:
|
71 |
- gemini-1.5-pro
|
72 |
- gemini-1.5-flash
|
|
|
75 |
- claude-3-sonnet@20240229: claude-3-sonnet
|
76 |
- claude-3-haiku@20240307: claude-3-haiku
|
77 |
tools: true
|
78 |
+
notes: https://xxxxx.com/ # Can put the provider's website, notes, official documentation, optional
|
79 |
|
80 |
- provider: other-provider
|
81 |
base_url: https://api.xxx.com/v1/messages
|
|
|
83 |
model:
|
84 |
- causallm-35b-beta2ep-q6k: causallm-35b
|
85 |
tools: false
|
86 |
+
engine: openrouter # Force the use of a specific message format, currently supports gpt, claude, gemini, openrouter native format, optional
|
87 |
|
88 |
api_keys:
|
89 |
+
- api: sk-KjjI60Yf0JFWtfgRmXqFWyGtWUd9GZnmi3KlvowmRWpWpQRo # API Key, required for users to use this service
|
90 |
+
model: # Models that this API Key can use, required
|
91 |
+
- gpt-4o # Usable model name, can use all gpt-4o models provided by providers
|
92 |
+
- claude-3-5-sonnet # Usable model name, can use all claude-3-5-sonnet models provided by providers
|
93 |
+
- gemini/* # Usable model name, can only use all models provided by the provider named gemini, where gemini is the provider name, * represents all models
|
94 |
role: admin
|
95 |
|
96 |
- api: sk-pkhf60Yf0JGyJygRmXqFQyTgWUd9GZnmi3KlvowmRWpWqrhy
|
97 |
model:
|
98 |
+
- anthropic/claude-3-5-sonnet # Usable model name, can only use the claude-3-5-sonnet model provided by the provider named anthropic. Models of other providers' claude-3-5-sonnet cannot be used.
|
99 |
preferences:
|
100 |
+
USE_ROUND_ROBIN: true # Whether to use polling load balancing, true to use, false to not use, default is true. When polling is enabled, each request model is requested in the order configured in the model. It is not related to the original channel order in providers. Therefore, you can set different request sequences for each API key.
|
101 |
+
AUTO_RETRY: true # Whether to automatically retry, automatically retry the next provider, true to automatically retry, false to not automatically retry, default is true
|
102 |
+
RATE_LIMIT: 2/min # Supports rate limiting, maximum number of requests per minute, can be set to an integer, such as 2/min, 2 times per minute, 5/hour, 5 times per hour, 10/day, 10 times per day, 10/month, 10 times per month, 10/year, 10 times per year. Default is 60/min, optional
|
103 |
|
104 |
+
# Channel-level weighted load balancing configuration example
|
105 |
- api: sk-KjjI60Yf0JFWtxxxxxxxxxxxxxxwmRWpWpQRo
|
106 |
model:
|
107 |
+
- gcp1/*: 5 # The number after the colon is the weight, weights only support positive integers.
|
108 |
+
- gcp2/*: 3 # The larger the number, the greater the probability of being requested.
|
109 |
+
- gcp3/*: 2 # In this example, there are a total of 10 weights for all channels, and 5 out of 10 requests will request the gcp1/* model, 2 requests will request the gcp2/* model, and 3 requests will request the gcp3/* model.
|
110 |
|
111 |
preferences:
|
112 |
+
USE_ROUND_ROBIN: true # When USE_ROUND_ROBIN must be true and there is no weight after the channels above, it will request in the original channel order, if there is weight, it will request in the weighted order.
|
113 |
AUTO_RETRY: true
|
114 |
```
|
115 |
|
116 |
+
## Environment variables
|
117 |
|
118 |
+
- CONFIG_URL: The download address of the configuration file, it can be a local file or a remote file, optional
|
119 |
+
- TIMEOUT: Request timeout, default is 20 seconds. The timeout can control the time needed to switch to the next channel when a channel does not respond. Optional
|
120 |
|
121 |
## Docker Local Deployment
|
122 |
|
|
|
143 |
- ./api.yaml:/home/api.yaml
|
144 |
```
|
145 |
|
146 |
+
CONFIG_URL is a direct link that can automatically download remote configuration files. For example, if you find it inconvenient to modify configuration files on a certain platform, you can upload the configuration file to a hosting service and provide a direct link for uni-api to download. CONFIG_URL is this direct link.
|
147 |
|
148 |
Run Docker Compose container in the background
|
149 |
|
README_CN.md
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# uni-api
|
2 |
+
|
3 |
+
<p align="center">
|
4 |
+
<a href="https://t.me/uni_api">
|
5 |
+
<img src="https://img.shields.io/badge/Join Telegram Group-blue?&logo=telegram">
|
6 |
+
</a>
|
7 |
+
<a href="https://hub.docker.com/repository/docker/yym68686/uni-api">
|
8 |
+
<img src="https://img.shields.io/docker/pulls/yym68686/uni-api?color=blue" alt="docker pull">
|
9 |
+
</a>
|
10 |
+
</p>
|
11 |
+
|
12 |
+
[英文](./README.md) | [中文](./README_CN.md)
|
13 |
+
|
14 |
+
## Introduction
|
15 |
+
|
16 |
+
如果个人使用的话,one/new-api 过于复杂,有很多个人不需要使用的商用功能,如果你不想要复杂的前端界面,有想要支持的模型多一点,可以试试 uni-api。这是一个统一管理大模型API的项目,可以通过一个统一的API接口调用多个后端服务,统一转换为 OpenAI 格式,支持负载均衡。目前支持的后端服务有:OpenAI、Anthropic、Gemini、Vertex、DeepBricks、OpenRouter 等。
|
17 |
+
|
18 |
+
## Features
|
19 |
+
|
20 |
+
- 无前端,纯配置文件配置 API 渠道。只要写一个文件就能运行起一个属于自己的 API 站,文档有详细的配置指南,小白友好。
|
21 |
+
- 统一管理多个后端服务,支持 OpenAI、Deepseek、DeepBricks、OpenRouter 等其他API 是 OpenAI 格式的提供商。支持 OpenAI Dalle-3 图像生成。
|
22 |
+
- 同时支持 Anthropic、Gemini、Vertex API。Vertex 同时支持 Claude 和 Gemini API。
|
23 |
+
- 支持 OpenAI、 Anthropic、Gemini、Vertex 原生 tool use 函数调用。
|
24 |
+
- 支持 OpenAI、Anthropic、Gemini、Vertex 原生识图 API。
|
25 |
+
- 支持四种负载均衡。
|
26 |
+
1. 支持渠道级加权负载均衡,可以根据不同的渠道权重分配请求。默认不开启,需要配置渠道权重。
|
27 |
+
2. 支持 Vertex 区域级负载均衡,支持 Vertex 高并发,最高可将 Gemini,Claude 并发提高 (API数量 * 区域数量) 倍。自动开启不需要额外配置。
|
28 |
+
3. 除了 Vertex 区域级负载均衡,所有 API 均支持渠道级顺序负载均衡,提高沉浸式翻译体验。自动开启不需要额外配置。
|
29 |
+
4. 支持单个渠道多个 API Key 自动开启 API key 级别的轮训负载均衡。
|
30 |
+
- 支持自动重试,当一个 API 渠道响应失败时,自动重试下一个 API 渠道。
|
31 |
+
- 支持细粒度的权限控制。支持使用通配符设置 API key 可用渠道的特定模型。
|
32 |
+
- 支持限流,可以设置每分钟最多请求次数,可以设置为整数,如 2/min,2 次每分钟、5/hour,5 次每小时、10/day,10 次每天,10/month,10 次每月,10/year,10 次每年。默认60/min。
|
33 |
+
|
34 |
+
## Configuration
|
35 |
+
|
36 |
+
使用 api.yaml 配置文件,可以配置多个模型,每个模型可以配置多个后端服务,支持负载均衡。下面是 api.yaml 配置文件的示例:
|
37 |
+
|
38 |
+
```yaml
|
39 |
+
providers:
|
40 |
+
- provider: provider_name # 服务提供商名称, 如 openai、anthropic、gemini、openrouter、deepbricks,随便取名字,必填
|
41 |
+
base_url: https://api.your.com/v1/chat/completions # 后端服务的API地址,必填
|
42 |
+
api: sk-YgS6GTi0b4bEabc4C # 提供商的API Key,必填
|
43 |
+
model: # 至少填一个模型
|
44 |
+
- gpt-4o # 可以使用的模型名称,必填
|
45 |
+
- claude-3-5-sonnet-20240620: claude-3-5-sonnet # 重命名模型,claude-3-5-sonnet-20240620 是服务商的模型名称,claude-3-5-sonnet 是重命名后的名字,可以使用简洁的名字代替原来复杂的名称,选填
|
46 |
+
- dall-e-3
|
47 |
+
|
48 |
+
- provider: anthropic
|
49 |
+
base_url: https://api.anthropic.com/v1/messages
|
50 |
+
api: # 支持多个 API Key,多个 key 自动开启轮训负载均衡,至少一个 key,必填
|
51 |
+
- sk-ant-api03-bNnAOJyA-xQw_twAA
|
52 |
+
- sk-ant-api02-bNnxxxx
|
53 |
+
model:
|
54 |
+
- claude-3-5-sonnet-20240620: claude-3-5-sonnet # 重命名模型,claude-3-5-sonnet-20240620 是服务商的模型名称,claude-3-5-sonnet 是重命名后的名字,可以使用简洁的名字代替原来复杂的名称,选填
|
55 |
+
tools: true # 是否支持工具,如生成代码、生成文档等,默认是 true,选填
|
56 |
+
|
57 |
+
- provider: gemini
|
58 |
+
base_url: https://generativelanguage.googleapis.com/v1beta # base_url 支持 v1beta/v1, 仅供 Gemini 模型使用,必填
|
59 |
+
api: AIzaSyAN2k6IRdgw
|
60 |
+
model:
|
61 |
+
- gemini-1.5-pro
|
62 |
+
- gemini-1.5-flash-exp-0827: gemini-1.5-flash # 重命名后,原来的模型名字 gemini-1.5-flash-exp-0827 无法使用,如果要使用原来的名字,可以在 model 中添加原来的名字,只要加上下面一行就可以使用原来的名字了
|
63 |
+
- gemini-1.5-flash-exp-0827 # 加上这一行,gemini-1.5-flash-exp-0827 和 gemini-1.5-flash 都可以被请求
|
64 |
+
tools: true
|
65 |
+
|
66 |
+
- provider: vertex
|
67 |
+
project_id: gen-lang-client-xxxxxxxxxxxxxx # 描述: 您的Google Cloud项目ID。格式: 字符串,通常由小写字母、数字和连字符组成。获取方式: 在Google Cloud Console的项目选择器中可以找到您的项目ID。
|
68 |
+
private_key: "-----BEGIN PRIVATE KEY-----\nxxxxx\n-----END PRIVATE" # 描述: Google Cloud Vertex AI服务账号的私钥。格式: 一个JSON格式的字符串,包含服务账号的私钥信息。获��方式: 在Google Cloud Console中创建服务账号,生成JSON格式的密钥文件,然后将其内容设置为此环境变量的值。
|
69 |
+
client_email: [email protected] # 描述: Google Cloud Vertex AI服务账号的电子邮件地址。格式: 通常是形如 "[email protected]" 的字符串。获取方式: 在创建服务账号时生成,也可以在Google Cloud Console的"IAM与管理"部分查看服务账号详情获得。
|
70 |
+
model:
|
71 |
+
- gemini-1.5-pro
|
72 |
+
- gemini-1.5-flash
|
73 |
+
- claude-3-5-sonnet@20240620: claude-3-5-sonnet
|
74 |
+
- claude-3-opus@20240229: claude-3-opus
|
75 |
+
- claude-3-sonnet@20240229: claude-3-sonnet
|
76 |
+
- claude-3-haiku@20240307: claude-3-haiku
|
77 |
+
tools: true
|
78 |
+
notes: https://xxxxx.com/ # 可以放服务商的网址,备注信息,官方文档,选填
|
79 |
+
|
80 |
+
- provider: other-provider
|
81 |
+
base_url: https://api.xxx.com/v1/messages
|
82 |
+
api: sk-bNnAOJyA-xQw_twAA
|
83 |
+
model:
|
84 |
+
- causallm-35b-beta2ep-q6k: causallm-35b
|
85 |
+
tools: false
|
86 |
+
engine: openrouter # 强制使用某个消息格式,目前支持 gpt,claude,gemini,openrouter 原生格式,选填
|
87 |
+
|
88 |
+
api_keys:
|
89 |
+
- api: sk-KjjI60Yf0JFWtfgRmXqFWyGtWUd9GZnmi3KlvowmRWpWpQRo # API Key,用户使用本服务需要 API key,必填
|
90 |
+
model: # 该 API Key 可以使用的模型,必填
|
91 |
+
- gpt-4o # 可以使用的模型名称,可以使用所有提供商提供的 gpt-4o 模型
|
92 |
+
- claude-3-5-sonnet # 可以使用的模型名称,可以使用所有提供商提供的 claude-3-5-sonnet 模型
|
93 |
+
- gemini/* # 可以使用的模型名称,仅可以使用名为 gemini 提供商提供的所有模型,其中 gemini 是 provider 名称,* 代表所有模型
|
94 |
+
role: admin
|
95 |
+
|
96 |
+
- api: sk-pkhf60Yf0JGyJygRmXqFQyTgWUd9GZnmi3KlvowmRWpWqrhy
|
97 |
+
model:
|
98 |
+
- anthropic/claude-3-5-sonnet # 可以使用的模型名称,仅可以使用名为 anthropic 提供商提供的 claude-3-5-sonnet 模型。其他提供商的 claude-3-5-sonnet 模型不可以使用。
|
99 |
+
preferences:
|
100 |
+
USE_ROUND_ROBIN: true # 是否使用轮询负载均衡,true 为使用,false 为不使用,默认为 true。开启轮训后每次请求模型按照 model 配置的顺序依次请求。与 providers 里面原始的渠道顺序无关。因此你可以设置每个 API key 请求顺序不一样。
|
101 |
+
AUTO_RETRY: true # 是否自动重试,自动重试下一个提供商,true 为自动重试,false 为不自动重试,默认为 true
|
102 |
+
RATE_LIMIT: 2/min # 支持限流,每分钟最多请求次数,可以设置为整数,如 2/min,2 次每分钟、5/hour,5 次每小时、10/day,10 次每天,10/month,10 次每月,10/year,10 次每年。默认60/min,选填
|
103 |
+
|
104 |
+
# 渠道级加权负载均衡配置示例
|
105 |
+
- api: sk-KjjI60Yf0JFWtxxxxxxxxxxxxxxwmRWpWpQRo
|
106 |
+
model:
|
107 |
+
- gcp1/*: 5 # 冒号后面就是权重,权重仅支持正整数。
|
108 |
+
- gcp2/*: 3 # 数字的大小代表权重,数字越大,请求的概率越大。
|
109 |
+
- gcp3/*: 2 # 在该示例中,所有渠道加起来一共有 10 个权重,及 10 个请求里面有 5 个请求会请求 gcp1/* 模型,2 个请求会请求 gcp2/* 模型,3 个请求会请求 gcp3/* 模型。
|
110 |
+
|
111 |
+
preferences:
|
112 |
+
USE_ROUND_ROBIN: true # 当 USE_ROUND_ROBIN 必须为 true 并且上面的渠道后面没有权重时,会按照原始的渠道顺序请求,如果有权重,会按照加权后的顺序请求。
|
113 |
+
AUTO_RETRY: true
|
114 |
+
```
|
115 |
+
|
116 |
+
## 环境变量
|
117 |
+
|
118 |
+
- CONFIG_URL: 配置文件的下载地址,可以是本地文件,也可以是远程文件,选填
|
119 |
+
- TIMEOUT: 请求超时时间,默认为 20 秒,超时时间可以控制当一个渠道没有响应时,切换下一个渠道需要的时间。选填
|
120 |
+
|
121 |
+
## Docker Local Deployment
|
122 |
+
|
123 |
+
Start the container
|
124 |
+
|
125 |
+
```bash
|
126 |
+
docker run --user root -p 8001:8000 --name uni-api -dit \
|
127 |
+
-v ./api.yaml:/home/api.yaml \
|
128 |
+
yym68686/uni-api:latest
|
129 |
+
```
|
130 |
+
|
131 |
+
Or if you want to use Docker Compose, here is a docker-compose.yml example:
|
132 |
+
|
133 |
+
```yaml
|
134 |
+
services:
|
135 |
+
uni-api:
|
136 |
+
container_name: uni-api
|
137 |
+
image: yym68686/uni-api:latest
|
138 |
+
environment:
|
139 |
+
- CONFIG_URL=http://file_url/api.yaml
|
140 |
+
ports:
|
141 |
+
- 8001:8000
|
142 |
+
volumes:
|
143 |
+
- ./api.yaml:/home/api.yaml
|
144 |
+
```
|
145 |
+
|
146 |
+
CONFIG_URL 就是可以自动下载远程的配置文件。比如你在某个平台不方便修改配置文件,可以把配置文件传到某个托管服务,可以提供直链给 uni-api 下载,CONFIG_URL 就是这个直链。
|
147 |
+
|
148 |
+
Run Docker Compose container in the background
|
149 |
+
|
150 |
+
```bash
|
151 |
+
docker-compose pull
|
152 |
+
docker-compose up -d
|
153 |
+
```
|
154 |
+
|
155 |
+
Docker build
|
156 |
+
|
157 |
+
```bash
|
158 |
+
docker build --no-cache -t uni-api:latest -f Dockerfile --platform linux/amd64 .
|
159 |
+
docker tag uni-api:latest yym68686/uni-api:latest
|
160 |
+
docker push yym68686/uni-api:latest
|
161 |
+
```
|
162 |
+
|
163 |
+
One-Click Restart Docker Image
|
164 |
+
|
165 |
+
```bash
|
166 |
+
set -eu
|
167 |
+
docker pull yym68686/uni-api:latest
|
168 |
+
docker rm -f uni-api
|
169 |
+
docker run --user root -p 8001:8000 -dit --name uni-api \
|
170 |
+
-e CONFIG_URL=http://file_url/api.yaml \
|
171 |
+
-v ./api.yaml:/home/api.yaml \
|
172 |
+
yym68686/uni-api:latest
|
173 |
+
docker logs -f uni-api
|
174 |
+
```
|
175 |
+
|
176 |
+
RESTful curl test
|
177 |
+
|
178 |
+
```bash
|
179 |
+
curl -X POST http://127.0.0.1:8000/v1/chat/completions \
|
180 |
+
-H "Content-Type: application/json" \
|
181 |
+
-H "Authorization: Bearer ${API}" \
|
182 |
+
-d '{"model": "gpt-4o","messages": [{"role": "user", "content": "Hello"}],"stream": true}'
|
183 |
+
```
|
184 |
+
|
185 |
+
|
186 |
+
## Star History
|
187 |
+
|
188 |
+
<a href="https://github.com/yym68686/uni-api/stargazers">
|
189 |
+
<img width="500" alt="Star History Chart" src="https://api.star-history.com/svg?repos=yym68686/uni-api&type=Date">
|
190 |
+
</a>
|
test/parse_markdown.py
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class MarkdownEntity:
|
2 |
+
def __init__(self, content: str, entity_type: str):
|
3 |
+
self.content = content
|
4 |
+
self.entity_type = entity_type
|
5 |
+
|
6 |
+
def __repr__(self):
|
7 |
+
return f'<{self.entity_type}: {self.content}>'
|
8 |
+
|
9 |
+
class Title(MarkdownEntity):
|
10 |
+
def __init__(self, content: str, level: int):
|
11 |
+
super().__init__(content, 'Title')
|
12 |
+
self.level = level
|
13 |
+
|
14 |
+
class CodeBlock(MarkdownEntity):
|
15 |
+
def __init__(self, content: str, language: str = 'python'):
|
16 |
+
super().__init__(content, 'CodeBlock')
|
17 |
+
self.language = language
|
18 |
+
|
19 |
+
class ListItem(MarkdownEntity):
|
20 |
+
def __init__(self, content: str):
|
21 |
+
super().__init__(content, 'ListItem')
|
22 |
+
|
23 |
+
class Link(MarkdownEntity):
|
24 |
+
def __init__(self, content: str, url: str):
|
25 |
+
super().__init__(content, 'Link')
|
26 |
+
self.url = url
|
27 |
+
|
28 |
+
class EmptyLine(MarkdownEntity):
|
29 |
+
def __init__(self, content: str):
|
30 |
+
super().__init__(content, 'EmptyLine')
|
31 |
+
|
32 |
+
class Paragraph(MarkdownEntity):
|
33 |
+
def __init__(self, content: str):
|
34 |
+
super().__init__(content, 'Paragraph')
|
35 |
+
|
36 |
+
def parse_markdown(lines, delimiter='\n\n'):
|
37 |
+
entities = []
|
38 |
+
current_code_block = []
|
39 |
+
in_code_block = False
|
40 |
+
language = None
|
41 |
+
|
42 |
+
for line in lines:
|
43 |
+
# line = line.strip()
|
44 |
+
|
45 |
+
if line.startswith('#'):
|
46 |
+
level = line.count('#')
|
47 |
+
title_content = line[level:].strip()
|
48 |
+
entities.append(Title(title_content, level))
|
49 |
+
|
50 |
+
elif line.startswith('```'):
|
51 |
+
if in_code_block and language:
|
52 |
+
entities.append(CodeBlock(''.join(current_code_block), language))
|
53 |
+
current_code_block = []
|
54 |
+
in_code_block = False
|
55 |
+
language = None
|
56 |
+
else:
|
57 |
+
in_code_block = True
|
58 |
+
language = line.lstrip('`').strip()
|
59 |
+
|
60 |
+
elif in_code_block:
|
61 |
+
current_code_block.append(line)
|
62 |
+
|
63 |
+
elif '[' in line and ']' in line and '(' in line and ')' in line and line.count('[') == 1:
|
64 |
+
start = line.index('[') + 1
|
65 |
+
end = line.index(']')
|
66 |
+
url_start = line.index('(') + 1
|
67 |
+
url_end = line.index(')')
|
68 |
+
link_text = line[start:end].strip()
|
69 |
+
link_url = line[url_start:url_end].strip()
|
70 |
+
entities.append(Link(link_text, link_url))
|
71 |
+
|
72 |
+
elif line == delimiter:
|
73 |
+
entities.append(EmptyLine(line))
|
74 |
+
|
75 |
+
elif line:
|
76 |
+
entities.append(Paragraph(line))
|
77 |
+
|
78 |
+
return entities
|
79 |
+
|
80 |
+
def convert_entities_to_text(entities):
|
81 |
+
result = []
|
82 |
+
for entity in entities:
|
83 |
+
if isinstance(entity, Title):
|
84 |
+
result.append(f"{'#' * entity.level} {entity.content}")
|
85 |
+
elif isinstance(entity, CodeBlock):
|
86 |
+
code = entity.content.lstrip('\n').rstrip('\n')
|
87 |
+
result.append(f"```{entity.language}\n{code}\n```")
|
88 |
+
elif isinstance(entity, ListItem):
|
89 |
+
result.append(f"- {entity.content}")
|
90 |
+
elif isinstance(entity, Link):
|
91 |
+
result.append(f"[{entity.content}]({entity.url})")
|
92 |
+
elif isinstance(entity, EmptyLine):
|
93 |
+
result.append(f"{entity.content}")
|
94 |
+
elif isinstance(entity, Paragraph):
|
95 |
+
result.append(f"{entity.content}")
|
96 |
+
return ''.join(result)
|
97 |
+
|
98 |
+
def save_text_to_file(text: str, file_path: str):
|
99 |
+
with open(file_path, 'w', encoding='utf-8') as file:
|
100 |
+
file.write(text)
|
101 |
+
|
102 |
+
def process_markdown_entities_and_save(entities, file_path, raw_text=None):
|
103 |
+
# Step 1: Convert entities to text
|
104 |
+
text_output = convert_entities_to_text(entities)
|
105 |
+
if raw_text and raw_text != text_output:
|
106 |
+
raise ValueError("The text output does not match the raw text input.")
|
107 |
+
# Step 2: Save to file
|
108 |
+
save_text_to_file(text_output, file_path)
|
109 |
+
|
110 |
+
def read_markdown_file(file_path):
|
111 |
+
with open(file_path, 'r', encoding='utf-8') as file:
|
112 |
+
return file.read()
|
113 |
+
|
114 |
+
def split_markdown(text, delimiter='\n\n'):
|
115 |
+
# 使用两个换行符作为分割标记,分割段落
|
116 |
+
# 创建一个新的列表来存储结果
|
117 |
+
paragraphs = text.split(delimiter)
|
118 |
+
result = []
|
119 |
+
|
120 |
+
# 遍历分割后的段落,在它们之间插入空行实体
|
121 |
+
for i, paragraph in enumerate(paragraphs):
|
122 |
+
if i > 0:
|
123 |
+
# 在非第一段之前插入空行实体
|
124 |
+
result.append(delimiter)
|
125 |
+
|
126 |
+
# 添加当前段落
|
127 |
+
result.append(paragraph)
|
128 |
+
|
129 |
+
return result
|
130 |
+
|
131 |
+
def get_entities_from_markdown_file(file_path, delimiter='\n\n'):
|
132 |
+
# 读取 Markdown 文件
|
133 |
+
markdown_text = read_markdown_file(file_path)
|
134 |
+
|
135 |
+
# 分割 Markdown 文档
|
136 |
+
paragraphs = split_markdown(markdown_text, delimiter=delimiter)
|
137 |
+
|
138 |
+
# 解析 Markdown 文档
|
139 |
+
return parse_markdown(paragraphs, delimiter=delimiter)
|
140 |
+
|
141 |
+
if __name__ == '__main__':
|
142 |
+
markdown_file_path = "README_CN.md" # 替换为你的 Markdown 文件路径
|
143 |
+
|
144 |
+
# 读取 Markdown 文件
|
145 |
+
delimiter = '\n'
|
146 |
+
markdown_text = read_markdown_file(markdown_file_path)
|
147 |
+
paragraphs = split_markdown(markdown_text, delimiter=delimiter)
|
148 |
+
parsed_entities = parse_markdown(paragraphs, delimiter=delimiter)
|
149 |
+
|
150 |
+
# # 显示解析结果
|
151 |
+
# result = [str(entity) for entity in parsed_entities]
|
152 |
+
# for idx, entity in enumerate(result):
|
153 |
+
# print(f"段落 {idx + 1} 解析:{entity}\n")
|
154 |
+
|
155 |
+
# 保存到文件
|
156 |
+
output_file_path = "output.md"
|
157 |
+
process_markdown_entities_and_save(parsed_entities, output_file_path, raw_text=markdown_text)
|
158 |
+
|
159 |
+
print(f"Markdown 文档已保存到 {output_file_path}")
|
test/translate_md.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from ModelMerge import chatgpt
|
3 |
+
from parse_markdown import get_entities_from_markdown_file, process_markdown_entities_and_save
|
4 |
+
|
5 |
+
def translate_text(text, agent):
|
6 |
+
result = agent.ask(text)
|
7 |
+
return result
|
8 |
+
|
9 |
+
def translate(input_file_path, output_file_path="output.md", language="English", api_key=None, api_url="https://api.openai.com/v1/chat/completions", engine="gpt-4o"):
|
10 |
+
if not api_key:
|
11 |
+
raise ValueError("API key is required for translation.")
|
12 |
+
translator_prompt = (
|
13 |
+
"You are a translation engine, you can only translate text and cannot interpret it, and do not explain. "
|
14 |
+
"Translate the text to {}, please do not explain any sentences, just translate or leave them as they are. "
|
15 |
+
"Retain all spaces and line breaks in the original text. "
|
16 |
+
"Please do not wrap the code in code blocks, I will handle it myself. "
|
17 |
+
"If the code has comments, you should translate the comments as well. "
|
18 |
+
"This is the content you need to translate: "
|
19 |
+
).format(language)
|
20 |
+
|
21 |
+
agent = chatgpt(
|
22 |
+
api_key=api_key,
|
23 |
+
api_url=api_url,
|
24 |
+
engine=engine,
|
25 |
+
system_prompt=translator_prompt,
|
26 |
+
use_plugins=False
|
27 |
+
)
|
28 |
+
|
29 |
+
# 读取 Markdown 文件
|
30 |
+
raw_paragraphs = get_entities_from_markdown_file(input_file_path, delimiter='\n')
|
31 |
+
target_paragraphs = raw_paragraphs
|
32 |
+
|
33 |
+
# 逐段翻译
|
34 |
+
for index, paragraph in enumerate(raw_paragraphs):
|
35 |
+
if paragraph.content and paragraph.content.strip() != "":
|
36 |
+
translated_text = translate_text(paragraph.content, agent)
|
37 |
+
if translated_text:
|
38 |
+
target_paragraphs[index].content = translated_text
|
39 |
+
|
40 |
+
# 输出翻译结果
|
41 |
+
process_markdown_entities_and_save(target_paragraphs, output_file_path)
|
42 |
+
|
43 |
+
if __name__ == "__main__":
|
44 |
+
input_file_path = "README_CN.md"
|
45 |
+
output_file_path = "README.md"
|
46 |
+
language = "English"
|
47 |
+
api_key = os.getenv("API")
|
48 |
+
api_url = os.getenv("API_URL")
|
49 |
+
engine = "gpt-4o"
|
50 |
+
translate(input_file_path, output_file_path, language, api_key, api_url, engine)
|