diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index 2f081ce..ff901b1 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -10,11 +10,12 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: - go-version: '1.22' + go-version-file: go.mod + cache: true - name: Build run: go build -v ./... - name: Test diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ef98b14..3ea02be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,60 +1,95 @@ name: Build and Release on: - push: - branches: - - main - paths-ignore: - - 'README.md' workflow_dispatch: +permissions: + contents: write + jobs: + version: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + tag: ${{ steps.version.outputs.tag }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Read version + id: version + shell: bash + run: | + version="$(tr -d '[:space:]' < VERSION)" + echo "version=${version}" >> "${GITHUB_OUTPUT}" + echo "tag=v${version}" >> "${GITHUB_OUTPUT}" + build: + needs: version + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - goos: linux + goarch: amd64 + - goos: linux + goarch: arm64 + - goos: windows + goarch: amd64 + - goos: darwin + goarch: amd64 + - goos: freebsd + goarch: amd64 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Build binary + env: + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + CGO_ENABLED: 0 + VERSION: ${{ needs.version.outputs.version }} + shell: bash + run: | + artifact="duck2api-${VERSION}-${GOOS}-${GOARCH}" + mkdir -p "${artifact}" + output="duck2api" + if [ "${GOOS}" = "windows" ]; then + output="duck2api.exe" + fi + go build -trimpath -ldflags="-s -w" -o "${artifact}/${output}" . + tar -czf "${artifact}.tar.gz" "${artifact}" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: duck2api-${{ needs.version.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }} + path: duck2api-${{ needs.version.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz + if-no-files-found: error + + release: + needs: + - version + - build runs-on: ubuntu-latest - name: Build steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: 'stable' - check-latest: true - - - name: Build binary - run: | - mkdir -p artifact - 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 .. - 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 .. - 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 .. - 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 .. - - - name: Upload artifact - uses: actions/upload-artifact@main - with: - name: duck2api-pre-built.zip - path: | - duck2api-linux-amd64.tar.gz - duck2api-windows-amd64.tar.gz - duck2api-darwin-amd64.tar.gz - duck2api-freebsd-amd64.tar.gz - - - name: Create release - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GHCR_PAT }} - with: - tag_name: v2.1.5 - files: | - duck2api-linux-amd64.tar.gz - duck2api-windows-amd64.tar.gz - duck2api-darwin-amd64.tar.gz - duck2api-freebsd-amd64.tar.gz - - - name: Delete workflow runs - uses: Mattraks/delete-workflow-runs@v2 - with: - token: ${{ github.token }} - repository: ${{ github.repository }} - retain_days: 1 - keep_minimum_runs: 8 + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: dist + merge-multiple: true + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.version.outputs.tag }} + name: ${{ needs.version.outputs.tag }} + files: dist/*.tar.gz diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml index 5d3e59f..2be1c9c 100644 --- a/.github/workflows/build_docker.yml +++ b/.github/workflows/build_docker.yml @@ -5,41 +5,56 @@ on: branches: - main paths-ignore: - - 'README.md' + - README.md workflow_dispatch: +permissions: + contents: read + packages: write + env: GHCR_REPO: ghcr.io/aurora-develop/duck2api jobs: - main: + docker: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Read version + id: version + shell: bash + run: | + version="$(tr -d '[:space:]' < VERSION)" + echo "version=${version}" >> "${GITHUB_OUTPUT}" + echo "tag=v${version}" >> "${GITHUB_OUTPUT}" + echo "sha_short=${GITHUB_SHA::12}" >> "${GITHUB_OUTPUT}" - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: - registry: ghcr.io + registry: ghcr username: ${{ github.repository_owner }} password: ${{ secrets.GHCR_PAT }} - name: Build and push to GHCR - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: context: . - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x file: Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/s390x push: true tags: | ${{ env.GHCR_REPO }}:latest - ${{ env.GHCR_REPO }}:${{ github.sha }} + ${{ env.GHCR_REPO }}:${{ steps.version.outputs.version }} + ${{ env.GHCR_REPO }}:${{ steps.version.outputs.tag }} + ${{ env.GHCR_REPO }}:${{ steps.version.outputs.sha_short }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index 64e0b01..4280857 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -# 使用 Go 1.21 官方镜像作为构建环境 -FROM golang:1.21 AS builder +# 使用 Go 1.25 官方镜像作为构建环境 +FROM golang:1.25 AS builder # 禁用 CGO ENV CGO_ENABLED=0 @@ -20,6 +20,7 @@ FROM alpine:latest # 设置工作目录 WORKDIR /app +RUN apk add --no-cache tzdata # 从构建阶段复制编译好的应用和资源 COPY --from=builder /app/duck2api /app/duck2api diff --git a/README.md b/README.md index 0df188b..72c3d2b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ # duck2api -# Web端 - -访问http://你的服务器ip:8080/web - -![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) - ## Deploy diff --git a/VERSION b/VERSION index 227cea2..399088b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.0 +2.1.6 diff --git a/build.sh b/build.sh index 3ccae9a..9904a16 100644 --- a/build.sh +++ b/build.sh @@ -5,7 +5,7 @@ export GOPROXY=https://goproxy.io go get export CGO_ENABLED=0 -PKG=aurora +PKG=duck2api targets=( "windows/amd64" diff --git a/conversion/requests/duckgo/convert.go b/conversion/requests/duckgo/convert.go index 9b3ea90..06d4693 100644 --- a/conversion/requests/duckgo/convert.go +++ b/conversion/requests/duckgo/convert.go @@ -3,15 +3,20 @@ package duckgo import ( duckgotypes "aurora/typings/duckgo" officialtypes "aurora/typings/official" + "crypto/rand" + "crypto/rsa" + "encoding/base64" + "math/big" "strings" + + "github.com/google/uuid" ) -func ConvertAPIRequest(api_request officialtypes.APIRequest) duckgotypes.ApiRequest { - inputModel := api_request.Model - duckgo_request := duckgotypes.NewApiRequest(inputModel) +func ConvertAPIRequest(apiRequest officialtypes.APIRequest) duckgotypes.ApiRequest { + inputModel := apiRequest.Model + duckgoRequest := duckgotypes.NewApiRequest(inputModel) realModel := inputModel - // 如果模型未进行映射,则直接使用输入模型,方便后续用户使用 duckduckgo 添加的新模型。 modelLower := strings.ToLower(inputModel) switch { case strings.HasPrefix(modelLower, "gpt-3.5"): @@ -22,40 +27,66 @@ func ConvertAPIRequest(api_request officialtypes.APIRequest) duckgotypes.ApiRequ realModel = "meta-llama/Llama-3.3-70B-Instruct-Turbo" case strings.HasPrefix(modelLower, "mixtral-8x7b"): realModel = "mistralai/Mixtral-8x7B-Instruct-v0.1" + case strings.HasPrefix(modelLower, "llama-4-scout"): + realModel = "meta-llama/Llama-4-Scout-17B-16E-Instruct" + case strings.HasPrefix(modelLower, "mistral-small"): + realModel = "mistralai/Mistral-Small-24B-Instruct-2501" } - duckgo_request.Model = realModel - content := buildContent(&api_request) - duckgo_request.AddMessage("user", content) + duckgoRequest.Model = realModel + for _, message := range apiRequest.Messages { + role := message.Role + if role == "system" { + role = "user" + } + if role != "user" && role != "assistant" { + continue + } - return duckgo_request + content := extractContent(message.Content) + if content != "" { + duckgoRequest.AddMessage(role, content) + } + } + duckgoRequest.DurableStream = newDurableStream() + return duckgoRequest } -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) +func extractContent(content interface{}) string { + if arrayContent, ok := content.([]interface{}); ok { + var text strings.Builder + for _, element := range arrayContent { + elementMap, ok := element.(map[string]interface{}) + if !ok || elementMap["type"] != "text" { + continue } - content.WriteString(role + ":" + contentStr + ";\r\n") + contentStr, _ := elementMap["text"].(string) + text.WriteString(contentStr) } + return text.String() + } + + contentStr, _ := content.(string) + return contentStr +} + +func newDurableStream() duckgotypes.DurableStream { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return duckgotypes.DurableStream{} + } + + return duckgotypes.DurableStream{ + MessageID: uuid.NewString(), + ConversationID: uuid.NewString(), + PublicKey: duckgotypes.PublicKey{ + Alg: "RSA-OAEP-256", + E: base64.RawURLEncoding.EncodeToString(big.NewInt(int64(key.PublicKey.E)).Bytes()), + Ext: true, + KeyOps: []string{"encrypt"}, + Kty: "RSA", + N: base64.RawURLEncoding.EncodeToString(key.PublicKey.N.Bytes()), + Use: "enc", + }, } - return content.String() } diff --git a/go.mod b/go.mod index 9240505..1f54776 100644 --- a/go.mod +++ b/go.mod @@ -1,55 +1,62 @@ module aurora -go 1.21 +go 1.25.0 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/bogdanfinn/fhttp v0.6.8 + github.com/bogdanfinn/tls-client v1.15.0 + github.com/dop251/goja v0.0.0-20260607104546-987c54ae30db + github.com/gin-gonic/gin v1.12.0 + github.com/go-resty/resty/v2 v2.17.2 github.com/joho/godotenv v1.5.1 - github.com/pkoukk/tiktoken-go v0.1.7 + github.com/pkoukk/tiktoken-go v0.1.8 ) 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/PuerkitoBio/goquery v1.12.0 // indirect + github.com/andybalholm/brotli v1.2.1 // indirect + github.com/andybalholm/cascadia v1.3.4 // indirect + github.com/bdandy/go-errors v1.2.2 // indirect + github.com/bdandy/go-socks4 v1.2.3 // indirect + github.com/bogdanfinn/quic-go-utls v1.0.9-utls // indirect + github.com/bogdanfinn/utls v1.7.7-barnius // indirect + github.com/bogdanfinn/websocket v1.5.5-barnius // indirect + github.com/bytedance/gopkg v0.1.4 // indirect + github.com/bytedance/sonic v1.15.2 // indirect + github.com/bytedance/sonic/loader v0.5.1 // indirect + github.com/cloudwego/base64x v0.1.7 // indirect + github.com/dlclark/regexp2 v1.12.0 // indirect + github.com/dlclark/regexp2/v2 v2.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/gin-contrib/sse v1.1.1 // 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/go-playground/validator/v10 v10.30.3 // indirect + github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // 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/klauspost/compress v1.18.6 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-isatty v0.0.22 // 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/pelletier/go-toml/v2 v2.3.1 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.60.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 + github.com/ugorji/go/codec v1.3.1 // indirect + go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect + golang.org/x/arch v0.27.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index 1cff10a..3ba327c 100644 --- a/go.sum +++ b/go.sum @@ -1,206 +1,148 @@ 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/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo= +github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ= 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/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= +github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg= +github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM= +github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q= +github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM= +github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic= +github.com/bdandy/go-socks4 v1.2.3/go.mod h1:98kiVFgpdogR8aIGLWLvjDVZ8XcKPsSI/ypGrO+bqHI= +github.com/bogdanfinn/fhttp v0.6.8 h1:LiQyHOY3i0QoxxNB7nq27/nGNNbtPj0fuBPozhR7Ws4= +github.com/bogdanfinn/fhttp v0.6.8/go.mod h1:A+EKDzMx2hb4IUbMx4TlkoHnaJEiLl8r/1Ss1Y+5e5M= +github.com/bogdanfinn/quic-go-utls v1.0.9-utls h1:tV6eDEiRbRCcepALSzxR94JUVD3N3ACIiRLgyc2Ep8s= +github.com/bogdanfinn/quic-go-utls v1.0.9-utls/go.mod h1:aHph9B9H9yPOt5xnhWKSOum27DJAqpiHzwX+gjvaXcg= +github.com/bogdanfinn/tls-client v1.15.0 h1:A+J8ZIggYbIg/6Cnzz4tX3aY0Hyt9k2lhO9qVnImDxQ= +github.com/bogdanfinn/tls-client v1.15.0/go.mod h1:LsU6mXVn8MOFDwTkyRfI7V1BZM1p0wf2ZfZsICW/1fM= +github.com/bogdanfinn/utls v1.7.7-barnius h1:OuJ497cc7F3yKNVHRsYPQdGggmk5x6+V5ZlrCR7fOLU= +github.com/bogdanfinn/utls v1.7.7-barnius/go.mod h1:aAK1VZQlpKZClF1WEQeq6kyclbkPq4hz6xTbB5xSlmg= +github.com/bogdanfinn/websocket v1.5.5-barnius h1:bY+qnxpai1qe7Jmjx+Sds/cmOSpuuLoR8x61rWltjOI= +github.com/bogdanfinn/websocket v1.5.5-barnius/go.mod h1:gvvEw6pTKHb7yOiFvIfAFTStQWyrm25BMVCTj5wRSsI= +github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM= +github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4= +github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo= +github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA= +github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI= +github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI= +github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg= 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/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= +github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2/v2 v2.1.1 h1:LCUGyd9Wf+r+VVOl8Ny38JTpWJcAsdVnCIuhhtthmKw= +github.com/dlclark/regexp2/v2 v2.1.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU= +github.com/dop251/goja v0.0.0-20260607104546-987c54ae30db h1:ubW1GFF76d2lU5Ypl+3YW2FbB0KNsUFCsQTFWHWm7Tk= +github.com/dop251/goja v0.0.0-20260607104546-987c54ae30db/go.mod h1:h4/KduHCHa/1EOf928I/bvV0g2zIkvYdQaUqll4KxSg= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko= +github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= 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/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= +github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= +github.com/go-resty/resty/v2 v2.17.2 h1:FQW5oHYcIlkCNrMD2lloGScxcHJ0gkjshV3qcQAyHQk= +github.com/go-resty/resty/v2 v2.17.2/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= +github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= +github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= 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/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 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/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= 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/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkoukk/tiktoken-go v0.1.8 h1:85ENo+3FpWgAACBaEUVp+lctuTcYUO7BtmfhlN/QTRo= +github.com/pkoukk/tiktoken-go v0.1.8/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/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= +github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0= +github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk= 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/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8= +go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +golang.org/x/arch v0.27.0 h1:0WNVcR8u9yFz8j5FvdHpgwNp3FS5U4guYdzHwEiGjoU= +golang.org/x/arch v0.27.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/net v0.0.0-20211104170005-ce137452f963/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= 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/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 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/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 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= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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/initialize/handlers.go b/initialize/handlers.go index 2461cce..5012b4b 100644 --- a/initialize/handlers.go +++ b/initialize/handlers.go @@ -5,7 +5,9 @@ import ( "aurora/httpclient/bogdanfinn" "aurora/internal/duckgo" "aurora/internal/proxys" + duckgotypes "aurora/typings/duckgo" officialtypes "aurora/typings/official" + "net/http" "github.com/gin-gonic/gin" ) @@ -40,31 +42,19 @@ func (h *Handler) duckduckgo(c *gin.Context) { }}) return } - proxyUrl := h.proxy.GetProxyIP() - client := bogdanfinn.NewStdClient() - token, err := duckgo.InitXVQD(client, proxyUrl) + translated_request, response, err := h.startDuckDuckGoRequest(original_request) 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) + response_part := duckgo.Handler(c, response, translated_request, original_request.Stream) if c.Writer.Status() != 200 { return } @@ -75,6 +65,103 @@ func (h *Handler) duckduckgo(c *gin.Context) { } } +func (h *Handler) responses(c *gin.Context) { + var responseRequest officialtypes.ResponseAPIRequest + err := c.BindJSON(&responseRequest) + 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 + } + + chatRequest := responseRequest.ToChatCompletionRequest() + translatedRequest, response, err := h.startDuckDuckGoRequest(chatRequest) + if err != nil { + c.JSON(500, gin.H{ + "error": err.Error(), + }) + return + } + defer response.Body.Close() + if response.StatusCode != http.StatusOK { + c.JSON(response.StatusCode, gin.H{ + "error": duckgo.ReadResponseError(response).Error(), + }) + return + } + + responseText := duckgo.ReadResponseText(response) + + if responseRequest.Stream { + writeResponsesStream(c, responseText, translatedRequest.Model) + return + } + + c.JSON(http.StatusOK, officialtypes.NewResponseAPIWithModel(responseText, translatedRequest.Model)) +} + +func (h *Handler) startDuckDuckGoRequest(originalRequest officialtypes.APIRequest) (duckgotypes.ApiRequest, *http.Response, error) { + proxyUrl := h.proxy.GetProxyIP() + client := bogdanfinn.NewStdClient() + token, err := duckgo.InitXVQD(client, proxyUrl) + if err != nil { + return duckgotypes.ApiRequest{}, nil, err + } + + translatedRequest := duckgoConvert.ConvertAPIRequest(originalRequest) + response, err := duckgo.POSTconversation(client, translatedRequest, token, proxyUrl) + if err != nil { + return duckgotypes.ApiRequest{}, nil, err + } + return translatedRequest, response, nil +} + +func writeResponsesStream(c *gin.Context, text string, model string) { + c.Header("Content-Type", "text/event-stream") + c.Header("Cache-Control", "no-cache") + c.Header("Connection", "keep-alive") + + response := officialtypes.NewResponseAPIWithModel("", model) + response.Status = "in_progress" + response.Output = []officialtypes.ResponseOutput{} + output := officialtypes.NewResponseOutput("") + output.Status = "in_progress" + part := officialtypes.ResponseOutputContent{ + Type: "output_text", + Text: "", + Annotations: []interface{}{}, + } + donePart := officialtypes.ResponseOutputContent{ + Type: "output_text", + Text: text, + Annotations: []interface{}{}, + } + events := []officialtypes.ResponseStreamEvent{ + {Type: "response.created", Sequence: 1, Response: &response}, + {Type: "response.output_item.added", Sequence: 2, OutputIndex: 0, Item: &output}, + {Type: "response.content_part.added", Sequence: 3, ItemID: output.ID, OutputIndex: 0, ContentIndex: 0, Part: part}, + {Type: "response.output_text.delta", Sequence: 4, ItemID: output.ID, OutputIndex: 0, ContentIndex: 0, Delta: text}, + {Type: "response.output_text.done", Sequence: 5, ItemID: output.ID, OutputIndex: 0, ContentIndex: 0, Text: text}, + {Type: "response.content_part.done", Sequence: 6, ItemID: output.ID, OutputIndex: 0, ContentIndex: 0, Part: donePart}, + } + + completed := officialtypes.NewResponseAPIWithModel(text, model) + events = append(events, + officialtypes.ResponseStreamEvent{Type: "response.output_item.done", Sequence: 7, OutputIndex: 0, Item: &completed.Output[0]}, + officialtypes.ResponseStreamEvent{Type: "response.completed", Sequence: 8, Response: &completed}, + ) + + for _, event := range events { + c.Writer.WriteString("event: " + event.Type + "\n") + c.Writer.WriteString("data: " + event.String() + "\n\n") + c.Writer.Flush() + } +} + func (h *Handler) engines(c *gin.Context) { type ResData struct { ID string `json:"id"` @@ -96,11 +183,16 @@ func (h *Handler) engines(c *gin.Context) { // Supported models modelIDs := []string{ "gpt-4o-mini", - "o3-mini", + "gpt-5-mini", + "tinfoil/gpt-oss-120b", "gpt-3.5-turbo-0125", "claude-3-haiku-20240307", - "meta-llama/Llama-3.3-70B-Instruct-Turbo", - "mistralai/Mixtral-8x7B-Instruct-v0.1", + "claude-haiku-4-5", + "llama-3.3-70b", + "llama-4-scout", + "mistral-small", + "meta-llama/Llama-4-Scout-17B-16E-Instruct", + "mistralai/Mistral-Small-24B-Instruct-2501", } for _, modelID := range modelIDs { diff --git a/initialize/router.go b/initialize/router.go index 94d06e6..e47f540 100644 --- a/initialize/router.go +++ b/initialize/router.go @@ -33,15 +33,23 @@ func RegisterRouter() *gin.Engine { { prefixRouter.OPTIONS("/v1/chat/completions", optionsHandler) prefixRouter.OPTIONS("/v1/chat/models", optionsHandler) + prefixRouter.OPTIONS("/v1/responses", optionsHandler) + prefixRouter.OPTIONS("/v1/response", optionsHandler) prefixRouter.POST("/v1/chat/completions", middlewares.Authorization, handler.duckduckgo) + prefixRouter.POST("/v1/responses", middlewares.Authorization, handler.responses) + prefixRouter.POST("/v1/response", middlewares.Authorization, handler.responses) prefixRouter.GET("/v1/models", middlewares.Authorization, handler.engines) } } router.OPTIONS("/v1/chat/completions", optionsHandler) router.OPTIONS("/v1/chat/models", optionsHandler) + router.OPTIONS("/v1/responses", optionsHandler) + router.OPTIONS("/v1/response", optionsHandler) authGroup := router.Group("").Use(middlewares.Authorization) authGroup.POST("/v1/chat/completions", handler.duckduckgo) + authGroup.POST("/v1/responses", handler.responses) + authGroup.POST("/v1/response", handler.responses) authGroup.GET("/v1/models", handler.engines) return router } diff --git a/internal/duckgo/request.go b/internal/duckgo/request.go index c640dfb..1235fbd 100644 --- a/internal/duckgo/request.go +++ b/internal/duckgo/request.go @@ -6,19 +6,26 @@ import ( officialtypes "aurora/typings/official" "bufio" "bytes" + "crypto/rand" + "encoding/base64" + "encoding/hex" "encoding/json" "errors" - "github.com/gin-gonic/gin" + "fmt" "io" "net/http" + "regexp" "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" + Token *XqdgToken + FEVersion *XqdgToken + UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" ) type XqdgToken struct { @@ -36,18 +43,21 @@ func InitXVQD(client httpclient.AuroraHttpClient, proxyUrl string) (string, erro } Token.M.Lock() defer Token.M.Unlock() - if Token.Token == "" || Token.ExpireAt.Before(time.Now()) { + if Token.Token == "" { 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") + vqdHash := status.Header.Get("x-vqd-hash-1") + if vqdHash == "" { + return "", errors.New("no x-vqd-hash-1 token") + } + token, err := GenerateVQDHash(vqdHash) + if err != nil { + return "", err } Token.Token = token - Token.ExpireAt = time.Now().Add(time.Minute * 3) } return Token.Token, nil @@ -60,7 +70,7 @@ func postStatus(client httpclient.AuroraHttpClient, proxyUrl string) (*http.Resp 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) + response, err := client.Request(httpclient.GET, "https://duck.ai/duckchat/v1/status", header, nil, nil) if err != nil { return nil, err } @@ -71,17 +81,29 @@ func POSTconversation(client httpclient.AuroraHttpClient, request duckgotypes.Ap 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 + + maxRetries := 3 + var response *http.Response + var err error + + for i := 0; i <= maxRetries; i++ { + response, err = postConversationOnce(client, request, token) + if err != nil { + return nil, err + } + + if response.StatusCode != http.StatusTeapot && response.StatusCode != http.StatusTooManyRequests { + return response, nil + } + + response.Body.Close() + resetXVQD() + token, err = InitXVQD(client, proxyUrl) + if err != nil { + return nil, err + } } + return response, nil } @@ -115,11 +137,11 @@ func Handle_request_error(c *gin.Context, response *http.Response) bool { func createHeader() httpclient.AuroraHeaders { header := make(httpclient.AuroraHeaders) - header.Set("accept-language", "zh-CN,zh;q=0.9") + header.Set("accept-language", "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7") 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("origin", "https://duck.ai") + header.Set("referer", "https://duck.ai/") + header.Set("sec-ch-ua", `"Chromium";v="148", "Google Chrome";v="148", "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") @@ -129,6 +151,140 @@ func createHeader() httpclient.AuroraHeaders { return header } +func postConversationOnce(client httpclient.AuroraHttpClient, request duckgotypes.ApiRequest, token string) (*http.Response, error) { + bodyJSON, err := json.Marshal(request) + if err != nil { + return &http.Response{}, err + } + header := createHeader() + header.Set("accept", "text/event-stream") + header.Set("priority", "u=1, i") + header.Set("x-ddg-journey-id", randomHex(16)) + header.Set("x-fe-signals", createFESignals()) + if feVersion, err := InitFEVersion(client, ""); err == nil && feVersion != "" { + header.Set("x-fe-version", feVersion) + } + header.Set("x-vqd-hash-1", token) + return client.Request(httpclient.POST, "https://duck.ai/duckchat/v1/chat", header, nil, bytes.NewBuffer(bodyJSON)) +} + +func InitFEVersion(client httpclient.AuroraHttpClient, proxyUrl string) (string, error) { + if FEVersion == nil { + FEVersion = &XqdgToken{ + Token: "", + M: sync.Mutex{}, + } + } + FEVersion.M.Lock() + defer FEVersion.M.Unlock() + if FEVersion.Token != "" && FEVersion.ExpireAt.After(time.Now()) { + return FEVersion.Token, nil + } + + if proxyUrl != "" { + client.SetProxy(proxyUrl) + } + header := createHeader() + header.Set("accept", "text/html") + response, err := client.Request(httpclient.GET, "https://duck.ai/", header, nil, nil) + if err != nil { + return "", err + } + defer response.Body.Close() + + body, err := io.ReadAll(response.Body) + if err != nil { + return "", err + } + versionTagMatch := regexp.MustCompile(`data-version-tag="([^"]+)"`).FindSubmatch(body) + versionShaMatch := regexp.MustCompile(`data-version-sha="([^"]+)"`).FindSubmatch(body) + if len(versionTagMatch) < 2 || len(versionShaMatch) < 2 { + return "", errors.New("duck.ai version metadata not found") + } + + FEVersion.Token = fmt.Sprintf("%s-%s", versionTagMatch[1], versionShaMatch[1]) + FEVersion.ExpireAt = time.Now().Add(30 * time.Minute) + return FEVersion.Token, nil +} + +func createFESignals() string { + start := time.Now().UnixMilli() + payload := map[string]interface{}{ + "start": start, + "events": []map[string]interface{}{ + { + "name": "startNewChat_free", + "delta": 56, + }, + }, + "end": 246, + } + body, _ := json.Marshal(payload) + return base64.StdEncoding.EncodeToString(body) +} + +func randomHex(byteLength int) string { + buffer := make([]byte, byteLength) + if _, err := rand.Read(buffer); err != nil { + return fmt.Sprintf("%x", time.Now().UnixNano()) + } + return hex.EncodeToString(buffer) +} + +func resetXVQD() { + if Token == nil { + return + } + Token.M.Lock() + defer Token.M.Unlock() + Token.Token = "" +} + +func ReadResponseError(response *http.Response) error { + var errorResponse map[string]interface{} + if err := json.NewDecoder(response.Body).Decode(&errorResponse); err == nil { + if detail, ok := errorResponse["detail"]; ok { + return fmt.Errorf("%s: %v", response.Status, detail) + } + return fmt.Errorf("%s: %v", response.Status, errorResponse) + } + + body, _ := io.ReadAll(response.Body) + if len(body) == 0 { + return fmt.Errorf("%s", response.Status) + } + return fmt.Errorf("%s: %s", response.Status, string(body)) +} + +func ReadResponseText(response *http.Response) string { + reader := bufio.NewReader(response.Body) + 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]") { + continue + } + + var originalResponse duckgotypes.ApiResponse + err = json.Unmarshal([]byte(line), &originalResponse) + if err != nil || originalResponse.Action != "success" { + continue + } + previousText.WriteString(originalResponse.Message) + } + return previousText.String() +} + func Handler(c *gin.Context, response *http.Response, oldRequest duckgotypes.ApiRequest, stream bool) string { reader := bufio.NewReader(response.Body) if stream { diff --git a/internal/duckgo/vqd.go b/internal/duckgo/vqd.go new file mode 100644 index 0000000..4a4fac9 --- /dev/null +++ b/internal/duckgo/vqd.go @@ -0,0 +1,457 @@ +package duckgo + +import ( + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "math/rand" + "os" + + "github.com/dop251/goja" +) + +const ( + defaultVQDUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" + defaultVQDStack = "l@https://duck.ai/dist/duckai-dist/entry.duckai.c9340e95bd2f7fdc3302.js:2:1308110\n" +) + +func GenerateVQDHash(vqdHashRequest string) (string, error) { + if vqdHashRequest == "" { + return "", errors.New("empty vqd hash request") + } + + jsBytes, err := base64.StdEncoding.DecodeString(vqdHashRequest) + if err != nil { + return "", fmt.Errorf("decode vqd hash request: %w", err) + } + + payload, err := executeVQDHashScript(string(jsBytes)) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString([]byte(payload)), nil +} + +func vqdUserAgent() string { + if value := os.Getenv("X_USER_AGENT"); value != "" { + return value + } + return defaultVQDUserAgent +} + +func vqdStack() string { + if value := os.Getenv("X_VQD_STACK"); value != "" { + return value + } + return defaultVQDStack +} + +func executeVQDHashScript(jsCode string) (string, error) { + vm := goja.New() + if err := installVQDHelpers(vm); err != nil { + return "", err + } + if _, err := vm.RunString(vqdBrowserPrelude); err != nil { + return "", fmt.Errorf("initialize vqd browser mock: %w", err) + } + + value, err := vm.RunString(jsCode) + if err != nil { + return "", fmt.Errorf("execute vqd hash script: %s", jsErrorString(vm, err, nil)) + } + + if promise, ok := value.Export().(*goja.Promise); ok { + switch promise.State() { + case goja.PromiseStateFulfilled: + value = promise.Result() + case goja.PromiseStateRejected: + return "", fmt.Errorf("vqd hash script rejected: %s", jsErrorString(vm, nil, promise.Result())) + default: + return "", errors.New("vqd hash script did not resolve") + } + } + + vm.Set("__vqd_result", value) + payload, err := vm.RunString(vqdResultMutationScript) + if err != nil { + return "", fmt.Errorf("encode vqd hash result: %s", jsErrorString(vm, err, nil)) + } + if goja.IsUndefined(payload) || goja.IsNull(payload) { + return "", errors.New("vqd hash script returned an empty result") + } + + return payload.String(), nil +} + +func installVQDHelpers(vm *goja.Runtime) error { + if err := vm.Set("__goVQDUserAgent", vqdUserAgent()); err != nil { + return fmt.Errorf("install vqd user agent helper: %w", err) + } + if err := vm.Set("__goVQDStack", vqdStack()); err != nil { + return fmt.Errorf("install vqd stack helper: %w", err) + } + if err := vm.Set("__goVQDDuration", fmt.Sprintf("%d", 20+rand.Intn(30))); err != nil { + return fmt.Errorf("install vqd duration helper: %w", err) + } + if err := vm.Set("__goSha256Base64", func(value string) string { + sum := sha256.Sum256([]byte(value)) + return base64.StdEncoding.EncodeToString(sum[:]) + }); err != nil { + return fmt.Errorf("install vqd sha256 helper: %w", err) + } + if err := vm.Set("__goBase64Encode", func(value string) string { + return base64.StdEncoding.EncodeToString([]byte(value)) + }); err != nil { + return fmt.Errorf("install vqd btoa helper: %w", err) + } + if err := vm.Set("__goBase64Decode", func(value string) (string, error) { + decoded, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return "", err + } + return string(decoded), nil + }); err != nil { + return fmt.Errorf("install vqd atob helper: %w", err) + } + return nil +} + +func jsErrorString(vm *goja.Runtime, err error, value goja.Value) string { + if err != nil { + if exception, ok := err.(*goja.Exception); ok { + return exception.String() + } + return err.Error() + } + if value == nil || goja.IsUndefined(value) || goja.IsNull(value) { + return "" + } + object := value.ToObject(vm) + if stack := object.Get("stack"); !goja.IsUndefined(stack) && !goja.IsNull(stack) { + return stack.String() + } + if message := object.Get("message"); !goja.IsUndefined(message) && !goja.IsNull(message) { + return message.String() + } + return value.String() +} + +const vqdResultMutationScript = ` +(function (result) { + if (!result || typeof result !== "object") { + throw new Error("VQD hash script did not return an object"); + } + if (!Array.isArray(result.client_hashes)) { + throw new Error("VQD hash script did not return client_hashes"); + } + + result.client_hashes[0] = __goVQDUserAgent; + result.client_hashes = result.client_hashes.map(function (value) { + return __goSha256Base64(value); + }); + + if (result.meta && typeof result.meta === "object") { + result.meta.origin = "https://duck.ai"; + result.meta.stack = __goVQDStack; + result.meta.duration = __goVQDDuration; + } + + return JSON.stringify(result); +})(__vqd_result); +` + +const vqdBrowserPrelude = ` +(function () { + const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"; + const originalObjectKeys = Object.keys; + Object.keys = function (value) { + if (value === null || value === undefined) { + return []; + } + return originalObjectKeys(value); + }; + + function toByteArray(value) { + const text = String(value); + const bytes = []; + for (let i = 0; i < text.length; i++) { + bytes.push(text.charCodeAt(i) & 255); + } + return bytes; + } + + function TextEncoder() {} + TextEncoder.prototype.encode = function (value) { + const encoded = encodeURIComponent(String(value)); + const bytes = []; + for (let i = 0; i < encoded.length; i++) { + if (encoded[i] === "%") { + bytes.push(parseInt(encoded.slice(i + 1, i + 3), 16)); + i += 2; + } else { + bytes.push(encoded.charCodeAt(i)); + } + } + return new Uint8Array(bytes); + }; + + const navigator = { + userAgent, + platform: "Win32", + language: "en-US", + languages: ["en-US", "en"], + cookieEnabled: true, + onLine: true, + hardwareConcurrency: 4, + maxTouchPoints: 0, + vendor: "Google Inc.", + vendorSub: "", + productSub: "20030107", + appName: "Netscape", + appVersion: userAgent, + product: "Gecko" + }; + + function Window() {} + Window.Array = Array; + Window.Promise = Promise; + Window.Proxy = Proxy; + Window.Symbol = Symbol; + Window.Window = Window; + Window.__Array = Array; + Window.__Promise = Promise; + Window.__Proxy = Proxy; + Window.__Symbol = Symbol; + Window.__Window = Window; + + function HTMLElement() {} + function HTMLDivElement() {} + function HTMLLIElement() {} + function HTMLIFrameElement() {} + + function NodeList(items) { + const values = items || []; + for (let i = 0; i < values.length; i++) { + this[i] = values[i]; + } + this.length = values.length; + } + NodeList.prototype.item = function (index) { + return this[index] || null; + }; + + function Element(tagName) { + this.tagName = String(tagName || "").toUpperCase(); + this.children = []; + this.parentNode = null; + this.ownerDocument = null; + this.attributes = {}; + this.innerHTML = ""; + this.textContent = ""; + this.srcdoc = ""; + this.style = { + cssText: "", + display: "inline-block", + getPropertyValue(name) { + if (String(name).toLowerCase() === "display") { + return this.display || "inline-block"; + } + return ""; + } + }; + this.offsetWidth = 1; + this.offsetHeight = 1; + this.scrollHeight = 1; + } + + Element.prototype.constructor = Element; + HTMLElement.prototype = Object.create(Element.prototype); + HTMLElement.prototype.constructor = HTMLElement; + HTMLDivElement.prototype = Object.create(HTMLElement.prototype); + HTMLDivElement.prototype.constructor = HTMLDivElement; + HTMLLIElement.prototype = Object.create(HTMLElement.prototype); + HTMLLIElement.prototype.constructor = HTMLLIElement; + HTMLIFrameElement.prototype = Object.create(HTMLElement.prototype); + HTMLIFrameElement.prototype.constructor = HTMLIFrameElement; + + Element.prototype.appendChild = function (child) { + child.parentNode = this; + child.ownerDocument = this.ownerDocument; + this.children.push(child); + return child; + }; + + Element.prototype.removeChild = function (child) { + const index = this.children.indexOf(child); + if (index >= 0) { + this.children.splice(index, 1); + child.parentNode = null; + } + return child; + }; + + Element.prototype.querySelectorAll = function (selector) { + selector = String(selector || "").toLowerCase(); + if (selector === "*") { + if (String(this.innerHTML || "") === "


]*>/gi); + return new NodeList(new Array(matches ? matches.length : 0).fill({})); + } + if (selector === "div") { + const matches = String(this.innerHTML || "").match(/ 0 ? matches[0] : null; + }; + + Element.prototype.getAttribute = function (name) { + return this.attributes[String(name)] || null; + }; + + Element.prototype.setAttribute = function (name, value) { + this.attributes[String(name)] = String(value); + }; + + Element.prototype.getBoundingClientRect = function () { + return { + width: this.offsetWidth || 1, + height: this.offsetHeight || 1, + top: 0, + right: this.offsetWidth || 1, + bottom: this.offsetHeight || 1, + left: 0 + }; + }; + + function createElement(tagName) { + tagName = String(tagName || "").toLowerCase(); + let element; + if (tagName === "div") { + element = new HTMLDivElement(); + } else if (tagName === "li") { + element = new HTMLLIElement(); + } else if (tagName === "iframe") { + element = new HTMLIFrameElement(); + } else { + element = new Element(tagName); + } + Element.call(element, tagName); + return element; + } + + function makeDocument() { + const document = {}; + const documentElement = new Element("html"); + const head = new Element("head"); + const body = new Element("body"); + + documentElement.ownerDocument = document; + head.ownerDocument = document; + body.ownerDocument = document; + documentElement.appendChild(head); + documentElement.appendChild(body); + + Object.assign(document, { + documentElement, + head, + body, + createElement(tagName) { + const element = createElement(tagName); + element.ownerDocument = document; + return element; + }, + querySelectorAll(selector) { + selector = String(selector || ""); + if (selector === "#jsa" && this.__jsa) { + return new NodeList([this.__jsa]); + } + return documentElement.querySelectorAll(selector); + }, + querySelector(selector) { + const matches = this.querySelectorAll(selector); + return matches.length > 0 ? matches[0] : null; + } + }); + + return document; + } + + function makeFrameWindow(contentDocument) { + const frameWindow = { + Array, + Promise, + Proxy, + Symbol, + Window, + document: contentDocument + }; + frameWindow.self = frameWindow; + frameWindow.window = frameWindow; + frameWindow.top = globalThis; + contentDocument.defaultView = frameWindow; + return frameWindow; + } + + const document = makeDocument(); + const contentDocument = makeDocument(); + const cspMeta = contentDocument.createElement("meta"); + cspMeta.setAttribute("http-equiv", "Content-Security-Policy"); + cspMeta.setAttribute("content", "default-src 'none'; script-src 'unsafe-inline';"); + contentDocument.head.appendChild(cspMeta); + + const hashFrame = document.createElement("iframe"); + hashFrame.setAttribute("id", "jsa"); + hashFrame.setAttribute("sandbox", "allow-scripts allow-same-origin"); + hashFrame.style.cssText = "position: absolute; left: -9999px; top: -9999px;"; + hashFrame.contentDocument = contentDocument; + hashFrame.contentWindow = makeFrameWindow(contentDocument); + document.__jsa = hashFrame; + document.body.appendChild(hashFrame); + + Object.defineProperty(globalThis, "window", { value: globalThis, writable: true, configurable: true }); + Object.defineProperty(globalThis, "self", { value: globalThis, writable: true, configurable: true }); + Object.defineProperty(globalThis, "top", { value: globalThis, writable: true, configurable: true }); + Object.defineProperty(globalThis, Symbol.toStringTag, { value: "Window", writable: true, configurable: true }); + Object.defineProperty(globalThis, "__DDG_BE_VERSION__", { value: 1, writable: true, configurable: true }); + Object.defineProperty(globalThis, "__DDG_FE_CHAT_HASH__", { value: 1, writable: true, configurable: true }); + Object.defineProperty(globalThis, "Window", { value: Window, writable: true, configurable: true }); + Object.defineProperty(globalThis, "Element", { value: Element, writable: true, configurable: true }); + Object.defineProperty(globalThis, "HTMLElement", { value: HTMLElement, writable: true, configurable: true }); + Object.defineProperty(globalThis, "HTMLDivElement", { value: HTMLDivElement, writable: true, configurable: true }); + Object.defineProperty(globalThis, "HTMLLIElement", { value: HTMLLIElement, writable: true, configurable: true }); + Object.defineProperty(globalThis, "HTMLIFrameElement", { value: HTMLIFrameElement, writable: true, configurable: true }); + Object.defineProperty(globalThis, "NodeList", { value: NodeList, writable: true, configurable: true }); + Object.defineProperty(globalThis, "TextEncoder", { value: TextEncoder, writable: true, configurable: true }); + Object.defineProperty(globalThis, "navigator", { value: navigator, writable: true, configurable: true }); + Object.defineProperty(globalThis, "document", { value: document, writable: true, configurable: true }); + Object.defineProperty(globalThis, "btoa", { value: function (value) { return __goBase64Encode(String(value)); }, writable: true, configurable: true }); + Object.defineProperty(globalThis, "atob", { value: function (value) { return __goBase64Decode(String(value)); }, writable: true, configurable: true }); + Object.defineProperty(globalThis, "getComputedStyle", { + value(element) { + return element && element.style ? element.style : { + getPropertyValue() { + return ""; + } + }; + }, + writable: true, + configurable: true + }); +})(); +` diff --git a/main.go b/main.go index 63c416a..1f02e2e 100644 --- a/main.go +++ b/main.go @@ -2,10 +2,6 @@ package main import ( "aurora/initialize" - "embed" - "io/fs" - "log" - "net/http" "os" "github.com/gin-gonic/gin" @@ -14,18 +10,10 @@ import ( "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") diff --git a/typings/duckgo/request.go b/typings/duckgo/request.go index b901d1c..1835bd8 100644 --- a/typings/duckgo/request.go +++ b/typings/duckgo/request.go @@ -1,9 +1,43 @@ package duckgo +type DurableStream struct { + MessageID string `json:"messageId"` + ConversationID string `json:"conversationId"` + PublicKey PublicKey `json:"publicKey"` +} + +type Metadata struct { + ToolChoice ToolChoice `json:"toolChoice"` +} + +type PublicKey struct { + Alg string `json:"alg"` + E string `json:"e"` + Ext bool `json:"ext"` + KeyOps []string `json:"key_ops"` + Kty string `json:"kty"` + N string `json:"n"` + Use string `json:"use"` +} + +type ToolChoice struct { + NewsSearch bool `json:"NewsSearch"` + VideosSearch bool `json:"VideosSearch"` + LocalSearch bool `json:"LocalSearch"` + WeatherForecast bool `json:"WeatherForecast"` +} + type ApiRequest struct { - Model string `json:"model"` - Messages []messages `json:"messages"` + Model string `json:"model"` + Metadata Metadata `json:"metadata"` + Messages []messages `json:"messages"` + CanUseTools bool `json:"canUseTools"` + ReasoningEffort string `json:"reasoningEffort"` + CanUseApproxLocation *bool `json:"canUseApproxLocation"` + CanDelegateImageGeneration *bool `json:"canDelegateImageGeneration"` + DurableStream DurableStream `json:"durableStream"` } + type messages struct { Role string `json:"role"` Content string `json:"content"` @@ -18,6 +52,16 @@ func (a *ApiRequest) AddMessage(role string, content string) { func NewApiRequest(model string) ApiRequest { return ApiRequest{ - Model: model, + Model: model, + CanUseTools: true, + ReasoningEffort: "minimal", + Metadata: Metadata{ + ToolChoice: ToolChoice{ + NewsSearch: false, + VideosSearch: false, + LocalSearch: false, + WeatherForecast: false, + }, + }, } } diff --git a/typings/official/request.go b/typings/official/request.go index 87345ba..0b588b9 100644 --- a/typings/official/request.go +++ b/typings/official/request.go @@ -1,5 +1,10 @@ package official +import ( + "fmt" + "strings" +) + type APIRequest struct { Messages []api_message `json:"messages"` Stream bool `json:"stream"` @@ -8,10 +13,117 @@ type APIRequest struct { } type api_message struct { - Role string `json:"role"` + Role string `json:"role"` Content interface{} `json:"content"` } +type ResponseAPIRequest struct { + Model string `json:"model"` + Input interface{} `json:"input"` + Instructions string `json:"instructions"` + Stream bool `json:"stream"` + PreviousResponseID string `json:"previous_response_id"` + MaxOutputTokens int `json:"max_output_tokens"` + Tools interface{} `json:"tools"` + ToolChoice interface{} `json:"tool_choice"` +} + +func (r ResponseAPIRequest) ToChatCompletionRequest() APIRequest { + request := APIRequest{ + Model: r.Model, + Stream: r.Stream, + } + + if strings.TrimSpace(request.Model) == "" { + request.Model = "gpt-5-mini" + } + if strings.TrimSpace(r.Instructions) != "" { + request.Messages = append(request.Messages, api_message{ + Role: "system", + Content: r.Instructions, + }) + } + + request.Messages = append(request.Messages, responseInputMessages(r.Input)...) + return request +} + +func responseInputMessages(input interface{}) []api_message { + switch value := input.(type) { + case string: + if strings.TrimSpace(value) == "" { + return nil + } + return []api_message{{Role: "user", Content: value}} + case []interface{}: + messages := make([]api_message, 0, len(value)) + for _, item := range value { + messages = append(messages, responseInputItemToMessages(item)...) + } + return messages + default: + return nil + } +} + +func responseInputItemToMessages(item interface{}) []api_message { + itemMap, ok := item.(map[string]interface{}) + if !ok { + return nil + } + + itemType, _ := itemMap["type"].(string) + switch itemType { + case "message", "": + role, _ := itemMap["role"].(string) + if role == "" { + role = "user" + } + content := responseContentText(itemMap["content"]) + if strings.TrimSpace(content) == "" { + return nil + } + return []api_message{{Role: role, Content: content}} + case "function_call_output": + output := responseContentText(itemMap["output"]) + if output == "" { + return nil + } + callID, _ := itemMap["call_id"].(string) + if callID != "" { + output = fmt.Sprintf("Tool output for %s:\n%s", callID, output) + } + return []api_message{{Role: "user", Content: output}} + default: + return nil + } +} + +func responseContentText(content interface{}) string { + switch value := content.(type) { + case string: + return value + case []interface{}: + var text strings.Builder + for _, part := range value { + partMap, ok := part.(map[string]interface{}) + if !ok { + continue + } + partType, _ := partMap["type"].(string) + switch partType { + case "input_text", "output_text", "text", "": + if partText, ok := partMap["text"].(string); ok { + text.WriteString(partText) + } + } + } + return text.String() + default: + return "" + } +} + type OpenAISessionToken struct { SessionToken string `json:"session_token"` } diff --git a/typings/official/response.go b/typings/official/response.go index 6dcaaa2..0dc2064 100644 --- a/typings/official/response.go +++ b/typings/official/response.go @@ -115,6 +115,98 @@ type usage struct { TotalTokens int `json:"total_tokens"` } +type ResponseAPI struct { + ID string `json:"id"` + Object string `json:"object"` + CreatedAt int64 `json:"created_at"` + Status string `json:"status"` + Model string `json:"model"` + Output []ResponseOutput `json:"output"` + OutputText string `json:"output_text"` + Usage ResponseUsage `json:"usage"` + ParallelToolCalls bool `json:"parallel_tool_calls"` + PreviousResponseID interface{} `json:"previous_response_id"` + Error interface{} `json:"error"` + IncompleteDetails interface{} `json:"incomplete_details"` + Metadata map[string]interface{} `json:"metadata"` +} + +type ResponseOutput struct { + ID string `json:"id"` + Type string `json:"type"` + Status string `json:"status"` + Role string `json:"role"` + Content []ResponseOutputContent `json:"content"` +} + +type ResponseOutputContent struct { + Type string `json:"type"` + Text string `json:"text"` + Annotations []interface{} `json:"annotations"` +} + +type ResponseUsage struct { + InputTokens int `json:"input_tokens"` + OutputTokens int `json:"output_tokens"` + TotalTokens int `json:"total_tokens"` + InputTokensDetails any `json:"input_tokens_details,omitempty"` + OutputTokensDetails any `json:"output_tokens_details,omitempty"` +} + +type ResponseStreamEvent struct { + Type string `json:"type"` + Sequence int `json:"sequence_number,omitempty"` + Response *ResponseAPI `json:"response,omitempty"` + Item *ResponseOutput `json:"item,omitempty"` + ItemID string `json:"item_id,omitempty"` + Part any `json:"part,omitempty"` + OutputIndex int `json:"output_index,omitempty"` + ContentIndex int `json:"content_index,omitempty"` + Delta string `json:"delta,omitempty"` + Text string `json:"text,omitempty"` +} + +func (event ResponseStreamEvent) String() string { + resp, _ := json.Marshal(event) + return string(resp) +} + +func NewResponseAPIWithModel(text string, model string) ResponseAPI { + if model == "" { + model = "gpt-5-mini" + } + return ResponseAPI{ + ID: "resp_QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Object: "response", + CreatedAt: 0, + Status: "completed", + Model: model, + Output: []ResponseOutput{ + NewResponseOutput(text), + }, + OutputText: text, + Usage: ResponseUsage{}, + ParallelToolCalls: true, + Metadata: map[string]interface{}{}, + } +} + +func NewResponseOutput(text string) ResponseOutput { + return ResponseOutput{ + ID: "msg_QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", + Type: "message", + Status: "completed", + Role: "assistant", + Content: []ResponseOutputContent{ + { + Type: "output_text", + Text: text, + Annotations: []interface{}{}, + }, + }, + } +} + func NewChatCompletionWithModel(text string, model string) ChatCompletion { return ChatCompletion{ ID: "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", diff --git a/util/util.go b/util/util.go index a507c62..f1818e0 100644 --- a/util/util.go +++ b/util/util.go @@ -32,7 +32,7 @@ func CountToken(input string) int { encoding := "gpt-4o-mini" tkm, err := tiktoken.EncodingForModel(encoding) if err != nil { - slog.Warn("tiktoken.EncodingForModel error:", err) + slog.Warn("tiktoken.EncodingForModel error", "err", err) return 0 } token := tkm.Encode(input, nil, nil) diff --git a/web/avatar.png b/web/avatar.png deleted file mode 100644 index 0d65fe7..0000000 Binary files a/web/avatar.png and /dev/null differ diff --git a/web/icon.png b/web/icon.png deleted file mode 100644 index 5730de9..0000000 Binary files a/web/icon.png and /dev/null differ diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 105bd6e..0000000 --- a/web/index.html +++ /dev/null @@ -1,7008 +0,0 @@ - - - - - - - - - - - - - - - - - ChatGPT - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
ChatGPT
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
ChatGPT
-
- - -
- -
-
-
-
- -
-
-
-
- - -
- - -
-
-
-
-
-
- - - -
-
-
-
-
- - - -
-
- - - -
-
- - - -
-
-
-
-
-
-
-
- - - -
-
- - - -
-
- - - -
-
-
- -
-
-
-
- - - -
-
- - - -
-
-
-
-
-
- - -
-
-
-
-
- -
-
-
- - -
-
-
-
- - - - - - - - - - - - - - - - - - -