mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-31 17:13:43 +00:00
Compare commits
25 Commits
alpha
...
refactor/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39c841e600 | ||
|
|
ede47ef014 | ||
|
|
6c7795238f | ||
|
|
0baacb2686 | ||
|
|
c5aaee9f2f | ||
|
|
1987c7e16c | ||
|
|
c13bb67360 | ||
|
|
77f8e51b56 | ||
|
|
e08f799994 | ||
|
|
cc41ac63bf | ||
|
|
4e0f4b207d | ||
|
|
8a7033e5a3 | ||
|
|
7cb60c7d83 | ||
|
|
e1c7a4f41f | ||
|
|
2d3fd5634a | ||
|
|
5cfa64838c | ||
|
|
55afd5cbdf | ||
|
|
65920983cc | ||
|
|
ced72951e4 | ||
|
|
63c01016e4 | ||
|
|
5810c05dab | ||
|
|
c29eef9b15 | ||
|
|
b472f9c5b5 | ||
|
|
a34d8f586e | ||
|
|
c7661167cf |
127
.github/workflows/docker-image-alpha.yml
vendored
127
.github/workflows/docker-image-alpha.yml
vendored
@@ -11,19 +11,42 @@ on:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
push_to_registries:
|
||||
name: Push Docker image to multiple registries
|
||||
runs-on: ubuntu-latest
|
||||
build_single_arch:
|
||||
name: Build & push (${{ matrix.arch }}) [native]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- arch: arm64
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
- name: Check out (shallow)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Save version info
|
||||
- name: Determine alpha version
|
||||
id: version
|
||||
run: |
|
||||
echo "alpha-$(date +'%Y%m%d')-$(git rev-parse --short HEAD)" > VERSION
|
||||
VERSION="alpha-$(date +'%Y%m%d')-$(git rev-parse --short HEAD)"
|
||||
echo "$VERSION" > VERSION
|
||||
echo "value=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "Publishing version: $VERSION for ${{ matrix.arch }}"
|
||||
|
||||
- name: Normalize GHCR repository
|
||||
run: echo "GHCR_REPOSITORY=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
@@ -31,32 +54,98 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the Container registry
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
- name: Extract metadata (labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
calciumion/new-api
|
||||
ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=raw,value=alpha
|
||||
type=raw,value=alpha-{{date 'YYYYMMDD'}}-{{sha}}
|
||||
ghcr.io/${{ env.GHCR_REPOSITORY }}
|
||||
|
||||
- name: Build and push Docker images
|
||||
uses: docker/build-push-action@v5
|
||||
- name: Build & push single-arch (to both registries)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
tags: |
|
||||
calciumion/new-api:alpha-${{ matrix.arch }}
|
||||
calciumion/new-api:${{ steps.version.outputs.value }}-${{ matrix.arch }}
|
||||
ghcr.io/${{ env.GHCR_REPOSITORY }}:alpha-${{ matrix.arch }}
|
||||
ghcr.io/${{ env.GHCR_REPOSITORY }}:${{ steps.version.outputs.value }}-${{ matrix.arch }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: false
|
||||
sbom: false
|
||||
|
||||
create_manifests:
|
||||
name: Create multi-arch manifests (Docker Hub + GHCR)
|
||||
needs: [build_single_arch]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out (shallow)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Normalize GHCR repository
|
||||
run: echo "GHCR_REPOSITORY=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Determine alpha version
|
||||
id: version
|
||||
run: |
|
||||
VERSION="alpha-$(date +'%Y%m%d')-$(git rev-parse --short HEAD)"
|
||||
echo "value=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Create & push manifest (Docker Hub - alpha)
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t calciumion/new-api:alpha \
|
||||
calciumion/new-api:alpha-amd64 \
|
||||
calciumion/new-api:alpha-arm64
|
||||
|
||||
- name: Create & push manifest (Docker Hub - versioned alpha)
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t calciumion/new-api:${VERSION} \
|
||||
calciumion/new-api:${VERSION}-amd64 \
|
||||
calciumion/new-api:${VERSION}-arm64
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create & push manifest (GHCR - alpha)
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ghcr.io/${GHCR_REPOSITORY}:alpha \
|
||||
ghcr.io/${GHCR_REPOSITORY}:alpha-amd64 \
|
||||
ghcr.io/${GHCR_REPOSITORY}:alpha-arm64
|
||||
|
||||
- name: Create & push manifest (GHCR - versioned alpha)
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ghcr.io/${GHCR_REPOSITORY}:${VERSION} \
|
||||
ghcr.io/${GHCR_REPOSITORY}:${VERSION}-amd64 \
|
||||
ghcr.io/${GHCR_REPOSITORY}:${VERSION}-arm64
|
||||
|
||||
125
.github/workflows/docker-image-arm64.yml
vendored
125
.github/workflows/docker-image-arm64.yml
vendored
@@ -1,26 +1,45 @@
|
||||
name: Publish Docker image (Multi Registries)
|
||||
name: Publish Docker image (Multi Registries, native amd64+arm64)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
push_to_registries:
|
||||
name: Push Docker image to multiple registries
|
||||
runs-on: ubuntu-latest
|
||||
build_single_arch:
|
||||
name: Build & push (${{ matrix.arch }}) [native]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- arch: arm64
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
- name: Check out (shallow)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Save version info
|
||||
- name: Resolve tag & write VERSION
|
||||
run: |
|
||||
git describe --tags > VERSION
|
||||
git fetch --tags --force --depth=1
|
||||
echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
echo "$TAG" > VERSION
|
||||
echo "Building tag: $TAG for ${{ matrix.arch }}"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# - name: Normalize GHCR repository
|
||||
# run: echo "GHCR_REPOSITORY=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -31,26 +50,88 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
# - name: Log in to GHCR
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.actor }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
- name: Extract metadata (labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
calciumion/new-api
|
||||
ghcr.io/${{ github.repository }}
|
||||
# ghcr.io/${{ env.GHCR_REPOSITORY }}
|
||||
|
||||
- name: Build and push Docker images
|
||||
uses: docker/build-push-action@v5
|
||||
- name: Build & push single-arch (to both registries)
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: |
|
||||
calciumion/new-api:${{ env.TAG }}-${{ matrix.arch }}
|
||||
calciumion/new-api:latest-${{ matrix.arch }}
|
||||
# ghcr.io/${{ env.GHCR_REPOSITORY }}:${{ env.TAG }}-${{ matrix.arch }}
|
||||
# ghcr.io/${{ env.GHCR_REPOSITORY }}:latest-${{ matrix.arch }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
provenance: false
|
||||
sbom: false
|
||||
|
||||
create_manifests:
|
||||
name: Create multi-arch manifests (Docker Hub)
|
||||
needs: [build_single_arch]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- name: Extract tag
|
||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
#
|
||||
# - name: Normalize GHCR repository
|
||||
# run: echo "GHCR_REPOSITORY=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Create & push manifest (Docker Hub - version)
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t calciumion/new-api:${TAG} \
|
||||
calciumion/new-api:${TAG}-amd64 \
|
||||
calciumion/new-api:${TAG}-arm64
|
||||
|
||||
- name: Create & push manifest (Docker Hub - latest)
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t calciumion/new-api:latest \
|
||||
calciumion/new-api:latest-amd64 \
|
||||
calciumion/new-api:latest-arm64
|
||||
|
||||
# ---- GHCR ----
|
||||
# - name: Log in to GHCR
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.actor }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: Create & push manifest (GHCR - version)
|
||||
# run: |
|
||||
# docker buildx imagetools create \
|
||||
# -t ghcr.io/${GHCR_REPOSITORY}:${TAG} \
|
||||
# ghcr.io/${GHCR_REPOSITORY}:${TAG}-amd64 \
|
||||
# ghcr.io/${GHCR_REPOSITORY}:${TAG}-arm64
|
||||
#
|
||||
# - name: Create & push manifest (GHCR - latest)
|
||||
# run: |
|
||||
# docker buildx imagetools create \
|
||||
# -t ghcr.io/${GHCR_REPOSITORY}:latest \
|
||||
# ghcr.io/${GHCR_REPOSITORY}:latest-amd64 \
|
||||
# ghcr.io/${GHCR_REPOSITORY}:latest-arm64
|
||||
|
||||
4
.github/workflows/electron-build.yml
vendored
4
.github/workflows/electron-build.yml
vendored
@@ -53,13 +53,13 @@ jobs:
|
||||
# if: runner.os != 'Windows'
|
||||
# run: |
|
||||
# go mod download
|
||||
# go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o new-api
|
||||
# go build -ldflags "-s -w -X 'new-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o new-api
|
||||
|
||||
- name: Build Go binary (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
go mod download
|
||||
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)'" -o new-api.exe
|
||||
go build -ldflags "-s -w -X 'new-api/common.Version=$(git describe --tags)'" -o new-api.exe
|
||||
|
||||
- name: Update Electron version
|
||||
run: |
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -41,13 +41,13 @@ jobs:
|
||||
run: |
|
||||
go mod download
|
||||
VERSION=$(git describe --tags)
|
||||
go build -ldflags "-s -w -X 'one-api/common.Version=$VERSION' -extldflags '-static'" -o new-api-$VERSION
|
||||
go build -ldflags "-s -w -X 'new-api/common.Version=$VERSION' -extldflags '-static'" -o new-api-$VERSION
|
||||
- name: Build Backend (arm64)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||
VERSION=$(git describe --tags)
|
||||
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'one-api/common.Version=$VERSION' -extldflags '-static'" -o new-api-arm64-$VERSION
|
||||
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'new-api/common.Version=$VERSION' -extldflags '-static'" -o new-api-arm64-$VERSION
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
run: |
|
||||
go mod download
|
||||
VERSION=$(git describe --tags)
|
||||
go build -ldflags "-X 'one-api/common.Version=$VERSION'" -o new-api-macos-$VERSION
|
||||
go build -ldflags "-X 'new-api/common.Version=$VERSION'" -o new-api-macos-$VERSION
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
run: |
|
||||
go mod download
|
||||
VERSION=$(git describe --tags)
|
||||
go build -ldflags "-s -w -X 'one-api/common.Version=$VERSION'" -o new-api-$VERSION.exe
|
||||
go build -ldflags "-s -w -X 'new-api/common.Version=$VERSION'" -o new-api-$VERSION.exe
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
14
Dockerfile
14
Dockerfile
@@ -9,10 +9,12 @@ COPY ./VERSION .
|
||||
RUN DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat VERSION) bun run build
|
||||
|
||||
FROM golang:alpine AS builder2
|
||||
ENV GO111MODULE=on CGO_ENABLED=0
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ENV GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64}
|
||||
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=0 \
|
||||
GOOS=linux
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
@@ -21,7 +23,7 @@ RUN go mod download
|
||||
|
||||
COPY . .
|
||||
COPY --from=builder /build/dist ./web/dist
|
||||
RUN go build -ldflags "-s -w -X 'one-api/common.Version=$(cat VERSION)'" -o one-api
|
||||
RUN go build -ldflags "-s -w -X 'github.com/QuantumNous/new-api/common.Version=$(cat VERSION)'" -o new-api
|
||||
|
||||
FROM alpine
|
||||
|
||||
@@ -29,7 +31,7 @@ RUN apk upgrade --no-cache \
|
||||
&& apk add --no-cache ca-certificates tzdata ffmpeg \
|
||||
&& update-ca-certificates
|
||||
|
||||
COPY --from=builder2 /build/one-api /
|
||||
COPY --from=builder2 /build/new-api /
|
||||
EXPOSE 3000
|
||||
WORKDIR /data
|
||||
ENTRYPOINT ["/one-api"]
|
||||
ENTRYPOINT ["/new-api"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package common
|
||||
|
||||
import "one-api/constant"
|
||||
import "github.com/QuantumNous/new-api/constant"
|
||||
|
||||
func ChannelType2APIType(channelType int) (int, bool) {
|
||||
apiType := -1
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ package common
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"github.com/gin-contrib/static"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/static"
|
||||
)
|
||||
|
||||
// Credit: https://github.com/gin-contrib/static/issues/19
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package common
|
||||
|
||||
import "one-api/constant"
|
||||
import "github.com/QuantumNous/new-api/constant"
|
||||
|
||||
// EndpointInfo 描述单个端点的默认请求信息
|
||||
// path: 上游路径
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package common
|
||||
|
||||
import "one-api/constant"
|
||||
import "github.com/QuantumNous/new-api/constant"
|
||||
|
||||
// GetEndpointTypesByChannelType 获取渠道最优先端点类型(所有的渠道都支持 OpenAI 端点)
|
||||
func GetEndpointTypesByChannelType(channelType int, modelName string) []constant.EndpointType {
|
||||
|
||||
@@ -5,10 +5,11 @@ import (
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"one-api/constant"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
"math"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
)
|
||||
|
||||
var relayGoPool gopool.Pool
|
||||
|
||||
@@ -4,11 +4,12 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"one-api/constant"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -19,10 +20,10 @@ var (
|
||||
)
|
||||
|
||||
func printHelp() {
|
||||
fmt.Println("New API " + Version + " - All in one API service for OpenAI API.")
|
||||
fmt.Println("Copyright (C) 2023 JustSong. All rights reserved.")
|
||||
fmt.Println("GitHub: https://github.com/songquanpeng/one-api")
|
||||
fmt.Println("Usage: one-api [--port <port>] [--log-dir <log directory>] [--version] [--help]")
|
||||
fmt.Println("NewAPI(Based OneAPI) " + Version + " - The next-generation LLM gateway and AI asset management system supports multiple languages.")
|
||||
fmt.Println("Original Project: OneAPI by JustSong - https://github.com/songquanpeng/one-api")
|
||||
fmt.Println("Maintainer: QuantumNous - https://github.com/QuantumNous/new-api")
|
||||
fmt.Println("Usage: newapi [--port <port>] [--log-dir <log directory>] [--version] [--help]")
|
||||
}
|
||||
|
||||
func InitEnv() {
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"one-api/common"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
//go:embed lua/rate_limit.lua
|
||||
|
||||
@@ -2,10 +2,11 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
)
|
||||
|
||||
// Monitor 定时监控cpu使用率,超过阈值输出pprof文件
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type verificationValue struct {
|
||||
|
||||
161
config/plugin_config.go
Normal file
161
config/plugin_config.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/core/interfaces"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// PluginConfig 插件配置结构
|
||||
type PluginConfig struct {
|
||||
Channels map[string]interfaces.ChannelConfig `yaml:"channels"`
|
||||
Middlewares []interfaces.MiddlewareConfig `yaml:"middlewares"`
|
||||
Hooks HooksConfig `yaml:"hooks"`
|
||||
}
|
||||
|
||||
// HooksConfig Hook配置
|
||||
type HooksConfig struct {
|
||||
Relay []interfaces.HookConfig `yaml:"relay"`
|
||||
}
|
||||
|
||||
var (
|
||||
// 全局配置实例
|
||||
globalPluginConfig *PluginConfig
|
||||
)
|
||||
|
||||
// LoadPluginConfig 加载插件配置
|
||||
func LoadPluginConfig(configPath string) (*PluginConfig, error) {
|
||||
// 如果没有指定配置文件路径,使用默认路径
|
||||
if configPath == "" {
|
||||
configPath = "config/plugins.yaml"
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
common.SysLog(fmt.Sprintf("Plugin config file not found: %s, using default configuration", configPath))
|
||||
return getDefaultConfig(), nil
|
||||
}
|
||||
|
||||
// 读取配置文件
|
||||
data, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read plugin config: %w", err)
|
||||
}
|
||||
|
||||
// 解析YAML
|
||||
var config PluginConfig
|
||||
if err := yaml.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse plugin config: %w", err)
|
||||
}
|
||||
|
||||
// 环境变量替换
|
||||
expandEnvVars(&config)
|
||||
|
||||
common.SysLog(fmt.Sprintf("Loaded plugin config from: %s", configPath))
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// getDefaultConfig 返回默认配置
|
||||
func getDefaultConfig() *PluginConfig {
|
||||
return &PluginConfig{
|
||||
Channels: make(map[string]interfaces.ChannelConfig),
|
||||
Middlewares: make([]interfaces.MiddlewareConfig, 0),
|
||||
Hooks: HooksConfig{
|
||||
Relay: make([]interfaces.HookConfig, 0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// expandEnvVars 展开环境变量
|
||||
func expandEnvVars(config *PluginConfig) {
|
||||
// 展开Hook配置中的环境变量
|
||||
for i := range config.Hooks.Relay {
|
||||
for key, value := range config.Hooks.Relay[i].Config {
|
||||
if strValue, ok := value.(string); ok {
|
||||
config.Hooks.Relay[i].Config[key] = os.ExpandEnv(strValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 展开Middleware配置中的环境变量
|
||||
for i := range config.Middlewares {
|
||||
for key, value := range config.Middlewares[i].Config {
|
||||
if strValue, ok := value.(string); ok {
|
||||
config.Middlewares[i].Config[key] = os.ExpandEnv(strValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetGlobalPluginConfig 获取全局配置
|
||||
func GetGlobalPluginConfig() *PluginConfig {
|
||||
if globalPluginConfig == nil {
|
||||
configPath := os.Getenv("PLUGIN_CONFIG_PATH")
|
||||
if configPath == "" {
|
||||
configPath = "config/plugins.yaml"
|
||||
}
|
||||
|
||||
config, err := LoadPluginConfig(configPath)
|
||||
if err != nil {
|
||||
common.SysError(fmt.Sprintf("Failed to load plugin config: %v", err))
|
||||
config = getDefaultConfig()
|
||||
}
|
||||
|
||||
globalPluginConfig = config
|
||||
}
|
||||
|
||||
return globalPluginConfig
|
||||
}
|
||||
|
||||
// SavePluginConfig 保存插件配置
|
||||
func SavePluginConfig(config *PluginConfig, configPath string) error {
|
||||
if configPath == "" {
|
||||
configPath = "config/plugins.yaml"
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(configPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
// 序列化为YAML
|
||||
data, err := yaml.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if err := ioutil.WriteFile(configPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
|
||||
common.SysLog(fmt.Sprintf("Saved plugin config to: %s", configPath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReloadPluginConfig 重新加载配置
|
||||
func ReloadPluginConfig() error {
|
||||
configPath := os.Getenv("PLUGIN_CONFIG_PATH")
|
||||
if configPath == "" {
|
||||
configPath = "config/plugins.yaml"
|
||||
}
|
||||
|
||||
config, err := LoadPluginConfig(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
globalPluginConfig = config
|
||||
common.SysLog("Plugin config reloaded")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
52
config/plugins.yaml
Normal file
52
config/plugins.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
# New-API 插件配置
|
||||
# 此文件用于配置所有插件的启用状态和参数
|
||||
|
||||
# Channel插件配置
|
||||
channels:
|
||||
openai:
|
||||
enabled: true
|
||||
priority: 100
|
||||
|
||||
claude:
|
||||
enabled: true
|
||||
priority: 90
|
||||
|
||||
gemini:
|
||||
enabled: true
|
||||
priority: 85
|
||||
|
||||
# Middleware插件配置
|
||||
middlewares:
|
||||
- name: auth
|
||||
enabled: true
|
||||
priority: 100
|
||||
|
||||
- name: ratelimit
|
||||
enabled: true
|
||||
priority: 90
|
||||
config:
|
||||
default_rate: 60
|
||||
|
||||
# Hook插件配置
|
||||
hooks:
|
||||
# Relay层Hook
|
||||
relay:
|
||||
# 联网搜索插件
|
||||
- name: web_search
|
||||
enabled: false # 默认禁用,需要配置API key后启用
|
||||
priority: 50
|
||||
config:
|
||||
provider: google
|
||||
api_key: ${WEB_SEARCH_API_KEY} # 从环境变量读取
|
||||
|
||||
# 内容过滤插件
|
||||
- name: content_filter
|
||||
enabled: false # 默认禁用,需要配置后启用
|
||||
priority: 100 # 高优先级,最后执行
|
||||
config:
|
||||
filter_nsfw: true
|
||||
filter_political: false
|
||||
sensitive_words:
|
||||
- "敏感词1"
|
||||
- "敏感词2"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/common"
|
||||
"one-api/dto"
|
||||
"one-api/model"
|
||||
"one-api/setting/operation_setting"
|
||||
)
|
||||
|
||||
func GetSubscription(c *gin.Context) {
|
||||
|
||||
@@ -6,15 +6,16 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/model"
|
||||
"one-api/service"
|
||||
"one-api/setting/operation_setting"
|
||||
"one-api/types"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -10,23 +10,24 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/middleware"
|
||||
"one-api/model"
|
||||
"one-api/relay"
|
||||
relaycommon "one-api/relay/common"
|
||||
relayconstant "one-api/relay/constant"
|
||||
"one-api/relay/helper"
|
||||
"one-api/service"
|
||||
"one-api/setting/operation_setting"
|
||||
"one-api/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/middleware"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/relay"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
relayconstant "github.com/QuantumNous/new-api/relay/constant"
|
||||
"github.com/QuantumNous/new-api/relay/helper"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
"github.com/samber/lo"
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/model"
|
||||
"one-api/service"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@ package controller
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -6,11 +6,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -2,9 +2,10 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"one-api/setting/ratio_setting"
|
||||
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -2,10 +2,11 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,15 +7,16 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/dto"
|
||||
"one-api/logger"
|
||||
"one-api/model"
|
||||
"one-api/service"
|
||||
"one-api/setting"
|
||||
"one-api/setting/system_setting"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,16 +4,17 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/middleware"
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"one-api/setting/console_setting"
|
||||
"one-api/setting/operation_setting"
|
||||
"one-api/setting/system_setting"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/middleware"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/console_setting"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"one-api/model"
|
||||
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -2,21 +2,22 @@ package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/relay"
|
||||
"github.com/QuantumNous/new-api/relay/channel/ai360"
|
||||
"github.com/QuantumNous/new-api/relay/channel/lingyiwanwu"
|
||||
"github.com/QuantumNous/new-api/relay/channel/minimax"
|
||||
"github.com/QuantumNous/new-api/relay/channel/moonshot"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/samber/lo"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/model"
|
||||
"one-api/relay"
|
||||
"one-api/relay/channel/ai360"
|
||||
"one-api/relay/channel/lingyiwanwu"
|
||||
"one-api/relay/channel/minimax"
|
||||
"one-api/relay/channel/moonshot"
|
||||
relaycommon "one-api/relay/common"
|
||||
"one-api/setting"
|
||||
"time"
|
||||
)
|
||||
|
||||
// https://platform.openai.com/docs/api-reference/models/list
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/model"
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -6,13 +6,14 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"one-api/setting/system_setting"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -4,14 +4,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"one-api/setting/console_setting"
|
||||
"one-api/setting/ratio_setting"
|
||||
"one-api/setting/system_setting"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/console_setting"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
passkeysvc "one-api/service/passkey"
|
||||
"one-api/setting/system_setting"
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
passkeysvc "github.com/QuantumNous/new-api/service/passkey"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -3,13 +3,14 @@ package controller
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/middleware"
|
||||
"one-api/model"
|
||||
"one-api/types"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/middleware"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package controller
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"one-api/setting/ratio_setting"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -2,7 +2,8 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"one-api/setting/ratio_setting"
|
||||
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -7,14 +7,15 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"one-api/logger"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"one-api/dto"
|
||||
"one-api/model"
|
||||
"one-api/setting/ratio_setting"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -3,11 +3,12 @@ package controller
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,21 +6,22 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/logger"
|
||||
"one-api/middleware"
|
||||
"one-api/model"
|
||||
"one-api/relay"
|
||||
relaycommon "one-api/relay/common"
|
||||
relayconstant "one-api/relay/constant"
|
||||
"one-api/relay/helper"
|
||||
"one-api/service"
|
||||
"one-api/setting"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/middleware"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/relay"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
relayconstant "github.com/QuantumNous/new-api/relay/constant"
|
||||
"github.com/QuantumNous/new-api/relay/helper"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -139,9 +140,13 @@ func Relay(c *gin.Context, relayFormat types.RelayFormat) {
|
||||
|
||||
// common.SetContextKey(c, constant.ContextKeyTokenCountMeta, meta)
|
||||
|
||||
newAPIError = service.PreConsumeQuota(c, priceData.ShouldPreConsumedQuota, relayInfo)
|
||||
if newAPIError != nil {
|
||||
return
|
||||
if priceData.FreeModel {
|
||||
logger.LogInfo(c, fmt.Sprintf("模型 %s 免费,跳过预扣费", relayInfo.OriginModelName))
|
||||
} else {
|
||||
newAPIError = service.PreConsumeQuota(c, priceData.QuotaToPreConsume, relayInfo)
|
||||
if newAPIError != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
||||
@@ -3,12 +3,13 @@ package controller
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
passkeysvc "one-api/service/passkey"
|
||||
"one-api/setting/system_setting"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
passkeysvc "github.com/QuantumNous/new-api/service/passkey"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/model"
|
||||
"one-api/setting/operation_setting"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Setup struct {
|
||||
|
||||
@@ -7,16 +7,17 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/logger"
|
||||
"one-api/model"
|
||||
"one-api/relay"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/relay"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
@@ -5,16 +5,17 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/logger"
|
||||
"one-api/model"
|
||||
"one-api/relay"
|
||||
"one-api/relay/channel"
|
||||
relaycommon "one-api/relay/common"
|
||||
"one-api/setting/ratio_setting"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/relay"
|
||||
"github.com/QuantumNous/new-api/relay/channel"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
)
|
||||
|
||||
func UpdateVideoTaskAll(ctx context.Context, platform constant.TaskPlatform, taskChannelM map[int][]string, taskM map[string]*model.Task) error {
|
||||
|
||||
@@ -6,10 +6,11 @@ import (
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"sort"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -2,11 +2,12 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,17 +4,18 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"one-api/common"
|
||||
"one-api/logger"
|
||||
"one-api/model"
|
||||
"one-api/service"
|
||||
"one-api/setting"
|
||||
"one-api/setting/operation_setting"
|
||||
"one-api/setting/system_setting"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/Calcium-Ion/go-epay/epay"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/samber/lo"
|
||||
|
||||
@@ -5,15 +5,16 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"one-api/setting/operation_setting"
|
||||
"one-api/setting/system_setting"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
"github.com/QuantumNous/new-api/setting/system_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stripe/stripe-go/v81"
|
||||
"github.com/stripe/stripe-go/v81/checkout/session"
|
||||
|
||||
@@ -4,10 +4,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"one-api/setting/console_setting"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/setting/console_setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -2,10 +2,11 @@ package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,16 +5,17 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"one-api/common"
|
||||
"one-api/dto"
|
||||
"one-api/logger"
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"one-api/constant"
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -3,8 +3,8 @@ package controller
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -4,10 +4,11 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"one-api/logger"
|
||||
"one-api/model"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/model"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
66
core/interfaces/channel.go
Normal file
66
core/interfaces/channel.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
relaycommon "github.com/QuantumNous/new-api/relay/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ChannelPlugin 定义Channel插件接口
|
||||
// 继承原有的Adaptor接口,增加插件元数据
|
||||
type ChannelPlugin interface {
|
||||
// 插件元数据
|
||||
Name() string
|
||||
Version() string
|
||||
Priority() int
|
||||
|
||||
// 原有Adaptor接口方法
|
||||
Init(info *relaycommon.RelayInfo)
|
||||
GetRequestURL(info *relaycommon.RelayInfo) (string, error)
|
||||
SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error
|
||||
ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error)
|
||||
ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error)
|
||||
ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error)
|
||||
ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error)
|
||||
ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error)
|
||||
ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.OpenAIResponsesRequest) (any, error)
|
||||
DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (any, error)
|
||||
DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage any, err *types.NewAPIError)
|
||||
GetModelList() []string
|
||||
GetChannelName() string
|
||||
ConvertClaudeRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.ClaudeRequest) (any, error)
|
||||
ConvertGeminiRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeminiChatRequest) (any, error)
|
||||
}
|
||||
|
||||
// TaskChannelPlugin 定义Task类型的Channel插件接口
|
||||
type TaskChannelPlugin interface {
|
||||
// 插件元数据
|
||||
Name() string
|
||||
Version() string
|
||||
Priority() int
|
||||
|
||||
// 原有TaskAdaptor接口方法
|
||||
Init(info *relaycommon.RelayInfo)
|
||||
ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) *dto.TaskError
|
||||
BuildRequestURL(info *relaycommon.RelayInfo) (string, error)
|
||||
BuildRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error
|
||||
BuildRequestBody(c *gin.Context, info *relaycommon.RelayInfo) (io.Reader, error)
|
||||
DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (*http.Response, error)
|
||||
DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, err *dto.TaskError)
|
||||
GetModelList() []string
|
||||
GetChannelName() string
|
||||
FetchTask(baseUrl, key string, body map[string]any) (*http.Response, error)
|
||||
ParseTaskResult(respBody []byte) (*relaycommon.TaskInfo, error)
|
||||
}
|
||||
|
||||
// ChannelConfig 插件配置
|
||||
type ChannelConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Priority int `yaml:"priority"`
|
||||
Config map[string]interface{} `yaml:"config"`
|
||||
}
|
||||
|
||||
93
core/interfaces/hook.go
Normal file
93
core/interfaces/hook.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// HookContext Relay Hook执行上下文
|
||||
type HookContext struct {
|
||||
// Gin Context
|
||||
GinContext *gin.Context
|
||||
|
||||
// Request相关
|
||||
Request *http.Request
|
||||
RequestBody []byte
|
||||
|
||||
// Response相关
|
||||
Response *http.Response
|
||||
ResponseBody []byte
|
||||
|
||||
// Channel信息
|
||||
ChannelID int
|
||||
ChannelType int
|
||||
ChannelName string
|
||||
|
||||
// Model信息
|
||||
Model string
|
||||
OriginalModel string
|
||||
|
||||
// User信息
|
||||
UserID int
|
||||
TokenID int
|
||||
Group string
|
||||
|
||||
// 扩展数据(插件间共享)
|
||||
Data map[string]interface{}
|
||||
|
||||
// 错误信息
|
||||
Error error
|
||||
|
||||
// 是否跳过后续处理
|
||||
ShouldSkip bool
|
||||
}
|
||||
|
||||
// RelayHook Relay Hook接口
|
||||
type RelayHook interface {
|
||||
// 插件元数据
|
||||
Name() string
|
||||
Priority() int
|
||||
Enabled() bool
|
||||
|
||||
// 生命周期钩子
|
||||
// OnBeforeRequest 在请求发送到上游之前执行
|
||||
OnBeforeRequest(ctx *HookContext) error
|
||||
|
||||
// OnAfterResponse 在收到上游响应之后执行
|
||||
OnAfterResponse(ctx *HookContext) error
|
||||
|
||||
// OnError 在发生错误时执行
|
||||
OnError(ctx *HookContext, err error) error
|
||||
}
|
||||
|
||||
// RequestModifier 请求修改器接口
|
||||
// 实现此接口的Hook可以修改请求内容
|
||||
type RequestModifier interface {
|
||||
RelayHook
|
||||
ModifyRequest(ctx *HookContext, body io.Reader) (io.Reader, error)
|
||||
}
|
||||
|
||||
// ResponseProcessor 响应处理器接口
|
||||
// 实现此接口的Hook可以处理响应内容
|
||||
type ResponseProcessor interface {
|
||||
RelayHook
|
||||
ProcessResponse(ctx *HookContext, body []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// StreamProcessor 流式响应处理器接口
|
||||
// 实现此接口的Hook可以处理流式响应
|
||||
type StreamProcessor interface {
|
||||
RelayHook
|
||||
ProcessStreamChunk(ctx *HookContext, chunk []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// HookConfig Hook配置
|
||||
type HookConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Priority int `yaml:"priority"`
|
||||
Config map[string]interface{} `yaml:"config"`
|
||||
}
|
||||
|
||||
31
core/interfaces/middleware.go
Normal file
31
core/interfaces/middleware.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package interfaces
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// MiddlewarePlugin 中间件插件接口
|
||||
type MiddlewarePlugin interface {
|
||||
// 插件元数据
|
||||
Name() string
|
||||
Priority() int
|
||||
Enabled() bool
|
||||
|
||||
// 返回Gin中间件处理函数
|
||||
Handler() gin.HandlerFunc
|
||||
|
||||
// 初始化(可选)
|
||||
Initialize(config MiddlewareConfig) error
|
||||
}
|
||||
|
||||
// MiddlewareConfig 中间件配置
|
||||
type MiddlewareConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Priority int `yaml:"priority"`
|
||||
Config map[string]interface{} `yaml:"config"`
|
||||
}
|
||||
|
||||
// MiddlewareFactory 中间件工厂函数类型
|
||||
type MiddlewareFactory func(config MiddlewareConfig) (MiddlewarePlugin, error)
|
||||
|
||||
171
core/registry/channel_registry.go
Normal file
171
core/registry/channel_registry.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/core/interfaces"
|
||||
)
|
||||
|
||||
var (
|
||||
// 全局Channel注册表
|
||||
channelRegistry = &ChannelRegistry{plugins: make(map[int]interfaces.ChannelPlugin)}
|
||||
channelRegistryLock sync.RWMutex
|
||||
|
||||
// 全局TaskChannel注册表
|
||||
taskChannelRegistry = &TaskChannelRegistry{plugins: make(map[string]interfaces.TaskChannelPlugin)}
|
||||
taskChannelRegistryLock sync.RWMutex
|
||||
)
|
||||
|
||||
// ChannelRegistry Channel插件注册中心
|
||||
type ChannelRegistry struct {
|
||||
plugins map[int]interfaces.ChannelPlugin // channelType -> plugin
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Register 注册Channel插件
|
||||
func (r *ChannelRegistry) Register(channelType int, plugin interfaces.ChannelPlugin) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if _, exists := r.plugins[channelType]; exists {
|
||||
return fmt.Errorf("channel plugin for type %d already registered", channelType)
|
||||
}
|
||||
|
||||
r.plugins[channelType] = plugin
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取Channel插件
|
||||
func (r *ChannelRegistry) Get(channelType int) (interfaces.ChannelPlugin, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugin, exists := r.plugins[channelType]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("channel plugin for type %d not found", channelType)
|
||||
}
|
||||
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
// List 列出所有已注册的Channel插件
|
||||
func (r *ChannelRegistry) List() []interfaces.ChannelPlugin {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugins := make([]interfaces.ChannelPlugin, 0, len(r.plugins))
|
||||
for _, plugin := range r.plugins {
|
||||
plugins = append(plugins, plugin)
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// Has 检查是否存在指定的Channel插件
|
||||
func (r *ChannelRegistry) Has(channelType int) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
_, exists := r.plugins[channelType]
|
||||
return exists
|
||||
}
|
||||
|
||||
// TaskChannelRegistry TaskChannel插件注册中心
|
||||
type TaskChannelRegistry struct {
|
||||
plugins map[string]interfaces.TaskChannelPlugin // platform -> plugin
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Register 注册TaskChannel插件
|
||||
func (r *TaskChannelRegistry) Register(platform string, plugin interfaces.TaskChannelPlugin) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if _, exists := r.plugins[platform]; exists {
|
||||
return fmt.Errorf("task channel plugin for platform %s already registered", platform)
|
||||
}
|
||||
|
||||
r.plugins[platform] = plugin
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取TaskChannel插件
|
||||
func (r *TaskChannelRegistry) Get(platform string) (interfaces.TaskChannelPlugin, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugin, exists := r.plugins[platform]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("task channel plugin for platform %s not found", platform)
|
||||
}
|
||||
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
// List 列出所有已注册的TaskChannel插件
|
||||
func (r *TaskChannelRegistry) List() []interfaces.TaskChannelPlugin {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugins := make([]interfaces.TaskChannelPlugin, 0, len(r.plugins))
|
||||
for _, plugin := range r.plugins {
|
||||
plugins = append(plugins, plugin)
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// 全局函数 - Channel Registry
|
||||
|
||||
// RegisterChannel 注册Channel插件
|
||||
func RegisterChannel(channelType int, plugin interfaces.ChannelPlugin) error {
|
||||
channelRegistryLock.Lock()
|
||||
defer channelRegistryLock.Unlock()
|
||||
return channelRegistry.Register(channelType, plugin)
|
||||
}
|
||||
|
||||
// GetChannel 获取Channel插件
|
||||
func GetChannel(channelType int) (interfaces.ChannelPlugin, error) {
|
||||
channelRegistryLock.RLock()
|
||||
defer channelRegistryLock.RUnlock()
|
||||
return channelRegistry.Get(channelType)
|
||||
}
|
||||
|
||||
// ListChannels 列出所有Channel插件
|
||||
func ListChannels() []interfaces.ChannelPlugin {
|
||||
channelRegistryLock.RLock()
|
||||
defer channelRegistryLock.RUnlock()
|
||||
return channelRegistry.List()
|
||||
}
|
||||
|
||||
// HasChannel 检查是否存在指定的Channel插件
|
||||
func HasChannel(channelType int) bool {
|
||||
channelRegistryLock.RLock()
|
||||
defer channelRegistryLock.RUnlock()
|
||||
return channelRegistry.Has(channelType)
|
||||
}
|
||||
|
||||
// 全局函数 - TaskChannel Registry
|
||||
|
||||
// RegisterTaskChannel 注册TaskChannel插件
|
||||
func RegisterTaskChannel(platform string, plugin interfaces.TaskChannelPlugin) error {
|
||||
taskChannelRegistryLock.Lock()
|
||||
defer taskChannelRegistryLock.Unlock()
|
||||
return taskChannelRegistry.Register(platform, plugin)
|
||||
}
|
||||
|
||||
// GetTaskChannel 获取TaskChannel插件
|
||||
func GetTaskChannel(platform string) (interfaces.TaskChannelPlugin, error) {
|
||||
taskChannelRegistryLock.RLock()
|
||||
defer taskChannelRegistryLock.RUnlock()
|
||||
return taskChannelRegistry.Get(platform)
|
||||
}
|
||||
|
||||
// ListTaskChannels 列出所有TaskChannel插件
|
||||
func ListTaskChannels() []interfaces.TaskChannelPlugin {
|
||||
taskChannelRegistryLock.RLock()
|
||||
defer taskChannelRegistryLock.RUnlock()
|
||||
return taskChannelRegistry.List()
|
||||
}
|
||||
|
||||
183
core/registry/hook_registry.go
Normal file
183
core/registry/hook_registry.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/core/interfaces"
|
||||
)
|
||||
|
||||
var (
|
||||
// 全局Hook注册表
|
||||
hookRegistry = &HookRegistry{hooks: make([]interfaces.RelayHook, 0)}
|
||||
hookRegistryLock sync.RWMutex
|
||||
)
|
||||
|
||||
// HookRegistry Hook插件注册中心
|
||||
type HookRegistry struct {
|
||||
hooks []interfaces.RelayHook
|
||||
sorted bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Register 注册Hook插件
|
||||
func (r *HookRegistry) Register(hook interfaces.RelayHook) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
// 检查是否已存在同名Hook
|
||||
for _, h := range r.hooks {
|
||||
if h.Name() == hook.Name() {
|
||||
return fmt.Errorf("hook %s already registered", hook.Name())
|
||||
}
|
||||
}
|
||||
|
||||
r.hooks = append(r.hooks, hook)
|
||||
r.sorted = false // 标记需要重新排序
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取指定名称的Hook插件
|
||||
func (r *HookRegistry) Get(name string) (interfaces.RelayHook, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
for _, hook := range r.hooks {
|
||||
if hook.Name() == name {
|
||||
return hook, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("hook %s not found", name)
|
||||
}
|
||||
|
||||
// List 列出所有已注册且启用的Hook插件(按优先级排序)
|
||||
func (r *HookRegistry) List() []interfaces.RelayHook {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
// 如果未排序,先排序
|
||||
if !r.sorted {
|
||||
r.sortHooks()
|
||||
}
|
||||
|
||||
// 只返回启用的hooks
|
||||
enabledHooks := make([]interfaces.RelayHook, 0)
|
||||
for _, hook := range r.hooks {
|
||||
if hook.Enabled() {
|
||||
enabledHooks = append(enabledHooks, hook)
|
||||
}
|
||||
}
|
||||
|
||||
return enabledHooks
|
||||
}
|
||||
|
||||
// ListAll 列出所有已注册的Hook插件(包括未启用的)
|
||||
func (r *HookRegistry) ListAll() []interfaces.RelayHook {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
hooks := make([]interfaces.RelayHook, len(r.hooks))
|
||||
copy(hooks, r.hooks)
|
||||
|
||||
return hooks
|
||||
}
|
||||
|
||||
// sortHooks 按优先级排序hooks(优先级数字越大越先执行)
|
||||
func (r *HookRegistry) sortHooks() {
|
||||
sort.SliceStable(r.hooks, func(i, j int) bool {
|
||||
return r.hooks[i].Priority() > r.hooks[j].Priority()
|
||||
})
|
||||
r.sorted = true
|
||||
}
|
||||
|
||||
// Has 检查是否存在指定的Hook插件
|
||||
func (r *HookRegistry) Has(name string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
for _, hook := range r.hooks {
|
||||
if hook.Name() == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Count 返回已注册的Hook数量
|
||||
func (r *HookRegistry) Count() int {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
return len(r.hooks)
|
||||
}
|
||||
|
||||
// EnabledCount 返回已启用的Hook数量
|
||||
func (r *HookRegistry) EnabledCount() int {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
count := 0
|
||||
for _, hook := range r.hooks {
|
||||
if hook.Enabled() {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// 全局函数
|
||||
|
||||
// RegisterHook 注册Hook插件
|
||||
func RegisterHook(hook interfaces.RelayHook) error {
|
||||
hookRegistryLock.Lock()
|
||||
defer hookRegistryLock.Unlock()
|
||||
return hookRegistry.Register(hook)
|
||||
}
|
||||
|
||||
// GetHook 获取Hook插件
|
||||
func GetHook(name string) (interfaces.RelayHook, error) {
|
||||
hookRegistryLock.RLock()
|
||||
defer hookRegistryLock.RUnlock()
|
||||
return hookRegistry.Get(name)
|
||||
}
|
||||
|
||||
// ListHooks 列出所有已启用的Hook插件
|
||||
func ListHooks() []interfaces.RelayHook {
|
||||
hookRegistryLock.RLock()
|
||||
defer hookRegistryLock.RUnlock()
|
||||
return hookRegistry.List()
|
||||
}
|
||||
|
||||
// ListAllHooks 列出所有Hook插件
|
||||
func ListAllHooks() []interfaces.RelayHook {
|
||||
hookRegistryLock.RLock()
|
||||
defer hookRegistryLock.RUnlock()
|
||||
return hookRegistry.ListAll()
|
||||
}
|
||||
|
||||
// HasHook 检查是否存在指定的Hook插件
|
||||
func HasHook(name string) bool {
|
||||
hookRegistryLock.RLock()
|
||||
defer hookRegistryLock.RUnlock()
|
||||
return hookRegistry.Has(name)
|
||||
}
|
||||
|
||||
// HookCount 返回已注册的Hook数量
|
||||
func HookCount() int {
|
||||
hookRegistryLock.RLock()
|
||||
defer hookRegistryLock.RUnlock()
|
||||
return hookRegistry.Count()
|
||||
}
|
||||
|
||||
// EnabledHookCount 返回已启用的Hook数量
|
||||
func EnabledHookCount() int {
|
||||
hookRegistryLock.RLock()
|
||||
defer hookRegistryLock.RUnlock()
|
||||
return hookRegistry.EnabledCount()
|
||||
}
|
||||
|
||||
133
core/registry/middleware_registry.go
Normal file
133
core/registry/middleware_registry.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/core/interfaces"
|
||||
)
|
||||
|
||||
var (
|
||||
// 全局Middleware注册表
|
||||
middlewareRegistry = &MiddlewareRegistry{plugins: make(map[string]interfaces.MiddlewarePlugin)}
|
||||
middlewareRegistryLock sync.RWMutex
|
||||
)
|
||||
|
||||
// MiddlewareRegistry 中间件插件注册中心
|
||||
type MiddlewareRegistry struct {
|
||||
plugins map[string]interfaces.MiddlewarePlugin
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// Register 注册Middleware插件
|
||||
func (r *MiddlewareRegistry) Register(plugin interfaces.MiddlewarePlugin) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
name := plugin.Name()
|
||||
if _, exists := r.plugins[name]; exists {
|
||||
return fmt.Errorf("middleware plugin %s already registered", name)
|
||||
}
|
||||
|
||||
r.plugins[name] = plugin
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取Middleware插件
|
||||
func (r *MiddlewareRegistry) Get(name string) (interfaces.MiddlewarePlugin, error) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugin, exists := r.plugins[name]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("middleware plugin %s not found", name)
|
||||
}
|
||||
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
// List 列出所有已注册的Middleware插件(按优先级排序)
|
||||
func (r *MiddlewareRegistry) List() []interfaces.MiddlewarePlugin {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugins := make([]interfaces.MiddlewarePlugin, 0, len(r.plugins))
|
||||
for _, plugin := range r.plugins {
|
||||
plugins = append(plugins, plugin)
|
||||
}
|
||||
|
||||
// 按优先级排序(优先级数字越大越先执行)
|
||||
sort.SliceStable(plugins, func(i, j int) bool {
|
||||
return plugins[i].Priority() > plugins[j].Priority()
|
||||
})
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// ListEnabled 列出所有已启用的Middleware插件(按优先级排序)
|
||||
func (r *MiddlewareRegistry) ListEnabled() []interfaces.MiddlewarePlugin {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
plugins := make([]interfaces.MiddlewarePlugin, 0, len(r.plugins))
|
||||
for _, plugin := range r.plugins {
|
||||
if plugin.Enabled() {
|
||||
plugins = append(plugins, plugin)
|
||||
}
|
||||
}
|
||||
|
||||
// 按优先级排序
|
||||
sort.SliceStable(plugins, func(i, j int) bool {
|
||||
return plugins[i].Priority() > plugins[j].Priority()
|
||||
})
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// Has 检查是否存在指定的Middleware插件
|
||||
func (r *MiddlewareRegistry) Has(name string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
_, exists := r.plugins[name]
|
||||
return exists
|
||||
}
|
||||
|
||||
// 全局函数
|
||||
|
||||
// RegisterMiddleware 注册Middleware插件
|
||||
func RegisterMiddleware(plugin interfaces.MiddlewarePlugin) error {
|
||||
middlewareRegistryLock.Lock()
|
||||
defer middlewareRegistryLock.Unlock()
|
||||
return middlewareRegistry.Register(plugin)
|
||||
}
|
||||
|
||||
// GetMiddleware 获取Middleware插件
|
||||
func GetMiddleware(name string) (interfaces.MiddlewarePlugin, error) {
|
||||
middlewareRegistryLock.RLock()
|
||||
defer middlewareRegistryLock.RUnlock()
|
||||
return middlewareRegistry.Get(name)
|
||||
}
|
||||
|
||||
// ListMiddlewares 列出所有Middleware插件
|
||||
func ListMiddlewares() []interfaces.MiddlewarePlugin {
|
||||
middlewareRegistryLock.RLock()
|
||||
defer middlewareRegistryLock.RUnlock()
|
||||
return middlewareRegistry.List()
|
||||
}
|
||||
|
||||
// ListEnabledMiddlewares 列出所有已启用的Middleware插件
|
||||
func ListEnabledMiddlewares() []interfaces.MiddlewarePlugin {
|
||||
middlewareRegistryLock.RLock()
|
||||
defer middlewareRegistryLock.RUnlock()
|
||||
return middlewareRegistry.ListEnabled()
|
||||
}
|
||||
|
||||
// HasMiddleware 检查是否存在指定的Middleware插件
|
||||
func HasMiddleware(name string) bool {
|
||||
middlewareRegistryLock.RLock()
|
||||
defer middlewareRegistryLock.RUnlock()
|
||||
return middlewareRegistry.Has(name)
|
||||
}
|
||||
|
||||
116
core/registry/registry_test.go
Normal file
116
core/registry/registry_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/QuantumNous/new-api/core/interfaces"
|
||||
)
|
||||
|
||||
// Mock Hook实现
|
||||
type mockHook struct {
|
||||
name string
|
||||
priority int
|
||||
enabled bool
|
||||
}
|
||||
|
||||
func (m *mockHook) Name() string { return m.name }
|
||||
func (m *mockHook) Priority() int { return m.priority }
|
||||
func (m *mockHook) Enabled() bool { return m.enabled }
|
||||
func (m *mockHook) OnBeforeRequest(ctx *interfaces.HookContext) error { return nil }
|
||||
func (m *mockHook) OnAfterResponse(ctx *interfaces.HookContext) error { return nil }
|
||||
func (m *mockHook) OnError(ctx *interfaces.HookContext, err error) error { return nil }
|
||||
|
||||
func TestHookRegistry(t *testing.T) {
|
||||
// 创建新的注册表(用于测试)
|
||||
registry := &HookRegistry{hooks: make([]interfaces.RelayHook, 0)}
|
||||
|
||||
// 测试注册Hook
|
||||
hook1 := &mockHook{name: "test_hook_1", priority: 100, enabled: true}
|
||||
hook2 := &mockHook{name: "test_hook_2", priority: 50, enabled: true}
|
||||
hook3 := &mockHook{name: "test_hook_3", priority: 75, enabled: false}
|
||||
|
||||
if err := registry.Register(hook1); err != nil {
|
||||
t.Errorf("Failed to register hook1: %v", err)
|
||||
}
|
||||
|
||||
if err := registry.Register(hook2); err != nil {
|
||||
t.Errorf("Failed to register hook2: %v", err)
|
||||
}
|
||||
|
||||
if err := registry.Register(hook3); err != nil {
|
||||
t.Errorf("Failed to register hook3: %v", err)
|
||||
}
|
||||
|
||||
// 测试重复注册
|
||||
if err := registry.Register(hook1); err == nil {
|
||||
t.Error("Expected error when registering duplicate hook")
|
||||
}
|
||||
|
||||
// 测试获取Hook
|
||||
if hook, err := registry.Get("test_hook_1"); err != nil {
|
||||
t.Errorf("Failed to get hook: %v", err)
|
||||
} else if hook.Name() != "test_hook_1" {
|
||||
t.Errorf("Got wrong hook: %s", hook.Name())
|
||||
}
|
||||
|
||||
// 测试不存在的Hook
|
||||
if _, err := registry.Get("nonexistent"); err == nil {
|
||||
t.Error("Expected error when getting nonexistent hook")
|
||||
}
|
||||
|
||||
// 测试List(应该只返回enabled的hooks)
|
||||
hooks := registry.List()
|
||||
if len(hooks) != 2 {
|
||||
t.Errorf("Expected 2 enabled hooks, got %d", len(hooks))
|
||||
}
|
||||
|
||||
// 测试优先级排序(100应该在50之前)
|
||||
if hooks[0].Priority() != 100 {
|
||||
t.Errorf("Expected first hook to have priority 100, got %d", hooks[0].Priority())
|
||||
}
|
||||
|
||||
// 测试Count
|
||||
if count := registry.Count(); count != 3 {
|
||||
t.Errorf("Expected count 3, got %d", count)
|
||||
}
|
||||
|
||||
// 测试EnabledCount
|
||||
if count := registry.EnabledCount(); count != 2 {
|
||||
t.Errorf("Expected enabled count 2, got %d", count)
|
||||
}
|
||||
|
||||
// 测试Has
|
||||
if !registry.Has("test_hook_1") {
|
||||
t.Error("Expected to find test_hook_1")
|
||||
}
|
||||
|
||||
if registry.Has("nonexistent") {
|
||||
t.Error("Should not find nonexistent hook")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelRegistry(t *testing.T) {
|
||||
// 这里可以添加Channel Registry的测试
|
||||
// 但需要mock ChannelPlugin接口,比较复杂
|
||||
// 作为示例,我们只测试基本功能
|
||||
|
||||
registry := &ChannelRegistry{plugins: make(map[int]interfaces.ChannelPlugin)}
|
||||
|
||||
// 测试Has方法
|
||||
if registry.Has(1) {
|
||||
t.Error("Should not find channel type 1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMiddlewareRegistry(t *testing.T) {
|
||||
// Middleware Registry测试
|
||||
// 需要mock MiddlewarePlugin接口
|
||||
|
||||
registry := &MiddlewareRegistry{plugins: make(map[string]interfaces.MiddlewarePlugin)}
|
||||
|
||||
// 测试Has方法
|
||||
if registry.Has("test_middleware") {
|
||||
t.Error("Should not find test_middleware")
|
||||
}
|
||||
}
|
||||
|
||||
359
docs/architecture/plugin-system-architecture.md
Normal file
359
docs/architecture/plugin-system-architecture.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# New-API 插件化架构说明
|
||||
|
||||
## 完整目录结构
|
||||
|
||||
```
|
||||
new-api-2/
|
||||
├── core/ # 核心层(高性能,不可插件化)
|
||||
│ ├── interfaces/ # 插件接口定义
|
||||
│ │ ├── channel.go # Channel插件接口
|
||||
│ │ ├── hook.go # Hook插件接口
|
||||
│ │ └── middleware.go # Middleware插件接口
|
||||
│ └── registry/ # 插件注册中心
|
||||
│ ├── channel_registry.go # Channel注册器(线程安全)
|
||||
│ ├── hook_registry.go # Hook注册器(优先级排序)
|
||||
│ └── middleware_registry.go # Middleware注册器
|
||||
│
|
||||
├── plugins/ # 🔵 Tier 1: 编译时插件(已实施)
|
||||
│ ├── channels/ # Channel插件
|
||||
│ │ ├── base_plugin.go # 基础插件包装器
|
||||
│ │ └── registry.go # 自动注册31个AI Provider
|
||||
│ └── hooks/ # Hook插件
|
||||
│ ├── web_search/ # 联网搜索Hook
|
||||
│ │ ├── web_search_hook.go
|
||||
│ │ └── init.go
|
||||
│ └── content_filter/ # 内容过滤Hook
|
||||
│ ├── content_filter_hook.go
|
||||
│ └── init.go
|
||||
│
|
||||
├── marketplace/ # 🟣 Tier 2: 运行时插件(待实施,Phase 2)
|
||||
│ ├── loader/ # go-plugin加载器
|
||||
│ │ ├── plugin_client.go # 插件客户端
|
||||
│ │ ├── plugin_server.go # 插件服务器
|
||||
│ │ └── lifecycle.go # 生命周期管理
|
||||
│ ├── manager/ # 插件管理器
|
||||
│ │ ├── installer.go # 安装/卸载
|
||||
│ │ ├── updater.go # 版本更新
|
||||
│ │ └── registry.go # 插件注册表
|
||||
│ ├── security/ # 安全模块
|
||||
│ │ ├── signature.go # Ed25519签名验证
|
||||
│ │ ├── checksum.go # SHA256校验
|
||||
│ │ └── sandbox.go # 沙箱配置
|
||||
│ ├── store/ # 插件商店客户端
|
||||
│ │ ├── client.go # 商店API客户端
|
||||
│ │ ├── search.go # 搜索功能
|
||||
│ │ └── download.go # 下载管理
|
||||
│ └── proto/ # gRPC协议定义
|
||||
│ ├── hook.proto # Hook插件协议
|
||||
│ ├── channel.proto # Channel插件协议
|
||||
│ └── common.proto # 通用消息
|
||||
│
|
||||
├── plugins_external/ # 第三方插件安装目录
|
||||
│ ├── installed/ # 已安装插件
|
||||
│ │ ├── awesome-hook-v1.0.0/
|
||||
│ │ ├── custom-llm-v2.1.0/
|
||||
│ │ └── slack-notify-v1.5.0/
|
||||
│ ├── cache/ # 下载缓存
|
||||
│ └── temp/ # 临时文件
|
||||
│
|
||||
├── relay/ # Relay层
|
||||
│ ├── hooks/ # Hook执行链
|
||||
│ │ ├── chain.go # Hook链管理器
|
||||
│ │ ├── context.go # Hook上下文
|
||||
│ │ └── context_builder.go # 上下文构建器
|
||||
│ └── relay_adaptor.go # Channel适配器(优先从Registry获取)
|
||||
│
|
||||
├── config/ # 配置系统
|
||||
│ ├── plugins.yaml # 插件配置(Tier 1 + Tier 2)
|
||||
│ └── plugin_config.go # 配置加载器(支持环境变量)
|
||||
│
|
||||
└── (其他现有目录保持不变)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整架构图
|
||||
|
||||
### 系统架构总览
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "🌐 API层"
|
||||
Client[客户端请求]
|
||||
end
|
||||
|
||||
subgraph "🔐 中间件层"
|
||||
Auth[认证中间件]
|
||||
RateLimit[限流中间件]
|
||||
Cache[缓存中间件]
|
||||
end
|
||||
|
||||
subgraph "🎯 核心层 Core"
|
||||
Registry[插件注册中心]
|
||||
ChannelReg[Channel Registry]
|
||||
HookReg[Hook Registry]
|
||||
MidReg[Middleware Registry]
|
||||
|
||||
Registry --> ChannelReg
|
||||
Registry --> HookReg
|
||||
Registry --> MidReg
|
||||
end
|
||||
|
||||
subgraph "🔵 Tier 1: 编译时插件(已实施)"
|
||||
direction TB
|
||||
|
||||
Channels[31个 Channel Plugins]
|
||||
OpenAI[OpenAI]
|
||||
Claude[Claude]
|
||||
Gemini[Gemini]
|
||||
Others[其他28个...]
|
||||
|
||||
Channels --> OpenAI
|
||||
Channels --> Claude
|
||||
Channels --> Gemini
|
||||
Channels --> Others
|
||||
|
||||
Hooks[Hook Plugins]
|
||||
WebSearch[Web Search Hook]
|
||||
ContentFilter[Content Filter Hook]
|
||||
|
||||
Hooks --> WebSearch
|
||||
Hooks --> ContentFilter
|
||||
end
|
||||
|
||||
subgraph "🟣 Tier 2: 运行时插件(待实施)"
|
||||
direction TB
|
||||
|
||||
Marketplace[🏪 Plugin Marketplace]
|
||||
ExtHook[External Hooks<br/>Python/Go/Node.js]
|
||||
ExtChannel[External Channels<br/>小众AI提供商]
|
||||
ExtMid[External Middleware<br/>企业集成]
|
||||
ExtUI[UI Extensions<br/>自定义仪表板]
|
||||
|
||||
Marketplace --> ExtHook
|
||||
Marketplace --> ExtChannel
|
||||
Marketplace --> ExtMid
|
||||
Marketplace --> ExtUI
|
||||
end
|
||||
|
||||
subgraph "⚡ Relay执行流程"
|
||||
direction LR
|
||||
HookChain[Hook Chain]
|
||||
BeforeHook[OnBeforeRequest]
|
||||
ChannelAdaptor[Channel Adaptor]
|
||||
AfterHook[OnAfterResponse]
|
||||
|
||||
HookChain --> BeforeHook
|
||||
BeforeHook --> ChannelAdaptor
|
||||
ChannelAdaptor --> AfterHook
|
||||
end
|
||||
|
||||
subgraph "🌍 上游服务"
|
||||
Upstream[AI Provider APIs]
|
||||
end
|
||||
|
||||
Client --> Auth
|
||||
Auth --> RateLimit
|
||||
RateLimit --> Cache
|
||||
Cache --> Registry
|
||||
|
||||
Channels --> ChannelReg
|
||||
Hooks --> HookReg
|
||||
|
||||
Registry --> HookChain
|
||||
HookChain --> Upstream
|
||||
Upstream --> HookChain
|
||||
|
||||
Registry -.gRPC/RPC.-> ExtHook
|
||||
Registry -.gRPC/RPC.-> ExtChannel
|
||||
Registry -.gRPC/RPC.-> ExtMid
|
||||
|
||||
style Marketplace fill:#f9f,stroke:#333,stroke-width:4px
|
||||
style Registry fill:#bbf,stroke:#333,stroke-width:4px
|
||||
style Channels fill:#bfb,stroke:#333,stroke-width:2px
|
||||
style Hooks fill:#bfb,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
### 双层插件系统架构
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "🔵 Tier 1: 编译时插件"
|
||||
T1[性能: 100%<br/>语言: Go only<br/>部署: 编译到二进制]
|
||||
T1Chan[31 Channels]
|
||||
T1Hook[2 Hooks]
|
||||
|
||||
T1 --> T1Chan
|
||||
T1 --> T1Hook
|
||||
end
|
||||
|
||||
subgraph "🟣 Tier 2: 运行时插件"
|
||||
T2[性能: 90-95%<br/>语言: Go/Python/Node.js<br/>部署: 独立进程]
|
||||
T2Hook[External Hooks]
|
||||
T2Chan[External Channels]
|
||||
T2Mid[External Middleware]
|
||||
T2UI[UI Extensions]
|
||||
|
||||
T2 --> T2Hook
|
||||
T2 --> T2Chan
|
||||
T2 --> T2Mid
|
||||
T2 --> T2UI
|
||||
end
|
||||
|
||||
T1 -.进程内调用.-> Core[Core System]
|
||||
T2 -.gRPC/RPC.-> Core
|
||||
|
||||
style T1 fill:#bfb,stroke:#333,stroke-width:3px
|
||||
style T2 fill:#f9f,stroke:#333,stroke-width:3px
|
||||
style Core fill:#bbf,stroke:#333,stroke-width:3px
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心要点说明
|
||||
|
||||
### 1. 双层插件架构
|
||||
|
||||
| 层级 | 技术方案 | 性能 | 适用场景 | 开发语言 |
|
||||
|------|---------|------|---------|---------|
|
||||
| **Tier 1<br/>编译时插件** | 编译时链接 | 100%<br/>零损失 | • 核心Channel(OpenAI等)<br/>• 内置Hook<br/>• 高频调用路径 | Go only |
|
||||
| **Tier 2<br/>运行时插件** | go-plugin<br/>gRPC | 90-95%<br/>5-10%开销 | • 第三方扩展<br/>• 企业定制<br/>• 多语言集成 | Go/Python/<br/>Node.js/Rust |
|
||||
|
||||
### 2. 核心组件
|
||||
|
||||
#### Core层(核心引擎)
|
||||
- **interfaces/**: 定义ChannelPlugin、RelayHook、MiddlewarePlugin接口
|
||||
- **registry/**: 线程安全的插件注册中心,支持O(1)查找、优先级排序
|
||||
|
||||
#### Relay Hook链
|
||||
- **执行流程**: OnBeforeRequest → Channel.DoRequest → OnAfterResponse
|
||||
- **特性**: 优先级排序、短路机制、数据共享(HookContext.Data)
|
||||
- **应用场景**: 联网搜索、内容过滤、日志增强、缓存策略
|
||||
|
||||
### 3. Tier 1: 编译时插件(已实施 ✅)
|
||||
|
||||
**特点**:
|
||||
- 零性能损失,编译后与硬编码无差异
|
||||
- init()函数自动注册到Registry
|
||||
- YAML配置启用/禁用
|
||||
|
||||
**已实现**:
|
||||
- ✅ 31个Channel插件(OpenAI、Claude、Gemini等)
|
||||
- ✅ 2个Hook插件(web_search、content_filter)
|
||||
- ✅ Hook执行链
|
||||
- ✅ 配置系统(支持环境变量展开)
|
||||
|
||||
### 4. Tier 2: 运行时插件(待实施 🚧)
|
||||
|
||||
**基于**: [hashicorp/go-plugin](https://github.com/hashicorp/go-plugin)(Vault/Terraform使用)
|
||||
|
||||
**优势**:
|
||||
- ✅ 进程隔离(第三方代码崩溃不影响主程序)
|
||||
- ✅ 多语言支持(gRPC协议)
|
||||
- ✅ 热插拔(无需重启)
|
||||
- ✅ 安全验证(Ed25519签名 + SHA256校验 + TLS加密)
|
||||
- ✅ 独立分发(插件商店)
|
||||
|
||||
**适用场景**:
|
||||
- 第三方开发者扩展
|
||||
- 企业定制业务逻辑
|
||||
- Python ML模型集成
|
||||
- 第三方服务集成(Slack/钉钉/企业微信)
|
||||
- UI扩展
|
||||
|
||||
### 5. 安全机制
|
||||
|
||||
**Tier 1(编译时)**:
|
||||
- 内部代码审查
|
||||
- 编译期类型安全
|
||||
|
||||
**Tier 2(运行时)**:
|
||||
- Ed25519签名验证
|
||||
- SHA256校验和
|
||||
- gRPC TLS加密
|
||||
- 进程资源限制(内存/CPU)
|
||||
- 插件商店审核机制
|
||||
- 可信发布者白名单
|
||||
|
||||
### 6. 配置系统
|
||||
|
||||
**单一配置文件**: `config/plugins.yaml`
|
||||
|
||||
```yaml
|
||||
# Tier 1: 编译时插件
|
||||
plugins:
|
||||
hooks:
|
||||
- name: web_search
|
||||
enabled: false
|
||||
priority: 50
|
||||
config:
|
||||
api_key: ${WEB_SEARCH_API_KEY}
|
||||
|
||||
# Tier 2: 运行时插件(待实施)
|
||||
external_plugins:
|
||||
enabled: true
|
||||
hooks:
|
||||
- name: awesome_hook
|
||||
binary: awesome-hook-v1.0.0/awesome-hook
|
||||
checksum: sha256:abc123...
|
||||
|
||||
# 插件商店
|
||||
marketplace:
|
||||
enabled: true
|
||||
api_url: https://plugins.new-api.com
|
||||
```
|
||||
|
||||
### 7. 性能对比
|
||||
|
||||
| 场景 | Tier 1 | Tier 2 | RPC开销 |
|
||||
|------|--------|--------|--------|
|
||||
| 核心Channel | 100% | N/A | 0% |
|
||||
| 内置Hook | 100% | N/A | 0% |
|
||||
| 第三方Hook | N/A | 92-95% | 5-8% |
|
||||
| Python插件 | N/A | 88-92% | 8-12% |
|
||||
|
||||
### 8. 实施路线图
|
||||
|
||||
#### Phase 1: 编译时插件系统 ✅ 已完成
|
||||
- Core Registry + Hook Chain
|
||||
- 31个Channel插件 + 2个Hook示例
|
||||
- YAML配置系统
|
||||
|
||||
#### Phase 2: go-plugin基础
|
||||
- protobuf协议定义
|
||||
- PluginLoader实现
|
||||
- 签名验证系统
|
||||
- Python/Go SDK
|
||||
|
||||
#### Phase 3: 插件商店
|
||||
- 商店后端API
|
||||
- Web UI(搜索、安装、管理)
|
||||
- CLI工具
|
||||
- 多语言SDK
|
||||
|
||||
### 9. 扩展示例
|
||||
|
||||
**新增Tier 1插件(编译时)**:
|
||||
```go
|
||||
// 1. 实现接口
|
||||
type MyHook struct{}
|
||||
func (h *MyHook) OnBeforeRequest(ctx *HookContext) error { /*...*/ }
|
||||
|
||||
// 2. 注册
|
||||
func init() { registry.RegisterHook(&MyHook{}) }
|
||||
|
||||
// 3. 导入到main.go
|
||||
import _ "github.com/xxx/plugins/hooks/my_hook"
|
||||
```
|
||||
|
||||
**新增Tier 2插件(运行时)**:
|
||||
```python
|
||||
# external-plugin/my_hook.py
|
||||
from new_api_plugin_sdk import HookPlugin, serve
|
||||
|
||||
class MyHook(HookPlugin):
|
||||
def on_before_request(self, ctx):
|
||||
return {"modified_body": ctx.request_body}
|
||||
|
||||
serve(MyHook())
|
||||
```
|
||||
107
docs/translation-glossary.fr.md
Normal file
107
docs/translation-glossary.fr.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Glossaire Français (French Glossary)
|
||||
|
||||
Ce document fournit des traductions standards françaises pour la terminologie clé du projet afin d'assurer la cohérence et la précision des traductions.
|
||||
|
||||
This document provides standard French translations for key project terminology to ensure consistency and accuracy in translations.
|
||||
|
||||
## Concepts de Base (Core Concepts)
|
||||
|
||||
- L'utilisation d'émojis dans les traductions est autorisée s'ils sont présents dans l'original
|
||||
- L'utilisation de termes purement techniques est autorisée s'ils sont présents dans l'original
|
||||
- L'utilisation de termes techniques en anglais est autorisée s'ils sont largement utilisés dans l'environnement technique francophone (par exemple, API)
|
||||
|
||||
| Chinois | Français | Anglais | Description |
|
||||
|---------|----------|---------|-------------|
|
||||
| 倍率 | Ratio | Ratio/Multiplier | Multiplicateur utilisé pour le calcul des prix. **Important :** Dans le contexte des calculs de prix, toujours utiliser "Ratio" plutôt que "Multiplicateur" pour assurer la cohérence terminologique |
|
||||
| 令牌 | Jeton | Token | Identifiants d'accès API ou unités de texte traitées par les modèles |
|
||||
| 渠道 | Canal | Channel | Canal d'accès aux fournisseurs d'API |
|
||||
| 分组 | Groupe | Group | Classification des utilisateurs ou des jetons |
|
||||
| 额度 | Quota | Quota | Quota de services disponible pour l'utilisateur |
|
||||
|
||||
## Modèles (Model Related)
|
||||
|
||||
| Chinois | Français | Anglais | Description |
|
||||
|---------|----------|---------|-------------|
|
||||
| 提示 | Invite | Prompt | Contenu d'entrée du modèle |
|
||||
| 补全 | Complétion | Completion | Contenu de sortie du modèle. **Important :** Ne pas utiliser "Achèvement" ou "Finalisation" - uniquement "Complétion" pour correspondre à la terminologie technique |
|
||||
| 输入 | Entrée | Input/Prompt | Contenu envoyé au modèle |
|
||||
| 输出 | Sortie | Output/Completion | Contenu retourné par le modèle |
|
||||
| 模型倍率 | Ratio du modèle | Model Ratio | Ratio de tarification pour différents modèles |
|
||||
| 补全倍率 | Ratio de complétion | Completion Ratio | Ratio de tarification supplémentaire pour la sortie |
|
||||
| 固定价格 | Prix fixe | Price per call | Prix par appel |
|
||||
| 按量计费 | Paiement à l'utilisation | Pay-as-you-go | Tarification basée sur l'utilisation |
|
||||
| 按次计费 | Paiement par appel | Pay-per-view | Prix fixe par appel |
|
||||
|
||||
## Gestion des Utilisateurs (User Management)
|
||||
|
||||
| Chinois | Français | Anglais | Description |
|
||||
|---------|----------|---------|-------------|
|
||||
| 超级管理员 | Super-administrateur | Root User | Administrateur avec les privilèges les plus élevés |
|
||||
| 管理员 | Administrateur | Admin User | Administrateur système |
|
||||
| 普通用户 | Utilisateur normal | Normal User | Utilisateur avec privilèges standards |
|
||||
|
||||
## Recharge et Échange (Recharge & Redemption)
|
||||
|
||||
| Chinois | Français | Anglais | Description |
|
||||
|---------|----------|---------|-------------|
|
||||
| 充值 | Recharge | Top Up | Ajout de quota au compte |
|
||||
| 兑换码 | Code d'échange | Redemption Code | Code qui peut être échangé contre du quota |
|
||||
|
||||
## Gestion des Canaux (Channel Management)
|
||||
|
||||
| Chinois | Français | Anglais | Description |
|
||||
|---------|----------|---------|-------------|
|
||||
| 渠道 | Canal | Channel | Canal du fournisseur d'API |
|
||||
| API密钥 | Clé API | API Key | Clé d'accès API. **Important :** Utiliser "Clé API" au lieu de "Jeton API" pour plus de précision et conformément à la terminologie technique francophone établie. Le terme "Clé" reflète mieux la fonctionnalité d'accès aux ressources, tandis que "Jeton" est plus souvent associé aux unités de texte dans le contexte du traitement des modèles linguistiques. |
|
||||
| 优先级 | Priorité | Priority | Priorité de sélection du canal |
|
||||
| 权重 | Poids | Weight | Poids d'équilibrage de charge |
|
||||
| 代理 | Proxy | Proxy | Adresse du serveur proxy |
|
||||
| 模型重定向 | Redirection de modèle | Model Mapping | Remplacement du nom du modèle dans le corps de la requête |
|
||||
| 供应商 | Fournisseur | Provider/Vendor | Fournisseur de services ou d'API |
|
||||
|
||||
## Sécurité (Security Related)
|
||||
|
||||
| Chinois | Français | Anglais | Description |
|
||||
|---------|----------|---------|-------------|
|
||||
| 两步验证 | Authentification à deux facteurs | Two-Factor Authentication | Méthode de vérification de sécurité supplémentaire pour les comptes |
|
||||
| 2FA | 2FA | Two-Factor Authentication | Abréviation de l'authentification à deux facteurs |
|
||||
|
||||
## Recommandations de Traduction (Translation Guidelines)
|
||||
|
||||
### Variantes Contextuelles de Traduction
|
||||
|
||||
**Invite/Entrée (Prompt/Input)**
|
||||
|
||||
- **Invite** : Lors de l'interaction avec les LLM, dans l'interface utilisateur, lors de la description de l'interaction avec le modèle
|
||||
- **Entrée** : Dans la tarification, la documentation technique, la description du processus de traitement des données
|
||||
- **Règle** : S'il s'agit de l'expérience utilisateur et de l'interaction avec l'IA → "Invite", s'il s'agit du processus technique ou des calculs → "Entrée"
|
||||
|
||||
**Jeton (Token)**
|
||||
|
||||
- Jeton d'accès API (API Token)
|
||||
- Unité de texte traitée par le modèle (Text Token)
|
||||
- Jeton d'accès système (Access Token)
|
||||
|
||||
**Quota (Quota)**
|
||||
|
||||
- Quota de services disponible pour l'utilisateur
|
||||
- Parfois traduit comme "Crédit"
|
||||
|
||||
### Particularités de la Langue Française
|
||||
|
||||
- **Formes plurielles** : Nécessite une implémentation correcte des formes plurielles (_one, _other)
|
||||
- **Accords grammaticaux** : Attention aux accords grammaticaux dans les termes techniques
|
||||
- **Genre grammatical** : Accord du genre des termes techniques (par exemple, "modèle" - masculin, "canal" - masculin)
|
||||
|
||||
### Termes Standardisés
|
||||
|
||||
- **Complétion (Completion)** : Contenu de sortie du modèle
|
||||
- **Ratio (Ratio)** : Multiplicateur pour le calcul des prix
|
||||
- **Code d'échange (Redemption Code)** : Utilisé au lieu de "Code d'échange" pour plus de précision
|
||||
- **Fournisseur (Provider/Vendor)** : Organisation ou service fournissant des API ou des modèles d'IA
|
||||
|
||||
---
|
||||
|
||||
**Note pour les contributeurs :** Si vous trouvez des incohérences dans les traductions de terminologie ou si vous avez de meilleures suggestions de traduction pour le français, n'hésitez pas à créer une Issue ou une Pull Request.
|
||||
|
||||
**Contribution Note for French:** If you find any inconsistencies in terminology translations or have better translation suggestions for French, please feel free to submit an Issue or Pull Request.
|
||||
@@ -54,6 +54,20 @@ This document provides standard translation references for key terminology in th
|
||||
| 代理 | Proxy | 代理服务器地址 | Proxy server address |
|
||||
| 模型重定向 | Model Mapping | 请求体中模型名称替换 | Model name replacement in request body |
|
||||
|
||||
## 安全相关 (Security Related)
|
||||
|
||||
| 中文 | English | 说明 | Description |
|
||||
|------|---------|------|-------------|
|
||||
| 两步验证 | Two-Factor Authentication | 为账户提供额外安全保护的验证方式 | Additional security verification method for accounts |
|
||||
| 2FA | Two-Factor Authentication | 两步验证的缩写 | Abbreviation for Two-Factor Authentication |
|
||||
|
||||
## 计费相关 (Billing Related)
|
||||
|
||||
| 中文 | English | 说明 | Description |
|
||||
|------|---------|------|-------------|
|
||||
| 倍率 | Ratio | 价格计算的乘数因子 | Multiplier factor used for price calculation |
|
||||
| 倍率 | Multiplier | 价格计算的乘数因子(同义词) | Multiplier factor used for price calculation (synonym) |
|
||||
|
||||
## 翻译注意事项 (Translation Guidelines)
|
||||
|
||||
- **提示 (Prompt)** = 模型输入内容 / Model input content
|
||||
|
||||
107
docs/translation-glossary.ru.md
Normal file
107
docs/translation-glossary.ru.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Русский глоссарий (Russian Glossary)
|
||||
|
||||
Данный раздел предоставляет стандартные переводы ключевой терминологии проекта на русский язык для обеспечения согласованности и точности переводов.
|
||||
|
||||
This section provides standard Russian translations for key project terminology to ensure consistency and accuracy in translations.
|
||||
|
||||
## Основные концепции (Core Concepts)
|
||||
|
||||
- Допускается использовать символы Emoji в переводе, если они были в оригинале.
|
||||
- Допускается использование сугубо технических терминов, если они были в оригинале.
|
||||
- Допускается использование технических терминов на английском языке, если они широко используются в русскоязычной технической среде (например, API).
|
||||
|
||||
| Китайский | Русский | Английский | Описание |
|
||||
|-----------|--------|-----------|----------|
|
||||
| 倍率 | Коэффициент | Ratio/Multiplier | Множитель для расчета цены. **Важно:** В контексте расчетов цен всегда использовать "Коэффициент", а не "Множитель" для обеспечения консистентности терминологии |
|
||||
| 令牌 | Токен | Token | Учетные данные API или текстовые единицы |
|
||||
| 渠道 | Канал | Channel | Канал доступа к поставщику API |
|
||||
| 分组 | Группа | Group | Классификация пользователей или токенов |
|
||||
| 额度 | Квота | Quota | Доступная квота услуг для пользователя |
|
||||
|
||||
## Модели (Model Related)
|
||||
|
||||
| Китайский | Русский | Английский | Описание |
|
||||
|-----------|--------|-----------|----------|
|
||||
| 提示 | Промпт/Ввод | Prompt | Содержимое ввода в модель |
|
||||
| 补全 | Вывод | Completion | Содержимое вывода модели. **Важно:** Не использовать "Дополнение" или "Завершение" - только "Вывод" для соответствия технической терминологии |
|
||||
| 输入 | Ввод | Input/Prompt | Содержимое, отправляемое в модель |
|
||||
| 输出 | Вывод | Output/Completion | Содержимое, возвращаемое моделью |
|
||||
| 模型倍率 | Коэффициент модели | Model Ratio | Коэффициент тарификации для разных моделей |
|
||||
| 补全倍率 | Коэффициент вывода | Completion Ratio | Дополнительный коэффициент тарификации для вывода |
|
||||
| 固定价格 | Цена за запрос | Price per call | Цена за один вызов |
|
||||
| 按量计费 | Оплата по объему | Pay-as-you-go | Тарификация на основе использования |
|
||||
| 按次计费 | Оплата за запрос | Pay-per-view | Фиксированная цена за вызов |
|
||||
|
||||
## Управление пользователями (User Management)
|
||||
|
||||
| Китайский | Русский | Английский | Описание |
|
||||
|-----------|--------|-----------|----------|
|
||||
| 超级管理员 | Суперадминистратор | Root User | Администратор с наивысшими привилегиями |
|
||||
| 管理员 | Администратор | Admin User | Системный администратор |
|
||||
| 普通用户 | Обычный пользователь | Normal User | Пользователь со стандартными привилегиями |
|
||||
|
||||
## Пополнение и обмен (Recharge & Redemption)
|
||||
|
||||
| Китайский | Русский | Английский | Описание |
|
||||
|-----------|--------|-----------|----------|
|
||||
| 充值 | Пополнение | Top Up | Добавление квоты на аккаунт |
|
||||
| 兑换码 | Код купона | Redemption Code | Код, который можно обменять на квоту |
|
||||
|
||||
## Управление каналами (Channel Management)
|
||||
|
||||
| Китайский | Русский | Английский | Описание |
|
||||
|-----------|--------|-----------|----------|
|
||||
| 渠道 | Канал | Channel | Канал поставщика API |
|
||||
| API密钥 | API ключ | API Key | Ключ доступа к API. **Важно:** Использовать "API ключ" вместо "API токен" для большей точности и соответствия общепринятой русскоязычной технической терминологии. Термин "ключ" более точно отражает функционал доступа к ресурсам, в то время как "токен" чаще ассоциируется с текстовыми единицами в контексте обработки языковых моделей. |
|
||||
| 优先级 | Приоритет | Priority | Приоритет выбора канала |
|
||||
| 权重 | Вес | Weight | Вес балансировки нагрузки |
|
||||
| 代理 | Прокси | Proxy | Адрес прокси-сервера |
|
||||
| 模型重定向 | Перенаправление модели | Model Mapping | Замена имени модели в теле запроса |
|
||||
| 供应商 | Поставщик | Provider/Vendor | Поставщик услуг или API |
|
||||
|
||||
## Безопасность (Security Related)
|
||||
|
||||
| Китайский | Русский | Английский | Описание |
|
||||
|-----------|--------|-----------|----------|
|
||||
| 两步验证 | Двухфакторная аутентификация | Two-Factor Authentication | Дополнительный метод проверки безопасности для аккаунтов |
|
||||
| 2FA | 2FA | Two-Factor Authentication | Аббревиатура двухфакторной аутентификации |
|
||||
|
||||
## Рекомендации по переводу (Translation Guidelines)
|
||||
|
||||
### Контекстуальные варианты перевода
|
||||
|
||||
**Промпт/Ввод (Prompt/Input)**
|
||||
|
||||
- **Промпт**: При общении с LLM, в пользовательском интерфейсе, при описании взаимодействия с моделью
|
||||
- **Ввод**: При тарификации, технической документации, описании процесса обработки данных
|
||||
- **Правило**: Если речь о пользовательском опыте и взаимодействии с AI → "Промпт", если о техническом процессе или расчетах → "Ввод"
|
||||
|
||||
**Token**
|
||||
|
||||
- API токен доступа (API Token)
|
||||
- Текстовая единица, обрабатываемая моделью (Text Token)
|
||||
- Токен доступа к системе (Access Token)
|
||||
|
||||
**Квота (Quota)**
|
||||
|
||||
- Доступная квота услуг пользователя
|
||||
- Иногда переводится как "Кредит"
|
||||
|
||||
### Особенности русского языка
|
||||
|
||||
- **Множественные формы**: Требуется правильная реализация множественных форм (_one,_few, _many,_other)
|
||||
- **Падежные окончания**: Внимательное отношение к падежным окончаниям в технических терминах
|
||||
- **Грамматический род**: Согласование рода технических терминов (например, "модель" - женский род, "канал" - мужской род)
|
||||
|
||||
### Стандартизированные термины
|
||||
|
||||
- **Вывод (Completion)**: Содержимое вывода модели
|
||||
- **Коэффициент (Ratio)**: Множитель для расчета цены
|
||||
- **Код купона (Redemption Code)**: Используется вместо "Код обмена" для большей точности
|
||||
- **Поставщик (Provider/Vendor)**: Организация или сервис, предоставляющий API или AI-модели
|
||||
|
||||
---
|
||||
|
||||
**Примечание для участников:** При обнаружении несогласованности в переводах терминологии или наличии лучших предложений по переводу, не стесняйтесь создавать Issue или Pull Request.
|
||||
|
||||
**Contribution Note for Russian:** If you find any inconsistencies in terminology translations or have better translation suggestions for Russian, please feel free to submit an Issue or Pull Request.
|
||||
@@ -1,7 +1,7 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"one-api/types"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -3,10 +3,11 @@ package dto
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"one-api/common"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dto
|
||||
|
||||
import "one-api/types"
|
||||
import "github.com/QuantumNous/new-api/types"
|
||||
|
||||
type OpenAIError struct {
|
||||
Message string `json:"message"`
|
||||
|
||||
@@ -2,11 +2,12 @@ package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"one-api/common"
|
||||
"one-api/logger"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ package dto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"one-api/common"
|
||||
"one-api/types"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@ package dto
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"one-api/common"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ package dto
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"one-api/types"
|
||||
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dto
|
||||
|
||||
import "one-api/constant"
|
||||
import "github.com/QuantumNous/new-api/constant"
|
||||
|
||||
// 这里不好动就不动了,本来想独立出来的(
|
||||
type OpenAIModels struct {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dto
|
||||
|
||||
import "one-api/types"
|
||||
import "github.com/QuantumNous/new-api/types"
|
||||
|
||||
const (
|
||||
RealtimeEventTypeError = "error"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/types"
|
||||
)
|
||||
|
||||
type Request interface {
|
||||
|
||||
@@ -2,9 +2,10 @@ package dto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type RerankRequest struct {
|
||||
|
||||
3
go.mod
3
go.mod
@@ -1,4 +1,4 @@
|
||||
module one-api
|
||||
module github.com/QuantumNous/new-api
|
||||
|
||||
// +heroku goVersion go1.18
|
||||
go 1.25.1
|
||||
@@ -40,6 +40,7 @@ require (
|
||||
golang.org/x/image v0.23.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/sync v0.17.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.4.3
|
||||
gorm.io/driver/postgres v1.5.2
|
||||
gorm.io/gorm v1.25.2
|
||||
|
||||
2
go.sum
2
go.sum
@@ -96,8 +96,6 @@ github.com/go-webauthn/x v0.1.25/go.mod h1:ieblaPY1/BVCV0oQTsA/VAo08/TWayQuJuo5Q
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
|
||||
@@ -6,13 +6,14 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"one-api/common"
|
||||
"one-api/setting/operation_setting"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/setting/operation_setting"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
55
main.go
55
main.go
@@ -6,20 +6,28 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/controller"
|
||||
"one-api/logger"
|
||||
"one-api/middleware"
|
||||
"one-api/model"
|
||||
"one-api/router"
|
||||
"one-api/service"
|
||||
"one-api/setting/ratio_setting"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/controller"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/QuantumNous/new-api/middleware"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/router"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
|
||||
// Plugin System
|
||||
coreregistry "github.com/QuantumNous/new-api/core/registry"
|
||||
_ "github.com/QuantumNous/new-api/plugins/channels" // 自动注册channel插件
|
||||
_ "github.com/QuantumNous/new-api/plugins/hooks/web_search" // 自动注册web_search hook
|
||||
_ "github.com/QuantumNous/new-api/plugins/hooks/content_filter" // 自动注册content_filter hook
|
||||
relayhooks "github.com/QuantumNous/new-api/relay/hooks"
|
||||
|
||||
"github.com/bytedance/gopkg/util/gopool"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
@@ -228,5 +236,34 @@ func InitResources() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize Plugin System
|
||||
InitPluginSystem()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitPluginSystem 初始化插件系统
|
||||
func InitPluginSystem() {
|
||||
common.SysLog("Initializing plugin system...")
|
||||
|
||||
// 1. 加载插件配置
|
||||
// config.LoadPluginConfig() 会在各个插件的init()中自动调用
|
||||
|
||||
// 2. 注册Channel插件
|
||||
// 注意:这会在 plugins/channels/registry.go 的 init() 中自动完成
|
||||
// 但为了确保加载,我们显式导入
|
||||
common.SysLog("Registering channel plugins...")
|
||||
|
||||
// 3. 初始化Hook链
|
||||
common.SysLog("Initializing hook chain...")
|
||||
_ = relayhooks.GetGlobalChain()
|
||||
|
||||
hookCount := coreregistry.HookCount()
|
||||
enabledHookCount := coreregistry.EnabledHookCount()
|
||||
common.SysLog(fmt.Sprintf("Plugin system initialized: %d hooks registered (%d enabled)",
|
||||
hookCount, enabledHookCount))
|
||||
|
||||
channelCount := len(coreregistry.ListChannels())
|
||||
common.SysLog(fmt.Sprintf("Registered %d channel plugins", channelCount))
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@ package middleware
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/model"
|
||||
"one-api/setting"
|
||||
"one-api/setting/ratio_setting"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -4,19 +4,20 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/model"
|
||||
relayconstant "one-api/relay/constant"
|
||||
"one-api/service"
|
||||
"one-api/setting"
|
||||
"one-api/setting/ratio_setting"
|
||||
"one-api/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/model"
|
||||
relayconstant "github.com/QuantumNous/new-api/relay/constant"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
"github.com/QuantumNous/new-api/setting/ratio_setting"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ package middleware
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"github.com/andybalholm/brotli"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/andybalholm/brotli"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func DecompressRequestMiddleware() gin.HandlerFunc {
|
||||
|
||||
@@ -3,12 +3,13 @@ package middleware
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
relayconstant "one-api/relay/constant"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
relayconstant "github.com/QuantumNous/new-api/relay/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func JimengRequestConvert() func(c *gin.Context) {
|
||||
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -2,8 +2,9 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/common"
|
||||
)
|
||||
|
||||
func SetUpLogger(server *gin.Engine) {
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"one-api/common/limiter"
|
||||
"one-api/constant"
|
||||
"one-api/setting"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/common/limiter"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/setting"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
@@ -3,10 +3,11 @@ package middleware
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"time"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var timeFormat = "2006-01-02T15:04:05.000Z"
|
||||
|
||||
@@ -2,10 +2,11 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"one-api/common"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RelayPanicRecover() gin.HandlerFunc {
|
||||
|
||||
@@ -2,8 +2,9 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/common"
|
||||
)
|
||||
|
||||
func RequestId() func(c *gin.Context) {
|
||||
|
||||
@@ -2,11 +2,12 @@ package middleware
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"one-api/common"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type turnstileCheckResponse struct {
|
||||
|
||||
@@ -2,9 +2,10 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
"one-api/common"
|
||||
"one-api/logger"
|
||||
)
|
||||
|
||||
func abortWithOpenAiMessage(c *gin.Context, statusCode int, message string, code ...string) {
|
||||
|
||||
@@ -3,10 +3,11 @@ package model
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"one-api/common"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
@@ -6,13 +6,14 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"one-api/common"
|
||||
"one-api/constant"
|
||||
"one-api/dto"
|
||||
"one-api/types"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/constant"
|
||||
"github.com/QuantumNous/new-api/dto"
|
||||
"github.com/QuantumNous/new-api/types"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user