update sth at 2026-01-15 15:33:59
Browse files- .env +28 -11
- .gitattributes +0 -2
- .github/workflows/docker.yml +0 -64
- .gitignore +0 -180
- Dockerfile +6 -9
- NGINX_SETUP.md +10 -10
- README_DOCKER.md +10 -10
- app/__pycache__/__init__.cpython-311.pyc +0 -0
- app/admin/api.py +1 -1
- app/core/__pycache__/__init__.cpython-311.pyc +0 -0
- app/core/__pycache__/config.cpython-311.pyc +0 -0
- app/core/__pycache__/openai.cpython-311.pyc +0 -0
- app/core/config.py +11 -11
- app/core/openai.py +5 -7
- app/models/__pycache__/__init__.cpython-311.pyc +0 -0
- app/models/__pycache__/schemas.cpython-311.pyc +0 -0
- app/providers/__pycache__/__init__.cpython-311.pyc +0 -0
- app/providers/__pycache__/base.cpython-311.pyc +0 -0
- app/providers/__pycache__/k2think_provider.cpython-311.pyc +0 -0
- app/providers/__pycache__/longcat_provider.cpython-311.pyc +0 -0
- app/providers/__pycache__/provider_factory.cpython-311.pyc +0 -0
- app/providers/__pycache__/zai_provider.cpython-311.pyc +0 -0
- app/providers/zai_provider.py +17 -14
- app/templates/config.html +1 -1
- app/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- app/utils/__pycache__/fe_version.cpython-311.pyc +0 -0
- app/utils/__pycache__/logger.cpython-311.pyc +0 -0
- app/utils/__pycache__/reload_config.cpython-311.pyc +0 -0
- app/utils/__pycache__/signature.cpython-311.pyc +0 -0
- app/utils/__pycache__/token_pool.cpython-311.pyc +0 -0
- app/utils/__pycache__/tool_call_handler.cpython-311.pyc +0 -0
- app/utils/__pycache__/user_agent.cpython-311.pyc +0 -0
- app/utils/fe_version.py +112 -0
- app/utils/logger.py +0 -1
- docker-compose.yml +3 -3
- nginx.conf +0 -157
- nginx.conf.example +7 -7
- xREADME.md +0 -347
.env
CHANGED
|
@@ -1,18 +1,35 @@
|
|
| 1 |
-
#
|
| 2 |
-
#
|
|
|
|
| 3 |
|
| 4 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
SKIP_AUTH_TOKEN=true
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
-
|
|
|
|
| 10 |
ANONYMOUS_MODE=true
|
| 11 |
|
| 12 |
-
#
|
| 13 |
-
|
| 14 |
|
| 15 |
-
# 调
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ==============================================
|
| 2 |
+
# Z.AI API Server - Docker 环境变量配置示例
|
| 3 |
+
# ==============================================
|
| 4 |
|
| 5 |
+
# 管理后台密码
|
| 6 |
+
ADMIN_PASSWORD=admin123
|
| 7 |
+
|
| 8 |
+
# API 认证密钥 (用于验证客户端请求)
|
| 9 |
+
AUTH_TOKEN=sk-your-key
|
| 10 |
+
|
| 11 |
+
# 是否跳过 API Key 验证 (开发环境可设为 true)
|
| 12 |
SKIP_AUTH_TOKEN=true
|
| 13 |
|
| 14 |
+
# 调试日志 (生产环境建议设为 false)
|
| 15 |
+
DEBUG_LOGGING=true
|
| 16 |
+
|
| 17 |
+
# 匿名模式 (允许无 token 访问,需要配合 SKIP_AUTH_TOKEN=true)
|
| 18 |
ANONYMOUS_MODE=true
|
| 19 |
|
| 20 |
+
# Function Call 功能开关 (是否支持工具调用)
|
| 21 |
+
TOOL_SUPPORT=true
|
| 22 |
|
| 23 |
+
# 工具调用扫描限制 (字符数)
|
| 24 |
+
SCAN_LIMIT=200000
|
| 25 |
+
|
| 26 |
+
# 数据库路径 (Docker 环境使用持久化卷)
|
| 27 |
+
DB_PATH=/app/data/tokens.db
|
| 28 |
|
| 29 |
+
# Token 池配置
|
| 30 |
+
TOKEN_FAILURE_THRESHOLD=3
|
| 31 |
+
TOKEN_RECOVERY_TIMEOUT=300
|
| 32 |
+
|
| 33 |
+
# 服务配置
|
| 34 |
+
SERVICE_NAME=Z.AI_API_Server
|
| 35 |
+
LISTEN_PORT=7860
|
.gitattributes
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
# Auto detect text files and perform LF normalization
|
| 2 |
-
* text=auto
|
|
|
|
|
|
|
|
|
.github/workflows/docker.yml
DELETED
|
@@ -1,64 +0,0 @@
|
|
| 1 |
-
name: Build and Push Docker Image
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
push:
|
| 5 |
-
branches:
|
| 6 |
-
- main
|
| 7 |
-
tags:
|
| 8 |
-
- 'v*'
|
| 9 |
-
|
| 10 |
-
env:
|
| 11 |
-
IMAGE_NAME: z-ai2api-python
|
| 12 |
-
|
| 13 |
-
jobs:
|
| 14 |
-
docker:
|
| 15 |
-
runs-on: ubuntu-latest
|
| 16 |
-
permissions:
|
| 17 |
-
contents: read
|
| 18 |
-
packages: write
|
| 19 |
-
|
| 20 |
-
steps:
|
| 21 |
-
- name: Checkout
|
| 22 |
-
uses: actions/checkout@v4
|
| 23 |
-
|
| 24 |
-
- name: Set up Docker Buildx
|
| 25 |
-
uses: docker/setup-buildx-action@v3
|
| 26 |
-
|
| 27 |
-
- name: Login to GitHub Container Registry
|
| 28 |
-
uses: docker/login-action@v3
|
| 29 |
-
with:
|
| 30 |
-
registry: ghcr.io
|
| 31 |
-
username: ${{ github.actor }}
|
| 32 |
-
password: ${{ secrets.GITHUB_TOKEN }}
|
| 33 |
-
|
| 34 |
-
- name: Login to Docker Hub
|
| 35 |
-
if: github.event_name != 'pull_request'
|
| 36 |
-
uses: docker/login-action@v3
|
| 37 |
-
with:
|
| 38 |
-
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
| 39 |
-
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
| 40 |
-
|
| 41 |
-
- name: Extract metadata
|
| 42 |
-
id: meta
|
| 43 |
-
uses: docker/metadata-action@v5
|
| 44 |
-
with:
|
| 45 |
-
images: |
|
| 46 |
-
ghcr.io/${{ github.repository }}
|
| 47 |
-
${{ secrets.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
|
| 48 |
-
tags: |
|
| 49 |
-
type=ref,event=branch
|
| 50 |
-
type=semver,pattern={{version}}
|
| 51 |
-
type=semver,pattern={{major}}.{{minor}}
|
| 52 |
-
type=raw,value=latest,enable={{is_default_branch}}
|
| 53 |
-
|
| 54 |
-
- name: Build and push
|
| 55 |
-
uses: docker/build-push-action@v5
|
| 56 |
-
with:
|
| 57 |
-
context: .
|
| 58 |
-
file: ./deploy/Dockerfile
|
| 59 |
-
platforms: linux/amd64,linux/arm64
|
| 60 |
-
push: true
|
| 61 |
-
tags: ${{ steps.meta.outputs.tags }}
|
| 62 |
-
labels: ${{ steps.meta.outputs.labels }}
|
| 63 |
-
cache-from: type=gha
|
| 64 |
-
cache-to: type=gha,mode=max
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
DELETED
|
@@ -1,180 +0,0 @@
|
|
| 1 |
-
# Custom
|
| 2 |
-
.vs/
|
| 3 |
-
.vscode/
|
| 4 |
-
.idea/
|
| 5 |
-
.conda/
|
| 6 |
-
*.zip
|
| 7 |
-
*.txt
|
| 8 |
-
*.pid
|
| 9 |
-
docs/
|
| 10 |
-
output/
|
| 11 |
-
main.build/
|
| 12 |
-
main.dist/
|
| 13 |
-
main.onefile-build/
|
| 14 |
-
*report.xml
|
| 15 |
-
*.yaml
|
| 16 |
-
logs/
|
| 17 |
-
backup/
|
| 18 |
-
uv.lock
|
| 19 |
-
AGENTS.md
|
| 20 |
-
*.db
|
| 21 |
-
|
| 22 |
-
# AI Toolset
|
| 23 |
-
.augment/
|
| 24 |
-
.cursor/
|
| 25 |
-
.claude/
|
| 26 |
-
CLAUDE.md
|
| 27 |
-
|
| 28 |
-
# Byte-compiled / optimized / DLL files
|
| 29 |
-
__pycache__/
|
| 30 |
-
*.py[cod]
|
| 31 |
-
*$py.class
|
| 32 |
-
|
| 33 |
-
# C extensions
|
| 34 |
-
*.so
|
| 35 |
-
|
| 36 |
-
# Distribution / packaging
|
| 37 |
-
.Python
|
| 38 |
-
build/
|
| 39 |
-
develop-eggs/
|
| 40 |
-
dist/
|
| 41 |
-
downloads/
|
| 42 |
-
eggs/
|
| 43 |
-
.eggs/
|
| 44 |
-
lib/
|
| 45 |
-
lib64/
|
| 46 |
-
parts/
|
| 47 |
-
sdist/
|
| 48 |
-
var/
|
| 49 |
-
wheels/
|
| 50 |
-
share/python-wheels/
|
| 51 |
-
*.egg-info/
|
| 52 |
-
.installed.cfg
|
| 53 |
-
*.egg
|
| 54 |
-
MANIFEST
|
| 55 |
-
|
| 56 |
-
# PyInstaller
|
| 57 |
-
# Usually these files are written by a python script from a template
|
| 58 |
-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 59 |
-
*.manifest
|
| 60 |
-
*.spec
|
| 61 |
-
|
| 62 |
-
# Installer logs
|
| 63 |
-
pip-log.txt
|
| 64 |
-
pip-delete-this-directory.txt
|
| 65 |
-
|
| 66 |
-
# Unit test / coverage reports
|
| 67 |
-
htmlcov/
|
| 68 |
-
.tox/
|
| 69 |
-
.nox/
|
| 70 |
-
.coverage
|
| 71 |
-
.coverage.*
|
| 72 |
-
.cache
|
| 73 |
-
nosetests.xml
|
| 74 |
-
coverage.xml
|
| 75 |
-
*.cover
|
| 76 |
-
*.py,cover
|
| 77 |
-
.hypothesis/
|
| 78 |
-
.pytest_cache/
|
| 79 |
-
cover/
|
| 80 |
-
|
| 81 |
-
# Translations
|
| 82 |
-
*.mo
|
| 83 |
-
*.pot
|
| 84 |
-
|
| 85 |
-
# Django stuff:
|
| 86 |
-
*.log
|
| 87 |
-
local_settings.py
|
| 88 |
-
db.sqlite3
|
| 89 |
-
db.sqlite3-journal
|
| 90 |
-
|
| 91 |
-
# Flask stuff:
|
| 92 |
-
instance/
|
| 93 |
-
.webassets-cache
|
| 94 |
-
|
| 95 |
-
# Scrapy stuff:
|
| 96 |
-
.scrapy
|
| 97 |
-
|
| 98 |
-
# Sphinx documentation
|
| 99 |
-
docs/_build/
|
| 100 |
-
|
| 101 |
-
# PyBuilder
|
| 102 |
-
.pybuilder/
|
| 103 |
-
target/
|
| 104 |
-
|
| 105 |
-
# Jupyter Notebook
|
| 106 |
-
.ipynb_checkpoints
|
| 107 |
-
|
| 108 |
-
# IPython
|
| 109 |
-
profile_default/
|
| 110 |
-
ipython_config.py
|
| 111 |
-
|
| 112 |
-
# pyenv
|
| 113 |
-
# For a library or package, you might want to ignore these files since the code is
|
| 114 |
-
# intended to run in multiple environments; otherwise, check them in:
|
| 115 |
-
# .python-version
|
| 116 |
-
|
| 117 |
-
# pipenv
|
| 118 |
-
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 119 |
-
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 120 |
-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 121 |
-
# install all needed dependencies.
|
| 122 |
-
#Pipfile.lock
|
| 123 |
-
|
| 124 |
-
# poetry
|
| 125 |
-
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 126 |
-
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 127 |
-
# commonly ignored for libraries.
|
| 128 |
-
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 129 |
-
#poetry.lock
|
| 130 |
-
|
| 131 |
-
# pdm
|
| 132 |
-
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 133 |
-
#pdm.lock
|
| 134 |
-
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
| 135 |
-
# in version control.
|
| 136 |
-
# https://pdm.fming.dev/#use-with-ide
|
| 137 |
-
.pdm.toml
|
| 138 |
-
|
| 139 |
-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 140 |
-
__pypackages__/
|
| 141 |
-
|
| 142 |
-
# Celery stuff
|
| 143 |
-
celerybeat-schedule
|
| 144 |
-
celerybeat.pid
|
| 145 |
-
|
| 146 |
-
# SageMath parsed files
|
| 147 |
-
*.sage.py
|
| 148 |
-
|
| 149 |
-
# Environments
|
| 150 |
-
.env
|
| 151 |
-
.venv
|
| 152 |
-
env/
|
| 153 |
-
venv/
|
| 154 |
-
ENV/
|
| 155 |
-
env.bak/
|
| 156 |
-
venv.bak/
|
| 157 |
-
|
| 158 |
-
# Spyder project settings
|
| 159 |
-
.spyderproject
|
| 160 |
-
.spyproject
|
| 161 |
-
|
| 162 |
-
# Rope project settings
|
| 163 |
-
.ropeproject
|
| 164 |
-
|
| 165 |
-
# mkdocs documentation
|
| 166 |
-
/site
|
| 167 |
-
|
| 168 |
-
# mypy
|
| 169 |
-
.mypy_cache/
|
| 170 |
-
.dmypy.json
|
| 171 |
-
dmypy.json
|
| 172 |
-
|
| 173 |
-
# Pyre type checker
|
| 174 |
-
.pyre/
|
| 175 |
-
|
| 176 |
-
# pytype static type analyzer
|
| 177 |
-
.pytype/
|
| 178 |
-
|
| 179 |
-
# Cython debug symbols
|
| 180 |
-
cython_debug/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dockerfile
CHANGED
|
@@ -4,8 +4,9 @@ FROM python:3.12-slim
|
|
| 4 |
WORKDIR /app
|
| 5 |
|
| 6 |
# Create data and logs directories with proper permissions
|
| 7 |
-
|
| 8 |
-
|
|
|
|
| 9 |
|
| 10 |
# Install dependencies
|
| 11 |
COPY requirements.txt .
|
|
@@ -14,18 +15,14 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|
| 14 |
# Copy application code
|
| 15 |
COPY . .
|
| 16 |
|
| 17 |
-
# Set environment
|
| 18 |
ENV DB_PATH=/app/data/tokens.db
|
|
|
|
| 19 |
ENV SKIP_AUTH_TOKEN=true
|
| 20 |
ENV ANONYMOUS_MODE=true
|
| 21 |
-
# 服务监听端口
|
| 22 |
-
ENV LISTEN_PORT=7860
|
| 23 |
-
# 调试日志
|
| 24 |
-
ENV DEBUG_LOGGING=true
|
| 25 |
|
| 26 |
-
# Expose port
|
| 27 |
EXPOSE 7860
|
| 28 |
|
| 29 |
-
|
| 30 |
# Run the application
|
| 31 |
CMD ["python", "main.py"]
|
|
|
|
| 4 |
WORKDIR /app
|
| 5 |
|
| 6 |
# Create data and logs directories with proper permissions
|
| 7 |
+
# HuggingFace Spaces runs as non-root user, need proper permissions
|
| 8 |
+
RUN mkdir -p /app/data /app/logs /app/app/static/css /app/app/static/js && \
|
| 9 |
+
chmod -R 777 /app/data /app/logs /app/app/static
|
| 10 |
|
| 11 |
# Install dependencies
|
| 12 |
COPY requirements.txt .
|
|
|
|
| 15 |
# Copy application code
|
| 16 |
COPY . .
|
| 17 |
|
| 18 |
+
# Set environment variables
|
| 19 |
ENV DB_PATH=/app/data/tokens.db
|
| 20 |
+
ENV LISTEN_PORT=7860
|
| 21 |
ENV SKIP_AUTH_TOKEN=true
|
| 22 |
ENV ANONYMOUS_MODE=true
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
# Expose HuggingFace Spaces default port
|
| 25 |
EXPOSE 7860
|
| 26 |
|
|
|
|
| 27 |
# Run the application
|
| 28 |
CMD ["python", "main.py"]
|
NGINX_SETUP.md
CHANGED
|
@@ -37,7 +37,7 @@ server {
|
|
| 37 |
|
| 38 |
location /ai2api {
|
| 39 |
# 代理到后端服务
|
| 40 |
-
proxy_pass http://127.0.0.1:
|
| 41 |
|
| 42 |
# 传递原始请求信息
|
| 43 |
proxy_set_header Host $host;
|
|
@@ -72,7 +72,7 @@ services:
|
|
| 72 |
- LISTEN_PORT=7860
|
| 73 |
# ... 其他环境变量
|
| 74 |
ports:
|
| 75 |
-
- "
|
| 76 |
```
|
| 77 |
|
| 78 |
### 4. 重启服务
|
|
@@ -93,8 +93,8 @@ docker-compose restart
|
|
| 93 |
|
| 94 |
配置完成后,服务访问地址如下:
|
| 95 |
|
| 96 |
-
- **API 端点**: `http://your-domain.com/ai2api/v1/chat/completions`
|
| 97 |
-
- **模型列表**: `http://your-domain.com/ai2api/v1/models`
|
| 98 |
- **管理后台**: `http://your-domain.com/ai2api/admin/login`
|
| 99 |
- **根路径**: `http://your-domain.com/ai2api/`
|
| 100 |
|
|
@@ -110,7 +110,7 @@ ROOT_PATH=/api
|
|
| 110 |
**Nginx 配置**:
|
| 111 |
```nginx
|
| 112 |
location /api {
|
| 113 |
-
proxy_pass http://127.0.0.1:
|
| 114 |
# ... 其他配置
|
| 115 |
}
|
| 116 |
```
|
|
@@ -127,7 +127,7 @@ ROOT_PATH=
|
|
| 127 |
**Nginx 配置**:
|
| 128 |
```nginx
|
| 129 |
location / {
|
| 130 |
-
proxy_pass http://127.0.0.1:
|
| 131 |
# ... 其他配置
|
| 132 |
}
|
| 133 |
```
|
|
@@ -144,7 +144,7 @@ ROOT_PATH=/services/ai/chat
|
|
| 144 |
**Nginx 配置**:
|
| 145 |
```nginx
|
| 146 |
location /services/ai/chat {
|
| 147 |
-
proxy_pass http://127.0.0.1:
|
| 148 |
# ... 其他配置
|
| 149 |
}
|
| 150 |
```
|
|
@@ -215,7 +215,7 @@ proxy_set_header X-Forwarded-Proto $scheme;
|
|
| 215 |
|
| 216 |
1. **访问健康检查端点**:
|
| 217 |
```bash
|
| 218 |
-
curl http://your-domain.com/ai2api/v1/models
|
| 219 |
```
|
| 220 |
|
| 221 |
2. **访问管理后台**:
|
|
@@ -246,7 +246,7 @@ server {
|
|
| 246 |
ssl_certificate_key /path/to/key.pem;
|
| 247 |
|
| 248 |
location /ai2api {
|
| 249 |
-
proxy_pass http://127.0.0.1:
|
| 250 |
proxy_set_header X-Forwarded-Proto https;
|
| 251 |
# ... 其他配置
|
| 252 |
}
|
|
@@ -257,7 +257,7 @@ server {
|
|
| 257 |
|
| 258 |
```nginx
|
| 259 |
upstream ai2api_backend {
|
| 260 |
-
server 127.0.0.1:
|
| 261 |
server 127.0.0.1:8081;
|
| 262 |
server 127.0.0.1:8082;
|
| 263 |
}
|
|
|
|
| 37 |
|
| 38 |
location /ai2api {
|
| 39 |
# 代理到后端服务
|
| 40 |
+
proxy_pass http://127.0.0.1:8080;
|
| 41 |
|
| 42 |
# 传递原始请求信息
|
| 43 |
proxy_set_header Host $host;
|
|
|
|
| 72 |
- LISTEN_PORT=7860
|
| 73 |
# ... 其他环境变量
|
| 74 |
ports:
|
| 75 |
+
- "8080:8080"
|
| 76 |
```
|
| 77 |
|
| 78 |
### 4. 重启服务
|
|
|
|
| 93 |
|
| 94 |
配置完成后,服务访问地址如下:
|
| 95 |
|
| 96 |
+
- **API 端点**: `http://your-domain.com/ai2api/hf/v1/chat/completions`
|
| 97 |
+
- **模型列表**: `http://your-domain.com/ai2api/hf/v1/models`
|
| 98 |
- **管理后台**: `http://your-domain.com/ai2api/admin/login`
|
| 99 |
- **根路径**: `http://your-domain.com/ai2api/`
|
| 100 |
|
|
|
|
| 110 |
**Nginx 配置**:
|
| 111 |
```nginx
|
| 112 |
location /api {
|
| 113 |
+
proxy_pass http://127.0.0.1:8080;
|
| 114 |
# ... 其他配置
|
| 115 |
}
|
| 116 |
```
|
|
|
|
| 127 |
**Nginx 配置**:
|
| 128 |
```nginx
|
| 129 |
location / {
|
| 130 |
+
proxy_pass http://127.0.0.1:8080;
|
| 131 |
# ... 其他配置
|
| 132 |
}
|
| 133 |
```
|
|
|
|
| 144 |
**Nginx 配置**:
|
| 145 |
```nginx
|
| 146 |
location /services/ai/chat {
|
| 147 |
+
proxy_pass http://127.0.0.1:8080;
|
| 148 |
# ... 其他配置
|
| 149 |
}
|
| 150 |
```
|
|
|
|
| 215 |
|
| 216 |
1. **访问健康检查端点**:
|
| 217 |
```bash
|
| 218 |
+
curl http://your-domain.com/ai2api/hf/v1/models
|
| 219 |
```
|
| 220 |
|
| 221 |
2. **访问管理后台**:
|
|
|
|
| 246 |
ssl_certificate_key /path/to/key.pem;
|
| 247 |
|
| 248 |
location /ai2api {
|
| 249 |
+
proxy_pass http://127.0.0.1:8080;
|
| 250 |
proxy_set_header X-Forwarded-Proto https;
|
| 251 |
# ... 其他配置
|
| 252 |
}
|
|
|
|
| 257 |
|
| 258 |
```nginx
|
| 259 |
upstream ai2api_backend {
|
| 260 |
+
server 127.0.0.1:8080;
|
| 261 |
server 127.0.0.1:8081;
|
| 262 |
server 127.0.0.1:8082;
|
| 263 |
}
|
README_DOCKER.md
CHANGED
|
@@ -16,7 +16,7 @@ mkdir -p data logs
|
|
| 16 |
# 快速启动
|
| 17 |
docker run -d \
|
| 18 |
--name z-ai-api-server \
|
| 19 |
-
-p
|
| 20 |
-e ADMIN_PASSWORD=admin123 \
|
| 21 |
-e AUTH_TOKEN=sk-your-api-key \
|
| 22 |
-e ANONYMOUS_MODE=true \
|
|
@@ -48,7 +48,7 @@ docker compose up -d
|
|
| 48 |
docker compose logs -f api-server
|
| 49 |
```
|
| 50 |
|
| 51 |
-
服务将在 `http://localhost:
|
| 52 |
|
| 53 |
## 架构说明
|
| 54 |
|
|
@@ -222,10 +222,10 @@ docker compose config
|
|
| 222 |
|
| 223 |
### 端口冲突
|
| 224 |
|
| 225 |
-
如端口
|
| 226 |
```yaml
|
| 227 |
ports:
|
| 228 |
-
- "8081:
|
| 229 |
```
|
| 230 |
|
| 231 |
### 健康检查失败
|
|
@@ -235,7 +235,7 @@ ports:
|
|
| 235 |
docker compose ps
|
| 236 |
|
| 237 |
# 手动测试接口
|
| 238 |
-
curl http://localhost:
|
| 239 |
|
| 240 |
# 进入容器排查
|
| 241 |
docker exec -it z-ai-api-server bash
|
|
@@ -245,11 +245,11 @@ docker exec -it z-ai-api-server bash
|
|
| 245 |
|
| 246 |
| 端点 | 地址 | 说明 |
|
| 247 |
|------|------|------|
|
| 248 |
-
| API 根路径 | `http://localhost:
|
| 249 |
-
| 模型列表 | `http://localhost:
|
| 250 |
-
| 管理后台 | `http://localhost:
|
| 251 |
-
| API 文档 | `http://localhost:
|
| 252 |
-
| 健康检查 | `http://localhost:
|
| 253 |
|
| 254 |
## 高级配置
|
| 255 |
|
|
|
|
| 16 |
# 快速启动
|
| 17 |
docker run -d \
|
| 18 |
--name z-ai-api-server \
|
| 19 |
+
-p 8080:8080 \
|
| 20 |
-e ADMIN_PASSWORD=admin123 \
|
| 21 |
-e AUTH_TOKEN=sk-your-api-key \
|
| 22 |
-e ANONYMOUS_MODE=true \
|
|
|
|
| 48 |
docker compose logs -f api-server
|
| 49 |
```
|
| 50 |
|
| 51 |
+
服务将在 `http://localhost:8080` 启动。
|
| 52 |
|
| 53 |
## 架构说明
|
| 54 |
|
|
|
|
| 222 |
|
| 223 |
### 端口冲突
|
| 224 |
|
| 225 |
+
如端口 8080 被占用,修改 `docker-compose.yml`:
|
| 226 |
```yaml
|
| 227 |
ports:
|
| 228 |
+
- "8081:8080" # 映射到宿主机 8081 端口
|
| 229 |
```
|
| 230 |
|
| 231 |
### 健康检查失败
|
|
|
|
| 235 |
docker compose ps
|
| 236 |
|
| 237 |
# 手动测试接口
|
| 238 |
+
curl http://localhost:8080/hf/v1/models
|
| 239 |
|
| 240 |
# 进入容器排查
|
| 241 |
docker exec -it z-ai-api-server bash
|
|
|
|
| 245 |
|
| 246 |
| 端点 | 地址 | 说明 |
|
| 247 |
|------|------|------|
|
| 248 |
+
| API 根路径 | `http://localhost:8080` | OpenAI 兼容 API |
|
| 249 |
+
| 模型列表 | `http://localhost:8080/hf/v1/models` | 获取可用模型 |
|
| 250 |
+
| 管理后台 | `http://localhost:8080/admin` | Web 管理界面 |
|
| 251 |
+
| API 文档 | `http://localhost:8080/docs` | OpenAPI/Swagger 文档 |
|
| 252 |
+
| 健康检查 | `http://localhost:8080/hf/v1/models` | 服务健康状态 |
|
| 253 |
|
| 254 |
## 高级配置
|
| 255 |
|
app/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (268 Bytes). View file
|
|
|
app/admin/api.py
CHANGED
|
@@ -166,7 +166,7 @@ async def get_recent_logs(request: Request):
|
|
| 166 |
logs = [
|
| 167 |
{
|
| 168 |
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 169 |
-
"endpoint": "/v1/chat/completions",
|
| 170 |
"model": "gpt-4o",
|
| 171 |
"status": 200,
|
| 172 |
"duration": "1.23s",
|
|
|
|
| 166 |
logs = [
|
| 167 |
{
|
| 168 |
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 169 |
+
"endpoint": "/hf/v1/chat/completions",
|
| 170 |
"model": "gpt-4o",
|
| 171 |
"status": 200,
|
| 172 |
"duration": "1.23s",
|
app/core/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (267 Bytes). View file
|
|
|
app/core/__pycache__/config.cpython-311.pyc
ADDED
|
Binary file (4.86 kB). View file
|
|
|
app/core/__pycache__/openai.cpython-311.pyc
ADDED
|
Binary file (9.16 kB). View file
|
|
|
app/core/config.py
CHANGED
|
@@ -24,11 +24,11 @@ class Settings(BaseSettings):
|
|
| 24 |
GLM45_THINKING_MODEL: str = os.getenv("GLM45_THINKING_MODEL", "GLM-4.5-Thinking")
|
| 25 |
GLM45_SEARCH_MODEL: str = os.getenv("GLM45_SEARCH_MODEL", "GLM-4.5-Search")
|
| 26 |
GLM45_AIR_MODEL: str = os.getenv("GLM45_AIR_MODEL", "GLM-4.5-Air")
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
|
| 33 |
# Provider Model Mapping
|
| 34 |
@property
|
|
@@ -40,11 +40,11 @@ class Settings(BaseSettings):
|
|
| 40 |
"GLM-4.5-Thinking": "zai",
|
| 41 |
"GLM-4.5-Search": "zai",
|
| 42 |
"GLM-4.5-Air": "zai",
|
| 43 |
-
"GLM-4.
|
| 44 |
-
"GLM-4.
|
| 45 |
-
"GLM-4.
|
| 46 |
-
"GLM-4.
|
| 47 |
-
"GLM-4.
|
| 48 |
# K2Think models
|
| 49 |
"MBZUAI-IFM/K2-Think": "k2think",
|
| 50 |
# LongCat models
|
|
@@ -54,7 +54,7 @@ class Settings(BaseSettings):
|
|
| 54 |
}
|
| 55 |
|
| 56 |
# Server Configuration
|
| 57 |
-
LISTEN_PORT: int = int(os.getenv("LISTEN_PORT", "7860"))
|
| 58 |
DEBUG_LOGGING: bool = os.getenv("DEBUG_LOGGING", "true").lower() == "true"
|
| 59 |
SERVICE_NAME: str = os.getenv("SERVICE_NAME", "z-ai2api-server")
|
| 60 |
ROOT_PATH: str = os.getenv("ROOT_PATH", "") # For Nginx reverse proxy path prefix, e.g., "/api" or "/path-prefix"
|
|
|
|
| 24 |
GLM45_THINKING_MODEL: str = os.getenv("GLM45_THINKING_MODEL", "GLM-4.5-Thinking")
|
| 25 |
GLM45_SEARCH_MODEL: str = os.getenv("GLM45_SEARCH_MODEL", "GLM-4.5-Search")
|
| 26 |
GLM45_AIR_MODEL: str = os.getenv("GLM45_AIR_MODEL", "GLM-4.5-Air")
|
| 27 |
+
GLM46V_MODEL: str = os.getenv("GLM46V_MODEL", "GLM-4.6V")
|
| 28 |
+
GLM47_MODEL: str = os.getenv("GLM47_MODEL", "GLM-4.7")
|
| 29 |
+
GLM47_THINKING_MODEL: str = os.getenv("GLM47_THINKING_MODEL", "GLM-4.7-Thinking")
|
| 30 |
+
GLM47_SEARCH_MODEL: str = os.getenv("GLM47_SEARCH_MODEL", "GLM-4.7-Search")
|
| 31 |
+
GLM47_ADVANCED_SEARCH_MODEL: str = os.getenv("GLM47_ADVANCED_SEARCH_MODEL", "GLM-4.7-advanced-search")
|
| 32 |
|
| 33 |
# Provider Model Mapping
|
| 34 |
@property
|
|
|
|
| 40 |
"GLM-4.5-Thinking": "zai",
|
| 41 |
"GLM-4.5-Search": "zai",
|
| 42 |
"GLM-4.5-Air": "zai",
|
| 43 |
+
"GLM-4.6V": "zai",
|
| 44 |
+
"GLM-4.7": "zai",
|
| 45 |
+
"GLM-4.7-Thinking": "zai",
|
| 46 |
+
"GLM-4.7-Search": "zai",
|
| 47 |
+
"GLM-4.7-advanced-search": "zai",
|
| 48 |
# K2Think models
|
| 49 |
"MBZUAI-IFM/K2-Think": "k2think",
|
| 50 |
# LongCat models
|
|
|
|
| 54 |
}
|
| 55 |
|
| 56 |
# Server Configuration
|
| 57 |
+
LISTEN_PORT: int = int(os.getenv("LISTEN_PORT", "7860")) # HuggingFace Spaces 默认端口
|
| 58 |
DEBUG_LOGGING: bool = os.getenv("DEBUG_LOGGING", "true").lower() == "true"
|
| 59 |
SERVICE_NAME: str = os.getenv("SERVICE_NAME", "z-ai2api-server")
|
| 60 |
ROOT_PATH: str = os.getenv("ROOT_PATH", "") # For Nginx reverse proxy path prefix, e.g., "/api" or "/path-prefix"
|
app/core/openai.py
CHANGED
|
@@ -92,9 +92,8 @@ async def handle_non_stream_response(stream_response, request: OpenAIRequest) ->
|
|
| 92 |
return JSONResponse(content=response_data.model_dump(exclude_none=True))
|
| 93 |
|
| 94 |
|
| 95 |
-
@router.get("/v1/models")
|
| 96 |
@router.get("/hf/v1/models")
|
| 97 |
-
@router.get("/
|
| 98 |
async def list_models():
|
| 99 |
"""List available models from all providers"""
|
| 100 |
try:
|
|
@@ -107,18 +106,17 @@ async def list_models():
|
|
| 107 |
current_time = int(time.time())
|
| 108 |
fallback_response = ModelsResponse(
|
| 109 |
data=[
|
| 110 |
-
Model(id=settings.
|
| 111 |
-
Model(id=settings.
|
| 112 |
-
Model(id=settings.
|
| 113 |
Model(id=settings.GLM45_AIR_MODEL, created=current_time, owned_by="z.ai"),
|
| 114 |
]
|
| 115 |
)
|
| 116 |
return fallback_response
|
| 117 |
|
| 118 |
|
| 119 |
-
@router.post("/v1/chat/completions")
|
| 120 |
@router.post("/hf/v1/chat/completions")
|
| 121 |
-
@router.post("/
|
| 122 |
async def chat_completions(request: OpenAIRequest, authorization: str = Header(...)):
|
| 123 |
"""Handle chat completion requests with multi-provider architecture"""
|
| 124 |
role = request.messages[0].role if request.messages else "unknown"
|
|
|
|
| 92 |
return JSONResponse(content=response_data.model_dump(exclude_none=True))
|
| 93 |
|
| 94 |
|
|
|
|
| 95 |
@router.get("/hf/v1/models")
|
| 96 |
+
@router.get("/v1/models")
|
| 97 |
async def list_models():
|
| 98 |
"""List available models from all providers"""
|
| 99 |
try:
|
|
|
|
| 106 |
current_time = int(time.time())
|
| 107 |
fallback_response = ModelsResponse(
|
| 108 |
data=[
|
| 109 |
+
Model(id=settings.GLM47_MODEL, created=current_time, owned_by="z.ai"),
|
| 110 |
+
Model(id=settings.GLM47_THINKING_MODEL, created=current_time, owned_by="z.ai"),
|
| 111 |
+
Model(id=settings.GLM47_SEARCH_MODEL, created=current_time, owned_by="z.ai"),
|
| 112 |
Model(id=settings.GLM45_AIR_MODEL, created=current_time, owned_by="z.ai"),
|
| 113 |
]
|
| 114 |
)
|
| 115 |
return fallback_response
|
| 116 |
|
| 117 |
|
|
|
|
| 118 |
@router.post("/hf/v1/chat/completions")
|
| 119 |
+
@router.post("/v1/chat/completions")
|
| 120 |
async def chat_completions(request: OpenAIRequest, authorization: str = Header(...)):
|
| 121 |
"""Handle chat completion requests with multi-provider architecture"""
|
| 122 |
role = request.messages[0].role if request.messages else "unknown"
|
app/models/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (239 Bytes). View file
|
|
|
app/models/__pycache__/schemas.cpython-311.pyc
ADDED
|
Binary file (8.32 kB). View file
|
|
|
app/providers/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (917 Bytes). View file
|
|
|
app/providers/__pycache__/base.cpython-311.pyc
ADDED
|
Binary file (12.3 kB). View file
|
|
|
app/providers/__pycache__/k2think_provider.cpython-311.pyc
ADDED
|
Binary file (26.1 kB). View file
|
|
|
app/providers/__pycache__/longcat_provider.cpython-311.pyc
ADDED
|
Binary file (22.2 kB). View file
|
|
|
app/providers/__pycache__/provider_factory.cpython-311.pyc
ADDED
|
Binary file (9.81 kB). View file
|
|
|
app/providers/__pycache__/zai_provider.cpython-311.pyc
ADDED
|
Binary file (51.6 kB). View file
|
|
|
app/providers/zai_provider.py
CHANGED
|
@@ -20,6 +20,7 @@ import random
|
|
| 20 |
from datetime import datetime
|
| 21 |
from typing import Dict, List, Any, Optional, AsyncGenerator, Union
|
| 22 |
from app.utils.user_agent import get_random_user_agent
|
|
|
|
| 23 |
from app.utils.signature import generate_signature
|
| 24 |
from app.providers.base import BaseProvider, ProviderConfig
|
| 25 |
from app.models.schemas import OpenAIRequest, Message
|
|
@@ -42,6 +43,7 @@ def get_zai_dynamic_headers(chat_id: str = "") -> Dict[str, str]:
|
|
| 42 |
browser_choices = ["chrome", "chrome", "chrome", "edge", "edge", "firefox", "safari"]
|
| 43 |
browser_type = random.choice(browser_choices)
|
| 44 |
user_agent = get_random_user_agent(browser_type)
|
|
|
|
| 45 |
|
| 46 |
chrome_version = "139"
|
| 47 |
edge_version = "139"
|
|
@@ -70,7 +72,7 @@ def get_zai_dynamic_headers(chat_id: str = "") -> Dict[str, str]:
|
|
| 70 |
"Cache-Control": "no-cache",
|
| 71 |
"User-Agent": user_agent,
|
| 72 |
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
| 73 |
-
"X-FE-Version":
|
| 74 |
"Origin": "https://chat.z.ai",
|
| 75 |
}
|
| 76 |
|
|
@@ -141,11 +143,11 @@ class ZAIProvider(BaseProvider):
|
|
| 141 |
settings.GLM45_THINKING_MODEL: "0727-360B-API", # GLM-4.5-Thinking
|
| 142 |
settings.GLM45_SEARCH_MODEL: "0727-360B-API", # GLM-4.5-Search
|
| 143 |
settings.GLM45_AIR_MODEL: "0727-106B-API", # GLM-4.5-Air
|
| 144 |
-
settings.
|
| 145 |
-
settings.
|
| 146 |
-
settings.
|
| 147 |
-
settings.
|
| 148 |
-
settings.
|
| 149 |
}
|
| 150 |
|
| 151 |
def get_supported_models(self) -> List[str]:
|
|
@@ -155,11 +157,11 @@ class ZAIProvider(BaseProvider):
|
|
| 155 |
settings.GLM45_THINKING_MODEL,
|
| 156 |
settings.GLM45_SEARCH_MODEL,
|
| 157 |
settings.GLM45_AIR_MODEL,
|
| 158 |
-
settings.
|
| 159 |
-
settings.
|
| 160 |
-
settings.
|
| 161 |
-
settings.
|
| 162 |
-
settings.
|
| 163 |
]
|
| 164 |
|
| 165 |
def _get_proxy_config(self) -> Optional[str]:
|
|
@@ -528,7 +530,7 @@ class ZAIProvider(BaseProvider):
|
|
| 528 |
requested_model = request.model
|
| 529 |
is_thinking = "-thinking" in requested_model.casefold()
|
| 530 |
is_search = "-search" in requested_model.casefold()
|
| 531 |
-
is_advanced_search = requested_model == settings.
|
| 532 |
is_air = "-air" in requested_model.casefold()
|
| 533 |
|
| 534 |
# 获取上游模型ID
|
|
@@ -636,6 +638,7 @@ class ZAIProvider(BaseProvider):
|
|
| 636 |
user_id = _extract_user_id_from_token(token)
|
| 637 |
timestamp_ms = int(time.time() * 1000)
|
| 638 |
request_id = generate_uuid()
|
|
|
|
| 639 |
try:
|
| 640 |
signing_metadata = f"requestId,{request_id},timestamp,{timestamp_ms},user_id,{user_id}"
|
| 641 |
prompt_for_signature = last_user_text or ""
|
|
@@ -650,11 +653,11 @@ class ZAIProvider(BaseProvider):
|
|
| 650 |
logger.error(f"[Z.AI] 签名生成失败: {e}")
|
| 651 |
signature = ""
|
| 652 |
|
| 653 |
-
# 构建请求头
|
| 654 |
headers = {
|
| 655 |
"Authorization": f"Bearer {token}",
|
| 656 |
"Content-Type": "application/json",
|
| 657 |
-
"X-FE-Version":
|
| 658 |
"X-Signature": signature,
|
| 659 |
}
|
| 660 |
|
|
|
|
| 20 |
from datetime import datetime
|
| 21 |
from typing import Dict, List, Any, Optional, AsyncGenerator, Union
|
| 22 |
from app.utils.user_agent import get_random_user_agent
|
| 23 |
+
from app.utils.fe_version import get_latest_fe_version
|
| 24 |
from app.utils.signature import generate_signature
|
| 25 |
from app.providers.base import BaseProvider, ProviderConfig
|
| 26 |
from app.models.schemas import OpenAIRequest, Message
|
|
|
|
| 43 |
browser_choices = ["chrome", "chrome", "chrome", "edge", "edge", "firefox", "safari"]
|
| 44 |
browser_type = random.choice(browser_choices)
|
| 45 |
user_agent = get_random_user_agent(browser_type)
|
| 46 |
+
fe_version = get_latest_fe_version()
|
| 47 |
|
| 48 |
chrome_version = "139"
|
| 49 |
edge_version = "139"
|
|
|
|
| 72 |
"Cache-Control": "no-cache",
|
| 73 |
"User-Agent": user_agent,
|
| 74 |
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
|
| 75 |
+
"X-FE-Version": fe_version,
|
| 76 |
"Origin": "https://chat.z.ai",
|
| 77 |
}
|
| 78 |
|
|
|
|
| 143 |
settings.GLM45_THINKING_MODEL: "0727-360B-API", # GLM-4.5-Thinking
|
| 144 |
settings.GLM45_SEARCH_MODEL: "0727-360B-API", # GLM-4.5-Search
|
| 145 |
settings.GLM45_AIR_MODEL: "0727-106B-API", # GLM-4.5-Air
|
| 146 |
+
settings.GLM46V_MODEL: "glm-4.6v", # GLM-4.6V多模态
|
| 147 |
+
settings.GLM47_MODEL: "glm-4.7", # GLM-4.7
|
| 148 |
+
settings.GLM47_THINKING_MODEL: "glm-4.7", # GLM-4.7-Thinking
|
| 149 |
+
settings.GLM47_SEARCH_MODEL: "glm-4.7", # GLM-4.7-Search
|
| 150 |
+
settings.GLM47_ADVANCED_SEARCH_MODEL: "glm-4.7", # GLM-4.7-advanced-search
|
| 151 |
}
|
| 152 |
|
| 153 |
def get_supported_models(self) -> List[str]:
|
|
|
|
| 157 |
settings.GLM45_THINKING_MODEL,
|
| 158 |
settings.GLM45_SEARCH_MODEL,
|
| 159 |
settings.GLM45_AIR_MODEL,
|
| 160 |
+
settings.GLM46V_MODEL,
|
| 161 |
+
settings.GLM47_MODEL,
|
| 162 |
+
settings.GLM47_THINKING_MODEL,
|
| 163 |
+
settings.GLM47_SEARCH_MODEL,
|
| 164 |
+
settings.GLM47_ADVANCED_SEARCH_MODEL,
|
| 165 |
]
|
| 166 |
|
| 167 |
def _get_proxy_config(self) -> Optional[str]:
|
|
|
|
| 530 |
requested_model = request.model
|
| 531 |
is_thinking = "-thinking" in requested_model.casefold()
|
| 532 |
is_search = "-search" in requested_model.casefold()
|
| 533 |
+
is_advanced_search = requested_model == settings.GLM47_ADVANCED_SEARCH_MODEL
|
| 534 |
is_air = "-air" in requested_model.casefold()
|
| 535 |
|
| 536 |
# 获取上游模型ID
|
|
|
|
| 638 |
user_id = _extract_user_id_from_token(token)
|
| 639 |
timestamp_ms = int(time.time() * 1000)
|
| 640 |
request_id = generate_uuid()
|
| 641 |
+
fe_version = get_latest_fe_version()
|
| 642 |
try:
|
| 643 |
signing_metadata = f"requestId,{request_id},timestamp,{timestamp_ms},user_id,{user_id}"
|
| 644 |
prompt_for_signature = last_user_text or ""
|
|
|
|
| 653 |
logger.error(f"[Z.AI] 签名生成失败: {e}")
|
| 654 |
signature = ""
|
| 655 |
|
| 656 |
+
# 构建请求头
|
| 657 |
headers = {
|
| 658 |
"Authorization": f"Bearer {token}",
|
| 659 |
"Content-Type": "application/json",
|
| 660 |
+
"X-FE-Version": fe_version,
|
| 661 |
"X-Signature": signature,
|
| 662 |
}
|
| 663 |
|
app/templates/config.html
CHANGED
|
@@ -43,7 +43,7 @@
|
|
| 43 |
<label class="block text-sm font-medium text-gray-700">监听端口</label>
|
| 44 |
<input type="number"
|
| 45 |
name="listen_port"
|
| 46 |
-
value="{{ config.LISTEN_PORT or
|
| 47 |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
|
| 48 |
<p class="mt-1 text-sm text-gray-500">服务监听的端口号</p>
|
| 49 |
</div>
|
|
|
|
| 43 |
<label class="block text-sm font-medium text-gray-700">监听端口</label>
|
| 44 |
<input type="number"
|
| 45 |
name="listen_port"
|
| 46 |
+
value="{{ config.LISTEN_PORT or 7860 }}"
|
| 47 |
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm">
|
| 48 |
<p class="mt-1 text-sm text-gray-500">服务监听的端口号</p>
|
| 49 |
</div>
|
app/utils/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (276 Bytes). View file
|
|
|
app/utils/__pycache__/fe_version.cpython-311.pyc
ADDED
|
Binary file (4.65 kB). View file
|
|
|
app/utils/__pycache__/logger.cpython-311.pyc
ADDED
|
Binary file (4.28 kB). View file
|
|
|
app/utils/__pycache__/reload_config.cpython-311.pyc
ADDED
|
Binary file (1.13 kB). View file
|
|
|
app/utils/__pycache__/signature.cpython-311.pyc
ADDED
|
Binary file (1.93 kB). View file
|
|
|
app/utils/__pycache__/token_pool.cpython-311.pyc
ADDED
|
Binary file (30.3 kB). View file
|
|
|
app/utils/__pycache__/tool_call_handler.cpython-311.pyc
ADDED
|
Binary file (13.7 kB). View file
|
|
|
app/utils/__pycache__/user_agent.cpython-311.pyc
ADDED
|
Binary file (4.04 kB). View file
|
|
|
app/utils/fe_version.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
|
| 4 |
+
"""
|
| 5 |
+
Utility helpers for resolving the latest X-FE-Version value from chat.z.ai.
|
| 6 |
+
|
| 7 |
+
The upstream service embeds the current front-end release identifier inside
|
| 8 |
+
its landing page static asset URLs (e.g. `prod-fe-1.0.107`). The helpers in
|
| 9 |
+
this module fetch the landing page, extract the version string, and cache it
|
| 10 |
+
with a configurable TTL so the expensive network fetch only happens when
|
| 11 |
+
necessary.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import re
|
| 17 |
+
import time
|
| 18 |
+
from typing import Optional
|
| 19 |
+
|
| 20 |
+
import httpx
|
| 21 |
+
|
| 22 |
+
from app.utils.logger import get_logger
|
| 23 |
+
from app.utils.user_agent import get_random_user_agent
|
| 24 |
+
|
| 25 |
+
# Base URL to probe for the version string.
|
| 26 |
+
FE_VERSION_SOURCE_URL = "https://chat.z.ai"
|
| 27 |
+
|
| 28 |
+
# Cache TTL in seconds (default: 30 minutes).
|
| 29 |
+
CACHE_TTL_SECONDS = 1800
|
| 30 |
+
|
| 31 |
+
_logger = get_logger()
|
| 32 |
+
_version_pattern = re.compile(r"prod-fe-\d+\.\d+\.\d+")
|
| 33 |
+
|
| 34 |
+
_cached_version: str = ""
|
| 35 |
+
_cached_at: float = 0.0
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _extract_version(page_content: str) -> Optional[str]:
|
| 39 |
+
"""Extract the version string from the page content."""
|
| 40 |
+
if not page_content:
|
| 41 |
+
return None
|
| 42 |
+
|
| 43 |
+
matches = _version_pattern.findall(page_content)
|
| 44 |
+
if not matches:
|
| 45 |
+
return None
|
| 46 |
+
|
| 47 |
+
# Choose the highest lexical value to guard against mixed versions.
|
| 48 |
+
return max(matches)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _should_use_cache(force_refresh: bool) -> bool:
|
| 54 |
+
"""Determine whether the cached value can be reused."""
|
| 55 |
+
if force_refresh:
|
| 56 |
+
return False
|
| 57 |
+
if not _cached_version:
|
| 58 |
+
return False
|
| 59 |
+
if _cached_at <= 0:
|
| 60 |
+
return False
|
| 61 |
+
return (time.time() - _cached_at) < CACHE_TTL_SECONDS
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def get_latest_fe_version(force_refresh: bool = False) -> str:
|
| 65 |
+
"""
|
| 66 |
+
Resolve the latest X-FE-Version value from chat.z.ai.
|
| 67 |
+
|
| 68 |
+
The lookup order is:
|
| 69 |
+
1. Cached value within TTL.
|
| 70 |
+
2. Remote fetch from chat.z.ai.
|
| 71 |
+
|
| 72 |
+
Raises:
|
| 73 |
+
Exception: If unable to fetch the version from the remote source.
|
| 74 |
+
"""
|
| 75 |
+
global _cached_version, _cached_at
|
| 76 |
+
|
| 77 |
+
if _should_use_cache(force_refresh):
|
| 78 |
+
return _cached_version
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
headers = {"User-Agent": get_random_user_agent("chrome")}
|
| 82 |
+
except Exception:
|
| 83 |
+
headers = {
|
| 84 |
+
"User-Agent": (
|
| 85 |
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
| 86 |
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
| 87 |
+
"Chrome/120.0.0.0 Safari/537.36"
|
| 88 |
+
)
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
try:
|
| 92 |
+
with httpx.Client(timeout=10.0, follow_redirects=True) as client:
|
| 93 |
+
response = client.get(FE_VERSION_SOURCE_URL, headers=headers)
|
| 94 |
+
response.raise_for_status()
|
| 95 |
+
version = _extract_version(response.text)
|
| 96 |
+
if version:
|
| 97 |
+
if version != _cached_version:
|
| 98 |
+
_logger.info(f"[Z.AI] Detected X-FE-Version update: {version}")
|
| 99 |
+
_cached_version = version
|
| 100 |
+
_cached_at = time.time()
|
| 101 |
+
return version
|
| 102 |
+
|
| 103 |
+
_logger.error("[Z.AI] Unable to locate X-FE-Version in landing page")
|
| 104 |
+
raise Exception("Unable to locate X-FE-Version in landing page")
|
| 105 |
+
except Exception as exc:
|
| 106 |
+
_logger.error(f"[Z.AI] Failed to fetch X-FE-Version from {FE_VERSION_SOURCE_URL}: {exc}")
|
| 107 |
+
raise Exception(f"Failed to fetch X-FE-Version: {exc}")
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def refresh_fe_version() -> str:
|
| 111 |
+
"""Force refresh the cached version by bypassing the TTL."""
|
| 112 |
+
return get_latest_fe_version(force_refresh=True)
|
app/utils/logger.py
CHANGED
|
@@ -56,7 +56,6 @@ def setup_logger(log_dir, log_retention_days=7, log_rotation="1 day", debug_mode
|
|
| 56 |
enqueue=True,
|
| 57 |
catch=True,
|
| 58 |
)
|
| 59 |
-
logger.info(f"✅ 日志文件输出已启用: {log_dir}")
|
| 60 |
except (PermissionError, OSError) as e:
|
| 61 |
# 如果无法创建日志目录或文件,降级为仅控制台输出
|
| 62 |
logger.warning(f"⚠️ 无法创建日志文件 ({e}),将仅使用控制台输出")
|
|
|
|
| 56 |
enqueue=True,
|
| 57 |
catch=True,
|
| 58 |
)
|
|
|
|
| 59 |
except (PermissionError, OSError) as e:
|
| 60 |
# 如果无法创建日志目录或文件,降级为仅控制台输出
|
| 61 |
logger.warning(f"⚠️ 无法创建日志文件 ({e}),将仅使用控制台输出")
|
docker-compose.yml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
services:
|
| 2 |
api-server:
|
| 3 |
build:
|
| 4 |
-
context: .
|
| 5 |
-
dockerfile:
|
| 6 |
container_name: z-ai-api-server
|
| 7 |
ports:
|
| 8 |
- "7860:7860"
|
|
@@ -29,7 +29,7 @@ services:
|
|
| 29 |
- DB_PATH=/app/data/tokens.db
|
| 30 |
restart: unless-stopped
|
| 31 |
healthcheck:
|
| 32 |
-
test: ["CMD", "curl", "-f", "http://localhost:7860/v1/models"]
|
| 33 |
interval: 30s
|
| 34 |
timeout: 10s
|
| 35 |
retries: 3
|
|
|
|
| 1 |
services:
|
| 2 |
api-server:
|
| 3 |
build:
|
| 4 |
+
context: .
|
| 5 |
+
dockerfile: Dockerfile
|
| 6 |
container_name: z-ai-api-server
|
| 7 |
ports:
|
| 8 |
- "7860:7860"
|
|
|
|
| 29 |
- DB_PATH=/app/data/tokens.db
|
| 30 |
restart: unless-stopped
|
| 31 |
healthcheck:
|
| 32 |
+
test: ["CMD", "curl", "-f", "http://localhost:7860/hf/v1/models"]
|
| 33 |
interval: 30s
|
| 34 |
timeout: 10s
|
| 35 |
retries: 3
|
nginx.conf
DELETED
|
@@ -1,157 +0,0 @@
|
|
| 1 |
-
# Nginx reverse proxy configuration example for Z.AI2API
|
| 2 |
-
# This example shows how to deploy the service behind Nginx with a custom path prefix
|
| 3 |
-
|
| 4 |
-
# Example 1: Deploy at http://your-domain.com/ai2api
|
| 5 |
-
server {
|
| 6 |
-
listen 80;
|
| 7 |
-
server_name your-domain.com;
|
| 8 |
-
|
| 9 |
-
# Forward requests with /ai2api prefix to the backend service
|
| 10 |
-
location /ai2api {
|
| 11 |
-
# Remove trailing slash redirect (optional, but recommended)
|
| 12 |
-
rewrite ^(/ai2api)$ $1/ permanent;
|
| 13 |
-
|
| 14 |
-
# Proxy to the backend service
|
| 15 |
-
proxy_pass http://127.0.0.1:7860;
|
| 16 |
-
|
| 17 |
-
# Pass original host and IP information
|
| 18 |
-
proxy_set_header Host $host;
|
| 19 |
-
proxy_set_header X-Real-IP $remote_addr;
|
| 20 |
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 21 |
-
proxy_set_header X-Forwarded-Proto $scheme;
|
| 22 |
-
|
| 23 |
-
# IMPORTANT: Tell the backend about the path prefix
|
| 24 |
-
# This ensures all generated URLs include the prefix
|
| 25 |
-
proxy_set_header X-Forwarded-Prefix /ai2api;
|
| 26 |
-
|
| 27 |
-
# WebSocket and SSE support (for streaming responses)
|
| 28 |
-
proxy_http_version 1.1;
|
| 29 |
-
proxy_set_header Upgrade $http_upgrade;
|
| 30 |
-
proxy_set_header Connection "upgrade";
|
| 31 |
-
|
| 32 |
-
# Disable buffering for streaming responses
|
| 33 |
-
proxy_buffering off;
|
| 34 |
-
proxy_cache off;
|
| 35 |
-
|
| 36 |
-
# Timeout settings (adjust as needed)
|
| 37 |
-
proxy_connect_timeout 60s;
|
| 38 |
-
proxy_send_timeout 300s;
|
| 39 |
-
proxy_read_timeout 300s;
|
| 40 |
-
}
|
| 41 |
-
}
|
| 42 |
-
|
| 43 |
-
# Example 2: Deploy at http://your-domain.com/api/chat
|
| 44 |
-
server {
|
| 45 |
-
listen 80;
|
| 46 |
-
server_name example.com;
|
| 47 |
-
|
| 48 |
-
location /api/chat {
|
| 49 |
-
# Proxy configuration
|
| 50 |
-
proxy_pass http://127.0.0.1:7860;
|
| 51 |
-
|
| 52 |
-
# Headers
|
| 53 |
-
proxy_set_header Host $host;
|
| 54 |
-
proxy_set_header X-Real-IP $remote_addr;
|
| 55 |
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 56 |
-
proxy_set_header X-Forwarded-Proto $scheme;
|
| 57 |
-
proxy_set_header X-Forwarded-Prefix /api/chat;
|
| 58 |
-
|
| 59 |
-
# SSE/WebSocket support
|
| 60 |
-
proxy_http_version 1.1;
|
| 61 |
-
proxy_set_header Upgrade $http_upgrade;
|
| 62 |
-
proxy_set_header Connection "upgrade";
|
| 63 |
-
proxy_buffering off;
|
| 64 |
-
proxy_cache off;
|
| 65 |
-
}
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
# Example 3: Deploy with SSL (HTTPS)
|
| 69 |
-
server {
|
| 70 |
-
listen 443 ssl http2;
|
| 71 |
-
server_name secure.example.com;
|
| 72 |
-
|
| 73 |
-
# SSL configuration
|
| 74 |
-
ssl_certificate /path/to/cert.pem;
|
| 75 |
-
ssl_certificate_key /path/to/key.pem;
|
| 76 |
-
|
| 77 |
-
location /ai2api {
|
| 78 |
-
proxy_pass http://127.0.0.1:7860;
|
| 79 |
-
|
| 80 |
-
proxy_set_header Host $host;
|
| 81 |
-
proxy_set_header X-Real-IP $remote_addr;
|
| 82 |
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 83 |
-
proxy_set_header X-Forwarded-Proto https;
|
| 84 |
-
proxy_set_header X-Forwarded-Prefix /ai2api;
|
| 85 |
-
|
| 86 |
-
# SSE/WebSocket support
|
| 87 |
-
proxy_http_version 1.1;
|
| 88 |
-
proxy_set_header Upgrade $http_upgrade;
|
| 89 |
-
proxy_set_header Connection "upgrade";
|
| 90 |
-
proxy_buffering off;
|
| 91 |
-
proxy_cache off;
|
| 92 |
-
|
| 93 |
-
# Security headers (optional)
|
| 94 |
-
add_header X-Content-Type-Options nosniff;
|
| 95 |
-
add_header X-Frame-Options DENY;
|
| 96 |
-
add_header X-XSS-Protection "1; mode=block";
|
| 97 |
-
}
|
| 98 |
-
}
|
| 99 |
-
|
| 100 |
-
# Example 4: Load balancing with multiple backend instances
|
| 101 |
-
upstream ai2api_backend {
|
| 102 |
-
# Round-robin by default
|
| 103 |
-
server 127.0.0.1:7860;
|
| 104 |
-
server 127.0.0.1:8081;
|
| 105 |
-
server 127.0.0.1:8082;
|
| 106 |
-
|
| 107 |
-
# Or use least connections
|
| 108 |
-
# least_conn;
|
| 109 |
-
|
| 110 |
-
# Or use IP hash for session persistence
|
| 111 |
-
# ip_hash;
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
-
server {
|
| 115 |
-
listen 80;
|
| 116 |
-
server_name loadbalanced.example.com;
|
| 117 |
-
|
| 118 |
-
location /ai2api {
|
| 119 |
-
proxy_pass http://ai2api_backend;
|
| 120 |
-
|
| 121 |
-
proxy_set_header Host $host;
|
| 122 |
-
proxy_set_header X-Real-IP $remote_addr;
|
| 123 |
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
| 124 |
-
proxy_set_header X-Forwarded-Proto $scheme;
|
| 125 |
-
proxy_set_header X-Forwarded-Prefix /ai2api;
|
| 126 |
-
|
| 127 |
-
proxy_http_version 1.1;
|
| 128 |
-
proxy_set_header Upgrade $http_upgrade;
|
| 129 |
-
proxy_set_header Connection "upgrade";
|
| 130 |
-
proxy_buffering off;
|
| 131 |
-
proxy_cache off;
|
| 132 |
-
}
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
# Important Notes:
|
| 136 |
-
#
|
| 137 |
-
# 1. Set ROOT_PATH in your .env file to match the Nginx location path:
|
| 138 |
-
# ROOT_PATH=/ai2api
|
| 139 |
-
#
|
| 140 |
-
# 2. Restart both Nginx and the application after configuration changes:
|
| 141 |
-
# sudo systemctl reload nginx
|
| 142 |
-
# docker-compose restart (or restart your application)
|
| 143 |
-
#
|
| 144 |
-
# 3. Access URLs will include the prefix:
|
| 145 |
-
# - Admin panel: http://your-domain.com/ai2api/admin/login
|
| 146 |
-
# - API endpoint: http://your-domain.com/ai2api/v1/chat/completions
|
| 147 |
-
# - Health check: http://your-domain.com/ai2api/v1/models
|
| 148 |
-
#
|
| 149 |
-
# 4. For Docker deployments, make sure to:
|
| 150 |
-
# - Add ROOT_PATH to docker-compose.yml environment variables
|
| 151 |
-
# - Expose the container port (7860 by default)
|
| 152 |
-
#
|
| 153 |
-
# 5. Common issues:
|
| 154 |
-
# - 404 errors: Check that ROOT_PATH matches the Nginx location path exactly
|
| 155 |
-
# - CORS errors: Verify proxy headers are set correctly
|
| 156 |
-
# - Streaming not working: Ensure proxy_buffering is off
|
| 157 |
-
# - Admin panel CSS/JS not loading: Confirm static files are served with the prefix
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nginx.conf.example
CHANGED
|
@@ -12,7 +12,7 @@ server {
|
|
| 12 |
rewrite ^(/ai2api)$ $1/ permanent;
|
| 13 |
|
| 14 |
# Proxy to the backend service
|
| 15 |
-
proxy_pass http://127.0.0.1:
|
| 16 |
|
| 17 |
# Pass original host and IP information
|
| 18 |
proxy_set_header Host $host;
|
|
@@ -47,7 +47,7 @@ server {
|
|
| 47 |
|
| 48 |
location /api/chat {
|
| 49 |
# Proxy configuration
|
| 50 |
-
proxy_pass http://127.0.0.1:
|
| 51 |
|
| 52 |
# Headers
|
| 53 |
proxy_set_header Host $host;
|
|
@@ -75,7 +75,7 @@ server {
|
|
| 75 |
ssl_certificate_key /path/to/key.pem;
|
| 76 |
|
| 77 |
location /ai2api {
|
| 78 |
-
proxy_pass http://127.0.0.1:
|
| 79 |
|
| 80 |
proxy_set_header Host $host;
|
| 81 |
proxy_set_header X-Real-IP $remote_addr;
|
|
@@ -100,7 +100,7 @@ server {
|
|
| 100 |
# Example 4: Load balancing with multiple backend instances
|
| 101 |
upstream ai2api_backend {
|
| 102 |
# Round-robin by default
|
| 103 |
-
server 127.0.0.1:
|
| 104 |
server 127.0.0.1:8081;
|
| 105 |
server 127.0.0.1:8082;
|
| 106 |
|
|
@@ -143,12 +143,12 @@ server {
|
|
| 143 |
#
|
| 144 |
# 3. Access URLs will include the prefix:
|
| 145 |
# - Admin panel: http://your-domain.com/ai2api/admin/login
|
| 146 |
-
# - API endpoint: http://your-domain.com/ai2api/v1/chat/completions
|
| 147 |
-
# - Health check: http://your-domain.com/ai2api/v1/models
|
| 148 |
#
|
| 149 |
# 4. For Docker deployments, make sure to:
|
| 150 |
# - Add ROOT_PATH to docker-compose.yml environment variables
|
| 151 |
-
# - Expose the container port (
|
| 152 |
#
|
| 153 |
# 5. Common issues:
|
| 154 |
# - 404 errors: Check that ROOT_PATH matches the Nginx location path exactly
|
|
|
|
| 12 |
rewrite ^(/ai2api)$ $1/ permanent;
|
| 13 |
|
| 14 |
# Proxy to the backend service
|
| 15 |
+
proxy_pass http://127.0.0.1:8080;
|
| 16 |
|
| 17 |
# Pass original host and IP information
|
| 18 |
proxy_set_header Host $host;
|
|
|
|
| 47 |
|
| 48 |
location /api/chat {
|
| 49 |
# Proxy configuration
|
| 50 |
+
proxy_pass http://127.0.0.1:8080;
|
| 51 |
|
| 52 |
# Headers
|
| 53 |
proxy_set_header Host $host;
|
|
|
|
| 75 |
ssl_certificate_key /path/to/key.pem;
|
| 76 |
|
| 77 |
location /ai2api {
|
| 78 |
+
proxy_pass http://127.0.0.1:8080;
|
| 79 |
|
| 80 |
proxy_set_header Host $host;
|
| 81 |
proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
| 100 |
# Example 4: Load balancing with multiple backend instances
|
| 101 |
upstream ai2api_backend {
|
| 102 |
# Round-robin by default
|
| 103 |
+
server 127.0.0.1:8080;
|
| 104 |
server 127.0.0.1:8081;
|
| 105 |
server 127.0.0.1:8082;
|
| 106 |
|
|
|
|
| 143 |
#
|
| 144 |
# 3. Access URLs will include the prefix:
|
| 145 |
# - Admin panel: http://your-domain.com/ai2api/admin/login
|
| 146 |
+
# - API endpoint: http://your-domain.com/ai2api/hf/v1/chat/completions
|
| 147 |
+
# - Health check: http://your-domain.com/ai2api/hf/v1/models
|
| 148 |
#
|
| 149 |
# 4. For Docker deployments, make sure to:
|
| 150 |
# - Add ROOT_PATH to docker-compose.yml environment variables
|
| 151 |
+
# - Expose the container port (8080 by default)
|
| 152 |
#
|
| 153 |
# 5. Common issues:
|
| 154 |
# - 404 errors: Check that ROOT_PATH matches the Nginx location path exactly
|
xREADME.md
DELETED
|
@@ -1,347 +0,0 @@
|
|
| 1 |
-
# OpenAI 代理服务
|
| 2 |
-
|
| 3 |
-

|
| 4 |
-

|
| 5 |
-

|
| 6 |
-
|
| 7 |
-
基于 FastAPI 的高性能 OpenAI API 兼容代理服务,采用多提供商架构设计,支持 Z.AI(GLM-4.5/4.6 系列)、K2Think、LongCat 等多种 AI 模型。
|
| 8 |
-
|
| 9 |
-
## ✨ 核心特性
|
| 10 |
-
|
| 11 |
-
- 🔌 **OpenAI API 兼容** - 无缝对接现有 OpenAI 客户端
|
| 12 |
-
- 🏗️ **多提供商架构** - 统一接口支持 Z.AI、K2Think、LongCat
|
| 13 |
-
- 🧬 **数据库管理** - SQLite + Web 后台统一管理 Token
|
| 14 |
-
- 🚀 **流式响应** - 高性能 SSE 实时流式输出
|
| 15 |
-
- 🧠 **思考模式** - 支持 Thinking 模型的推理过程展示
|
| 16 |
-
- 🐳 **容器化部署** - Docker/Docker Compose 一键部署
|
| 17 |
-
- 🔄 **Token 池** - 智能轮询、容错恢复、健康检查
|
| 18 |
-
- 📊 **管理后台** - 实时监控、配置管理
|
| 19 |
-
- 🔐 **安全认证** - 密码保护的管理后台访问
|
| 20 |
-
|
| 21 |
-
❤️ 感谢各位的反馈推动项目改进!
|
| 22 |
-
|
| 23 |
-
## 🚀 快速开始
|
| 24 |
-
|
| 25 |
-
### 环境要求
|
| 26 |
-
|
| 27 |
-
- Python 3.9-3.12
|
| 28 |
-
- pip 或 uv (推荐)
|
| 29 |
-
|
| 30 |
-
### 本地运行
|
| 31 |
-
|
| 32 |
-
```bash
|
| 33 |
-
# 1. 克隆项目
|
| 34 |
-
git clone https://github.com/ZyphrZero/z.ai2api_python.git
|
| 35 |
-
cd z.ai2api_python
|
| 36 |
-
|
| 37 |
-
# 2. 安装依赖(使用 uv 推荐)
|
| 38 |
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
| 39 |
-
uv sync
|
| 40 |
-
|
| 41 |
-
# 或使用 pip
|
| 42 |
-
pip install -r requirements.txt
|
| 43 |
-
|
| 44 |
-
# 3. 配置环境变量
|
| 45 |
-
cp .env.example .env
|
| 46 |
-
# 编辑 .env 文件,设置 AUTH_TOKEN 等配置
|
| 47 |
-
|
| 48 |
-
# 4. 启动服务
|
| 49 |
-
uv run python main.py # 或 python main.py
|
| 50 |
-
```
|
| 51 |
-
|
| 52 |
-
**首次运行会自动初始化数据库**,访问以下地址:
|
| 53 |
-
- API 文档:http://localhost:7860/docs
|
| 54 |
-
- 管理后台:http://localhost:7860/admin(**需要登录**)
|
| 55 |
-
- Token 管理:http://localhost:7860/admin/tokens
|
| 56 |
-
|
| 57 |
-
> ⚠️ **重要**:
|
| 58 |
-
> - 请妥善保管 `AUTH_TOKEN`,不要泄露给他人
|
| 59 |
-
> - 管理后台默认密码为 `admin123`,**首次使用后请立即修改**
|
| 60 |
-
|
| 61 |
-
### Docker 部署
|
| 62 |
-
|
| 63 |
-
从 Docker Hub 拉取镜像:
|
| 64 |
-
|
| 65 |
-
```bash
|
| 66 |
-
# 拉取最新镜像
|
| 67 |
-
docker pull zyphrzero/z-ai2api-python:latest
|
| 68 |
-
|
| 69 |
-
# 快速启动(创建数据目录)
|
| 70 |
-
mkdir -p data logs
|
| 71 |
-
|
| 72 |
-
# 运行容器
|
| 73 |
-
docker run -d \
|
| 74 |
-
--name z-ai-api-server \
|
| 75 |
-
-p 7860:7860 \
|
| 76 |
-
-e ADMIN_PASSWORD=admin123 \
|
| 77 |
-
-e AUTH_TOKEN=sk-your-api-key \
|
| 78 |
-
-e ANONYMOUS_MODE=true \
|
| 79 |
-
-e DB_PATH=/app/data/tokens.db \
|
| 80 |
-
-v $(pwd)/data:/app/data \
|
| 81 |
-
-v $(pwd)/logs:/app/logs \
|
| 82 |
-
--restart unless-stopped \
|
| 83 |
-
zyphrzero/z-ai2api-python:latest
|
| 84 |
-
```
|
| 85 |
-
|
| 86 |
-
启动服务:
|
| 87 |
-
|
| 88 |
-
```bash
|
| 89 |
-
docker compose up -d
|
| 90 |
-
```
|
| 91 |
-
|
| 92 |
-
#### 方式二:本地构建
|
| 93 |
-
|
| 94 |
-
```bash
|
| 95 |
-
# 进入部署目录
|
| 96 |
-
cd deploy
|
| 97 |
-
|
| 98 |
-
# 启动服务(会自动构建镜像)
|
| 99 |
-
docker compose up -d
|
| 100 |
-
|
| 101 |
-
# 查看日志
|
| 102 |
-
docker compose logs -f api-server
|
| 103 |
-
```
|
| 104 |
-
|
| 105 |
-
#### 数据持久化
|
| 106 |
-
|
| 107 |
-
容器使用卷映射自动持久化数据:
|
| 108 |
-
|
| 109 |
-
```
|
| 110 |
-
data/ # 数据库文件存储目录
|
| 111 |
-
├── tokens.db # SQLite 数据库(自动创建)
|
| 112 |
-
logs/ # 日志文件存储目录
|
| 113 |
-
```
|
| 114 |
-
|
| 115 |
-
数据在容器重启或重建后仍然保留,无需担心丢失。
|
| 116 |
-
|
| 117 |
-
> 📖 **详细文档**:[Docker 部署指南](deploy/README_DOCKER.md)
|
| 118 |
-
|
| 119 |
-
## 📖 支持的模型
|
| 120 |
-
|
| 121 |
-
### Z.AI 提供商(GLM 系列)
|
| 122 |
-
|
| 123 |
-
| 模型 | 上游 ID | 特性 |
|
| 124 |
-
|------|---------|------|
|
| 125 |
-
| `GLM-4.5` | 0727-360B-API | 标准模型,通用对话 |
|
| 126 |
-
| `GLM-4.5-Thinking` | 0727-360B-API | 思考模型,显示推理过程 |
|
| 127 |
-
| `GLM-4.5-Search` | 0727-360B-API | 搜索模型,实时联网 |
|
| 128 |
-
| `GLM-4.5-Air` | 0727-106B-API | 轻量模型,快速响应 |
|
| 129 |
-
| `GLM-4.5V` | glm-4.5v | 多模态模型,支持图像理解 |
|
| 130 |
-
| `GLM-4.6` | GLM-4-6-API-V1 | 新版标准模型,200K 上下文 |
|
| 131 |
-
| `GLM-4.6-Thinking` | GLM-4-6-API-V1 | 新版思考模型,增强推理 |
|
| 132 |
-
| `GLM-4.6-Search` | GLM-4-6-API-V1 | 新版搜索模型,改进联网能力 |
|
| 133 |
-
| `GLM-4.6-advanced-search` | GLM-4-6-API-V1 | 高级搜索模型,深度研究 |
|
| 134 |
-
|
| 135 |
-
### K2Think 提供商
|
| 136 |
-
|
| 137 |
-
| 模型 | 特性 |
|
| 138 |
-
|------|------|
|
| 139 |
-
| `MBZUAI-IFM/K2-Think` | 高质量推理模型 |
|
| 140 |
-
|
| 141 |
-
### LongCat 提供商
|
| 142 |
-
|
| 143 |
-
| 模型 | 特性 |
|
| 144 |
-
|------|------|
|
| 145 |
-
| `LongCat-Flash` | 快速响应 |
|
| 146 |
-
| `LongCat` | 标准模型 |
|
| 147 |
-
| `LongCat-Search` | 搜索增强 |
|
| 148 |
-
|
| 149 |
-
## ⚙️ 配置说明
|
| 150 |
-
|
| 151 |
-
### 核心环境变量
|
| 152 |
-
|
| 153 |
-
| 变量名 | 默认值 | 说明 |
|
| 154 |
-
|--------|--------|------|
|
| 155 |
-
| `AUTH_TOKEN` | `sk-your-api-key` | 客户端访问密钥(必填) |
|
| 156 |
-
| `ADMIN_PASSWORD` | `admin123` | 管理后台登录密码(**强烈建议修改**) |
|
| 157 |
-
| `LISTEN_PORT` | `7860` | 服务监听端口 |
|
| 158 |
-
| `DEBUG_LOGGING` | `false` | 调试日志(支持热重载) |
|
| 159 |
-
| `ANONYMOUS_MODE` | `true` | Z.AI 匿名模式 |
|
| 160 |
-
| `TOOL_SUPPORT` | `true` | Function Call 开关 |
|
| 161 |
-
| `SKIP_AUTH_TOKEN` | `false` | 跳过认证(仅开发) |
|
| 162 |
-
| `DB_PATH` | `tokens.db` | 数据库文件路径(Docker: `/app/data/tokens.db`) |
|
| 163 |
-
|
| 164 |
-
### Token 配置
|
| 165 |
-
|
| 166 |
-
| 变量名 | 说明 |
|
| 167 |
-
|--------|------|
|
| 168 |
-
| `LONGCAT_TOKEN` | LongCat 认证 Token(可选) |
|
| 169 |
-
| `TOKEN_FAILURE_THRESHOLD` | Token 失败阈值(默认 3) |
|
| 170 |
-
| `TOKEN_RECOVERY_TIMEOUT` | Token 恢复超时(默认 1800 秒) |
|
| 171 |
-
|
| 172 |
-
> 💡 详细配置请参考 [.env.example](.env.example) 或 [deploy/.env.example](deploy/.env.example)
|
| 173 |
-
|
| 174 |
-
## 🔐 管理后台登录
|
| 175 |
-
|
| 176 |
-
### 首次登录
|
| 177 |
-
|
| 178 |
-
1. 启动服务后访问:http://localhost:7860/admin
|
| 179 |
-
2. 自动跳转到登录页面
|
| 180 |
-
3. 输入管理密码(默认:`admin123`)
|
| 181 |
-
4. 登录成功后进入仪表盘
|
| 182 |
-
|
| 183 |
-
### 修改密码
|
| 184 |
-
|
| 185 |
-
在 `.env` 文件中修改 `ADMIN_PASSWORD`:
|
| 186 |
-
|
| 187 |
-
```bash
|
| 188 |
-
# 使用强密码(推荐 12 位以上)
|
| 189 |
-
ADMIN_PASSWORD=Your_Secure_Password_2025!
|
| 190 |
-
```
|
| 191 |
-
|
| 192 |
-
重启服务后生效。
|
| 193 |
-
|
| 194 |
-
### 安全特性
|
| 195 |
-
|
| 196 |
-
- ✅ **Session 管理**:基于 Cookie 的安全 Session
|
| 197 |
-
- ✅ **自动过期**:登录后 24 小时自动失效
|
| 198 |
-
- ✅ **HttpOnly Cookie**:防止 XSS 攻击
|
| 199 |
-
- ✅ **SameSite 保护**:防止 CSRF 攻击
|
| 200 |
-
- ✅ **随机 Token**:使用加密安全的随机数生成
|
| 201 |
-
|
| 202 |
-
> 💡 详细文档:[管理后台登录功能使用说明](管理后台登录功能使用说明.md)
|
| 203 |
-
|
| 204 |
-
## 🔄 Token 管理
|
| 205 |
-
|
| 206 |
-
### 数据库方式(推荐)
|
| 207 |
-
|
| 208 |
-
项目使用 SQLite 数据库统一管理 Token,首次运行会自动初始化:
|
| 209 |
-
|
| 210 |
-
```bash
|
| 211 |
-
# 首次运行自动创建 tokens.db
|
| 212 |
-
python main.py
|
| 213 |
-
|
| 214 |
-
# 访问 Web 管理后台
|
| 215 |
-
http://localhost:7860/admin
|
| 216 |
-
```
|
| 217 |
-
|
| 218 |
-
### 管理后台功能
|
| 219 |
-
|
| 220 |
-
- ✅ **密码保护** - 安全的登录认证
|
| 221 |
-
- ✅ Token 增删改查
|
| 222 |
-
- ✅ 批量导入/导出
|
| 223 |
-
- ✅ 启用/禁用 Token
|
| 224 |
-
- ✅ Token 有效性检测
|
| 225 |
-
- ✅ 多提供商支持(Z.AI/K2Think/LongCat)
|
| 226 |
-
|
| 227 |
-
### Token 池机制
|
| 228 |
-
|
| 229 |
-
- **负载均衡**:轮询使用多个 Token 分散请求
|
| 230 |
-
- **自动容错**:Token 失败时自动切换
|
| 231 |
-
- **自动恢复**:失败 Token 超时后重试
|
| 232 |
-
- **智能去重**:自动检测重复 Token
|
| 233 |
-
- **回退机制**:认证失败自动降级匿名模式
|
| 234 |
-
|
| 235 |
-
## ❓ 常见问题
|
| 236 |
-
|
| 237 |
-
### Q: 如何获取 AUTH_TOKEN?
|
| 238 |
-
A: `AUTH_TOKEN` 是自定义的 API 密钥,用于客户端访问本服务,需在 `.env` 文件或 `docker-compose.yml` 中配置,确保客户端与服务端一致。
|
| 239 |
-
|
| 240 |
-
### Q: 匿名模式是什么?
|
| 241 |
-
A: 匿名模式使用临时 Token 访问 Z.AI,避免对话历史共享,保护隐私。设置 `ANONYMOUS_MODE=true` 启用。
|
| 242 |
-
|
| 243 |
-
### Q: 如何管理 Token?
|
| 244 |
-
A: 访问 Web 管理后台 http://localhost:7860/admin/tokens(需要先登录)即可增删改查 Token,支持批量导入导出。
|
| 245 |
-
|
| 246 |
-
### Q: 忘记管理后台密码怎么办?
|
| 247 |
-
A: 在 `.env` 文件或 `docker-compose.yml` 中修改 `ADMIN_PASSWORD` 为新密码,然后重启服务即可。
|
| 248 |
-
|
| 249 |
-
### Q: Docker 部署时数据库初始化失败?
|
| 250 |
-
A: 错误提示 `unable to open database file` 通常是权限问题。解决方案:
|
| 251 |
-
```bash
|
| 252 |
-
cd deploy
|
| 253 |
-
mkdir -p ./data ./logs
|
| 254 |
-
chmod 755 ./data ./logs
|
| 255 |
-
docker compose down && docker compose up -d --build
|
| 256 |
-
```
|
| 257 |
-
详见 [Docker 部署指南](deploy/README_DOCKER.md#故障排查)
|
| 258 |
-
|
| 259 |
-
### Q: 如何禁用管理后台登录?
|
| 260 |
-
A: 当前版本暂不支持禁用登录功能。如有需要,请手动移除路由中的 `dependencies=[Depends(require_auth)]`。
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
## 🔑 获取 Token
|
| 264 |
-
|
| 265 |
-
### Z.AI Token
|
| 266 |
-
|
| 267 |
-
1. 访问 [Z.AI 官网](https://chat.z.ai) 并登录
|
| 268 |
-
2. 按 F12 打开开发者工具
|
| 269 |
-
3. 进入 Application → Local Storage → Cookies
|
| 270 |
-
4. 复制 `token` 值
|
| 271 |
-
|
| 272 |
-
> ⚠️ 多模态功能需要非匿名 Token
|
| 273 |
-
|
| 274 |
-
### LongCat Token
|
| 275 |
-
|
| 276 |
-
1. 访问 [LongCat 官网](https://longcat.chat/) 并登录美团账号
|
| 277 |
-
2. 按 F12 打开开发者工具
|
| 278 |
-
3. 进入 "Application" -> "Local Storage" -> "Cookie"列表中找到名为`passport_token_key`的值
|
| 279 |
-
4. 复制 `passport_token_key` 值
|
| 280 |
-
|
| 281 |
-
## 🛠️ 技术栈
|
| 282 |
-
|
| 283 |
-
| 组件 | 技术 | 版本 | 说明 |
|
| 284 |
-
|------|------|------|------|
|
| 285 |
-
| Web 框架 | [FastAPI](https://fastapi.tiangolo.com/) | 0.116.1 | 高性能异步框架 |
|
| 286 |
-
| ASGI 服务器 | [Granian](https://github.com/emmett-framework/granian) | 2.5.2 | Rust 高性能服务器 |
|
| 287 |
-
| HTTP 客户端 | [HTTPX](https://www.python-httpx.org/) | 0.28.1 | 异步 HTTP 客户端 |
|
| 288 |
-
| 数据验证 | [Pydantic](https://pydantic.dev/) | 2.11.7 | 类型安全验证 |
|
| 289 |
-
| 数据库 | SQLite (aiosqlite) | 0.20.0 | Token 存储 |
|
| 290 |
-
| 模板引擎 | Jinja2 | 3.1.4 | Web 后台模板 |
|
| 291 |
-
| 日志系统 | [Loguru](https://loguru.readthedocs.io/) | 0.7.3 | 结构化日志 |
|
| 292 |
-
|
| 293 |
-
## 🏗️ 系统架构
|
| 294 |
-
|
| 295 |
-
```
|
| 296 |
-
┌─────────────┐ ┌────────────────────────────────┐ ┌──────────────┐
|
| 297 |
-
│ OpenAI │ │ FastAPI Server │ │ Z.AI API │
|
| 298 |
-
│ Client │─────▶│ │─────▶│ (GLM-4.x) │
|
| 299 |
-
└─────────────┘ │ ┌──────────────────────────┐ │ └──────────────┘
|
| 300 |
-
│ │ Provider Router │ │
|
| 301 |
-
│ │ ┌────────┬────────────┐ │ �� ┌──────────────┐
|
| 302 |
-
│ │ │ Z.AI │ K2Think │ │ │ │ K2Think API │
|
| 303 |
-
│ │ │Provider│ Provider │ │ │─────▶│ │
|
| 304 |
-
│ │ └────────┴────────────┘ │ │ └──────────────┘
|
| 305 |
-
│ │ ┌────────────┐ │ │
|
| 306 |
-
│ │ │ LongCat │ │ │ ┌──────────────┐
|
| 307 |
-
│ │ │ Provider │ │ │ │ LongCat API │
|
| 308 |
-
│ │ └────────────┘ │ │─────▶│ │
|
| 309 |
-
│ └──────────────────────────┘ │ └──────────────┘
|
| 310 |
-
│ │
|
| 311 |
-
│ ┌──────────────────────────┐ │
|
| 312 |
-
│ │ Web Admin Dashboard │ │
|
| 313 |
-
│ │ (Token/Stats/Monitor) │ │
|
| 314 |
-
│ └──────────────────────────┘ │
|
| 315 |
-
└────────────────────────────────┘
|
| 316 |
-
↕
|
| 317 |
-
┌─────────┐
|
| 318 |
-
│SQLite DB│
|
| 319 |
-
│(tokens) │
|
| 320 |
-
└─────────┘
|
| 321 |
-
```
|
| 322 |
-
|
| 323 |
-
## 🤝 贡献指南
|
| 324 |
-
|
| 325 |
-
欢迎提交 Issue 和 Pull Request!请确保代码符合 PEP 8 规范。
|
| 326 |
-
|
| 327 |
-
## ⭐ Star History
|
| 328 |
-
|
| 329 |
-
[](https://star-history.com/#ZyphrZero/z.ai2api_python&Date)
|
| 330 |
-
|
| 331 |
-
## 📄 许可证
|
| 332 |
-
|
| 333 |
-
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件。
|
| 334 |
-
|
| 335 |
-
## ⚠️ 免责声明
|
| 336 |
-
|
| 337 |
-
- 本项目与 Z.AI、K2Think、LongCat 等 AI 提供商官方无关
|
| 338 |
-
- 使用前请确保遵守各提供商的服务条款
|
| 339 |
-
- 请勿用于商业用途或违反使用条款的场景
|
| 340 |
-
- 项目仅供学习和研究使用
|
| 341 |
-
- 用户需自行承担使用风险
|
| 342 |
-
|
| 343 |
-
---
|
| 344 |
-
|
| 345 |
-
<div align="center">
|
| 346 |
-
Made with ❤️ by the community
|
| 347 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|