├── .gitattributes ├── .github └── workflows │ ├── Test.yml │ ├── build.yml │ └── build_docker.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── VERSION ├── api └── router.go ├── build.sh ├── conversion └── requests │ └── duckgo │ └── convert.go ├── docker-compose.yml ├── env.template ├── go.mod ├── go.sum ├── httpclient ├── Iaurorahttpclient.go ├── bogdanfinn │ ├── tls_client.go │ └── tls_client_test.go └── resty │ └── resty_client.go ├── initialize ├── handlers.go ├── proxy.go └── router.go ├── internal ├── duckgo │ └── request.go └── proxys │ └── proxys.go ├── main.go ├── middlewares ├── auth.go └── cors.go ├── release.bat ├── render.yaml ├── typings ├── duckgo │ ├── request.go │ └── response.go ├── official │ ├── request.go │ └── response.go └── typings.go ├── util ├── util.go └── utils_test.go ├── vercel.json └── web ├── avatar.png ├── icon.png └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-language=Go 2 | -------------------------------------------------------------------------------- /.github/workflows/Test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ main, test_au ] 6 | pull_request: 7 | branches: [ main, test_au ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Go 15 | uses: actions/setup-go@v2 16 | with: 17 | go-version: '1.22' 18 | - name: Build 19 | run: go build -v ./... 20 | - name: Test 21 | run: go test -v ./... 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - 'README.md' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | name: Build 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Set up Go 19 | uses: actions/setup-go@v4 20 | with: 21 | go-version: 'stable' 22 | check-latest: true 23 | 24 | - name: Build binary 25 | run: | 26 | mkdir -p artifact 27 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o duck2api -a -ldflags '-s -w -extldflags "-static"' . && rm -f artifact/duck2api && cp duck2api artifact/duck2api && cd artifact && tar -czvf ../duck2api-linux-amd64.tar.gz * && cd .. 28 | GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o duck2api -a -ldflags '-s -w -extldflags "-static"' . && rm -f artifact/duck2api && cp duck2api artifact/duck2api && cd artifact && tar -czvf ../duck2api-windows-amd64.tar.gz * && cd .. 29 | GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o duck2api -a -ldflags '-s -w -extldflags "-static"' . && rm -f artifact/duck2api && cp duck2api artifact/duck2api && cd artifact && tar -czvf ../duck2api-darwin-amd64.tar.gz * && cd .. 30 | GOOS=freebsd GOARCH=amd64 CGO_ENABLED=0 go build -o duck2api -a -ldflags '-s -w -extldflags "-static"' . && rm -f artifact/duck2api && cp duck2api artifact/duck2api && cd artifact && tar -czvf ../duck2api-freebsd-amd64.tar.gz * && cd .. 31 | 32 | - name: Upload artifact 33 | uses: actions/upload-artifact@main 34 | with: 35 | name: duck2api-pre-built.zip 36 | path: | 37 | duck2api-linux-amd64.tar.gz 38 | duck2api-windows-amd64.tar.gz 39 | duck2api-darwin-amd64.tar.gz 40 | duck2api-freebsd-amd64.tar.gz 41 | 42 | - name: Create release 43 | uses: softprops/action-gh-release@v1 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GHCR_PAT }} 46 | with: 47 | tag_name: v2.1.5 48 | files: | 49 | duck2api-linux-amd64.tar.gz 50 | duck2api-windows-amd64.tar.gz 51 | duck2api-darwin-amd64.tar.gz 52 | duck2api-freebsd-amd64.tar.gz 53 | 54 | - name: Delete workflow runs 55 | uses: Mattraks/delete-workflow-runs@v2 56 | with: 57 | token: ${{ github.token }} 58 | repository: ${{ github.repository }} 59 | retain_days: 1 60 | keep_minimum_runs: 8 61 | -------------------------------------------------------------------------------- /.github/workflows/build_docker.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Image 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths-ignore: 8 | - 'README.md' 9 | workflow_dispatch: 10 | 11 | env: 12 | GHCR_REPO: ghcr.io/aurora-develop/duck2api 13 | 14 | jobs: 15 | main: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Set up QEMU 22 | uses: docker/setup-qemu-action@v1 23 | 24 | - name: Set up Docker Buildx 25 | uses: docker/setup-buildx-action@v1 26 | 27 | - name: Login to GitHub Container Registry 28 | uses: docker/login-action@v1 29 | with: 30 | registry: ghcr.io 31 | username: ${{ github.repository_owner }} 32 | password: ${{ secrets.GHCR_PAT }} 33 | 34 | - name: Build and push to GHCR 35 | uses: docker/build-push-action@v2 36 | with: 37 | context: . 38 | platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x 39 | file: Dockerfile 40 | push: true 41 | tags: | 42 | ${{ env.GHCR_REPO }}:latest 43 | ${{ env.GHCR_REPO }}:${{ github.sha }} 44 | cache-from: type=gha 45 | cache-to: type=gha,mode=max 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tools/authenticator/100-ACCOUNTS_COMPILED.txt 2 | tools/authenticator/accounts.txt 3 | tools/authenticator/proxies.txt 4 | tools/authenticator/authenticated_accounts.txt 5 | tools/authenticator/access_tokens.txt 6 | *.txt 7 | aurora 8 | chatgpttoapi 9 | tools/authenticator/.proxies.txt.swp 10 | .env 11 | *.har 12 | .idea/ 13 | /logs/ 14 | /target/ 15 | /bin/ 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 使用 Go 1.21 官方镜像作为构建环境 2 | FROM golang:1.21 AS builder 3 | 4 | # 禁用 CGO 5 | ENV CGO_ENABLED=0 6 | 7 | # 设置工作目录 8 | WORKDIR /app 9 | 10 | # 复制 go.mod 和 go.sum 并下载依赖 11 | COPY go.mod go.sum ./ 12 | RUN go mod download 13 | 14 | # 复制源代码并构建应用 15 | COPY . . 16 | RUN go build -ldflags "-s -w" -o /app/duck2api . 17 | 18 | # 使用 Alpine Linux 作为最终镜像 19 | FROM alpine:latest 20 | 21 | # 设置工作目录 22 | WORKDIR /app 23 | 24 | # 从构建阶段复制编译好的应用和资源 25 | COPY --from=builder /app/duck2api /app/duck2api 26 | 27 | # 暴露端口 28 | EXPOSE 8080 29 | 30 | CMD ["/app/duck2api"] 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 aurora-develop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: aurora -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # duck2api 2 | 3 | # Web端 4 | 5 | 访问http://你的服务器ip:8080/web 6 | 7 | ![web使用](https://fastly.jsdelivr.net/gh/xiaozhou26/tuph@main/images/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202024-04-07%20111706.png) 8 | 9 | ## Deploy 10 | 11 | 12 | ### 编译部署 13 | 14 | ```bash 15 | git clone https://github.com/aurora-develop/duck2api 16 | cd duck2api 17 | go build -o duck2api 18 | chmod +x ./duck2api 19 | ./duck2api 20 | ``` 21 | 22 | ### Docker部署 23 | ## Docker部署 24 | 您需要安装Docker和Docker Compose。 25 | 26 | ```bash 27 | docker run -d \ 28 | --name duck2api \ 29 | -p 8080:8080 \ 30 | ghcr.io/aurora-develop/duck2api:latest 31 | ``` 32 | 33 | ## Docker Compose部署 34 | 创建一个新的目录,例如duck2api,并进入该目录: 35 | ```bash 36 | mkdir duck2api 37 | cd duck2api 38 | ``` 39 | 在此目录中下载库中的docker-compose.yml文件: 40 | 41 | ```bash 42 | docker-compose up -d 43 | ``` 44 | 45 | ## Usage 46 | 47 | ```bash 48 | curl --location 'http://你的服务器ip:8080/v1/chat/completions' \ 49 | --header 'Content-Type: application/json' \ 50 | --data '{ 51 | "model": "gpt-4o-mini", 52 | "messages": [{"role": "user", "content": "Say this is a test!"}], 53 | "stream": true 54 | }' 55 | ``` 56 | 57 | ## 支持的模型 58 | 59 | - ~~gpt-3.5-turbo~~ duckduckGO官方已移除3.5模型的支持 60 | - claude-3-haiku 61 | - llama-3.3-70b 62 | - mixtral-8x7b 63 | - gpt-4o-mini 64 | - o3-mini 65 | ## 高级设置 66 | 67 | 默认情况不需要设置,除非你有需求 68 | 69 | ### 环境变量 70 | ``` 71 | 72 | Authorization=your_authorization 用户认证 key。 73 | TLS_CERT=path_to_your_tls_cert 存储TLS(传输层安全协议)证书的路径。 74 | TLS_KEY=path_to_your_tls_key 存储TLS(传输层安全协议)证书的路径。 75 | PROXY_URL=your_proxy_url 添加代理池来。 76 | ``` 77 | 78 | ## 鸣谢 79 | 80 | 感谢各位大佬的pr支持,感谢。 81 | 82 | 83 | ## 参考项目 84 | 85 | 86 | https://github.com/xqdoo00o/ChatGPT-to-API 87 | 88 | ## License 89 | 90 | MIT License 91 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 2.0.0 2 | -------------------------------------------------------------------------------- /api/router.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "aurora/initialize" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | ) 8 | 9 | var router *gin.Engine 10 | 11 | func init() { 12 | // 初始化gin 13 | router = initialize.RegisterRouter() 14 | } 15 | 16 | func Listen(w http.ResponseWriter, r *http.Request) { 17 | router.ServeHTTP(w, r) 18 | } 19 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GOPROXY=https://goproxy.io 4 | 5 | go get 6 | 7 | export CGO_ENABLED=0 8 | PKG=aurora 9 | 10 | targets=( 11 | "windows/amd64" 12 | "linux/amd64" 13 | "darwin/amd64" 14 | "windows/386" 15 | "linux/386" 16 | "darwin/386" 17 | "linux/arm" 18 | "linux/arm64" 19 | "linux/s390x" 20 | ) 21 | 22 | upxPath=$(command -v upx) 23 | 24 | for target in "${targets[@]}"; do 25 | GOOS=${target%/*} 26 | GOARCH=${target#*/} 27 | outputDir="bin/${GOOS}_${GOARCH}" 28 | outputFile="${outputDir}/${PKG}" 29 | archiveName="${PKG}-${GOOS}-${GOARCH}.tar.gz" 30 | mkdir -p $(dirname ${outputFile}) 31 | GOOS=$GOOS GOARCH=$GOARCH go build -ldflags="-s -w -extldflags '-static'" -o ${outputFile} *.go 32 | if [ -n "$upxPath" ]; then 33 | $upxPath -9 ${outputFile} 34 | fi 35 | # Archive the binary 36 | if [ "$GOOS" = "windows" ]; then 37 | zip -j "${outputDir}/${PKG}-${GOOS}-${GOARCH}.zip" "${outputFile}" 38 | else 39 | tar -C "${outputDir}" -czf "${outputDir}/${archiveName}" "${PKG}" 40 | fi 41 | done 42 | -------------------------------------------------------------------------------- /conversion/requests/duckgo/convert.go: -------------------------------------------------------------------------------- 1 | package duckgo 2 | 3 | import ( 4 | duckgotypes "aurora/typings/duckgo" 5 | officialtypes "aurora/typings/official" 6 | "strings" 7 | ) 8 | 9 | func ConvertAPIRequest(api_request officialtypes.APIRequest) duckgotypes.ApiRequest { 10 | inputModel := api_request.Model 11 | duckgo_request := duckgotypes.NewApiRequest(inputModel) 12 | realModel := inputModel 13 | 14 | // 如果模型未进行映射,则直接使用输入模型,方便后续用户使用 duckduckgo 添加的新模型。 15 | modelLower := strings.ToLower(inputModel) 16 | switch { 17 | case strings.HasPrefix(modelLower, "gpt-3.5"): 18 | realModel = "gpt-4o-mini" 19 | case strings.HasPrefix(modelLower, "claude-3-haiku"): 20 | realModel = "claude-3-haiku-20240307" 21 | case strings.HasPrefix(modelLower, "llama-3.3-70b"): 22 | realModel = "meta-llama/Llama-3.3-70B-Instruct-Turbo" 23 | case strings.HasPrefix(modelLower, "mixtral-8x7b"): 24 | realModel = "mistralai/Mixtral-8x7B-Instruct-v0.1" 25 | } 26 | 27 | duckgo_request.Model = realModel 28 | content := buildContent(&api_request) 29 | duckgo_request.AddMessage("user", content) 30 | 31 | return duckgo_request 32 | } 33 | 34 | func buildContent(api_request *officialtypes.APIRequest) string { 35 | var content strings.Builder 36 | for _, apiMessage := range api_request.Messages { 37 | role := apiMessage.Role 38 | if role == "user" || role == "system" || role == "assistant" { 39 | if role == "system" { 40 | role = "user" 41 | } 42 | contentStr := "" 43 | // 判断 apiMessage.Content 是否为数组 44 | if arrayContent, ok := apiMessage.Content.([]interface{}); ok { 45 | // 如果是数组,遍历数组,查找第一个 type 为 "text" 的元素 46 | for _, element := range arrayContent { 47 | if elementMap, ok := element.(map[string]interface{}); ok { 48 | if elementMap["type"] == "text" { 49 | contentStr = elementMap["text"].(string) 50 | break 51 | } 52 | } 53 | } 54 | } else { 55 | contentStr, _ = apiMessage.Content.(string) 56 | } 57 | content.WriteString(role + ":" + contentStr + ";\r\n") 58 | } 59 | } 60 | return content.String() 61 | } 62 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | app: 5 | image: ghcr.io/aurora-develop/duck2api:latest 6 | container_name: duck2api 7 | restart: unless-stopped 8 | ports: 9 | - '8080:8080' 10 | -------------------------------------------------------------------------------- /env.template: -------------------------------------------------------------------------------- 1 | SERVER_HOST=0.0.0.0 2 | SERVER_PORT=8080 3 | FREE_ACCOUNTS=true 4 | FREE_ACCOUNTS_NUM=1024 5 | Authorization= 6 | TLS_CERT= 7 | TLS_KEY= 8 | PROXY_URL= 9 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module aurora 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/EDDYCJY/fake-useragent v0.2.0 7 | github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd 8 | github.com/bogdanfinn/fhttp v0.5.28 9 | github.com/bogdanfinn/tls-client v1.7.2 10 | github.com/gin-gonic/gin v1.10.0 11 | github.com/go-resty/resty/v2 v2.14.0 12 | github.com/joho/godotenv v1.5.1 13 | github.com/pkoukk/tiktoken-go v0.1.7 14 | ) 15 | 16 | require ( 17 | github.com/PuerkitoBio/goquery v1.9.2 // indirect 18 | github.com/andybalholm/brotli v1.1.0 // indirect 19 | github.com/andybalholm/cascadia v1.3.2 // indirect 20 | github.com/bogdanfinn/utls v1.6.1 // indirect 21 | github.com/bytedance/sonic v1.12.1 // indirect 22 | github.com/bytedance/sonic/loader v0.2.0 // indirect 23 | github.com/cloudflare/circl v1.3.9 // indirect 24 | github.com/cloudwego/base64x v0.1.4 // indirect 25 | github.com/cloudwego/iasm v0.2.0 // indirect 26 | github.com/dlclark/regexp2 v1.11.4 // indirect 27 | github.com/gabriel-vasile/mimetype v1.4.5 // indirect 28 | github.com/gin-contrib/sse v0.1.0 // indirect 29 | github.com/go-playground/locales v0.14.1 // indirect 30 | github.com/go-playground/universal-translator v0.18.1 // indirect 31 | github.com/go-playground/validator/v10 v10.22.0 // indirect 32 | github.com/goccy/go-json v0.10.3 // indirect 33 | github.com/google/uuid v1.6.0 // indirect 34 | github.com/json-iterator/go v1.1.12 // indirect 35 | github.com/klauspost/compress v1.17.0 // indirect 36 | github.com/klauspost/cpuid/v2 v2.2.8 // indirect 37 | github.com/kr/text v0.2.0 // indirect 38 | github.com/leodido/go-urn v1.4.0 // indirect 39 | github.com/mattn/go-isatty v0.0.20 // indirect 40 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 41 | github.com/modern-go/reflect2 v1.0.2 // indirect 42 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 43 | github.com/quic-go/quic-go v0.37.4 // indirect 44 | github.com/rogpeppe/go-internal v1.12.0 // indirect 45 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect 46 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 47 | github.com/ugorji/go/codec v1.2.12 // indirect 48 | golang.org/x/arch v0.9.0 // indirect 49 | golang.org/x/crypto v0.26.0 // indirect 50 | golang.org/x/net v0.28.0 // indirect 51 | golang.org/x/sys v0.24.0 // indirect 52 | golang.org/x/text v0.17.0 // indirect 53 | google.golang.org/protobuf v1.34.2 // indirect 54 | gopkg.in/yaml.v3 v3.0.1 // indirect 55 | ) 56 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/EDDYCJY/fake-useragent v0.2.0 h1:Jcnkk2bgXmDpX0z+ELlUErTkoLb/mxFBNd2YdcpvJBs= 2 | github.com/EDDYCJY/fake-useragent v0.2.0/go.mod h1:5wn3zzlDxhKW6NYknushqinPcAqZcAPHy8lLczCdJdc= 3 | github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= 4 | github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= 5 | github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd h1:oIpfrRhD7Jus41dotbK+SQjWSFRnf1cLZUYCZpF/o/4= 6 | github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd/go.mod h1:0yO7neMeJLvKk/B/fq5votDY8rByrOPDubpvU+6saKo= 7 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 8 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 9 | github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= 10 | github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= 11 | github.com/bogdanfinn/fhttp v0.5.28 h1:G6thT8s8v6z1IuvXMUsX9QKy3ZHseTQTzxuIhSiaaAw= 12 | github.com/bogdanfinn/fhttp v0.5.28/go.mod h1:oJiYPG3jQTKzk/VFmogH8jxjH5yiv2rrOH48Xso2lrE= 13 | github.com/bogdanfinn/tls-client v1.7.2 h1:vpL5qBYUfT9ueygEf1yLfymrXyUEZQatL25amfqGV8M= 14 | github.com/bogdanfinn/tls-client v1.7.2/go.mod h1:pOGa2euqTbEkGNqE5idx5jKKfs9ytlyn3fwEw8RSP+g= 15 | github.com/bogdanfinn/utls v1.6.1 h1:dKDYAcXEyFFJ3GaWaN89DEyjyRraD1qb4osdEK89ass= 16 | github.com/bogdanfinn/utls v1.6.1/go.mod h1:VXIbRZaiY/wHZc6Hu+DZ4O2CgTzjhjCg/Ou3V4r/39Y= 17 | github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24= 18 | github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= 19 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 20 | github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= 21 | github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 22 | github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= 23 | github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= 24 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 25 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 26 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 27 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 28 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 29 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 30 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 31 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 32 | github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= 33 | github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 34 | github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= 35 | github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= 36 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 37 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 38 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 39 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 40 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 41 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 42 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 43 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 44 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 45 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 46 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 47 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 48 | github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= 49 | github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 50 | github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU= 51 | github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg= 52 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 53 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 54 | github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= 55 | github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 56 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 57 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 58 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 59 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= 60 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 61 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 62 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 63 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 64 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 65 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 66 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 67 | github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= 68 | github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 69 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 70 | github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= 71 | github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 72 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 73 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 74 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 75 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 76 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 77 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 78 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 79 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 80 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 81 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 82 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 83 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 84 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 85 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 86 | github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= 87 | github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= 88 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 89 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 90 | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= 91 | github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 92 | github.com/pkoukk/tiktoken-go v0.1.7 h1:qOBHXX4PHtvIvmOtyg1EeKlwFRiMKAcoMp4Q+bLQDmw= 93 | github.com/pkoukk/tiktoken-go v0.1.7/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= 94 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 95 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 96 | github.com/quic-go/quic-go v0.37.4 h1:ke8B73yMCWGq9MfrCCAw0Uzdm7GaViC3i39dsIdDlH4= 97 | github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= 98 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 99 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 100 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 101 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 102 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 103 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 104 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 105 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 106 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 107 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 108 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 109 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 110 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 111 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 112 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc= 113 | github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng= 114 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 115 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 116 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 117 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 118 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 119 | golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= 120 | golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 121 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 122 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 123 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 124 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 125 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 126 | golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 127 | golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= 128 | golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= 129 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 130 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 131 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 132 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 133 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 134 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 135 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 136 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 137 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 138 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 139 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 140 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 141 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 142 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 143 | golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= 144 | golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= 145 | golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= 146 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 147 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 148 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 149 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 150 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 151 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 152 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 153 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 154 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 155 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 156 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 157 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 158 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 159 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 160 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 161 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 162 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 163 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 164 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 165 | golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= 166 | golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 167 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 168 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 169 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 170 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 171 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 172 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 173 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 174 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 175 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 176 | golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 177 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 178 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 179 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 180 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 181 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 182 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 183 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 184 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 185 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 186 | golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= 187 | golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 188 | golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 189 | golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 190 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 191 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 192 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 193 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 194 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 195 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= 196 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 197 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 198 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 199 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 200 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 201 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 202 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 203 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 204 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 205 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 206 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 207 | -------------------------------------------------------------------------------- /httpclient/Iaurorahttpclient.go: -------------------------------------------------------------------------------- 1 | package httpclient 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | ) 7 | 8 | type AuroraHttpClient interface { 9 | Request(method HttpMethod, url string, headers AuroraHeaders, cookies []*http.Cookie, body io.Reader) (*http.Response, error) 10 | SetProxy(url string) error 11 | } 12 | 13 | type HttpMethod string 14 | 15 | const ( 16 | GET HttpMethod = "GET" 17 | POST HttpMethod = "POST" 18 | PUT HttpMethod = "PUT" 19 | HEAD HttpMethod = "HEAD" 20 | DELETE HttpMethod = "DELETE" 21 | OPTIONS HttpMethod = "OPTIONS" 22 | ) 23 | 24 | type AuroraHeaders map[string]string 25 | 26 | func (a AuroraHeaders) Set(key, value string) { 27 | a[key] = value 28 | } 29 | -------------------------------------------------------------------------------- /httpclient/bogdanfinn/tls_client.go: -------------------------------------------------------------------------------- 1 | package bogdanfinn 2 | 3 | import ( 4 | "aurora/httpclient" 5 | "io" 6 | "net/http" 7 | 8 | fhttp "github.com/bogdanfinn/fhttp" 9 | tls_client "github.com/bogdanfinn/tls-client" 10 | "github.com/bogdanfinn/tls-client/profiles" 11 | ) 12 | 13 | type TlsClient struct { 14 | Client tls_client.HttpClient 15 | ReqBefore handler 16 | } 17 | 18 | type handler func(r *fhttp.Request) error 19 | 20 | func NewStdClient() *TlsClient { 21 | client, _ := tls_client.NewHttpClient(tls_client.NewNoopLogger(), []tls_client.HttpClientOption{ 22 | tls_client.WithCookieJar(tls_client.NewCookieJar()), 23 | tls_client.WithRandomTLSExtensionOrder(), 24 | tls_client.WithTimeoutSeconds(600), 25 | tls_client.WithClientProfile(profiles.Okhttp4Android13), 26 | }...) 27 | 28 | stdClient := &TlsClient{Client: client} 29 | return stdClient 30 | } 31 | 32 | func convertResponse(resp *fhttp.Response) *http.Response { 33 | response := &http.Response{ 34 | Status: resp.Status, 35 | StatusCode: resp.StatusCode, 36 | Proto: resp.Proto, 37 | ProtoMajor: resp.ProtoMajor, 38 | ProtoMinor: resp.ProtoMinor, 39 | Header: http.Header(resp.Header), 40 | Body: resp.Body, 41 | ContentLength: resp.ContentLength, 42 | TransferEncoding: resp.TransferEncoding, 43 | Close: resp.Close, 44 | Uncompressed: resp.Uncompressed, 45 | Trailer: http.Header(resp.Trailer), 46 | } 47 | return response 48 | } 49 | 50 | func (t *TlsClient) handleHeaders(req *fhttp.Request, headers httpclient.AuroraHeaders) { 51 | if headers == nil { 52 | return 53 | } 54 | for k, v := range headers { 55 | req.Header.Set(k, v) 56 | } 57 | } 58 | 59 | func (t *TlsClient) handleCookies(req *fhttp.Request, cookies []*http.Cookie) { 60 | if cookies == nil { 61 | return 62 | } 63 | for _, c := range cookies { 64 | req.AddCookie(&fhttp.Cookie{ 65 | Name: c.Name, 66 | Value: c.Value, 67 | Path: c.Path, 68 | Domain: c.Domain, 69 | Expires: c.Expires, 70 | RawExpires: c.RawExpires, 71 | MaxAge: c.MaxAge, 72 | Secure: c.Secure, 73 | HttpOnly: c.HttpOnly, 74 | SameSite: fhttp.SameSite(c.SameSite), 75 | Raw: c.Raw, 76 | Unparsed: c.Unparsed, 77 | }) 78 | } 79 | } 80 | 81 | func (t *TlsClient) Request(method httpclient.HttpMethod, url string, headers httpclient.AuroraHeaders, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 82 | req, err := fhttp.NewRequest(string(method), url, body) 83 | if err != nil { 84 | return nil, err 85 | } 86 | t.handleHeaders(req, headers) 87 | t.handleCookies(req, cookies) 88 | if t.ReqBefore != nil { 89 | if err := t.ReqBefore(req); err != nil { 90 | return nil, err 91 | } 92 | } 93 | do, err := t.Client.Do(req) 94 | if err != nil { 95 | return nil, err 96 | } 97 | return convertResponse(do), nil 98 | } 99 | 100 | func (t *TlsClient) SetProxy(url string) error { 101 | return t.Client.SetProxy(url) 102 | } 103 | -------------------------------------------------------------------------------- /httpclient/bogdanfinn/tls_client_test.go: -------------------------------------------------------------------------------- 1 | package bogdanfinn 2 | 3 | import ( 4 | "aurora/httpclient" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/joho/godotenv" 13 | ) 14 | 15 | var BaseURL string 16 | 17 | func init() { 18 | _ = godotenv.Load(".env") 19 | BaseURL = os.Getenv("BASE_URL") 20 | if BaseURL == "" { 21 | BaseURL = "https://chat.openai.com/backend-anon" 22 | } 23 | } 24 | func TestTlsClient_Request(t *testing.T) { 25 | client := NewStdClient() 26 | userAgent := "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" 27 | proxy := "http://127.0.0.1:7990" 28 | client.SetProxy(proxy) 29 | 30 | apiUrl := BaseURL + "/sentinel/chat-requirements" 31 | payload := strings.NewReader(`{"conversation_mode_kind":"primary_assistant"}`) 32 | header := make(httpclient.AuroraHeaders) 33 | header.Set("Content-Type", "application/json") 34 | header.Set("User-Agent", userAgent) 35 | header.Set("Accept", "*/*") 36 | header.Set("oai-language", "en-US") 37 | header.Set("origin", "https://chat.openai.com") 38 | header.Set("referer", "https://chat.openai.com/") 39 | header.Set("oai-device-id", "c83b24f0-5a9e-4c43-8915-3f67d4332609") 40 | response, err := client.Request(http.MethodPost, apiUrl, header, nil, payload) 41 | if err != nil { 42 | return 43 | } 44 | defer response.Body.Close() 45 | fmt.Println(response.StatusCode) 46 | if response.StatusCode != 200 { 47 | fmt.Println("Error: ", response.StatusCode) 48 | } 49 | } 50 | 51 | func TestChatGPTModel(t *testing.T) { 52 | client := NewStdClient() 53 | userAgent := "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" 54 | proxy := "http://127.0.0.1:7990" 55 | client.SetProxy(proxy) 56 | apiUrl := "https://chat.openai.com/backend-anon/models" 57 | 58 | header := make(httpclient.AuroraHeaders) 59 | header.Set("Content-Type", "application/json") 60 | header.Set("User-Agent", userAgent) 61 | header.Set("Accept", "*/*") 62 | header.Set("oai-language", "en-US") 63 | header.Set("origin", "https://chat.openai.com") 64 | header.Set("referer", "https://chat.openai.com/") 65 | header.Set("oai-device-id", "c83b24f0-5a9e-4c43-8915-3f67d4332609") 66 | response, err := client.Request(http.MethodGet, apiUrl, header, nil, nil) 67 | if err != nil { 68 | return 69 | } 70 | defer response.Body.Close() 71 | fmt.Println(response.StatusCode) 72 | if response.StatusCode != 200 { 73 | fmt.Println("Error: ", response.StatusCode) 74 | body, _ := io.ReadAll(response.Body) 75 | fmt.Println(string(body)) 76 | return 77 | } 78 | 79 | type EnginesData struct { 80 | Models []struct { 81 | Slug string `json:"slug"` 82 | MaxTokens int `json:"max_tokens"` 83 | Title string `json:"title"` 84 | Description string `json:"description"` 85 | Tags []string `json:"tags"` 86 | Capabilities struct { 87 | } `json:"capabilities,omitempty"` 88 | ProductFeatures struct { 89 | } `json:"product_features,omitempty"` 90 | } `json:"models"` 91 | Categories []struct { 92 | Category string `json:"category"` 93 | HumanCategoryName string `json:"human_category_name"` 94 | SubscriptionLevel string `json:"subscription_level"` 95 | DefaultModel string `json:"default_model"` 96 | CodeInterpreterModel string `json:"code_interpreter_model,omitempty"` 97 | PluginsModel string `json:"plugins_model"` 98 | } `json:"categories"` 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /httpclient/resty/resty_client.go: -------------------------------------------------------------------------------- 1 | package resty 2 | 3 | import ( 4 | "aurora/util" 5 | "crypto/tls" 6 | browser "github.com/EDDYCJY/fake-useragent" 7 | "github.com/go-resty/resty/v2" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | type RestyClient struct { 13 | Client *resty.Client 14 | } 15 | 16 | func NewStdClient() *RestyClient { 17 | client := &RestyClient{ 18 | Client: resty.NewWithClient(&http.Client{ 19 | Transport: &http.Transport{ 20 | // 禁用长连接 21 | DisableKeepAlives: true, 22 | // 配置TLS设置,跳过证书验证 23 | TLSClientConfig: &tls.Config{ 24 | InsecureSkipVerify: true, 25 | }, 26 | }, 27 | }), 28 | } 29 | client.Client.SetBaseURL("https://chat.openai.com") 30 | client.Client.SetRetryCount(3) 31 | client.Client.SetRetryWaitTime(5 * time.Second) 32 | client.Client.SetRetryMaxWaitTime(20 * time.Second) 33 | 34 | client.Client.SetTimeout(600 * time.Second) 35 | client.Client.SetHeader("user-agent", browser.Random()). 36 | SetHeader("accept", "*/*"). 37 | SetHeader("accept-language", "en-US,en;q=0.9"). 38 | SetHeader("cache-control", "no-cache"). 39 | SetHeader("content-type", "application/json"). 40 | SetHeader("oai-language", util.RandomLanguage()). 41 | SetHeader("pragma", "no-cache"). 42 | SetHeader("sec-ch-ua", `"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"`). 43 | SetHeader("sec-ch-ua-mobile", "?0"). 44 | SetHeader("sec-ch-ua-platform", "Windows"). 45 | SetHeader("sec-fetch-dest", "empty"). 46 | SetHeader("sec-fetch-mode", "cors"). 47 | SetHeader("sec-fetch-site", "same-origin") 48 | return client 49 | } 50 | 51 | //func (c *RestyClient) Request(method string, url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 52 | //} 53 | 54 | //func (c *RestyClient) Post(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 55 | //} 56 | // 57 | //func (c *RestyClient) Get(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 58 | //} 59 | // 60 | //func (c *RestyClient) Head(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 61 | //} 62 | // 63 | //func (c *RestyClient) Options(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 64 | //} 65 | // 66 | //func (c *RestyClient) Put(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 67 | //} 68 | // 69 | //func (c *RestyClient) Delete(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { 70 | //} 71 | // 72 | //func (c *RestyClient) SetProxy(url string) error {} 73 | -------------------------------------------------------------------------------- /initialize/handlers.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | duckgoConvert "aurora/conversion/requests/duckgo" 5 | "aurora/httpclient/bogdanfinn" 6 | "aurora/internal/duckgo" 7 | "aurora/internal/proxys" 8 | officialtypes "aurora/typings/official" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | type Handler struct { 14 | proxy *proxys.IProxy 15 | } 16 | 17 | func NewHandle(proxy *proxys.IProxy) *Handler { 18 | return &Handler{proxy: proxy} 19 | } 20 | 21 | func optionsHandler(c *gin.Context) { 22 | // Set headers for CORS 23 | c.Header("Access-Control-Allow-Origin", "*") 24 | c.Header("Access-Control-Allow-Methods", "POST") 25 | c.Header("Access-Control-Allow-Headers", "*") 26 | c.JSON(200, gin.H{ 27 | "message": "pong", 28 | }) 29 | } 30 | 31 | func (h *Handler) duckduckgo(c *gin.Context) { 32 | var original_request officialtypes.APIRequest 33 | err := c.BindJSON(&original_request) 34 | if err != nil { 35 | c.JSON(400, gin.H{"error": gin.H{ 36 | "message": "Request must be proper JSON", 37 | "type": "invalid_request_error", 38 | "param": nil, 39 | "code": err.Error(), 40 | }}) 41 | return 42 | } 43 | proxyUrl := h.proxy.GetProxyIP() 44 | client := bogdanfinn.NewStdClient() 45 | token, err := duckgo.InitXVQD(client, proxyUrl) 46 | if err != nil { 47 | c.JSON(500, gin.H{ 48 | "error": err.Error(), 49 | }) 50 | return 51 | } 52 | 53 | translated_request := duckgoConvert.ConvertAPIRequest(original_request) 54 | response, err := duckgo.POSTconversation(client, translated_request, token, proxyUrl) 55 | if err != nil { 56 | c.JSON(500, gin.H{ 57 | "error": "request conversion error", 58 | }) 59 | return 60 | } 61 | 62 | defer response.Body.Close() 63 | if duckgo.Handle_request_error(c, response) { 64 | return 65 | } 66 | var response_part string 67 | response_part = duckgo.Handler(c, response, translated_request, original_request.Stream) 68 | if c.Writer.Status() != 200 { 69 | return 70 | } 71 | if !original_request.Stream { 72 | c.JSON(200, officialtypes.NewChatCompletionWithModel(response_part, translated_request.Model)) 73 | } else { 74 | c.String(200, "data: [DONE]\n\n") 75 | } 76 | } 77 | 78 | func (h *Handler) engines(c *gin.Context) { 79 | type ResData struct { 80 | ID string `json:"id"` 81 | Object string `json:"object"` 82 | Created int `json:"created"` 83 | OwnedBy string `json:"owned_by"` 84 | } 85 | 86 | type JSONData struct { 87 | Object string `json:"object"` 88 | Data []ResData `json:"data"` 89 | } 90 | 91 | modelS := JSONData{ 92 | Object: "list", 93 | } 94 | var resModelList []ResData 95 | 96 | // Supported models 97 | modelIDs := []string{ 98 | "gpt-4o-mini", 99 | "o3-mini", 100 | "gpt-3.5-turbo-0125", 101 | "claude-3-haiku-20240307", 102 | "meta-llama/Llama-3.3-70B-Instruct-Turbo", 103 | "mistralai/Mixtral-8x7B-Instruct-v0.1", 104 | } 105 | 106 | for _, modelID := range modelIDs { 107 | resModelList = append(resModelList, ResData{ 108 | ID: modelID, 109 | Object: "model", 110 | Created: 1685474247, 111 | OwnedBy: "duckduckgo", 112 | }) 113 | } 114 | 115 | modelS.Data = resModelList 116 | c.JSON(200, modelS) 117 | } 118 | -------------------------------------------------------------------------------- /initialize/proxy.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "aurora/internal/proxys" 5 | "bufio" 6 | "log/slog" 7 | "net/url" 8 | "os" 9 | ) 10 | 11 | func checkProxy() *proxys.IProxy { 12 | var proxies []string 13 | proxyUrl := os.Getenv("PROXY_URL") 14 | if proxyUrl != "" { 15 | proxies = append(proxies, proxyUrl) 16 | } 17 | 18 | if _, err := os.Stat("proxies.txt"); err == nil { 19 | file, _ := os.Open("proxies.txt") 20 | defer file.Close() 21 | scanner := bufio.NewScanner(file) 22 | for scanner.Scan() { 23 | proxy := scanner.Text() 24 | parsedURL, err := url.Parse(proxy) 25 | if err != nil { 26 | slog.Warn("proxy url is invalid", "url", proxy, "err", err) 27 | continue 28 | } 29 | 30 | // 如果缺少端口信息,不是完整的代理链接 31 | if parsedURL.Port() != "" { 32 | proxies = append(proxies, proxy) 33 | } else { 34 | continue 35 | } 36 | } 37 | } 38 | 39 | if len(proxies) == 0 { 40 | proxy := os.Getenv("http_proxy") 41 | if proxy != "" { 42 | proxies = append(proxies, proxy) 43 | } 44 | } 45 | 46 | proxyIP := proxys.NewIProxyIP(proxies) 47 | return &proxyIP 48 | } 49 | -------------------------------------------------------------------------------- /initialize/router.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "aurora/middlewares" 5 | "os" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func RegisterRouter() *gin.Engine { 11 | handler := NewHandle( 12 | checkProxy(), 13 | ) 14 | 15 | router := gin.Default() 16 | router.Use(middlewares.Cors) 17 | 18 | router.GET("/", func(c *gin.Context) { 19 | c.JSON(200, gin.H{ 20 | "message": "Hello, world!", 21 | }) 22 | }) 23 | 24 | router.GET("/ping", func(c *gin.Context) { 25 | c.JSON(200, gin.H{ 26 | "message": "pong", 27 | }) 28 | }) 29 | 30 | prefixGroup := os.Getenv("PREFIX") 31 | if prefixGroup != "" { 32 | prefixRouter := router.Group(prefixGroup) 33 | { 34 | prefixRouter.OPTIONS("/v1/chat/completions", optionsHandler) 35 | prefixRouter.OPTIONS("/v1/chat/models", optionsHandler) 36 | prefixRouter.POST("/v1/chat/completions", middlewares.Authorization, handler.duckduckgo) 37 | prefixRouter.GET("/v1/models", middlewares.Authorization, handler.engines) 38 | } 39 | } 40 | 41 | router.OPTIONS("/v1/chat/completions", optionsHandler) 42 | router.OPTIONS("/v1/chat/models", optionsHandler) 43 | authGroup := router.Group("").Use(middlewares.Authorization) 44 | authGroup.POST("/v1/chat/completions", handler.duckduckgo) 45 | authGroup.GET("/v1/models", handler.engines) 46 | return router 47 | } 48 | -------------------------------------------------------------------------------- /internal/duckgo/request.go: -------------------------------------------------------------------------------- 1 | package duckgo 2 | 3 | import ( 4 | "aurora/httpclient" 5 | duckgotypes "aurora/typings/duckgo" 6 | officialtypes "aurora/typings/official" 7 | "bufio" 8 | "bytes" 9 | "encoding/json" 10 | "errors" 11 | "github.com/gin-gonic/gin" 12 | "io" 13 | "net/http" 14 | "strings" 15 | "sync" 16 | "time" 17 | ) 18 | 19 | var ( 20 | Token *XqdgToken 21 | UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" 22 | ) 23 | 24 | type XqdgToken struct { 25 | Token string `json:"token"` 26 | M sync.Mutex `json:"-"` 27 | ExpireAt time.Time `json:"expire"` 28 | } 29 | 30 | func InitXVQD(client httpclient.AuroraHttpClient, proxyUrl string) (string, error) { 31 | if Token == nil { 32 | Token = &XqdgToken{ 33 | Token: "", 34 | M: sync.Mutex{}, 35 | } 36 | } 37 | Token.M.Lock() 38 | defer Token.M.Unlock() 39 | if Token.Token == "" || Token.ExpireAt.Before(time.Now()) { 40 | status, err := postStatus(client, proxyUrl) 41 | if err != nil { 42 | return "", err 43 | } 44 | defer status.Body.Close() 45 | token := status.Header.Get("x-vqd-4") 46 | if token == "" { 47 | return "", errors.New("no x-vqd-4 token") 48 | } 49 | Token.Token = token 50 | Token.ExpireAt = time.Now().Add(time.Minute * 3) 51 | } 52 | 53 | return Token.Token, nil 54 | } 55 | 56 | func postStatus(client httpclient.AuroraHttpClient, proxyUrl string) (*http.Response, error) { 57 | if proxyUrl != "" { 58 | client.SetProxy(proxyUrl) 59 | } 60 | header := createHeader() 61 | header.Set("accept", "*/*") 62 | header.Set("x-vqd-accept", "1") 63 | response, err := client.Request(httpclient.GET, "https://duckduckgo.com/duckchat/v1/status", header, nil, nil) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return response, nil 68 | } 69 | 70 | func POSTconversation(client httpclient.AuroraHttpClient, request duckgotypes.ApiRequest, token string, proxyUrl string) (*http.Response, error) { 71 | if proxyUrl != "" { 72 | client.SetProxy(proxyUrl) 73 | } 74 | body_json, err := json.Marshal(request) 75 | if err != nil { 76 | return &http.Response{}, err 77 | } 78 | header := createHeader() 79 | header.Set("accept", "text/event-stream") 80 | header.Set("x-vqd-4", token) 81 | response, err := client.Request(httpclient.POST, "https://duckduckgo.com/duckchat/v1/chat", header, nil, bytes.NewBuffer(body_json)) 82 | if err != nil { 83 | return nil, err 84 | } 85 | return response, nil 86 | } 87 | 88 | func Handle_request_error(c *gin.Context, response *http.Response) bool { 89 | if response.StatusCode != 200 { 90 | // Try read response body as JSON 91 | var error_response map[string]interface{} 92 | err := json.NewDecoder(response.Body).Decode(&error_response) 93 | if err != nil { 94 | // Read response body 95 | body, _ := io.ReadAll(response.Body) 96 | c.JSON(response.StatusCode, gin.H{"error": gin.H{ 97 | "message": "Unknown error", 98 | "type": "internal_server_error", 99 | "param": nil, 100 | "code": "500", 101 | "details": string(body), 102 | }}) 103 | return true 104 | } 105 | c.JSON(response.StatusCode, gin.H{"error": gin.H{ 106 | "message": error_response["detail"], 107 | "type": response.Status, 108 | "param": nil, 109 | "code": "error", 110 | }}) 111 | return true 112 | } 113 | return false 114 | } 115 | 116 | func createHeader() httpclient.AuroraHeaders { 117 | header := make(httpclient.AuroraHeaders) 118 | header.Set("accept-language", "zh-CN,zh;q=0.9") 119 | header.Set("content-type", "application/json") 120 | header.Set("origin", "https://duckduckgo.com") 121 | header.Set("referer", "https://duckduckgo.com/") 122 | header.Set("sec-ch-ua", `"Chromium";v="120", "Google Chrome";v="120", "Not-A.Brand";v="99"`) 123 | header.Set("sec-ch-ua-mobile", "?0") 124 | header.Set("sec-ch-ua-platform", `"Windows"`) 125 | header.Set("sec-fetch-dest", "empty") 126 | header.Set("sec-fetch-mode", "cors") 127 | header.Set("sec-fetch-site", "same-origin") 128 | header.Set("user-agent", UA) 129 | return header 130 | } 131 | 132 | func Handler(c *gin.Context, response *http.Response, oldRequest duckgotypes.ApiRequest, stream bool) string { 133 | reader := bufio.NewReader(response.Body) 134 | if stream { 135 | // Response content type is text/event-stream 136 | c.Header("Content-Type", "text/event-stream") 137 | } else { 138 | // Response content type is application/json 139 | c.Header("Content-Type", "application/json") 140 | } 141 | 142 | var previousText strings.Builder 143 | for { 144 | line, err := reader.ReadString('\n') 145 | if err != nil { 146 | if err == io.EOF { 147 | break 148 | } 149 | return "" 150 | } 151 | if len(line) < 6 { 152 | continue 153 | } 154 | line = line[6:] 155 | if !strings.HasPrefix(line, "[DONE]") { 156 | var originalResponse duckgotypes.ApiResponse 157 | err = json.Unmarshal([]byte(line), &originalResponse) 158 | if err != nil { 159 | continue 160 | } 161 | if originalResponse.Action != "success" { 162 | c.JSON(500, gin.H{"error": "Error"}) 163 | return "" 164 | } 165 | responseString := "" 166 | if originalResponse.Message != "" { 167 | previousText.WriteString(originalResponse.Message) 168 | translatedResponse := officialtypes.NewChatCompletionChunkWithModel(originalResponse.Message, originalResponse.Model) 169 | responseString = "data: " + translatedResponse.String() + "\n\n" 170 | } 171 | 172 | if responseString == "" { 173 | continue 174 | } 175 | 176 | if stream { 177 | _, err = c.Writer.WriteString(responseString) 178 | if err != nil { 179 | return "" 180 | } 181 | c.Writer.Flush() 182 | } 183 | } else { 184 | if stream { 185 | final_line := officialtypes.StopChunkWithModel("stop", oldRequest.Model) 186 | c.Writer.WriteString("data: " + final_line.String() + "\n\n") 187 | } 188 | } 189 | } 190 | return previousText.String() 191 | } 192 | -------------------------------------------------------------------------------- /internal/proxys/proxys.go: -------------------------------------------------------------------------------- 1 | package proxys 2 | 3 | import "sync" 4 | 5 | type IProxy struct { 6 | ips []string 7 | lock sync.Mutex 8 | } 9 | 10 | func NewIProxyIP(ips []string) IProxy { 11 | return IProxy{ 12 | ips: ips, 13 | } 14 | } 15 | 16 | func (p *IProxy) GetIPS() int { 17 | return len(p.ips) 18 | } 19 | 20 | func (p *IProxy) GetProxyIP() string { 21 | if p == nil { 22 | return "" 23 | } 24 | 25 | p.lock.Lock() 26 | defer p.lock.Unlock() 27 | 28 | if len(p.ips) == 0 { 29 | return "" 30 | } 31 | 32 | proxyIp := p.ips[0] 33 | p.ips = append(p.ips[1:], proxyIp) 34 | return proxyIp 35 | } 36 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aurora/initialize" 5 | "embed" 6 | "io/fs" 7 | "log" 8 | "net/http" 9 | "os" 10 | 11 | "github.com/gin-gonic/gin" 12 | 13 | "github.com/acheong08/endless" 14 | "github.com/joho/godotenv" 15 | ) 16 | 17 | //go:embed web/* 18 | var staticFiles embed.FS 19 | 20 | func main() { 21 | _ = godotenv.Load(".env") 22 | gin.SetMode(gin.ReleaseMode) 23 | router := initialize.RegisterRouter() 24 | subFS, err := fs.Sub(staticFiles, "web") 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | router.StaticFS("/web", http.FS(subFS)) 29 | host := os.Getenv("SERVER_HOST") 30 | port := os.Getenv("SERVER_PORT") 31 | tlsCert := os.Getenv("TLS_CERT") 32 | tlsKey := os.Getenv("TLS_KEY") 33 | 34 | if host == "" { 35 | host = "0.0.0.0" 36 | } 37 | if port == "" { 38 | port = os.Getenv("PORT") 39 | if port == "" { 40 | port = "8080" 41 | } 42 | } 43 | 44 | if tlsCert != "" && tlsKey != "" { 45 | _ = endless.ListenAndServeTLS(host+":"+port, tlsCert, tlsKey, router) 46 | } else { 47 | _ = endless.ListenAndServe(host+":"+port, router) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /middlewares/auth.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func Authorization(c *gin.Context) { 10 | customer_key := os.Getenv("Authorization") 11 | if customer_key != "" { 12 | authHeader := c.GetHeader("Authorization") 13 | if authHeader == "" { 14 | c.JSON(401, gin.H{"error": "Unauthorized"}) 15 | c.Abort() 16 | return 17 | } 18 | tokenParts := strings.Split(strings.Replace(authHeader, "Bearer ", "", 1)," ") 19 | customAccessToken := tokenParts[0] 20 | if customer_key != customAccessToken { 21 | c.JSON(401, gin.H{"error": "Unauthorized"}) 22 | c.Abort() 23 | return 24 | } 25 | if len(tokenParts) > 1 { 26 | openaiAccessToken := tokenParts[1] 27 | c.Request.Header.Set("Authorization", "Bearer " + openaiAccessToken) 28 | } 29 | } 30 | c.Next() 31 | } 32 | -------------------------------------------------------------------------------- /middlewares/cors.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Cors(c *gin.Context) { 6 | c.Header("Access-Control-Allow-Origin", "*") 7 | c.Header("Access-Control-Allow-Methods", "*") 8 | c.Header("Access-Control-Allow-Headers", "*") 9 | c.Next() 10 | } 11 | -------------------------------------------------------------------------------- /release.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | 4 | REM 指定编码为 UTF-8 5 | chcp 65001 6 | 7 | REM 设置要生成的可执行文件的名称 8 | set OUTPUT_NAME=aurora 9 | 10 | REM 设置 Go 源文件的名称 11 | SET GOFILE=aurora 12 | 13 | REM 设置输出目录 14 | SET OUTPUTDIR=target 15 | 16 | REM 确保输出目录存在 17 | IF NOT EXIST %OUTPUTDIR% MKDIR %OUTPUTDIR% 18 | 19 | REM 编译为 Windows/amd64 20 | echo 开始编译 Windows/amd64 21 | SET GOOS=windows 22 | SET GOARCH=amd64 23 | go build -o %OUTPUTDIR%/%OUTPUT_NAME%_windows_amd64.exe %GOFILE% 24 | echo 编译完成 Windows/amd64 25 | 26 | REM 编译为 Windows/386 27 | echo 开始编译 Windows/386 28 | SET GOOS=windows 29 | SET GOARCH=386 30 | go build -o %OUTPUTDIR%/%OUTPUT_NAME%_windows_386.exe %GOFILE% 31 | echo 编译完成 Windows/386 32 | 33 | REM 编译为 Linux/amd64 34 | echo 开始编译 Linux/amd64 35 | SET GOOS=linux 36 | SET GOARCH=amd64 37 | go build -o %OUTPUTDIR%/%OUTPUT_NAME%_linux_amd64 %GOFILE% 38 | echo 编译完成 Linux/amd64 39 | 40 | REM 编译为 macOS/amd64 41 | echo 开始编译 macOS/amd64 42 | SET GOOS=darwin 43 | SET GOARCH=amd64 44 | go build -o %OUTPUTDIR%/%OUTPUT_NAME%_macos_amd64 %GOFILE% 45 | echo 编译完成 macOS/amd64 46 | 47 | REM 编译为 freebsd/amd64 48 | echo 开始编译 freebsd/amd64 49 | SET GOOS=freebsd 50 | SET GOARCH=amd64 51 | go build -o %OUTPUTDIR%/%OUTPUT_NAME%_freebsd_amd64 %GOFILE% 52 | echo 编译完成 freebsd/amd64 53 | 54 | REM 结束批处理脚本 55 | ENDLOCAL 56 | echo 编译完成! 57 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: duck2api 4 | env: docker 5 | dockerfilePath: ./Dockerfile 6 | plan: free 7 | 8 | -------------------------------------------------------------------------------- /typings/duckgo/request.go: -------------------------------------------------------------------------------- 1 | package duckgo 2 | 3 | type ApiRequest struct { 4 | Model string `json:"model"` 5 | Messages []messages `json:"messages"` 6 | } 7 | type messages struct { 8 | Role string `json:"role"` 9 | Content string `json:"content"` 10 | } 11 | 12 | func (a *ApiRequest) AddMessage(role string, content string) { 13 | a.Messages = append(a.Messages, messages{ 14 | Role: role, 15 | Content: content, 16 | }) 17 | } 18 | 19 | func NewApiRequest(model string) ApiRequest { 20 | return ApiRequest{ 21 | Model: model, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /typings/duckgo/response.go: -------------------------------------------------------------------------------- 1 | package duckgo 2 | 3 | type ApiResponse struct { 4 | Message string `json:"message"` 5 | Created int `json:"created"` 6 | Id string `json:"id"` 7 | Action string `json:"action"` 8 | Model string `json:"model"` 9 | } 10 | -------------------------------------------------------------------------------- /typings/official/request.go: -------------------------------------------------------------------------------- 1 | package official 2 | 3 | type APIRequest struct { 4 | Messages []api_message `json:"messages"` 5 | Stream bool `json:"stream"` 6 | Model string `json:"model"` 7 | PluginIDs []string `json:"plugin_ids"` 8 | } 9 | 10 | type api_message struct { 11 | Role string `json:"role"` 12 | Content interface{} `json:"content"` 13 | } 14 | 15 | type OpenAISessionToken struct { 16 | SessionToken string `json:"session_token"` 17 | } 18 | 19 | type OpenAIRefreshToken struct { 20 | RefreshToken string `json:"refresh_token"` 21 | } 22 | -------------------------------------------------------------------------------- /typings/official/response.go: -------------------------------------------------------------------------------- 1 | package official 2 | 3 | import "encoding/json" 4 | 5 | type ChatCompletionChunk struct { 6 | ID string `json:"id"` 7 | Object string `json:"object"` 8 | Created int64 `json:"created"` 9 | Model string `json:"model"` 10 | Choices []Choices `json:"choices"` 11 | } 12 | 13 | func (chunk *ChatCompletionChunk) String() string { 14 | resp, _ := json.Marshal(chunk) 15 | return string(resp) 16 | } 17 | 18 | type Choices struct { 19 | Delta Delta `json:"delta"` 20 | Index int `json:"index"` 21 | FinishReason interface{} `json:"finish_reason"` 22 | } 23 | 24 | type Delta struct { 25 | Content string `json:"content,omitempty"` 26 | Role string `json:"role,omitempty"` 27 | } 28 | 29 | func NewChatCompletionChunk(text string) ChatCompletionChunk { 30 | return ChatCompletionChunk{ 31 | ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", 32 | Object: "chat.completion.chunk", 33 | Created: 0, 34 | Model: "gpt-4o-mini", 35 | Choices: []Choices{ 36 | { 37 | Index: 0, 38 | Delta: Delta{ 39 | Content: text, 40 | }, 41 | FinishReason: nil, 42 | }, 43 | }, 44 | } 45 | } 46 | 47 | func NewChatCompletionChunkWithModel(text string, model string) ChatCompletionChunk { 48 | return ChatCompletionChunk{ 49 | ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", 50 | Object: "chat.completion.chunk", 51 | Created: 0, 52 | Model: model, 53 | Choices: []Choices{ 54 | { 55 | Index: 0, 56 | Delta: Delta{ 57 | Content: text, 58 | }, 59 | FinishReason: nil, 60 | }, 61 | }, 62 | } 63 | } 64 | 65 | func StopChunkWithModel(reason string, model string) ChatCompletionChunk { 66 | return ChatCompletionChunk{ 67 | ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", 68 | Object: "chat.completion.chunk", 69 | Created: 0, 70 | Model: model, 71 | Choices: []Choices{ 72 | { 73 | Index: 0, 74 | FinishReason: reason, 75 | }, 76 | }, 77 | } 78 | } 79 | 80 | func StopChunk(reason string) ChatCompletionChunk { 81 | return ChatCompletionChunk{ 82 | ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", 83 | Object: "chat.completion.chunk", 84 | Created: 0, 85 | Model: "gpt-4o-mini", 86 | Choices: []Choices{ 87 | { 88 | Index: 0, 89 | FinishReason: reason, 90 | }, 91 | }, 92 | } 93 | } 94 | 95 | type ChatCompletion struct { 96 | ID string `json:"id"` 97 | Object string `json:"object"` 98 | Created int64 `json:"created"` 99 | Model string `json:"model"` 100 | Usage usage `json:"usage"` 101 | Choices []Choice `json:"choices"` 102 | } 103 | type Msg struct { 104 | Role string `json:"role"` 105 | Content string `json:"content"` 106 | } 107 | type Choice struct { 108 | Index int `json:"index"` 109 | Message Msg `json:"message"` 110 | FinishReason interface{} `json:"finish_reason"` 111 | } 112 | type usage struct { 113 | PromptTokens int `json:"prompt_tokens"` 114 | CompletionTokens int `json:"completion_tokens"` 115 | TotalTokens int `json:"total_tokens"` 116 | } 117 | 118 | func NewChatCompletionWithModel(text string, model string) ChatCompletion { 119 | return ChatCompletion{ 120 | ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", 121 | Object: "chat.completion", 122 | Created: int64(0), 123 | Model: model, 124 | Usage: usage{ 125 | PromptTokens: 0, 126 | CompletionTokens: 0, 127 | TotalTokens: 0, 128 | }, 129 | Choices: []Choice{ 130 | { 131 | Message: Msg{ 132 | Content: text, 133 | Role: "assistant", 134 | }, 135 | Index: 0, 136 | }, 137 | }, 138 | } 139 | } 140 | 141 | func NewChatCompletion(full_test string, input_tokens, output_tokens int) ChatCompletion { 142 | return ChatCompletion{ 143 | ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", 144 | Object: "chat.completion", 145 | Created: int64(0), 146 | Model: "gpt-4o-mini", 147 | Usage: usage{ 148 | PromptTokens: input_tokens, 149 | CompletionTokens: output_tokens, 150 | TotalTokens: input_tokens + output_tokens, 151 | }, 152 | Choices: []Choice{ 153 | { 154 | Message: Msg{ 155 | Content: full_test, 156 | Role: "assistant", 157 | }, 158 | Index: 0, 159 | }, 160 | }, 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /typings/typings.go: -------------------------------------------------------------------------------- 1 | package typings 2 | 3 | type GenericResponseLine struct { 4 | Line string `json:"line"` 5 | Error string `json:"error"` 6 | } 7 | 8 | type StringStruct struct { 9 | Text string `json:"text"` 10 | } 11 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "log/slog" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/pkoukk/tiktoken-go" 9 | ) 10 | 11 | func RandomLanguage() string { 12 | // 初始化随机数生成器 13 | rand.Seed(time.Now().UnixNano()) 14 | // 语言列表 15 | languages := []string{"af", "am", "ar-sa", "as", "az-Latn", "be", "bg", "bn-BD", "bn-IN", "bs", "ca", "ca-ES-valencia", "cs", "cy", "da", "de", "de-de", "el", "en-GB", "en-US", "es", "es-ES", "es-US", "es-MX", "et", "eu", "fa", "fi", "fil-Latn", "fr", "fr-FR", "fr-CA", "ga", "gd-Latn", "gl", "gu", "ha-Latn", "he", "hi", "hr", "hu", "hy", "id", "ig-Latn", "is", "it", "it-it", "ja", "ka", "kk", "km", "kn", "ko", "kok", "ku-Arab", "ky-Cyrl", "lb", "lt", "lv", "mi-Latn", "mk", "ml", "mn-Cyrl", "mr", "ms", "mt", "nb", "ne", "nl", "nl-BE", "nn", "nso", "or", "pa", "pa-Arab", "pl", "prs-Arab", "pt-BR", "pt-PT", "qut-Latn", "quz", "ro", "ru", "rw", "sd-Arab", "si", "sk", "sl", "sq", "sr-Cyrl-BA", "sr-Cyrl-RS", "sr-Latn-RS", "sv", "sw", "ta", "te", "tg-Cyrl", "th", "ti", "tk-Latn", "tn", "tr", "tt-Cyrl", "ug-Arab", "uk", "ur", "uz-Latn", "vi", "wo", "xh", "yo-Latn", "zh-Hans", "zh-Hant", "zu"} 16 | // 随机选择一个语言 17 | randomIndex := rand.Intn(len(languages)) 18 | return languages[randomIndex] 19 | } 20 | 21 | func RandomHexadecimalString() string { 22 | rand.Seed(time.Now().UnixNano()) 23 | const charset = "0123456789abcdef" 24 | const length = 16 // The length of the string you want to generate 25 | b := make([]byte, length) 26 | for i := range b { 27 | b[i] = charset[rand.Intn(len(charset))] 28 | } 29 | return string(b) 30 | } 31 | func CountToken(input string) int { 32 | encoding := "gpt-4o-mini" 33 | tkm, err := tiktoken.EncodingForModel(encoding) 34 | if err != nil { 35 | slog.Warn("tiktoken.EncodingForModel error:", err) 36 | return 0 37 | } 38 | token := tkm.Encode(input, nil, nil) 39 | return len(token) 40 | } 41 | -------------------------------------------------------------------------------- /util/utils_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestRandomHexadecimalString(t *testing.T) { 9 | var str = RandomHexadecimalString() 10 | fmt.Println(str) 11 | } 12 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "routes": [ 3 | { 4 | "src": "/.*", 5 | "dest": "/api/router.go" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /web/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurora-develop/Duck2api/00bc962794943c81a5860f52d400af6c306c821a/web/avatar.png -------------------------------------------------------------------------------- /web/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurora-develop/Duck2api/00bc962794943c81a5860f52d400af6c306c821a/web/icon.png --------------------------------------------------------------------------------