Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/Test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
137 changes: 86 additions & 51 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,60 +1,95 @@
name: Build and Release

on:
push:
branches:
- main
paths-ignore:
- 'README.md'
workflow_dispatch:

Comment on lines 3 to 5
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
35 changes: 25 additions & 10 deletions .github/workflows/build_docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Comment on lines 42 to 45

- 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
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,6 +20,7 @@ FROM alpine:latest

# 设置工作目录
WORKDIR /app
RUN apk add --no-cache tzdata

# 从构建阶段复制编译好的应用和资源
COPY --from=builder /app/duck2api /app/duck2api
Expand Down
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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


Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
2.1.6
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export GOPROXY=https://goproxy.io
go get

export CGO_ENABLED=0
PKG=aurora
PKG=duck2api

targets=(
"windows/amd64"
Expand Down
95 changes: 63 additions & 32 deletions conversion/requests/duckgo/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"):
Expand All @@ -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{}
}
Comment on lines +73 to +77

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()
}
Loading
Loading