diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8eac9076396488166426eefad51a17e54996885b --- /dev/null +++ b/.gitignore @@ -0,0 +1,91 @@ +.externalNativeBuild +import-summary.txt + +#java files +*.class +*.dex +.sync/ + +#for idea temp file +*.iws +*.ipr +*.iml +target/ +.idea/ +.gradle/ +release/ +build/ +spoon/ +releasebak/ + +#mac temp file +.idea/ +__MACOSX +.DS_Store +._.DS_Store + +#for eclipse +.settings/ +local.properties +*gen/ +*.classpath +*/bin/ +bin/ +.project + +#temp file +*.bak + +*.pmd +sh.exe.stackdump + +.vs/ +.vscode/ + +*.log +*.ctxt +.mtj.tmp/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Package Files # +# *.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar +*.cxx +*.cfg +# for nodejs +node_modules/ +# for python +package-lock.json +.$* +*.drawio.bkp + +firelineReport/ +fireline*.jar +format.txt +google-java-format-1.12.0-all-deps.jar +google-java-format-*.jar +spoon-runner-1.7.1-jar-with-dependencies.jar +spoon-*.jar +fireline_1.7.3.jar +fireline*.jar +firelineReport/ +*/ProguardJson.java +*/Key.java + +doc/*.html +doc/*.pdf +buildSrc/.gradle/ +TestCaseBase/ +MultiProcess/ +NezhaAppList/ + +# igone sdk testcase +dev-sdk/src/androidTest/ +dev-sdk/src/main/java/com/analysys/plugin diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..badbef0da5fecc8f1a25f85cff64ea81b3296c70 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# 使用 Go 1.21 官方镜像作为构建环境 +FROM golang:1.21 AS builder + +# 禁用 CGO +ENV CGO_ENABLED=0 + +# 设置工作目录 +WORKDIR /app + +# 复制 go.mod 和 go.sum 并下载依赖 +COPY go.mod go.sum ./ +RUN go mod download + +# 复制源代码并构建应用 +COPY . . +RUN go build -ldflags "-s -w" -o /app/duck2api . + +# 使用 Alpine Linux 作为最终镜像 +FROM alpine:latest + +# 设置工作目录 +WORKDIR /app + +# 从构建阶段复制编译好的应用和资源 +COPY --from=builder /app/duck2api /app/duck2api + +# hf 默认暴露端口 +EXPOSE 7860 + +CMD ["/app/duck2api"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d322e6dbf9a9471b0de438b2a539c4c9f4752a55 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 aurora-develop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000000000000000000000000000000000..ae76712ac18a1eac1744a2e4bd74b805e45d9286 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: aurora \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000000000000000000000000000000000000..38f77a65b3015cb4dc42eebe91514e49b47b8597 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.0.1 diff --git a/api/router.go b/api/router.go new file mode 100644 index 0000000000000000000000000000000000000000..481e8ebd3bbfbd70bd3f450d8673256007d8b1f6 --- /dev/null +++ b/api/router.go @@ -0,0 +1,18 @@ +package api + +import ( + "aurora/initialize" + "github.com/gin-gonic/gin" + "net/http" +) + +var router *gin.Engine + +func init() { + // 初始化gin + router = initialize.RegisterRouter() +} + +func Listen(w http.ResponseWriter, r *http.Request) { + router.ServeHTTP(w, r) +} diff --git a/build.sh b/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..3ccae9aeeb4d087bac5c84a7b51689d09966544e --- /dev/null +++ b/build.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +export GOPROXY=https://goproxy.io + +go get + +export CGO_ENABLED=0 +PKG=aurora + +targets=( + "windows/amd64" + "linux/amd64" + "darwin/amd64" + "windows/386" + "linux/386" + "darwin/386" + "linux/arm" + "linux/arm64" + "linux/s390x" +) + +upxPath=$(command -v upx) + +for target in "${targets[@]}"; do + GOOS=${target%/*} + GOARCH=${target#*/} + outputDir="bin/${GOOS}_${GOARCH}" + outputFile="${outputDir}/${PKG}" + archiveName="${PKG}-${GOOS}-${GOARCH}.tar.gz" + mkdir -p $(dirname ${outputFile}) + GOOS=$GOOS GOARCH=$GOARCH go build -ldflags="-s -w -extldflags '-static'" -o ${outputFile} *.go + if [ -n "$upxPath" ]; then + $upxPath -9 ${outputFile} + fi + # Archive the binary + if [ "$GOOS" = "windows" ]; then + zip -j "${outputDir}/${PKG}-${GOOS}-${GOARCH}.zip" "${outputFile}" + else + tar -C "${outputDir}" -czf "${outputDir}/${archiveName}" "${PKG}" + fi +done diff --git a/build_docker.sh b/build_docker.sh new file mode 100644 index 0000000000000000000000000000000000000000..e491c5e2919dd2c8efe0a095f2b08a16bb6035c8 --- /dev/null +++ b/build_docker.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +## 编译镜像 +docker build -t duckapi . + +## 启动镜像 +docker run -d -p 7860:7860 --name tillduck --restart=always duckapi diff --git a/call_demo/hg.py b/call_demo/hg.py new file mode 100644 index 0000000000000000000000000000000000000000..35a46a3218bdb272d29c780c0296915edf9b49d3 --- /dev/null +++ b/call_demo/hg.py @@ -0,0 +1,22 @@ +import requests +import json + +url = 'https://sanbo1200-duck2api.hf.space/completions' +headers = { + 'Content-Type': 'application/json' +} +data = { + "model": "gpt-4o-mini", + "messages": [ + {"role": "system", "content": "你是一个辅助机器人"}, + {"role": "user", "content": "你的知识库最后什么日期"} + ], + "stream": False +} + +response = requests.post(url, headers=headers, data=json.dumps(data), stream=True) +response.encoding = 'utf-8' +# 打印返回的数据流 +for line in response.iter_lines(): + if line: + print(line.decode('utf-8')) diff --git a/call_demo/hg_fd.py b/call_demo/hg_fd.py new file mode 100644 index 0000000000000000000000000000000000000000..d0d0aaefff6b7631b502d38f6650c57a720340c8 --- /dev/null +++ b/call_demo/hg_fd.py @@ -0,0 +1,23 @@ +import requests +import json + +# url = 'https://p.till.us.kg/till/https/sanbo1200-duck2api.hf.space/completions' +url = 'https://2.897653.xyz/aaabbbccc/https/sanbo1200-duck2api.hf.space/completions' +headers = { + 'Content-Type': 'application/json' +} +data = { + "model": "gpt-4o-mini", + "messages": [ + {"role": "system", "content": "你是一个辅助机器人"}, + {"role": "user", "content": "你的知识库最后什么日期"} + ], + "stream": False +} + +response = requests.post(url, headers=headers, data=json.dumps(data), stream=True) +response.encoding = 'utf-8' +# 打印返回的数据流 +for line in response.iter_lines(): + if line: + print(line.decode('utf-8')) diff --git a/conversion/requests/duckgo/convert.go b/conversion/requests/duckgo/convert.go new file mode 100644 index 0000000000000000000000000000000000000000..4f3ed2e3b11734a414bbe8a1417755ecc538ddf4 --- /dev/null +++ b/conversion/requests/duckgo/convert.go @@ -0,0 +1,61 @@ +package duckgo + +import ( + duckgotypes "aurora/typings/duckgo" + officialtypes "aurora/typings/official" + "strings" +) + +func ConvertAPIRequest(api_request officialtypes.APIRequest) duckgotypes.ApiRequest { + inputModel := api_request.Model + duckgo_request := duckgotypes.NewApiRequest(inputModel) + realModel := inputModel + + // 如果模型未进行映射,则直接使用输入模型,方便后续用户使用 duckduckgo 添加的新模型。 + modelLower := strings.ToLower(inputModel) + switch { + case strings.HasPrefix(modelLower, "gpt-3.5"): + realModel = "gpt-4o-mini" + case strings.HasPrefix(modelLower, "claude-3-haiku"): + realModel = "claude-3-haiku-20240307" + case strings.HasPrefix(modelLower, "llama-3.1-70b"): + realModel = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo" + case strings.HasPrefix(modelLower, "mixtral-8x7b"): + realModel = "mistralai/Mixtral-8x7B-Instruct-v0.1" + } + + duckgo_request.Model = realModel + content := buildContent(&api_request) + duckgo_request.AddMessage("user", content) + + return duckgo_request +} + +func buildContent(api_request *officialtypes.APIRequest) string { + var content strings.Builder + for _, apiMessage := range api_request.Messages { + role := apiMessage.Role + if role == "user" || role == "system" || role == "assistant" { + if role == "system" { + role = "user" + } + contentStr := "" + // 判断 apiMessage.Content 是否为数组 + if arrayContent, ok := apiMessage.Content.([]interface{}); ok { + // 如果是数组,遍历数组,查找第一个 type 为 "text" 的元素 + for _, element := range arrayContent { + if elementMap, ok := element.(map[string]interface{}); ok { + if elementMap["type"] == "text" { + contentStr = elementMap["text"].(string) + break + } + } + } + } else { + contentStr, _ = apiMessage.Content.(string) + } + content.WriteString(role + ":" + contentStr + ";\r\n") + } + } + return content.String() +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..8d9618b9d9e89985c2ff9b040f33486c82823ae0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' + +services: + app: + image: ghcr.io/aurora-develop/duck2api:latest + container_name: duck2api + restart: unless-stopped + ports: + - '7860:7860' diff --git a/env.template b/env.template new file mode 100644 index 0000000000000000000000000000000000000000..0fa9bad9e7fdf335196a36d85b157e46453a6d34 --- /dev/null +++ b/env.template @@ -0,0 +1,8 @@ +SERVER_HOST=0.0.0.0 +SERVER_PORT=7860 +FREE_ACCOUNTS=true +FREE_ACCOUNTS_NUM=1024 +Authorization= +TLS_CERT= +TLS_KEY= +PROXY_URL= diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..9240505b993a4332cf2c4aa6eb01817f95ddb1e7 --- /dev/null +++ b/go.mod @@ -0,0 +1,55 @@ +module aurora + +go 1.21 + +require ( + github.com/EDDYCJY/fake-useragent v0.2.0 + github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd + github.com/bogdanfinn/fhttp v0.5.28 + github.com/bogdanfinn/tls-client v1.7.2 + github.com/gin-gonic/gin v1.10.0 + github.com/go-resty/resty/v2 v2.14.0 + github.com/joho/godotenv v1.5.1 + github.com/pkoukk/tiktoken-go v0.1.7 +) + +require ( + github.com/PuerkitoBio/goquery v1.9.2 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/andybalholm/cascadia v1.3.2 // indirect + github.com/bogdanfinn/utls v1.6.1 // indirect + github.com/bytedance/sonic v1.12.1 // indirect + github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/cloudflare/circl v1.3.9 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/dlclark/regexp2 v1.11.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.5 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.22.0 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/quic-go/quic-go v0.37.4 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.9.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..1cff10a980982f8af337b4b9fd9e263c9876a9a9 --- /dev/null +++ b/go.sum @@ -0,0 +1,206 @@ +github.com/EDDYCJY/fake-useragent v0.2.0 h1:Jcnkk2bgXmDpX0z+ELlUErTkoLb/mxFBNd2YdcpvJBs= +github.com/EDDYCJY/fake-useragent v0.2.0/go.mod h1:5wn3zzlDxhKW6NYknushqinPcAqZcAPHy8lLczCdJdc= +github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= +github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= +github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd h1:oIpfrRhD7Jus41dotbK+SQjWSFRnf1cLZUYCZpF/o/4= +github.com/acheong08/endless v0.0.0-20230615162514-90545c7793fd/go.mod h1:0yO7neMeJLvKk/B/fq5votDY8rByrOPDubpvU+6saKo= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= +github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= +github.com/bogdanfinn/fhttp v0.5.28 h1:G6thT8s8v6z1IuvXMUsX9QKy3ZHseTQTzxuIhSiaaAw= +github.com/bogdanfinn/fhttp v0.5.28/go.mod h1:oJiYPG3jQTKzk/VFmogH8jxjH5yiv2rrOH48Xso2lrE= +github.com/bogdanfinn/tls-client v1.7.2 h1:vpL5qBYUfT9ueygEf1yLfymrXyUEZQatL25amfqGV8M= +github.com/bogdanfinn/tls-client v1.7.2/go.mod h1:pOGa2euqTbEkGNqE5idx5jKKfs9ytlyn3fwEw8RSP+g= +github.com/bogdanfinn/utls v1.6.1 h1:dKDYAcXEyFFJ3GaWaN89DEyjyRraD1qb4osdEK89ass= +github.com/bogdanfinn/utls v1.6.1/go.mod h1:VXIbRZaiY/wHZc6Hu+DZ4O2CgTzjhjCg/Ou3V4r/39Y= +github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24= +github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= +github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= +github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= +github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU= +github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= +github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkoukk/tiktoken-go v0.1.7 h1:qOBHXX4PHtvIvmOtyg1EeKlwFRiMKAcoMp4Q+bLQDmw= +github.com/pkoukk/tiktoken-go v0.1.7/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/quic-go v0.37.4 h1:ke8B73yMCWGq9MfrCCAw0Uzdm7GaViC3i39dsIdDlH4= +github.com/quic-go/quic-go v0.37.4/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc= +github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= +golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/httpclient/Iaurorahttpclient.go b/httpclient/Iaurorahttpclient.go new file mode 100644 index 0000000000000000000000000000000000000000..91f3beb800e1a44ad4fc8075bd587032f4e85c5b --- /dev/null +++ b/httpclient/Iaurorahttpclient.go @@ -0,0 +1,28 @@ +package httpclient + +import ( + "io" + "net/http" +) + +type AuroraHttpClient interface { + Request(method HttpMethod, url string, headers AuroraHeaders, cookies []*http.Cookie, body io.Reader) (*http.Response, error) + SetProxy(url string) error +} + +type HttpMethod string + +const ( + GET HttpMethod = "GET" + POST HttpMethod = "POST" + PUT HttpMethod = "PUT" + HEAD HttpMethod = "HEAD" + DELETE HttpMethod = "DELETE" + OPTIONS HttpMethod = "OPTIONS" +) + +type AuroraHeaders map[string]string + +func (a AuroraHeaders) Set(key, value string) { + a[key] = value +} diff --git a/httpclient/bogdanfinn/tls_client.go b/httpclient/bogdanfinn/tls_client.go new file mode 100644 index 0000000000000000000000000000000000000000..cf08e1404650a1be7196442ebab32c57ae082939 --- /dev/null +++ b/httpclient/bogdanfinn/tls_client.go @@ -0,0 +1,102 @@ +package bogdanfinn + +import ( + "aurora/httpclient" + "io" + "net/http" + + fhttp "github.com/bogdanfinn/fhttp" + tls_client "github.com/bogdanfinn/tls-client" + "github.com/bogdanfinn/tls-client/profiles" +) + +type TlsClient struct { + Client tls_client.HttpClient + ReqBefore handler +} + +type handler func(r *fhttp.Request) error + +func NewStdClient() *TlsClient { + client, _ := tls_client.NewHttpClient(tls_client.NewNoopLogger(), []tls_client.HttpClientOption{ + tls_client.WithCookieJar(tls_client.NewCookieJar()), + tls_client.WithRandomTLSExtensionOrder(), + tls_client.WithTimeoutSeconds(600), + tls_client.WithClientProfile(profiles.Okhttp4Android13), + }...) + + stdClient := &TlsClient{Client: client} + return stdClient +} + +func convertResponse(resp *fhttp.Response) *http.Response { + response := &http.Response{ + Status: resp.Status, + StatusCode: resp.StatusCode, + Proto: resp.Proto, + ProtoMajor: resp.ProtoMajor, + ProtoMinor: resp.ProtoMinor, + Header: http.Header(resp.Header), + Body: resp.Body, + ContentLength: resp.ContentLength, + TransferEncoding: resp.TransferEncoding, + Close: resp.Close, + Uncompressed: resp.Uncompressed, + Trailer: http.Header(resp.Trailer), + } + return response +} + +func (t *TlsClient) handleHeaders(req *fhttp.Request, headers httpclient.AuroraHeaders) { + if headers == nil { + return + } + for k, v := range headers { + req.Header.Set(k, v) + } +} + +func (t *TlsClient) handleCookies(req *fhttp.Request, cookies []*http.Cookie) { + if cookies == nil { + return + } + for _, c := range cookies { + req.AddCookie(&fhttp.Cookie{ + Name: c.Name, + Value: c.Value, + Path: c.Path, + Domain: c.Domain, + Expires: c.Expires, + RawExpires: c.RawExpires, + MaxAge: c.MaxAge, + Secure: c.Secure, + HttpOnly: c.HttpOnly, + SameSite: fhttp.SameSite(c.SameSite), + Raw: c.Raw, + Unparsed: c.Unparsed, + }) + } +} + +func (t *TlsClient) Request(method httpclient.HttpMethod, url string, headers httpclient.AuroraHeaders, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { + req, err := fhttp.NewRequest(string(method), url, body) + if err != nil { + return nil, err + } + t.handleHeaders(req, headers) + t.handleCookies(req, cookies) + if t.ReqBefore != nil { + if err := t.ReqBefore(req); err != nil { + return nil, err + } + } + do, err := t.Client.Do(req) + if err != nil { + return nil, err + } + return convertResponse(do), nil +} + +func (t *TlsClient) SetProxy(url string) error { + return t.Client.SetProxy(url) +} diff --git a/httpclient/bogdanfinn/tls_client_test.go b/httpclient/bogdanfinn/tls_client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..50794f0a48cfab9d4db89eb7d16ab03c94f8b19d --- /dev/null +++ b/httpclient/bogdanfinn/tls_client_test.go @@ -0,0 +1,101 @@ +package bogdanfinn + +import ( + "aurora/httpclient" + "fmt" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/joho/godotenv" +) + +var BaseURL string + +func init() { + _ = godotenv.Load(".env") + BaseURL = os.Getenv("BASE_URL") + if BaseURL == "" { + BaseURL = "https://chat.openai.com/backend-anon" + } +} +func TestTlsClient_Request(t *testing.T) { + client := NewStdClient() + userAgent := "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + proxy := "http://127.0.0.1:7990" + client.SetProxy(proxy) + + apiUrl := BaseURL + "/sentinel/chat-requirements" + payload := strings.NewReader(`{"conversation_mode_kind":"primary_assistant"}`) + header := make(httpclient.AuroraHeaders) + header.Set("Content-Type", "application/json") + header.Set("User-Agent", userAgent) + header.Set("Accept", "*/*") + header.Set("oai-language", "en-US") + header.Set("origin", "https://chat.openai.com") + header.Set("referer", "https://chat.openai.com/") + header.Set("oai-device-id", "c83b24f0-5a9e-4c43-8915-3f67d4332609") + response, err := client.Request(http.MethodPost, apiUrl, header, nil, payload) + if err != nil { + return + } + defer response.Body.Close() + fmt.Println(response.StatusCode) + if response.StatusCode != 200 { + fmt.Println("Error: ", response.StatusCode) + } +} + +func TestChatGPTModel(t *testing.T) { + client := NewStdClient() + userAgent := "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + proxy := "http://127.0.0.1:7990" + client.SetProxy(proxy) + apiUrl := "https://chat.openai.com/backend-anon/models" + + header := make(httpclient.AuroraHeaders) + header.Set("Content-Type", "application/json") + header.Set("User-Agent", userAgent) + header.Set("Accept", "*/*") + header.Set("oai-language", "en-US") + header.Set("origin", "https://chat.openai.com") + header.Set("referer", "https://chat.openai.com/") + header.Set("oai-device-id", "c83b24f0-5a9e-4c43-8915-3f67d4332609") + response, err := client.Request(http.MethodGet, apiUrl, header, nil, nil) + if err != nil { + return + } + defer response.Body.Close() + fmt.Println(response.StatusCode) + if response.StatusCode != 200 { + fmt.Println("Error: ", response.StatusCode) + body, _ := io.ReadAll(response.Body) + fmt.Println(string(body)) + return + } + + type EnginesData struct { + Models []struct { + Slug string `json:"slug"` + MaxTokens int `json:"max_tokens"` + Title string `json:"title"` + Description string `json:"description"` + Tags []string `json:"tags"` + Capabilities struct { + } `json:"capabilities,omitempty"` + ProductFeatures struct { + } `json:"product_features,omitempty"` + } `json:"models"` + Categories []struct { + Category string `json:"category"` + HumanCategoryName string `json:"human_category_name"` + SubscriptionLevel string `json:"subscription_level"` + DefaultModel string `json:"default_model"` + CodeInterpreterModel string `json:"code_interpreter_model,omitempty"` + PluginsModel string `json:"plugins_model"` + } `json:"categories"` + } + +} diff --git a/httpclient/resty/resty_client.go b/httpclient/resty/resty_client.go new file mode 100644 index 0000000000000000000000000000000000000000..36b0cd485055e158781801f568925a1e5edfe7e3 --- /dev/null +++ b/httpclient/resty/resty_client.go @@ -0,0 +1,72 @@ +package resty + +import ( + "aurora/util" + "crypto/tls" + browser "github.com/EDDYCJY/fake-useragent" + "github.com/go-resty/resty/v2" + "net/http" + "time" +) + +type RestyClient struct { + Client *resty.Client +} + +func NewStdClient() *RestyClient { + client := &RestyClient{ + Client: resty.NewWithClient(&http.Client{ + Transport: &http.Transport{ + // 禁用长连接 + DisableKeepAlives: true, + // 配置TLS设置,跳过证书验证 + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + }, + }), + } + client.Client.SetBaseURL("https://chat.openai.com") + client.Client.SetRetryCount(3) + client.Client.SetRetryWaitTime(5 * time.Second) + client.Client.SetRetryMaxWaitTime(20 * time.Second) + + client.Client.SetTimeout(600 * time.Second) + client.Client.SetHeader("user-agent", browser.Random()). + SetHeader("accept", "*/*"). + SetHeader("accept-language", "en-US,en;q=0.9"). + SetHeader("cache-control", "no-cache"). + SetHeader("content-type", "application/json"). + SetHeader("oai-language", util.RandomLanguage()). + SetHeader("pragma", "no-cache"). + SetHeader("sec-ch-ua", `"Google Chrome";v="123", "Not:A-Brand";v="8", "Chromium";v="123"`). + SetHeader("sec-ch-ua-mobile", "?0"). + SetHeader("sec-ch-ua-platform", "Windows"). + SetHeader("sec-fetch-dest", "empty"). + SetHeader("sec-fetch-mode", "cors"). + SetHeader("sec-fetch-site", "same-origin") + return client +} + +//func (c *RestyClient) Request(method string, url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} + +//func (c *RestyClient) Post(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} +// +//func (c *RestyClient) Get(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} +// +//func (c *RestyClient) Head(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} +// +//func (c *RestyClient) Options(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} +// +//func (c *RestyClient) Put(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} +// +//func (c *RestyClient) Delete(url string, headers map[string]string, cookies []*http.Cookie, body io.Reader) (*http.Response, error) { +//} +// +//func (c *RestyClient) SetProxy(url string) error {} diff --git a/initialize/handlers.go b/initialize/handlers.go new file mode 100644 index 0000000000000000000000000000000000000000..4f3d7a18b85ab6331f6f294e90510ecb06fcabad --- /dev/null +++ b/initialize/handlers.go @@ -0,0 +1,116 @@ +package initialize + +import ( + duckgoConvert "aurora/conversion/requests/duckgo" + "aurora/httpclient/bogdanfinn" + "aurora/internal/duckgo" + "aurora/internal/proxys" + officialtypes "aurora/typings/official" + + "github.com/gin-gonic/gin" +) + +type Handler struct { + proxy *proxys.IProxy +} + +func NewHandle(proxy *proxys.IProxy) *Handler { + return &Handler{proxy: proxy} +} + +func optionsHandler(c *gin.Context) { + // Set headers for CORS + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Methods", "POST") + c.Header("Access-Control-Allow-Headers", "*") + c.JSON(200, gin.H{ + "message": "pong", + }) +} + +func (h *Handler) duckduckgo(c *gin.Context) { + var original_request officialtypes.APIRequest + err := c.BindJSON(&original_request) + if err != nil { + c.JSON(400, gin.H{"error": gin.H{ + "message": "Request must be proper JSON", + "type": "invalid_request_error", + "param": nil, + "code": err.Error(), + }}) + return + } + proxyUrl := h.proxy.GetProxyIP() + client := bogdanfinn.NewStdClient() + token, err := duckgo.InitXVQD(client, proxyUrl) + if err != nil { + c.JSON(500, gin.H{ + "error": err.Error(), + }) + return + } + + translated_request := duckgoConvert.ConvertAPIRequest(original_request) + response, err := duckgo.POSTconversation(client, translated_request, token, proxyUrl) + if err != nil { + c.JSON(500, gin.H{ + "error": "request conversion error", + }) + return + } + + defer response.Body.Close() + if duckgo.Handle_request_error(c, response) { + return + } + var response_part string + response_part = duckgo.Handler(c, response, translated_request, original_request.Stream) + if c.Writer.Status() != 200 { + return + } + if !original_request.Stream { + c.JSON(200, officialtypes.NewChatCompletionWithModel(response_part, translated_request.Model)) + } else { + c.String(200, "data: [DONE]\n\n") + } +} + +func (h *Handler) engines(c *gin.Context) { + type ResData struct { + ID string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + OwnedBy string `json:"owned_by"` + } + + type JSONData struct { + Object string `json:"object"` + Data []ResData `json:"data"` + } + + modelS := JSONData{ + Object: "list", + } + var resModelList []ResData + + // Supported models + modelIDs := []string{ + "gpt-4o-mini", + "gpt-3.5-turbo-0125", + "claude-3-haiku-20240307", + "meta-llama/Llama-3-70b-chat-hf", + "mistralai/Mixtral-8x7B-Instruct-v0.1", + } + + for _, modelID := range modelIDs { + resModelList = append(resModelList, ResData{ + ID: modelID, + Object: "model", + Created: 1685474247, + OwnedBy: "duckduckgo", + }) + } + + modelS.Data = resModelList + c.JSON(200, modelS) +} diff --git a/initialize/proxy.go b/initialize/proxy.go new file mode 100644 index 0000000000000000000000000000000000000000..f343a0916512400df78004cf00a27f7743d5b1f1 --- /dev/null +++ b/initialize/proxy.go @@ -0,0 +1,48 @@ +package initialize + +import ( + "aurora/internal/proxys" + "bufio" + "log/slog" + "net/url" + "os" +) + +func checkProxy() *proxys.IProxy { + var proxies []string + proxyUrl := os.Getenv("PROXY_URL") + if proxyUrl != "" { + proxies = append(proxies, proxyUrl) + } + + if _, err := os.Stat("proxies.txt"); err == nil { + file, _ := os.Open("proxies.txt") + defer file.Close() + scanner := bufio.NewScanner(file) + for scanner.Scan() { + proxy := scanner.Text() + parsedURL, err := url.Parse(proxy) + if err != nil { + slog.Warn("proxy url is invalid", "url", proxy, "err", err) + continue + } + + // 如果缺少端口信息,不是完整的代理链接 + if parsedURL.Port() != "" { + proxies = append(proxies, proxy) + } else { + continue + } + } + } + + if len(proxies) == 0 { + proxy := os.Getenv("http_proxy") + if proxy != "" { + proxies = append(proxies, proxy) + } + } + + proxyIP := proxys.NewIProxyIP(proxies) + return &proxyIP +} diff --git a/initialize/router.go b/initialize/router.go new file mode 100644 index 0000000000000000000000000000000000000000..1d4a3078720526a55605b64f7163b7970cbff732 --- /dev/null +++ b/initialize/router.go @@ -0,0 +1,66 @@ +package initialize + +import ( + "aurora/middlewares" + "net/http" + "os" + + "github.com/gin-gonic/gin" +) + +func RegisterRouter() *gin.Engine { + handler := NewHandle( + checkProxy(), + ) + + router := gin.Default() + router.Use(middlewares.Cors) + // // 访问根目录时,跳转到 /web + // router.GET("/", func(c *gin.Context) { + // c.JSON(200, gin.H{ + // "message": "Hello, world!", + // }) + // }) + router.GET("/", func(c *gin.Context) { + c.Redirect(http.StatusFound, "/web") + }) + + router.GET("/ping", func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) + }) + + // 注册 prefixGroup 路由 + prefixGroup := os.Getenv("PREFIX") + if prefixGroup != "" { + registerGroupRoutes(router.Group(prefixGroup), handler) + } + // 注册无前缀的路由 + registerGroupRoutes(router.Group(""), handler) + + return router +} +func registerGroupRoutes(group *gin.RouterGroup, handler *Handler) { + // OPTIONS 路由 + group.OPTIONS("/v1/chat/completions", optionsHandler) + group.OPTIONS("/v1/chat/models", optionsHandler) + group.OPTIONS("/completions", optionsHandler) + group.OPTIONS("/models", optionsHandler) + group.OPTIONS("/api/v1/chat/completions", optionsHandler) + group.OPTIONS("/api/v1/models", optionsHandler) + group.OPTIONS("/hf/v1/chat/completions", optionsHandler) + group.OPTIONS("/hf/v1/models", optionsHandler) + + // POST 路由 + group.POST("/v1/chat/completions", middlewares.Authorization, handler.duckduckgo) + group.POST("/api/v1/chat/completions", middlewares.Authorization, handler.duckduckgo) + group.POST("/completions", middlewares.Authorization, handler.duckduckgo) + group.POST("/hf/v1/chat/completions", middlewares.Authorization, handler.duckduckgo) + + // GET 路由 + group.GET("/v1/models", middlewares.Authorization, handler.engines) + group.GET("/api/v1/models", middlewares.Authorization, handler.engines) + group.GET("/models", middlewares.Authorization, handler.engines) + group.GET("/hf/v1/models", middlewares.Authorization, handler.engines) +} diff --git a/internal/duckgo/request.go b/internal/duckgo/request.go new file mode 100644 index 0000000000000000000000000000000000000000..033997d29854980ea66aa6a29324b7c6346d0405 --- /dev/null +++ b/internal/duckgo/request.go @@ -0,0 +1,247 @@ +package duckgo + +import ( + "aurora/httpclient" + duckgotypes "aurora/typings/duckgo" + officialtypes "aurora/typings/official" + "bufio" + "bytes" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + "sync" + "time" + + "github.com/gin-gonic/gin" +) + +var ( + Token *XqdgToken + UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" +) + +type XqdgToken struct { + Token string `json:"token"` + M sync.Mutex `json:"-"` + ExpireAt time.Time `json:"expire"` +} + +// func InitXVQD(client httpclient.AuroraHttpClient, proxyUrl string) (string, error) { +// if Token == nil { +// Token = &XqdgToken{ +// Token: "", +// M: sync.Mutex{}, +// } +// } +// Token.M.Lock() +// defer Token.M.Unlock() +// if Token.Token == "" || Token.ExpireAt.Before(time.Now()) { +// status, err := postStatus(client, proxyUrl) +// if err != nil { +// return "", err +// } +// defer status.Body.Close() +// token := status.Header.Get("x-vqd-4") +// if token == "" { +// return "", errors.New("no x-vqd-4 token") +// } +// Token.Token = token +// Token.ExpireAt = time.Now().Add(time.Minute * 3) +// } +// +// return Token.Token, nil +// } + +func InitXVQD(client httpclient.AuroraHttpClient, proxyUrl string) (string, error) { + if Token == nil { + Token = &XqdgToken{ + Token: "", + M: sync.Mutex{}, + } + } + Token.M.Lock() + defer Token.M.Unlock() + + // 如果 token 已经失效或为空,尝试重新获取 + if Token.Token == "" || Token.ExpireAt.Before(time.Now()) { + const maxRetries = 3 // 设置最大重试次数 + const retryInterval = 2 * time.Second // 设置每次重试间隔 + + for retries := 0; retries < maxRetries; retries++ { + // 发送请求 + status, err := postStatus(client, proxyUrl) + if err != nil { + // 如果是非网络类错误,直接返回 + if retries == maxRetries-1 { + return "", err + } + // 网络错误,等待重试 + time.Sleep(retryInterval) + continue + } + + // 获取 x-vqd-4 token + defer status.Body.Close() + token := status.Header.Get("x-vqd-4") + if token == "" { + // 如果没有获取到 token,判断是否是最后一次重试 + if retries == maxRetries-1 { + return "", errors.New("no x-vqd-4 token after retries") + } + // 没有获取到 token,等待重试 + time.Sleep(retryInterval) + continue + } + + // 成功获取到 token + Token.Token = token + Token.ExpireAt = time.Now().Add(time.Minute * 3) + return Token.Token, nil + } + + // 重试完仍未成功,返回错误 + return "", errors.New("failed to get x-vqd-4 token after retries") + } + + // 如果 token 已存在且有效,直接返回 + return Token.Token, nil +} + +func postStatus(client httpclient.AuroraHttpClient, proxyUrl string) (*http.Response, error) { + if proxyUrl != "" { + client.SetProxy(proxyUrl) + } + header := createHeader() + header.Set("accept", "*/*") + header.Set("x-vqd-accept", "1") + response, err := client.Request(httpclient.GET, "https://duckduckgo.com/duckchat/v1/status", header, nil, nil) + if err != nil { + return nil, err + } + return response, nil +} + +func POSTconversation(client httpclient.AuroraHttpClient, request duckgotypes.ApiRequest, token string, proxyUrl string) (*http.Response, error) { + if proxyUrl != "" { + client.SetProxy(proxyUrl) + } + body_json, err := json.Marshal(request) + if err != nil { + return &http.Response{}, err + } + header := createHeader() + header.Set("accept", "text/event-stream") + header.Set("x-vqd-4", token) + response, err := client.Request(httpclient.POST, "https://duckduckgo.com/duckchat/v1/chat", header, nil, bytes.NewBuffer(body_json)) + if err != nil { + return nil, err + } + return response, nil +} + +func Handle_request_error(c *gin.Context, response *http.Response) bool { + if response.StatusCode != 200 { + // Try read response body as JSON + var error_response map[string]interface{} + err := json.NewDecoder(response.Body).Decode(&error_response) + if err != nil { + // Read response body + body, _ := io.ReadAll(response.Body) + c.JSON(response.StatusCode, gin.H{"error": gin.H{ + "message": "Unknown error", + "type": "internal_server_error", + "param": nil, + "code": "500", + "details": string(body), + }}) + return true + } + c.JSON(response.StatusCode, gin.H{"error": gin.H{ + "message": error_response["detail"], + "type": response.Status, + "param": nil, + "code": "error", + }}) + return true + } + return false +} + +func createHeader() httpclient.AuroraHeaders { + header := make(httpclient.AuroraHeaders) + header.Set("accept-language", "zh-CN,zh;q=0.9") + header.Set("content-type", "application/json") + header.Set("origin", "https://duckduckgo.com") + header.Set("referer", "https://duckduckgo.com/") + header.Set("sec-ch-ua", `"Chromium";v="120", "Google Chrome";v="120", "Not-A.Brand";v="99"`) + header.Set("sec-ch-ua-mobile", "?0") + header.Set("sec-ch-ua-platform", `"Windows"`) + header.Set("sec-fetch-dest", "empty") + header.Set("sec-fetch-mode", "cors") + header.Set("sec-fetch-site", "same-origin") + header.Set("user-agent", UA) + return header +} + +func Handler(c *gin.Context, response *http.Response, oldRequest duckgotypes.ApiRequest, stream bool) string { + reader := bufio.NewReader(response.Body) + if stream { + // Response content type is text/event-stream + c.Header("Content-Type", "text/event-stream") + } else { + // Response content type is application/json + c.Header("Content-Type", "application/json") + } + + var previousText strings.Builder + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + break + } + return "" + } + if len(line) < 6 { + continue + } + line = line[6:] + if !strings.HasPrefix(line, "[DONE]") { + var originalResponse duckgotypes.ApiResponse + err = json.Unmarshal([]byte(line), &originalResponse) + if err != nil { + continue + } + if originalResponse.Action != "success" { + c.JSON(500, gin.H{"error": "Error"}) + return "" + } + responseString := "" + if originalResponse.Message != "" { + previousText.WriteString(originalResponse.Message) + translatedResponse := officialtypes.NewChatCompletionChunkWithModel(originalResponse.Message, originalResponse.Model) + responseString = "data: " + translatedResponse.String() + "\n\n" + } + + if responseString == "" { + continue + } + + if stream { + _, err = c.Writer.WriteString(responseString) + if err != nil { + return "" + } + c.Writer.Flush() + } + } else { + if stream { + final_line := officialtypes.StopChunkWithModel("stop", oldRequest.Model) + c.Writer.WriteString("data: " + final_line.String() + "\n\n") + } + } + } + return previousText.String() +} diff --git a/internal/proxys/proxys.go b/internal/proxys/proxys.go new file mode 100644 index 0000000000000000000000000000000000000000..8ee40d222f0509c892ee53ff649c6cb06b3bebde --- /dev/null +++ b/internal/proxys/proxys.go @@ -0,0 +1,35 @@ +package proxys + +import "sync" + +type IProxy struct { + ips []string + lock sync.Mutex +} + +func NewIProxyIP(ips []string) IProxy { + return IProxy{ + ips: ips, + } +} + +func (p *IProxy) GetIPS() int { + return len(p.ips) +} + +func (p *IProxy) GetProxyIP() string { + if p == nil { + return "" + } + + p.lock.Lock() + defer p.lock.Unlock() + + if len(p.ips) == 0 { + return "" + } + + proxyIp := p.ips[0] + p.ips = append(p.ips[1:], proxyIp) + return proxyIp +} diff --git a/main.go b/main.go new file mode 100644 index 0000000000000000000000000000000000000000..1c34fc862bb783d800c7e88b66142433b9a4e9ef --- /dev/null +++ b/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "aurora/initialize" + "embed" + "io/fs" + "log" + "net/http" + "os" + + "github.com/gin-gonic/gin" + + "github.com/acheong08/endless" + "github.com/joho/godotenv" +) + +//go:embed web/* +var staticFiles embed.FS + +func main() { + _ = godotenv.Load(".env") + gin.SetMode(gin.ReleaseMode) + router := initialize.RegisterRouter() + subFS, err := fs.Sub(staticFiles, "web") + if err != nil { + log.Fatal(err) + } + router.StaticFS("/web", http.FS(subFS)) + host := os.Getenv("SERVER_HOST") + port := os.Getenv("SERVER_PORT") + tlsCert := os.Getenv("TLS_CERT") + tlsKey := os.Getenv("TLS_KEY") + + if host == "" { + host = "0.0.0.0" + } + if port == "" { + port = os.Getenv("PORT") + if port == "" { + port = "7860" + } + } + + if tlsCert != "" && tlsKey != "" { + _ = endless.ListenAndServeTLS(host+":"+port, tlsCert, tlsKey, router) + } else { + _ = endless.ListenAndServe(host+":"+port, router) + } +} diff --git a/middlewares/auth.go b/middlewares/auth.go new file mode 100644 index 0000000000000000000000000000000000000000..f67e5198e5fdf9043a79520910d839569e1516f4 --- /dev/null +++ b/middlewares/auth.go @@ -0,0 +1,31 @@ +package middlewares + +import ( + "github.com/gin-gonic/gin" + "os" + "strings" +) + +func Authorization(c *gin.Context) { + customer_key := os.Getenv("Authorization") + if customer_key != "" { + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.JSON(401, gin.H{"error": "Unauthorized"}) + c.Abort() + return + } + tokenParts := strings.Split(strings.Replace(authHeader, "Bearer ", "", 1)," ") + customAccessToken := tokenParts[0] + if customer_key != customAccessToken { + c.JSON(401, gin.H{"error": "Unauthorized"}) + c.Abort() + return + } + if len(tokenParts) > 1 { + openaiAccessToken := tokenParts[1] + c.Request.Header.Set("Authorization", "Bearer " + openaiAccessToken) + } + } + c.Next() +} diff --git a/middlewares/cors.go b/middlewares/cors.go new file mode 100644 index 0000000000000000000000000000000000000000..8818637675f48b98abf5211570750767f5ad1232 --- /dev/null +++ b/middlewares/cors.go @@ -0,0 +1,10 @@ +package middlewares + +import "github.com/gin-gonic/gin" + +func Cors(c *gin.Context) { + c.Header("Access-Control-Allow-Origin", "*") + c.Header("Access-Control-Allow-Methods", "*") + c.Header("Access-Control-Allow-Headers", "*") + c.Next() +} diff --git a/release.bat b/release.bat new file mode 100644 index 0000000000000000000000000000000000000000..b7b5ac8dfe996ac4c5f7d1fc8e58bea8c205515b --- /dev/null +++ b/release.bat @@ -0,0 +1,56 @@ +@echo off +SETLOCAL + +REM 指定编码为 UTF-8 +chcp 65001 + +REM 设置要生成的可执行文件的名称 +set OUTPUT_NAME=aurora + +REM 设置 Go 源文件的名称 +SET GOFILE=aurora + +REM 设置输出目录 +SET OUTPUTDIR=target + +REM 确保输出目录存在 +IF NOT EXIST %OUTPUTDIR% MKDIR %OUTPUTDIR% + +REM 编译为 Windows/amd64 +echo 开始编译 Windows/amd64 +SET GOOS=windows +SET GOARCH=amd64 +go build -o %OUTPUTDIR%/%OUTPUT_NAME%_windows_amd64.exe %GOFILE% +echo 编译完成 Windows/amd64 + +REM 编译为 Windows/386 +echo 开始编译 Windows/386 +SET GOOS=windows +SET GOARCH=386 +go build -o %OUTPUTDIR%/%OUTPUT_NAME%_windows_386.exe %GOFILE% +echo 编译完成 Windows/386 + +REM 编译为 Linux/amd64 +echo 开始编译 Linux/amd64 +SET GOOS=linux +SET GOARCH=amd64 +go build -o %OUTPUTDIR%/%OUTPUT_NAME%_linux_amd64 %GOFILE% +echo 编译完成 Linux/amd64 + +REM 编译为 macOS/amd64 +echo 开始编译 macOS/amd64 +SET GOOS=darwin +SET GOARCH=amd64 +go build -o %OUTPUTDIR%/%OUTPUT_NAME%_macos_amd64 %GOFILE% +echo 编译完成 macOS/amd64 + +REM 编译为 freebsd/amd64 +echo 开始编译 freebsd/amd64 +SET GOOS=freebsd +SET GOARCH=amd64 +go build -o %OUTPUTDIR%/%OUTPUT_NAME%_freebsd_amd64 %GOFILE% +echo 编译完成 freebsd/amd64 + +REM 结束批处理脚本 +ENDLOCAL +echo 编译完成! diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2593b666e39d9a6893b8cea0b279112aeffeda92 --- /dev/null +++ b/render.yaml @@ -0,0 +1,7 @@ +services: + - type: web + name: duck2api + env: docker + dockerfilePath: ./Dockerfile + plan: free + diff --git a/typings/duckgo/request.go b/typings/duckgo/request.go new file mode 100644 index 0000000000000000000000000000000000000000..b901d1c7f37ff3ee588c989ce66232e55a79fa25 --- /dev/null +++ b/typings/duckgo/request.go @@ -0,0 +1,23 @@ +package duckgo + +type ApiRequest struct { + Model string `json:"model"` + Messages []messages `json:"messages"` +} +type messages struct { + Role string `json:"role"` + Content string `json:"content"` +} + +func (a *ApiRequest) AddMessage(role string, content string) { + a.Messages = append(a.Messages, messages{ + Role: role, + Content: content, + }) +} + +func NewApiRequest(model string) ApiRequest { + return ApiRequest{ + Model: model, + } +} diff --git a/typings/duckgo/response.go b/typings/duckgo/response.go new file mode 100644 index 0000000000000000000000000000000000000000..010bea4db4aa17f374ecde33455f85c3548f4953 --- /dev/null +++ b/typings/duckgo/response.go @@ -0,0 +1,9 @@ +package duckgo + +type ApiResponse struct { + Message string `json:"message"` + Created int `json:"created"` + Id string `json:"id"` + Action string `json:"action"` + Model string `json:"model"` +} diff --git a/typings/official/request.go b/typings/official/request.go new file mode 100644 index 0000000000000000000000000000000000000000..87345ba76f697dd228564f8b9f3bbb6d759aee65 --- /dev/null +++ b/typings/official/request.go @@ -0,0 +1,21 @@ +package official + +type APIRequest struct { + Messages []api_message `json:"messages"` + Stream bool `json:"stream"` + Model string `json:"model"` + PluginIDs []string `json:"plugin_ids"` +} + +type api_message struct { + Role string `json:"role"` + Content interface{} `json:"content"` +} + +type OpenAISessionToken struct { + SessionToken string `json:"session_token"` +} + +type OpenAIRefreshToken struct { + RefreshToken string `json:"refresh_token"` +} diff --git a/typings/official/response.go b/typings/official/response.go new file mode 100644 index 0000000000000000000000000000000000000000..6dcaaa2916d0379bd4f7769cbc8f1a525f8ec917 --- /dev/null +++ b/typings/official/response.go @@ -0,0 +1,162 @@ +package official + +import "encoding/json" + +type ChatCompletionChunk struct { + ID string `json:"id"` + Object string `json:"object"` + Created int64 `json:"created"` + Model string `json:"model"` + Choices []Choices `json:"choices"` +} + +func (chunk *ChatCompletionChunk) String() string { + resp, _ := json.Marshal(chunk) + return string(resp) +} + +type Choices struct { + Delta Delta `json:"delta"` + Index int `json:"index"` + FinishReason interface{} `json:"finish_reason"` +} + +type Delta struct { + Content string `json:"content,omitempty"` + Role string `json:"role,omitempty"` +} + +func NewChatCompletionChunk(text string) ChatCompletionChunk { + return ChatCompletionChunk{ + ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "chat.completion.chunk", + Created: 0, + Model: "gpt-4o-mini", + Choices: []Choices{ + { + Index: 0, + Delta: Delta{ + Content: text, + }, + FinishReason: nil, + }, + }, + } +} + +func NewChatCompletionChunkWithModel(text string, model string) ChatCompletionChunk { + return ChatCompletionChunk{ + ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "chat.completion.chunk", + Created: 0, + Model: model, + Choices: []Choices{ + { + Index: 0, + Delta: Delta{ + Content: text, + }, + FinishReason: nil, + }, + }, + } +} + +func StopChunkWithModel(reason string, model string) ChatCompletionChunk { + return ChatCompletionChunk{ + ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "chat.completion.chunk", + Created: 0, + Model: model, + Choices: []Choices{ + { + Index: 0, + FinishReason: reason, + }, + }, + } +} + +func StopChunk(reason string) ChatCompletionChunk { + return ChatCompletionChunk{ + ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "chat.completion.chunk", + Created: 0, + Model: "gpt-4o-mini", + Choices: []Choices{ + { + Index: 0, + FinishReason: reason, + }, + }, + } +} + +type ChatCompletion struct { + ID string `json:"id"` + Object string `json:"object"` + Created int64 `json:"created"` + Model string `json:"model"` + Usage usage `json:"usage"` + Choices []Choice `json:"choices"` +} +type Msg struct { + Role string `json:"role"` + Content string `json:"content"` +} +type Choice struct { + Index int `json:"index"` + Message Msg `json:"message"` + FinishReason interface{} `json:"finish_reason"` +} +type usage struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` +} + +func NewChatCompletionWithModel(text string, model string) ChatCompletion { + return ChatCompletion{ + ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "chat.completion", + Created: int64(0), + Model: model, + Usage: usage{ + PromptTokens: 0, + CompletionTokens: 0, + TotalTokens: 0, + }, + Choices: []Choice{ + { + Message: Msg{ + Content: text, + Role: "assistant", + }, + Index: 0, + }, + }, + } +} + +func NewChatCompletion(full_test string, input_tokens, output_tokens int) ChatCompletion { + return ChatCompletion{ + ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "chat.completion", + Created: int64(0), + Model: "gpt-4o-mini", + Usage: usage{ + PromptTokens: input_tokens, + CompletionTokens: output_tokens, + TotalTokens: input_tokens + output_tokens, + }, + Choices: []Choice{ + { + Message: Msg{ + Content: full_test, + Role: "assistant", + }, + Index: 0, + }, + }, + } +} diff --git a/typings/typings.go b/typings/typings.go new file mode 100644 index 0000000000000000000000000000000000000000..044a5ead907fcea53649191a71cc78b58ff62cf0 --- /dev/null +++ b/typings/typings.go @@ -0,0 +1,10 @@ +package typings + +type GenericResponseLine struct { + Line string `json:"line"` + Error string `json:"error"` +} + +type StringStruct struct { + Text string `json:"text"` +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000000000000000000000000000000000000..a507c6279304ebc999621672f1e74c70b32e2af1 --- /dev/null +++ b/util/util.go @@ -0,0 +1,40 @@ +package util + +import ( + "log/slog" + "math/rand" + "time" + + "github.com/pkoukk/tiktoken-go" +) + +func RandomLanguage() string { + // 初始化随机数生成器 + rand.Seed(time.Now().UnixNano()) + // 语言列表 + 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"} + // 随机选择一个语言 + randomIndex := rand.Intn(len(languages)) + return languages[randomIndex] +} + +func RandomHexadecimalString() string { + rand.Seed(time.Now().UnixNano()) + const charset = "0123456789abcdef" + const length = 16 // The length of the string you want to generate + b := make([]byte, length) + for i := range b { + b[i] = charset[rand.Intn(len(charset))] + } + return string(b) +} +func CountToken(input string) int { + encoding := "gpt-4o-mini" + tkm, err := tiktoken.EncodingForModel(encoding) + if err != nil { + slog.Warn("tiktoken.EncodingForModel error:", err) + return 0 + } + token := tkm.Encode(input, nil, nil) + return len(token) +} diff --git a/util/utils_test.go b/util/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..be2a0d9a7a515492d65893fa5c0de29a110d550a --- /dev/null +++ b/util/utils_test.go @@ -0,0 +1,11 @@ +package util + +import ( + "fmt" + "testing" +) + +func TestRandomHexadecimalString(t *testing.T) { + var str = RandomHexadecimalString() + fmt.Println(str) +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000000000000000000000000000000000000..15103b0d4c6b439a6568160fcbc0eb03ec1bf9cc --- /dev/null +++ b/vercel.json @@ -0,0 +1,8 @@ +{ + "routes": [ + { + "src": "/.*", + "dest": "/api/router.go" + } + ] +} diff --git a/web/avatar.png b/web/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..0d65fe7c5d9b508416b34ac8a08e50e5616a46cc Binary files /dev/null and b/web/avatar.png differ diff --git a/web/icon.png b/web/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5730de9c25af4ab0082e4f81f222da76bd1ab9a9 Binary files /dev/null and b/web/icon.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000000000000000000000000000000000000..f658696206ff58062a793ad0c2f9ee1f7bd95af2 --- /dev/null +++ b/web/index.html @@ -0,0 +1,7007 @@ + + + + + + + + + + + + + + + + + ChatGPT + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
ChatGPT
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
ChatGPT
+
+ + +
+ +
+
+
+
+ +
+
+
+
+ + +
+ + +
+
+
+
+
+
+ + + +
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+
+
+
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ +
+
+
+
+ + + +
+
+ + + +
+
+
+
+
+
+ + +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + + + + + + + + + + + + + + + + +