malt666 commited on
Commit
ad9a66f
·
verified ·
1 Parent(s): ecc6c72

Upload 12 files

Browse files
.gitignore CHANGED
@@ -1,5 +1,28 @@
1
- config.json
2
- password.txt
3
- venv/
4
- __pycache__/
5
- *.pyc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 二进制文件
2
+ *.exe
3
+ *.exe~
4
+ *.dll
5
+ *.so
6
+ *.dylib
7
+ main
8
+
9
+ # 测试文件
10
+ *.test
11
+ *.out
12
+
13
+ # IDE配置
14
+ .idea/
15
+ .vscode/
16
+ *.swp
17
+ *.swo
18
+
19
+ # 依赖目录
20
+ vendor/
21
+
22
+ # 环境变量
23
+ .env
24
+ *.env
25
+
26
+ # 系统文件
27
+ .DS_Store
28
+ Thumbs.db
Dockerfile CHANGED
@@ -1,24 +1,71 @@
1
- FROM python:3.11-slim
2
-
3
- # 设置用户为root
4
- USER root
5
-
6
- WORKDIR /app
7
-
8
- COPY requirements.txt .
9
- RUN pip install --no-cache-dir -r requirements.txt
10
-
11
- COPY . .
12
-
13
- # 设置环境变量
14
- ENV HOST=0.0.0.0
15
- ENV PORT=7860
16
-
17
- # 删除敏感文件
18
- RUN rm -f config.json password.txt
19
-
20
- # 暴露端口(Hugging Face默认使用7860端口)
21
- EXPOSE 7860
22
-
23
- # 启动命令
24
- CMD ["python", "app.py"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 这是一个临时的Dockerfile,用于在Hugging Face上部署占位符服务
2
+ # TODO: 解决完整版本的依赖问题后替换此文件
3
+
4
+ # 第一阶段:构建Go服务
5
+ FROM golang:1.20-alpine AS go-builder
6
+
7
+ # 安装基本依赖
8
+ RUN apk add --no-cache git build-base ca-certificates
9
+
10
+ # 设置工作目录
11
+ WORKDIR /app
12
+
13
+ # 初始化go.mod
14
+ RUN go mod init tokenizer
15
+
16
+ # 预先下载依赖
17
+ RUN go get github.com/google/generative-ai-go/[email protected]
18
+ RUN go get google.golang.org/[email protected]
19
+ RUN go get github.com/gin-gonic/[email protected]
20
+ RUN go get github.com/go-playground/validator/[email protected]
21
+ RUN go get github.com/gabriel-vasile/[email protected]
22
+
23
+ # 复制源代码
24
+ COPY main.go .
25
+
26
+ # 确保依赖关系
27
+ RUN go mod tidy
28
+ RUN go mod download
29
+
30
+ # 构建
31
+ RUN CGO_ENABLED=0 GOOS=linux go build -o tokenizer -a -installsuffix cgo -ldflags="-w -s" .
32
+
33
+ # 第二阶段:构建Python环境
34
+ FROM python:3.9-slim
35
+
36
+ # 安装基本依赖
37
+ RUN apt-get update && apt-get install -y --no-install-recommends \
38
+ ca-certificates \
39
+ && rm -rf /var/lib/apt/lists/*
40
+
41
+ # 设置工作目录
42
+ WORKDIR /app
43
+
44
+ # 复制Go二进制文件
45
+ COPY --from=go-builder /app/tokenizer .
46
+
47
+ # 复制Python服务文件和tokenizer文件
48
+ COPY deepseek_v3_tokenizer /app/deepseek_v3_tokenizer
49
+ COPY openai_service.py /app/
50
+
51
+ # 安装Python依赖
52
+ RUN pip install --no-cache-dir flask transformers tiktoken
53
+
54
+ # 设置环境变量
55
+ ENV GIN_MODE=release
56
+ ENV PORT=7860
57
+ ENV DEEPSEEK_URL=http://127.0.0.1:7861
58
+ ENV OPENAI_URL=http://127.0.0.1:7862
59
+
60
+ # 创建启动脚本
61
+ RUN echo '#!/bin/sh\n\
62
+ python /app/deepseek_v3_tokenizer/deepseek_service.py & \n\
63
+ python /app/openai_service.py & \n\
64
+ sleep 5\n\
65
+ ./tokenizer' > /app/start.sh && chmod +x /app/start.sh
66
+
67
+ # 暴露端口
68
+ EXPOSE 7860 7861 7862
69
+
70
+ # 启动服务
71
+ CMD ["/app/start.sh"]
README.md CHANGED
@@ -1,121 +1,159 @@
1
- ---
2
- title: Abacus Chat Proxy
3
- emoji: 🤖
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: docker
7
- sdk_version: "3.9"
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
-
14
- # Abacus Chat Proxy
15
-
16
- > 📢 本项目基于 [orbitoo/abacus_chat_proxy](https://github.com/orbitoo/abacus_chat_proxy) 改进
17
- >
18
- > 特别感谢 orbitoo 大佬提供的原始项目!
19
- >
20
- > 本项目增加了:Docker部署支持、Hugging Face一键部署、自动保活功能等
21
-
22
- 一个用于中转API请求的代理服务器。
23
-
24
- #### 注意创建space的时候把private改为public,因为不改为public导致的错误别来找我
25
-
26
- [![Deploy to Hugging Face Spaces](https://huggingface.co/datasets/huggingface/badges/raw/main/deploy-to-spaces-lg.svg)](https://huggingface.co/spaces/malt666/abacus_chat_proxy?duplicate=true)
27
-
28
- ## ⚠️ 警告
29
-
30
- **本地部署方式已失效!**为了适配hugging face,本项目的本地部署方式已不再可用。目前只能通过Hugging Face Spaces部署来使用本代理服务。请使用下方的Hugging Face一键部署方法。
31
-
32
- ## 🚀 快速开始
33
-
34
- ### Hugging Face一键部署
35
-
36
- #### 注意创建space的时候把private改为public,因为不改为public导致的错误别来找我
37
-
38
- 1. 点击上方的"Deploy to Hugging Face Spaces"按钮
39
- 2. 登录你的Hugging Face账号(如果还没有,需要注册一个)
40
- 3. 在弹出的页面中设置你的Space名称,注意创建space的时候把private改为public,因为不改为为public导致的错误别来找我
41
- 4. 创建完Space后,在Space的Settings -> Repository Secrets中添加以下配置:
42
- - `cookie_1`: 第一个cookie
43
- - `cookie_2`: 第二个cookie(如果有)
44
- - 以此类推...
45
- - `password`: (必填,最好超过8位数,不然被盗用api导致点数用光,损失自负)api调用密码,也是dashboard登录密码
46
- 5. 等待自动部署完成即可
47
- 6. 登录dashboard查看使用情况,space里面是没办法登录的,点击弹出的登录页面的小窗口提示“请点击 https://xyz-abacus-chat-proxy.hf.space 来登录并查看使用情况”的链接来登录,登录密码是你设置的password
48
- 7. **获取API链接**:部署成功后,网络的登录页面会显示api接口链接;或者你点击右上角的三个点按钮,在弹出的选项卡里面点击"Embed this Space",然后在弹出的"Embed this Space"界面里的"Direct URL"就是你的访问链接,你可以用这个链接调用API和查看使用情况
49
- 8. api调用:api调用的时候注意密钥填写你的password,不然没办法调用
50
- ### 本地运行(已失效)
51
-
52
- > ⚠️ 以下本地运行方法已失效,仅作参考。请使用Hugging Face部署方式。
53
-
54
- #### Windows用户
55
-
56
- 1. 双击运行 `start.bat`
57
- 2. 首次运行选择 `0` 进行配置
58
- 3. 配置完成后选择 `Y` 直接启动,或 `N` 返回菜单
59
- 4. 之后可直接选择 `1` 启动代理
60
- 5. 代理服务器默认运行在 `http://127.0.0.1:9876/`
61
-
62
- #### Linux/macOS用户
63
-
64
- ```bash
65
- # 赋予脚本执行权限
66
- chmod +x start.sh
67
-
68
- # 运行脚本
69
- ./start.sh
70
- ```
71
-
72
- 选项说明同Windows。
73
-
74
- ### 🌐 Hugging Face部署
75
-
76
- 1. Fork本仓库到你的GitHub账号
77
- 2. 在Hugging Face上创建新的Space(选择Docker类型)
78
- 3. 在Space的设置中连接你的GitHub仓库
79
- 4. 在Space的设置中添加以下Secrets:
80
- - 第1组配置:
81
- - `cookie_1`: 第1个cookies字符串
82
- - 第2组配置(如果需要):
83
- - `cookie_2`: 第2个cookies字符串
84
- - 更多配置以此类推(`cookie_3`...)
85
- - `password`: (可选)访问密码
86
- 5. Space会自动部署,服务将在 `https://你的空间名-你的用户名.hf.space` 上运行
87
-
88
- ## ⚙️ 环境要求
89
-
90
- - Python 3.8+
91
- - pip
92
-
93
- ## 📦 依赖
94
-
95
- ```bash
96
- Flask==3.1.0
97
- requests==2.32.3
98
- PyJWT==2.8.0
99
- ```
100
-
101
- ## 📝 配置说明
102
-
103
- ### 本地配置
104
-
105
- 首次运行时,请选择 `0` 进行配置,按照提示填写相关信息。配置文件将保存在 `config.json` 中。
106
-
107
- ### 环境变量配置
108
-
109
- 在Docker或云平台部署时,需要配置以下环境变量:
110
-
111
- - 必需的配置(至少需要一组):
112
- - `cookie_1`: 第1组配置
113
- - `cookie_2`: 第2组配置(可选)
114
- - 以此类推...
115
- - 可选配置:
116
- - `password`: 访问密码
117
-
118
- ## 🔒 安全说明
119
-
120
- - 建议在部署到Hugging Face时设置访问密码
121
- - 在Hugging Face上配置时,请使用Secrets来存储敏感信息
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: tokenizer
3
+ emoji: 🔢
4
+ colorFrom: gray
5
+ colorTo: blue
6
+ sdk: docker
7
+ sdk_version: "3.0.0"
8
+ app_file: main.go
9
+ pinned: false
10
+ ---
11
+
12
+ # Tokenizer Service
13
+
14
+ 一个高效的API服务,用于计算大型语言模型的输入token数量。
15
+
16
+ ## 支持的模型
17
+
18
+ ### Claude系列模型
19
+ - claude-3-7-sonnet-latest
20
+ - claude-3-5-sonnet-latest
21
+ - claude-3-5-haiku-latest
22
+ - claude-3-opus-latest
23
+ - claude-3-haiku-20240307
24
+
25
+ ### Gemini系列模型
26
+ - gemini-1.5-flash
27
+ - gemini-2.0-flash
28
+ - 其他Gemini系列模型
29
+
30
+ ## 自动模型匹配
31
+ 服务支持智能模型名称匹配,不区分大小写:
32
+
33
+ ### Claude模型匹配规则
34
+ - 包含"claude"、"3"和"7"的名称会匹配到`claude-3-7-sonnet-latest`
35
+ - 包含"claude"、"3"、"5"和"sonnet"的名称会匹配到`claude-3-5-sonnet-latest`
36
+ - 包含"claude"、"3"、"5"和"haiku"的名称会匹配到`claude-3-5-haiku-latest`
37
+ - 包含"claude"、"3"和"opus"的名称会匹配到`claude-3-opus-latest`
38
+ - 包含"claude"、"3"和"haiku"的名称会匹配到`claude-3-haiku-20240307`
39
+
40
+ ### Gemini模型匹配规则
41
+ - 包含"gemini"和"2.0"或"2.5"的名称会匹配到`gemini-2.0-flash`
42
+ - 包含"gemini"和"1.5"的名称会匹配到`gemini-1.5-flash`
43
+
44
+ ## 环境变量
45
+
46
+ 运行服务需要以下环境变量:
47
+
48
+ - `ANTHROPIC_API_KEY`: Anthropic API密钥,用于计算Claude模型的token
49
+ - `GOOGLE_API_KEY`: Google API密钥,用于计算Gemini模型的token
50
+ - `SERVICE_URL`(可选): 服务的URL,用于防止Hugging Face空间休眠
51
+ - `PORT`(可选): 服务监听的端口号,默认为7860
52
+
53
+ ## API接口
54
+
55
+ ### Token计算
56
+
57
+ **端点**: `/count_tokens`
58
+
59
+ **方法**: POST
60
+
61
+ **请求格式**:
62
+ ```json
63
+ {
64
+ "model": "模型名称",
65
+ "messages": [
66
+ {
67
+ "role": "user或assistant",
68
+ "content": "消息内容"
69
+ }
70
+ ],
71
+ "system": "系统提示(可选)"
72
+ }
73
+ ```
74
+
75
+ **响应格式**:
76
+ ```json
77
+ {
78
+ "input_tokens": 123
79
+ }
80
+ ```
81
+
82
+ ### 健康检查
83
+
84
+ **端点**: `/health`
85
+
86
+ **方法**: GET
87
+
88
+ **响应格式**:
89
+ ```json
90
+ {
91
+ "status": "healthy",
92
+ "time": "2023-04-01T12:34:56Z"
93
+ }
94
+ ```
95
+
96
+ ## 部署指南
97
+
98
+ ### 本地运行
99
+
100
+ 1. 设置必要的环境变量
101
+ ```bash
102
+ export ANTHROPIC_API_KEY=your_anthropic_key
103
+ export GOOGLE_API_KEY=your_google_key
104
+ ```
105
+
106
+ 2. 启动服务
107
+ ```bash
108
+ go run main.go
109
+ ```
110
+
111
+ ### Docker部署
112
+
113
+ 1. 构建Docker镜像
114
+ ```bash
115
+ docker build -t tokenizer .
116
+ ```
117
+
118
+ 2. 运行容器
119
+ ```bash
120
+ docker run -p 7860:7860 -e ANTHROPIC_API_KEY=your_anthropic_key -e GOOGLE_API_KEY=your_google_key tokenizer
121
+ ```
122
+
123
+ ### Hugging Face部署
124
+
125
+ 1. 创建一个新的Space,选择Docker作为运行环境
126
+ 2. 添加以下Secret:
127
+ - `ANTHROPIC_API_KEY`: 你的Anthropic API密钥
128
+ - `GOOGLE_API_KEY`: 你的Google API密钥
129
+ - `SERVICE_URL`: 服务的完整URL,格式为`https://你的空间名-用户名.hf.space`
130
+
131
+ 3. 将代码推送到Space关联的仓库
132
+ 4. Hugging Face会自动构建并部署服务
133
+
134
+ ## 示例代码
135
+
136
+ ### Python调用示例
137
+
138
+ ```python
139
+ import requests
140
+ import json
141
+
142
+ url = "https://your-service-url/count_tokens"
143
+ data = {
144
+ "model": "claude-3-5-sonnet-latest",
145
+ "messages": [
146
+ {
147
+ "role": "user",
148
+ "content": "Hello, world!"
149
+ }
150
+ ]
151
+ }
152
+
153
+ response = requests.post(url, json=data)
154
+ print(response.json())
155
+ ```
156
+
157
+ ## License
158
+
159
+ MIT
deepseek_v3_tokenizer/.DS_Store ADDED
Binary file (6.15 kB). View file
 
deepseek_v3_tokenizer/deepseek_service.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ from transformers import AutoTokenizer
3
+ import os
4
+
5
+ app = Flask(__name__)
6
+
7
+ # 加载tokenizer
8
+ tokenizer = AutoTokenizer.from_pretrained(os.path.dirname(__file__))
9
+
10
+ @app.route('/count_tokens', methods=['POST'])
11
+ def count_tokens():
12
+ try:
13
+ data = request.json
14
+ messages = data.get('messages', [])
15
+ system = data.get('system')
16
+
17
+ # 构建完整文本
18
+ text = ""
19
+ if system:
20
+ text += f"System: {system}\n\n"
21
+
22
+ for msg in messages:
23
+ role = msg.get('role', '')
24
+ content = msg.get('content', '')
25
+ if role == 'user':
26
+ text += f"User: {content}\n"
27
+ elif role == 'assistant':
28
+ text += f"Assistant: {content}\n"
29
+ else:
30
+ text += f"{role}: {content}\n"
31
+
32
+ # 计算token数量
33
+ tokens = tokenizer.encode(text)
34
+ token_count = len(tokens)
35
+
36
+ return jsonify({
37
+ 'input_tokens': token_count
38
+ })
39
+ except Exception as e:
40
+ return jsonify({
41
+ 'error': str(e)
42
+ }), 400
43
+
44
+ @app.route('/health', methods=['GET'])
45
+ def health():
46
+ return jsonify({
47
+ 'status': 'healthy',
48
+ 'tokenizer': 'deepseek-v3'
49
+ })
50
+
51
+ if __name__ == '__main__':
52
+ app.run(host='127.0.0.1', port=7861)
deepseek_v3_tokenizer/deepseek_tokenizer.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # pip3 install transformers
2
+ # python3 deepseek_tokenizer.py
3
+ import transformers
4
+
5
+ chat_tokenizer_dir = "./"
6
+
7
+ tokenizer = transformers.AutoTokenizer.from_pretrained(
8
+ chat_tokenizer_dir, trust_remote_code=True
9
+ )
10
+
11
+ result = tokenizer.encode("Hello!")
12
+ print(result)
deepseek_v3_tokenizer/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
deepseek_v3_tokenizer/tokenizer_config.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_eos_token": false,
4
+ "bos_token": {
5
+ "__type": "AddedToken",
6
+ "content": "<|begin▁of▁sentence|>",
7
+ "lstrip": false,
8
+ "normalized": true,
9
+ "rstrip": false,
10
+ "single_word": false
11
+ },
12
+ "clean_up_tokenization_spaces": false,
13
+ "eos_token": {
14
+ "__type": "AddedToken",
15
+ "content": "<|end▁of▁sentence|>",
16
+ "lstrip": false,
17
+ "normalized": true,
18
+ "rstrip": false,
19
+ "single_word": false
20
+ },
21
+ "legacy": true,
22
+ "model_max_length": 16384,
23
+ "pad_token": {
24
+ "__type": "AddedToken",
25
+ "content": "<|end▁of▁sentence|>",
26
+ "lstrip": false,
27
+ "normalized": true,
28
+ "rstrip": false,
29
+ "single_word": false
30
+ },
31
+ "sp_model_kwargs": {},
32
+ "unk_token": null,
33
+ "tokenizer_class": "LlamaTokenizerFast",
34
+ "chat_template": "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='', is_first_sp=true) %}{%- for message in messages %}{%- if message['role'] == 'system' %}{%- if ns.is_first_sp %}{% set ns.system_prompt = ns.system_prompt + message['content'] %}{% set ns.is_first_sp = false %}{%- else %}{% set ns.system_prompt = ns.system_prompt + '\\n\\n' + message['content'] %}{%- endif %}{%- endif %}{%- endfor %}{{ bos_token }}{{ ns.system_prompt }}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and 'tool_calls' in message %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls'] %}{%- if not ns.is_first %}{%- if message['content'] is none %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- else %}{{'<|Assistant|>' + message['content'] + '<|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- endif %}{%- set ns.is_first = true -%}{%- else %}{{'\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\n' + '```json' + '\\n' + tool['function']['arguments'] + '\\n' + '```' + '<|tool▁call▁end|>'}}{%- endif %}{%- endfor %}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- if message['role'] == 'assistant' and 'tool_calls' not in message %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '</think>' in content %}{% set content = content.split('</think>')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|>'}}{% endif %}"
35
+ }
go.mod ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module tokenizer
2
+
3
+ go 1.21
4
+
5
+ toolchain go1.24.1
6
+
7
+ require github.com/gin-gonic/gin v1.9.1
8
+
9
+ require (
10
+ cloud.google.com/go/ai v0.3.0 // indirect
11
+ cloud.google.com/go/compute v1.23.3 // indirect
12
+ cloud.google.com/go/compute/metadata v0.2.3 // indirect
13
+ cloud.google.com/go/longrunning v0.5.4 // indirect
14
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
15
+ github.com/golang/protobuf v1.5.3 // indirect
16
+ github.com/google/s2a-go v0.1.7 // indirect
17
+ github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
18
+ github.com/googleapis/gax-go/v2 v2.12.0 // indirect
19
+ go.opencensus.io v0.24.0 // indirect
20
+ golang.org/x/oauth2 v0.14.0 // indirect
21
+ golang.org/x/sync v0.5.0 // indirect
22
+ golang.org/x/time v0.5.0 // indirect
23
+ google.golang.org/appengine v1.6.7 // indirect
24
+ google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
25
+ google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
26
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
27
+ google.golang.org/grpc v1.59.0 // indirect
28
+ )
29
+
30
+ require (
31
+ github.com/bytedance/sonic v1.9.1 // indirect
32
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
33
+ github.com/gabriel-vasile/mimetype v1.4.2 // indirect
34
+ github.com/gin-contrib/sse v0.1.0 // indirect
35
+ github.com/go-playground/locales v0.14.1 // indirect
36
+ github.com/go-playground/universal-translator v0.18.1 // indirect
37
+ github.com/go-playground/validator/v10 v10.14.0 // indirect
38
+ github.com/goccy/go-json v0.10.2 // indirect
39
+ github.com/google/generative-ai-go v0.8.0
40
+ github.com/json-iterator/go v1.1.12 // indirect
41
+ github.com/klauspost/cpuid/v2 v2.2.4 // indirect
42
+ github.com/leodido/go-urn v1.2.4 // indirect
43
+ github.com/mattn/go-isatty v0.0.19 // indirect
44
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
45
+ github.com/modern-go/reflect2 v1.0.2 // indirect
46
+ github.com/pelletier/go-toml/v2 v2.0.8 // indirect
47
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
48
+ github.com/ugorji/go/codec v1.2.11 // indirect
49
+ golang.org/x/arch v0.3.0 // indirect
50
+ golang.org/x/crypto v0.15.0 // indirect
51
+ golang.org/x/net v0.18.0 // indirect
52
+ golang.org/x/sys v0.14.0 // indirect
53
+ golang.org/x/text v0.14.0 // indirect
54
+ google.golang.org/api v0.152.0
55
+ google.golang.org/protobuf v1.31.0 // indirect
56
+ gopkg.in/yaml.v3 v3.0.1 // indirect
57
+ )
go.sum ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2
+ cloud.google.com/go/ai v0.3.0 h1:M617N0brv+XFch2KToZUhv6ggzgFZMUnmDkNQjW2pYg=
3
+ cloud.google.com/go/ai v0.3.0/go.mod h1:dTuQIBA8Kljuas5z1WNot1QZOl476A9TsFqEi6pzJlI=
4
+ cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
5
+ cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
6
+ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
7
+ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
8
+ cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg=
9
+ cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI=
10
+ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
11
+ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
12
+ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
13
+ github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
14
+ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
15
+ github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
16
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
17
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
18
+ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
19
+ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
20
+ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
21
+ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
+ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
23
+ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
24
+ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
25
+ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
26
+ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
27
+ github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
28
+ github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
29
+ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
30
+ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
31
+ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
32
+ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
33
+ github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
34
+ github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
35
+ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
36
+ github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
37
+ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
38
+ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
39
+ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
40
+ github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
41
+ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
42
+ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
43
+ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
44
+ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
45
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
46
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
47
+ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
48
+ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
49
+ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
50
+ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
51
+ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
52
+ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
53
+ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
54
+ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
55
+ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
56
+ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
57
+ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
58
+ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
59
+ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
60
+ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
61
+ github.com/google/generative-ai-go v0.8.0 h1:sbpEC4rdjby19jehqmQ5pF0eXDTGHmNhXuNN+elfC5I=
62
+ github.com/google/generative-ai-go v0.8.0/go.mod h1:8fXQk4w+eyTzFokGGJrBFL0/xwXqm3QNhTqOWyX11zs=
63
+ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
64
+ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
65
+ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
66
+ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
67
+ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
68
+ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
69
+ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
70
+ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
71
+ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
72
+ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
73
+ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
74
+ github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
75
+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
76
+ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
77
+ github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
78
+ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
79
+ github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
80
+ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
81
+ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
82
+ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
83
+ github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
84
+ github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
85
+ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
86
+ github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
87
+ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
88
+ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
89
+ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
90
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
91
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
92
+ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
93
+ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
94
+ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
95
+ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
96
+ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
97
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
98
+ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
99
+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
100
+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
101
+ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
102
+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
103
+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
104
+ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
105
+ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
106
+ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
107
+ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
108
+ github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
109
+ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
110
+ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
111
+ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
112
+ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
113
+ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
114
+ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
115
+ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
116
+ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
117
+ golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
118
+ golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
119
+ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
120
+ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
121
+ golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
122
+ golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
123
+ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
124
+ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
125
+ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
126
+ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
127
+ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
128
+ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
129
+ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
130
+ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
131
+ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
132
+ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
133
+ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
134
+ golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
135
+ golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
136
+ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
137
+ golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
138
+ golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
139
+ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
140
+ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
141
+ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
142
+ golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
143
+ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
144
+ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
145
+ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
146
+ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
147
+ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
148
+ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
149
+ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
150
+ golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
151
+ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
152
+ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
153
+ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
154
+ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
155
+ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
156
+ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
157
+ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
158
+ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
159
+ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
160
+ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
161
+ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
162
+ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
163
+ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
164
+ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
165
+ google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY=
166
+ google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
167
+ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
168
+ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
169
+ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
170
+ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
171
+ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
172
+ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
173
+ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
174
+ google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
175
+ google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
176
+ google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
177
+ google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
178
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
179
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
180
+ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
181
+ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
182
+ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
183
+ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
184
+ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
185
+ google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
186
+ google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
187
+ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
188
+ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
189
+ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
190
+ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
191
+ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
192
+ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
193
+ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
194
+ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
195
+ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
196
+ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
197
+ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
198
+ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
199
+ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
200
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
201
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
202
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
203
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
204
+ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
205
+ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
206
+ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
207
+ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
main.go ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "encoding/json"
7
+ "fmt"
8
+ "log"
9
+ "net/http"
10
+ "os"
11
+ "strings"
12
+ "sync"
13
+ "time"
14
+
15
+ "github.com/gin-gonic/gin"
16
+ "github.com/google/generative-ai-go/genai"
17
+ "google.golang.org/api/option"
18
+ )
19
+
20
+ // 配置结构
21
+ type Config struct {
22
+ AnthropicKey string
23
+ GoogleKey string
24
+ ServiceURL string
25
+ DeepseekURL string
26
+ OpenAIURL string
27
+ }
28
+
29
+ var (
30
+ config Config
31
+ configOnce sync.Once
32
+ )
33
+
34
+ // 请求结构
35
+ type TokenCountRequest struct {
36
+ Model string `json:"model" binding:"required"`
37
+ Messages []Message `json:"messages" binding:"required"`
38
+ System *string `json:"system,omitempty"`
39
+ }
40
+
41
+ type Message struct {
42
+ Role string `json:"role" binding:"required"`
43
+ Content string `json:"content" binding:"required"`
44
+ }
45
+
46
+ // 响应结构
47
+ type TokenCountResponse struct {
48
+ InputTokens int `json:"input_tokens"`
49
+ }
50
+
51
+ // 错误响应结构
52
+ type ErrorResponse struct {
53
+ Error string `json:"error"`
54
+ }
55
+
56
+ // 模型映射规则
57
+ type ModelRule struct {
58
+ Keywords []string
59
+ Target string
60
+ }
61
+
62
+ var modelRules = []ModelRule{
63
+ {
64
+ Keywords: []string{"gpt"},
65
+ Target: "gpt-3.5-turbo",
66
+ },
67
+ {
68
+ Keywords: []string{"openai"},
69
+ Target: "gpt-3.5-turbo",
70
+ },
71
+ {
72
+ Keywords: []string{"deepseek"},
73
+ Target: "deepseek-v3",
74
+ },
75
+ {
76
+ Keywords: []string{"claude", "3", "sonnet"},
77
+ Target: "claude-3-sonnet-20240229",
78
+ },
79
+ {
80
+ Keywords: []string{"claude", "3", "7"},
81
+ Target: "claude-3-7-sonnet-latest",
82
+ },
83
+ {
84
+ Keywords: []string{"claude", "3", "5", "sonnet"},
85
+ Target: "claude-3-5-sonnet-latest",
86
+ },
87
+ {
88
+ Keywords: []string{"claude", "3", "5", "haiku"},
89
+ Target: "claude-3-5-haiku-latest",
90
+ },
91
+ {
92
+ Keywords: []string{"claude", "3", "opus"},
93
+ Target: "claude-3-opus-latest",
94
+ },
95
+ {
96
+ Keywords: []string{"claude", "3", "haiku"},
97
+ Target: "claude-3-haiku-20240307",
98
+ },
99
+ {
100
+ Keywords: []string{"gemini", "2.0"},
101
+ Target: "gemini-2.0-flash",
102
+ },
103
+ {
104
+ Keywords: []string{"gemini", "2.5"},
105
+ Target: "gemini-2.0-flash", // 目前使用2.0-flash作为2.5的替代
106
+ },
107
+ {
108
+ Keywords: []string{"gemini", "1.5"},
109
+ Target: "gemini-1.5-flash",
110
+ },
111
+ }
112
+
113
+ // 智能匹配模型名称
114
+ func matchModelName(input string) string {
115
+ // 转换为小写进行匹配
116
+ input = strings.ToLower(input)
117
+
118
+ // 特殊规则:OpenAI GPT-4o
119
+ if (strings.Contains(input, "gpt") && strings.Contains(input, "4o")) ||
120
+ strings.Contains(input, "o1") ||
121
+ strings.Contains(input, "o3") {
122
+ return "gpt-4o"
123
+ }
124
+
125
+ // 特殊规则:OpenAI GPT-4
126
+ if (strings.Contains(input, "gpt") && strings.Contains(input, "3") && strings.Contains(input, "5")) ||
127
+ (strings.Contains(input, "gpt") && strings.Contains(input, "4") && !strings.Contains(input, "4o")) {
128
+ return "gpt-4"
129
+ }
130
+
131
+ // 遍历所有规则
132
+ for _, rule := range modelRules {
133
+ matches := true
134
+ for _, keyword := range rule.Keywords {
135
+ if !strings.Contains(input, strings.ToLower(keyword)) {
136
+ matches = false
137
+ break
138
+ }
139
+ }
140
+ if matches {
141
+ return rule.Target
142
+ }
143
+ }
144
+
145
+ // 如果没有匹配到,返回原始输入
146
+ return input
147
+ }
148
+
149
+ // 加载配置
150
+ func loadConfig() Config {
151
+ configOnce.Do(func() {
152
+ config.AnthropicKey = os.Getenv("ANTHROPIC_API_KEY")
153
+ if config.AnthropicKey == "" {
154
+ log.Println("警告: ANTHROPIC_API_KEY 环境变量未设置,Claude模型将无法使用")
155
+ }
156
+
157
+ config.GoogleKey = os.Getenv("GOOGLE_API_KEY")
158
+ if config.GoogleKey == "" {
159
+ log.Println("警告: GOOGLE_API_KEY 环境变量未设置,Gemini模型将无法使用")
160
+ }
161
+
162
+ // 获取Deepseek服务URL
163
+ config.DeepseekURL = os.Getenv("DEEPSEEK_URL")
164
+ if config.DeepseekURL == "" {
165
+ config.DeepseekURL = "http://127.0.0.1:7861" // 默认本地地址
166
+ log.Println("使用默认Deepseek服务地址:", config.DeepseekURL)
167
+ }
168
+
169
+ // 获取OpenAI服务URL
170
+ config.OpenAIURL = os.Getenv("OPENAI_URL")
171
+ if config.OpenAIURL == "" {
172
+ config.OpenAIURL = "http://127.0.0.1:7862" // 默认本地地址
173
+ log.Println("使用默认OpenAI服务地址:", config.OpenAIURL)
174
+ }
175
+
176
+ // 获取服务URL,用于防休眠
177
+ config.ServiceURL = os.Getenv("SERVICE_URL")
178
+ if config.ServiceURL == "" {
179
+ log.Println("SERVICE_URL 未设置,防休眠功能将被禁用")
180
+ }
181
+ })
182
+ return config
183
+ }
184
+
185
+ // 使用Claude API计算token
186
+ func countTokensWithClaude(req TokenCountRequest) (TokenCountResponse, error) {
187
+ // 准备请求Anthropic API
188
+ client := &http.Client{}
189
+ data, err := json.Marshal(req)
190
+ if err != nil {
191
+ return TokenCountResponse{}, fmt.Errorf("序列化请求失败: %v", err)
192
+ }
193
+
194
+ // 创建请求
195
+ request, err := http.NewRequest("POST", "https://api.anthropic.com/v1/messages/count_tokens", bytes.NewBuffer(data))
196
+ if err != nil {
197
+ return TokenCountResponse{}, fmt.Errorf("创建请求失败: %v", err)
198
+ }
199
+
200
+ // 设置请求头
201
+ request.Header.Set("x-api-key", config.AnthropicKey)
202
+ request.Header.Set("anthropic-version", "2023-06-01")
203
+ request.Header.Set("content-type", "application/json")
204
+
205
+ // 发送请求
206
+ response, err := client.Do(request)
207
+ if err != nil {
208
+ return TokenCountResponse{}, fmt.Errorf("发送请求到Anthropic API失败: %v", err)
209
+ }
210
+ defer response.Body.Close()
211
+
212
+ // 读取响应
213
+ var result TokenCountResponse
214
+ if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
215
+ return TokenCountResponse{}, fmt.Errorf("解码响应失败: %v", err)
216
+ }
217
+
218
+ return result, nil
219
+ }
220
+
221
+ // 使用Gemini API计算token
222
+ func countTokensWithGemini(req TokenCountRequest) (TokenCountResponse, error) {
223
+ // 检查API密钥
224
+ if config.GoogleKey == "" {
225
+ return TokenCountResponse{}, fmt.Errorf("GOOGLE_API_KEY 未设置")
226
+ }
227
+
228
+ // 创建Gemini客户端
229
+ ctx := context.Background()
230
+ client, err := genai.NewClient(ctx, option.WithAPIKey(config.GoogleKey))
231
+ if err != nil {
232
+ return TokenCountResponse{}, fmt.Errorf("创建Gemini客户端失败: %v", err)
233
+ }
234
+ defer client.Close()
235
+
236
+ // 使用已经匹配好的模型名称
237
+ modelName := req.Model
238
+
239
+ // 创建Gemini模型
240
+ model := client.GenerativeModel(modelName)
241
+
242
+ // 构建提示内容
243
+ var content string
244
+ if req.System != nil && *req.System != "" {
245
+ content += *req.System + "\n\n"
246
+ }
247
+
248
+ for _, msg := range req.Messages {
249
+ if msg.Role == "user" {
250
+ content += "用户: " + msg.Content + "\n"
251
+ } else if msg.Role == "assistant" {
252
+ content += "助手: " + msg.Content + "\n"
253
+ } else {
254
+ content += msg.Role + ": " + msg.Content + "\n"
255
+ }
256
+ }
257
+
258
+ // 计算token
259
+ tokResp, err := model.CountTokens(ctx, genai.Text(content))
260
+ if err != nil {
261
+ return TokenCountResponse{}, fmt.Errorf("计算Gemini token失败: %v", err)
262
+ }
263
+
264
+ return TokenCountResponse{InputTokens: int(tokResp.TotalTokens)}, nil
265
+ }
266
+
267
+ // 使用Deepseek API计算token
268
+ func countTokensWithDeepseek(req TokenCountRequest) (TokenCountResponse, error) {
269
+ // 准备请求
270
+ client := &http.Client{}
271
+ data, err := json.Marshal(req)
272
+ if err != nil {
273
+ return TokenCountResponse{}, fmt.Errorf("序列化请求失败: %v", err)
274
+ }
275
+
276
+ // 创建请求
277
+ request, err := http.NewRequest("POST", config.DeepseekURL+"/count_tokens", bytes.NewBuffer(data))
278
+ if err != nil {
279
+ return TokenCountResponse{}, fmt.Errorf("创建请求失败: %v", err)
280
+ }
281
+
282
+ // 设置请求头
283
+ request.Header.Set("Content-Type", "application/json")
284
+
285
+ // 发送请求
286
+ response, err := client.Do(request)
287
+ if err != nil {
288
+ return TokenCountResponse{}, fmt.Errorf("发送请求到Deepseek服务失败: %v", err)
289
+ }
290
+ defer response.Body.Close()
291
+
292
+ // 读取响应
293
+ var result TokenCountResponse
294
+ if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
295
+ return TokenCountResponse{}, fmt.Errorf("解码响应失败: %v", err)
296
+ }
297
+
298
+ return result, nil
299
+ }
300
+
301
+ // 使用OpenAI API计算token
302
+ func countTokensWithOpenAI(req TokenCountRequest) (TokenCountResponse, error) {
303
+ // 准备请求
304
+ client := &http.Client{}
305
+ data, err := json.Marshal(req)
306
+ if err != nil {
307
+ return TokenCountResponse{}, fmt.Errorf("序列化请求失败: %v", err)
308
+ }
309
+
310
+ // 创建请求
311
+ request, err := http.NewRequest("POST", config.OpenAIURL+"/count_tokens", bytes.NewBuffer(data))
312
+ if err != nil {
313
+ return TokenCountResponse{}, fmt.Errorf("创建请求失败: %v", err)
314
+ }
315
+
316
+ // 设置请求头
317
+ request.Header.Set("Content-Type", "application/json")
318
+
319
+ // 发送请求
320
+ response, err := client.Do(request)
321
+ if err != nil {
322
+ return TokenCountResponse{}, fmt.Errorf("发送请求到OpenAI服务失败: %v", err)
323
+ }
324
+ defer response.Body.Close()
325
+
326
+ // 读取响应
327
+ var result struct {
328
+ InputTokens int `json:"input_tokens"`
329
+ Model string `json:"model"`
330
+ Encoding string `json:"encoding"`
331
+ }
332
+ if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
333
+ return TokenCountResponse{}, fmt.Errorf("解码响应失败: %v", err)
334
+ }
335
+
336
+ return TokenCountResponse{InputTokens: result.InputTokens}, nil
337
+ }
338
+
339
+ // 计算token
340
+ func countTokens(c *gin.Context) {
341
+ var req TokenCountRequest
342
+ if err := c.ShouldBindJSON(&req); err != nil {
343
+ c.JSON(http.StatusBadRequest, ErrorResponse{Error: err.Error()})
344
+ return
345
+ }
346
+
347
+ // 保存原始模型名称
348
+ originalModel := req.Model
349
+
350
+ // 检查是否为不支持的模型
351
+ isUnsupportedModel := true
352
+
353
+ // 检查是否为支持的模型类型
354
+ modelLower := strings.ToLower(req.Model)
355
+ if strings.Contains(modelLower, "gpt") || strings.Contains(modelLower, "openai") ||
356
+ strings.Contains(modelLower, "o1") || strings.Contains(modelLower, "o3") ||
357
+ strings.HasPrefix(modelLower, "claude") ||
358
+ strings.Contains(modelLower, "gemini") ||
359
+ strings.Contains(modelLower, "deepseek") {
360
+ isUnsupportedModel = false
361
+ }
362
+
363
+ // 智能匹配模型名称
364
+ req.Model = matchModelName(req.Model)
365
+
366
+ var result TokenCountResponse
367
+ var err error
368
+
369
+ // 优先检查是否为Deepseek模型
370
+ if strings.Contains(strings.ToLower(req.Model), "deepseek") {
371
+ // 使用Deepseek API
372
+ result, err = countTokensWithDeepseek(req)
373
+ } else if strings.Contains(strings.ToLower(req.Model), "gpt") || strings.Contains(strings.ToLower(req.Model), "openai") {
374
+ // 使用OpenAI API
375
+ result, err = countTokensWithOpenAI(req)
376
+ } else if strings.HasPrefix(strings.ToLower(req.Model), "claude") {
377
+ // 使用Claude API
378
+ if config.AnthropicKey == "" {
379
+ c.JSON(http.StatusBadRequest, ErrorResponse{Error: "ANTHROPIC_API_KEY 未设置,无法使用Claude模型"})
380
+ return
381
+ }
382
+ result, err = countTokensWithClaude(req)
383
+ } else if strings.Contains(strings.ToLower(req.Model), "gemini") {
384
+ // 使用Gemini API
385
+ if config.GoogleKey == "" {
386
+ c.JSON(http.StatusBadRequest, ErrorResponse{Error: "GOOGLE_API_KEY 未设置,无法使用Gemini模型"})
387
+ return
388
+ }
389
+ result, err = countTokensWithGemini(req)
390
+ } else if isUnsupportedModel {
391
+ // 不支持的模型,使用GPT-4o估算
392
+ // 创建新的请求,使用GPT-4o
393
+ gptReq := req
394
+ gptReq.Model = "gpt-4o"
395
+
396
+ // 使用OpenAI API
397
+ result, err = countTokensWithOpenAI(gptReq)
398
+
399
+ if err == nil {
400
+ // 返回估算值,但添加警告信息
401
+ c.JSON(http.StatusOK, gin.H{
402
+ "input_tokens": result.InputTokens,
403
+ "warning": fmt.Sprintf("The tokenizer for model '%s' is not supported yet. This is an estimation based on gpt-4o and may not be accurate.", originalModel),
404
+ "estimated_with": "gpt-4o",
405
+ })
406
+ return
407
+ }
408
+ } else {
409
+ // 完全不支持的情况,返回错误但仍提供估算值
410
+ // 使用GPT-4o进行估算
411
+ gptReq := req
412
+ gptReq.Model = "gpt-4o"
413
+
414
+ estimatedResult, estimateErr := countTokensWithOpenAI(gptReq)
415
+ if estimateErr == nil {
416
+ c.JSON(http.StatusOK, gin.H{
417
+ "input_tokens": estimatedResult.InputTokens,
418
+ "warning": fmt.Sprintf("The tokenizer for model '%s' is not supported yet. This is an estimation based on gpt-4o and may not be accurate.", originalModel),
419
+ "estimated_with": "gpt-4o",
420
+ })
421
+ } else {
422
+ c.JSON(http.StatusBadRequest, ErrorResponse{Error: fmt.Sprintf("The tokenizer for model '%s' is not supported yet.", originalModel)})
423
+ }
424
+ return
425
+ }
426
+
427
+ if err != nil {
428
+ c.JSON(http.StatusInternalServerError, ErrorResponse{Error: err.Error()})
429
+ return
430
+ }
431
+
432
+ // 返回结果
433
+ c.JSON(http.StatusOK, result)
434
+ }
435
+
436
+ // 健康检查
437
+ func healthCheck(c *gin.Context) {
438
+ c.JSON(http.StatusOK, gin.H{
439
+ "status": "healthy",
440
+ "time": time.Now().Format(time.RFC3339),
441
+ })
442
+ }
443
+
444
+ // 防休眠任务
445
+ func startKeepAlive() {
446
+ if config.ServiceURL == "" {
447
+ return
448
+ }
449
+
450
+ healthURL := fmt.Sprintf("%s/health", config.ServiceURL)
451
+ ticker := time.NewTicker(10 * time.Hour)
452
+
453
+ // 立即执行一次检查
454
+ go func() {
455
+ log.Printf("Starting keep-alive checks to %s", healthURL)
456
+ for {
457
+ resp, err := http.Get(healthURL)
458
+ if err != nil {
459
+ log.Printf("Keep-alive check failed: %v", err)
460
+ } else {
461
+ resp.Body.Close()
462
+ log.Printf("Keep-alive check successful")
463
+ }
464
+
465
+ // 等待下一次触发
466
+ <-ticker.C
467
+ }
468
+ }()
469
+ }
470
+
471
+ func main() {
472
+ // 加载配置
473
+ loadConfig()
474
+
475
+ // 设置gin模式
476
+ gin.SetMode(gin.ReleaseMode)
477
+
478
+ // 创建路由
479
+ r := gin.Default()
480
+
481
+ // 添加中间件
482
+ r.Use(gin.Recovery())
483
+ r.Use(func(c *gin.Context) {
484
+ c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
485
+ c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
486
+ c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
487
+ if c.Request.Method == "OPTIONS" {
488
+ c.AbortWithStatus(204)
489
+ return
490
+ }
491
+ c.Next()
492
+ })
493
+
494
+ // 路由
495
+ r.GET("/health", healthCheck)
496
+ r.POST("/count_tokens", countTokens)
497
+
498
+ // 获取端口
499
+ port := os.Getenv("PORT")
500
+ if port == "" {
501
+ port = "7860" // Hugging Face默认端口
502
+ }
503
+
504
+ // 启动防休眠任务
505
+ startKeepAlive()
506
+
507
+ // 启动服务器
508
+ log.Printf("Server starting on port %s", port)
509
+ if err := r.Run(":" + port); err != nil {
510
+ log.Fatal(err)
511
+ }
512
+ }
openai_service.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ import tiktoken
3
+ import os
4
+
5
+ app = Flask(__name__)
6
+
7
+ # OpenAI模型映射
8
+ MODEL_MAPPINGS = {
9
+ # GPT-4系列
10
+ "gpt-4o": "o200k_base",
11
+ "gpt-4-turbo": "cl100k_base",
12
+ "gpt-4": "cl100k_base",
13
+
14
+ # GPT-3.5系列
15
+ "gpt-3.5-turbo": "cl100k_base",
16
+ "gpt-35-turbo": "cl100k_base",
17
+
18
+ # 旧模型
19
+ "text-davinci-003": "p50k_base",
20
+ "text-davinci-002": "p50k_base",
21
+ "davinci": "r50k_base",
22
+
23
+ # 嵌入模型
24
+ "text-embedding-ada-002": "cl100k_base",
25
+ }
26
+
27
+ @app.route('/count_tokens', methods=['POST'])
28
+ def count_tokens():
29
+ try:
30
+ data = request.json
31
+ messages = data.get('messages', [])
32
+ system = data.get('system')
33
+ model = data.get('model', 'gpt-3.5-turbo')
34
+
35
+ # 根据模型名称选择合适的编码器
36
+ model_key = model.lower()
37
+ encoding_name = None
38
+
39
+ # 查找完全匹配
40
+ if model_key in MODEL_MAPPINGS:
41
+ encoding_name = MODEL_MAPPINGS[model_key]
42
+ else:
43
+ # 查找部分匹配
44
+ for key in MODEL_MAPPINGS:
45
+ if key in model_key:
46
+ encoding_name = MODEL_MAPPINGS[key]
47
+ break
48
+
49
+ # 如果没有找到匹配,使用默认的cl100k_base编码器
50
+ if not encoding_name:
51
+ encoding_name = "cl100k_base" # 最常用的编码器
52
+
53
+ # 获取编码器
54
+ try:
55
+ encoding = tiktoken.get_encoding(encoding_name)
56
+ except KeyError:
57
+ # 如果找不到编码器,使用gpt-3.5-turbo的编码器
58
+ encoding = tiktoken.encoding_for_model("gpt-3.5-turbo")
59
+
60
+ # 计算tokens
61
+ total_tokens = 0
62
+
63
+ # 按照OpenAI的格式计算tokens
64
+ # 参考: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
65
+
66
+ # 对于ChatGPT模型,每个请求都有3个隐藏tokens
67
+ if encoding_name in ["cl100k_base", "o200k_base"]:
68
+ # 每条消息开头有3个token,结尾有1个token
69
+ total_tokens += 3 # 每个请求的起始tokens
70
+
71
+ # 计算每条消息的tokens
72
+ for message in messages:
73
+ total_tokens += 4 # 每条消息增加4个token (包括角色)
74
+
75
+ for key, value in message.items():
76
+ total_tokens += len(encoding.encode(value))
77
+
78
+ # 名称字段比较少见,但也计入
79
+ if key == "name":
80
+ total_tokens -= 1 # 角色名称单独token计算减免
81
+
82
+ # 计算system消息的token
83
+ if system:
84
+ total_tokens += 4 # system消息也视为一条消息
85
+ total_tokens += len(encoding.encode(system))
86
+ else:
87
+ # 对于旧模型,只计算文本的token数量
88
+ all_text = ""
89
+ if system:
90
+ all_text += system + "\n\n"
91
+
92
+ for message in messages:
93
+ role = message.get('role', '')
94
+ content = message.get('content', '')
95
+ all_text += f"{role}: {content}\n"
96
+
97
+ total_tokens = len(encoding.encode(all_text))
98
+
99
+ return jsonify({
100
+ 'input_tokens': total_tokens,
101
+ 'model': model,
102
+ 'encoding': encoding_name
103
+ })
104
+ except Exception as e:
105
+ return jsonify({
106
+ 'error': str(e)
107
+ }), 400
108
+
109
+ @app.route('/health', methods=['GET'])
110
+ def health():
111
+ return jsonify({
112
+ 'status': 'healthy',
113
+ 'tokenizer': 'openai-tiktoken',
114
+ 'supported_models': list(MODEL_MAPPINGS.keys())
115
+ })
116
+
117
+ if __name__ == '__main__':
118
+ app.run(host='127.0.0.1', port=7862)