├── .github └── workflows │ └── docker-publish.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── config.json.example ├── docker-compose.yml ├── go.mod ├── go.sum ├── main.go └── scripts ├── install-all-users.vbs ├── install-current-user.vbs ├── install.sh ├── replace_max_tokens.vbs ├── restore_max_tokens.vbs ├── uninstall-all-users.vbs ├── uninstall-current-user.vbs └── uninstall.sh /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | tags: [ 'v*.*.*' ] 7 | pull_request: 8 | branches: [ "master" ] 9 | 10 | env: 11 | REGISTRY: ghcr.io 12 | IMAGE_NAME: ${{ github.repository }} 13 | 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: read 21 | packages: write 22 | id-token: write 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v3 27 | 28 | - name: Install cosign 29 | if: github.event_name != 'pull_request' 30 | uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 31 | with: 32 | cosign-release: 'v2.1.1' 33 | 34 | - name: Set up Docker Buildx 35 | uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 36 | 37 | - name: Log into registry ${{ env.REGISTRY }} 38 | if: github.event_name != 'pull_request' 39 | uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 40 | with: 41 | registry: ${{ env.REGISTRY }} 42 | username: ${{ github.actor }} 43 | password: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | - name: Extract Docker metadata 46 | id: meta 47 | uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 48 | with: 49 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 50 | tags: | 51 | type=ref,event=branch 52 | type=ref,event=tag 53 | type=semver,pattern={{version}} 54 | type=sha 55 | type=raw,value=latest 56 | 57 | - name: Build and push Docker image 58 | id: build-and-push 59 | uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 60 | with: 61 | context: . 62 | push: ${{ github.event_name != 'pull_request' }} 63 | tags: ${{ steps.meta.outputs.tags }} 64 | labels: ${{ steps.meta.outputs.labels }} 65 | platforms: linux/amd64,linux/arm64 66 | cache-from: type=gha 67 | cache-to: type=gha,mode=max 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Reference https://github.com/github/gitignore/blob/master/Go.gitignore 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # Dependency directories (remove the comment below to include it) 16 | vendor/ 17 | 18 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 19 | *.o 20 | *.a 21 | *.so 22 | 23 | # OS General 24 | Thumbs.db 25 | .DS_Store 26 | 27 | # project 28 | *.cert 29 | *.key 30 | *.log 31 | bin/ 32 | config.json 33 | 34 | # Develop tools 35 | .vscode/ 36 | .idea/ 37 | *.swp -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | WORKDIR /app 4 | COPY . . 5 | 6 | ENV GO111MODULE=on GOPROXY=https://goproxy.cn,direct 7 | RUN go mod download 8 | 9 | RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o override 10 | 11 | FROM alpine:latest 12 | 13 | RUN apk --no-cache add ca-certificates 14 | 15 | COPY --from=builder /app/override /usr/local/bin/ 16 | COPY config.json.example /app/config.json 17 | 18 | WORKDIR /app 19 | VOLUME /app 20 | 21 | EXPOSE 8181 22 | CMD ["override"] 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 LINUX DO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Override 2 | 3 | ## 这个仓库什么也不能做,请不要盯着我。 4 | 5 | ### VSCode 配置: 6 | 7 | ```json 8 | "github.copilot.advanced": { 9 | "debug.overrideCAPIUrl": "http://127.0.0.1:8181/v1", 10 | "debug.overrideProxyUrl": "http://127.0.0.1:8181", 11 | "debug.chatOverrideProxyUrl": "http://127.0.0.1:8181/v1/chat/completions", 12 | "authProvider": "github-enterprise" 13 | }, 14 | "github-enterprise.uri": "https://cocopilot.org", 15 | ``` 16 | 17 | ### JetBrains等 配置: 18 | 19 | 按照 coco dash 页面截图配置后,执行对应系统的脚本后重启IDE: 20 | * `scripts/install.sh` 适用于 `macOS` 和 `Linux` 21 | * `scripts/install-all-users.vbs` 适用于 `Windows`,为电脑上所有用户配置,需要有管理员权限。 22 | * `scripts/install-current-user.vbs` 适用于 `Windows`,为当前用户配置,无需管理员权限。 23 | * `scripts/uninstall` 相关脚本与之对应,为卸载配置。 24 | 25 | 其中 `http://127.0.0.1:8181` 是你启动的 `override` 服务地址。 26 | 27 | ### config.json 配置 28 | 29 | ```json 30 | { 31 | "bind": "127.0.0.1:8181", 32 | "proxy_url": "", 33 | "timeout": 600, 34 | "codex_api_base": "https://api-proxy.oaipro.com/v1", 35 | "codex_api_key": "sk-xxx", 36 | "codex_api_organization": "", 37 | "codex_api_project": "", 38 | "codex_max_tokens": 500, 39 | "code_instruct_model": "gpt-3.5-turbo-instruct", 40 | "chat_api_base": "https://api-proxy.oaipro.com/v1", 41 | "chat_api_key": "sk-xxx", 42 | "chat_api_organization": "", 43 | "chat_api_project": "", 44 | "chat_max_tokens": 4096, 45 | "chat_model_default": "gpt-4o", 46 | "chat_model_map": {}, 47 | "chat_locale": "zh_CN", 48 | "auth_token": "" 49 | } 50 | 51 | ``` 52 | 53 | `organization` 和 `project` 除非你有,且知道怎么回事再填。 54 | 55 | `chat_model_map` 是个模型映射的字典。会将请求的模型映射到你想要的,如果不存在映射,则使用 `chat_model_default` 。 56 | 57 | `codex_max_tokens` 可以设置为你希望的最大Token数,你设置的时候最好知道自己在做什么。代码生成通常使用 `500` 即可。 58 | 59 | `chat_max_tokens` 可以设置为你希望的最大Token数,你设置的时候最好知道自己在做什么。`gpt-4o` 输出最大为 `4096` 60 | 61 | 可以通过 `OVERRIDE_` + 大写配置项作为环境变量,可以覆盖 `config.json` 中的值。例如:`OVERRIDE_CODEX_API_KEY=sk-xxxx` 62 | 63 | ### DeepSeek Coder 设置 64 | 如果你希望使用 DeepSeek Coder FIM 来进行代码补全,着重修改以下配置: 65 | 66 | ```json 67 | "codex_api_base": "https://api.deepseek.com/beta/v1", 68 | "codex_api_key": "sk-xxx", 69 | "code_instruct_model": "deepseek-coder", 70 | ``` 71 | 72 | ### Siliconflow 设置 73 | 如果你希望使用 Siliconflow FIM 模型来进行代码补全,着重修改以下配置: 74 | 75 | ```json 76 | "codex_api_base": "https://api.siliconflow.cn/v1", 77 | "codex_api_key": "sk-xxx,sk-xxx2,sk-xxx3...", 78 | "code_instruct_model": "Qwen/Qwen2.5-Coder-7B-Instruct", 79 | ``` 80 | 81 | 截至目前,Siliconflow 共有三个模型支持 FIM。分别是 `Qwen/Qwen2.5-Coder-7B-Instruct`、`deepseek-ai/DeepSeek-Coder-V2-Instruct` 、`deepseek-ai/DeepSeek-V2.5`。其中 `Qwen/Qwen2.5-Coder-7B-Instruct` 是免费模型,另外两个是收费模型。 82 | 83 | 如果你有很多 Siliconflow API Key, 可以以英文逗号分隔填入`codex_api_key`字段, 这样可以很好的避免Siliconflow官方的 TPM RateLimit 对你编码速度影响(尤其使用收费模型时,用户级别较低,TPM 最低只有 10k)。 84 | 85 | 86 | 87 | ### 本地大模型设置 88 | 1. 安装ollama 89 | 2. ollama run stable-code:code (这个模型较小,大部分显卡都能跑) 90 | 或者你的显卡比较高安装这个:ollama run stable-code:3b-code-fp16 91 | 3. 修改config.json里面的codex_api_base为http://localhost:11434/v1/chat 92 | 4. 修改code_instruct_model为你的模型名称,stable-code:code或者stable-code:3b-code-fp16 93 | 5. 剩下的就按照正常流程走即可。 94 | 6. 如果调不通,请确认http://localhost:11434/v1/chat可用。 95 | 96 | ### 重要说明 97 | `codex_max_tokens` 工作并不完美,已经移除。**JetBrains IDE 完美工作**,`VSCode` 需要执行以下脚本Patch之: 98 | 99 | * macOS `sed -i '' -E 's/\.maxPromptCompletionTokens\(([a-zA-Z0-9_]+),([0-9]+)\)/.maxPromptCompletionTokens(\1,2048)/' ~/.vscode/extensions/github.copilot-*/dist/extension.js` 100 | * Linux `sed -E 's/\.maxPromptCompletionTokens\(([a-zA-Z0-9_]+),([0-9]+)\)/.maxPromptCompletionTokens(\1,2048)/' ~/.vscode/extensions/github.copilot-*/dist/extension.js` 101 | * Windows 可以用如下的python脚本进行替换 102 | * 因为是Patch,所以:**Copilot每次升级都要执行一次**。 103 | * 具体原因是客户端需要根据 `max_tokens` 精密计算prompt,后台删减会有问题。 104 | 105 | ``` 106 | # github copilot extention replace script 107 | import re 108 | import glob 109 | import os 110 | 111 | file_paths = glob.glob(os.getenv("USERPROFILE") + r'\.vscode\extensions\github.copilot-*\dist\extension.js') 112 | if file_paths == list(): 113 | print("no copilot extension found") 114 | exit() 115 | 116 | pattern = re.compile(r'\.maxPromptCompletionTokens\(([a-zA-Z0-9_]+),([0-9]+)\)') 117 | replacement = r'.maxPromptCompletionTokens(\1,2048)' 118 | 119 | for file_path in file_paths: 120 | with open(file_path, 'r', encoding="utf-8") as file: 121 | content = file.read() 122 | 123 | new_content = pattern.sub(replacement, content) 124 | if new_content == content: 125 | print("no match found in " + file_path) 126 | continue 127 | else: 128 | print("replaced " + file_path) 129 | 130 | with open(file_path, 'w', encoding='utf-8') as file: 131 | file.write(new_content) 132 | 133 | print("replace finish") 134 | ``` 135 | 136 | ### 其他说明 137 | 1. 理论上,Chat 部分可以使用 `chat2api` ,而 Codex 代码生成部分则不太适合使用 `chat2api` 。 138 | 2. 代码生成部分做过延时生成和客户端 Cancel 处理,很有效节省你的Token。 139 | 3. 项目基于 `MIT` 协议发布,你可以修改,请保留原作者信息。 140 | 4. 有什么问题,请在论坛 https://linux.do 讨论,欢迎PR。 141 | 142 | ### Star History 143 | 144 | [![Star History Chart](https://api.star-history.com/svg?repos=linux-do/override&type=Date)](https://star-history.com/#linux-do/override&Date) 145 | 146 | -------------------------------------------------------------------------------- /config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "bind": "127.0.0.1:8181", 3 | "proxy_url": "", 4 | "timeout": 600, 5 | "codex_api_base": "https://api-proxy.oaipro.com/v1", 6 | "codex_api_key": "sk-xxx", 7 | "codex_api_organization": "", 8 | "codex_api_project": "", 9 | "codex_max_tokens": 500, 10 | "code_instruct_model": "gpt-3.5-turbo-instruct", 11 | "chat_api_base": "https://api-proxy.oaipro.com/v1", 12 | "chat_api_key": "sk-xxx", 13 | "chat_api_organization": "", 14 | "chat_api_project": "", 15 | "chat_max_tokens": 4096, 16 | "chat_model_default": "gpt-4o", 17 | "chat_model_map": {}, 18 | "chat_locale": "zh_CN", 19 | "auth_token": "" 20 | } 21 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | override-app: 3 | image: linux-do/override:latest 4 | container_name: override-app 5 | restart: always 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | volumes: 10 | - ./config.json:/app/config.json 11 | ports: 12 | - "8181:8181" 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module override 2 | 3 | go 1.21 4 | 5 | toolchain go1.21.4 6 | 7 | require ( 8 | github.com/gin-gonic/gin v1.10.0 9 | github.com/tidwall/gjson v1.17.1 10 | github.com/tidwall/sjson v1.2.5 11 | golang.org/x/net v0.25.0 12 | ) 13 | 14 | require ( 15 | github.com/bytedance/sonic v1.11.6 // indirect 16 | github.com/bytedance/sonic/loader v0.1.1 // indirect 17 | github.com/cloudwego/base64x v0.1.4 // indirect 18 | github.com/cloudwego/iasm v0.2.0 // indirect 19 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 20 | github.com/gin-contrib/sse v0.1.0 // indirect 21 | github.com/go-playground/locales v0.14.1 // indirect 22 | github.com/go-playground/universal-translator v0.18.1 // indirect 23 | github.com/go-playground/validator/v10 v10.20.0 // indirect 24 | github.com/goccy/go-json v0.10.2 // indirect 25 | github.com/google/go-cmp v0.5.9 // indirect 26 | github.com/json-iterator/go v1.1.12 // indirect 27 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 28 | github.com/kr/pretty v0.3.0 // indirect 29 | github.com/leodido/go-urn v1.4.0 // indirect 30 | github.com/mattn/go-isatty v0.0.20 // indirect 31 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 32 | github.com/modern-go/reflect2 v1.0.2 // indirect 33 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 34 | github.com/rogpeppe/go-internal v1.12.0 // indirect 35 | github.com/tidwall/match v1.1.1 // indirect 36 | github.com/tidwall/pretty v1.2.1 // indirect 37 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 38 | github.com/ugorji/go/codec v1.2.12 // indirect 39 | golang.org/x/arch v0.8.0 // indirect 40 | golang.org/x/crypto v0.23.0 // indirect 41 | golang.org/x/sys v0.20.0 // indirect 42 | golang.org/x/text v0.15.0 // indirect 43 | google.golang.org/protobuf v1.34.1 // indirect 44 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 45 | gopkg.in/yaml.v3 v3.0.1 // indirect 46 | ) 47 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= 2 | github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= 3 | github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= 4 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 5 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 6 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 7 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 8 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 9 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 14 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 15 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 16 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 17 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 18 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 19 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 20 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 21 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 22 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 23 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 24 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 25 | github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= 26 | github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 27 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 28 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 29 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 30 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 31 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 32 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 33 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 34 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 35 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 36 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 37 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 38 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 39 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 40 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 41 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 42 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 43 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 44 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 45 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 46 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 47 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 48 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 49 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 50 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 51 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 52 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 53 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 54 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 55 | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= 56 | github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 57 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 58 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 59 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 60 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 61 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 62 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 63 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 64 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 65 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 66 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 67 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 68 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 69 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 70 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 71 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 72 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 73 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 74 | github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 75 | github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= 76 | github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 77 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 78 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 79 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 80 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 81 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 82 | github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= 83 | github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= 84 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 85 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 86 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 87 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 88 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 89 | golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= 90 | golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 91 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 92 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 93 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 94 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 95 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 96 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 97 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 98 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 99 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 100 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 101 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 102 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 103 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 104 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 105 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 106 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 107 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 108 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 109 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 110 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 111 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 112 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 113 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "github.com/gin-gonic/gin" 10 | "github.com/tidwall/gjson" 11 | "github.com/tidwall/sjson" 12 | "golang.org/x/net/http2" 13 | "io" 14 | "log" 15 | "net/http" 16 | "net/url" 17 | "os" 18 | "reflect" 19 | "strconv" 20 | "strings" 21 | "time" 22 | "math/rand" 23 | ) 24 | 25 | const DefaultInstructModel = "gpt-3.5-turbo-instruct" 26 | 27 | const StableCodeModelPrefix = "stable-code" 28 | 29 | const DeepSeekCoderModel = "deepseek-coder" 30 | 31 | var SiliconflowModels = []string{"deepseek-ai/DeepSeek-V2.5", "deepseek-ai/DeepSeek-Coder-V2-Instruct", "Qwen/Qwen2.5-Coder-7B-Instruct"} 32 | 33 | type config struct { 34 | Bind string `json:"bind"` 35 | ProxyUrl string `json:"proxy_url"` 36 | Timeout int `json:"timeout"` 37 | CodexApiBase string `json:"codex_api_base"` 38 | CodexApiKey string `json:"codex_api_key"` 39 | CodexApiOrganization string `json:"codex_api_organization"` 40 | CodexApiProject string `json:"codex_api_project"` 41 | CodexMaxTokens int `json:"codex_max_tokens"` 42 | CodeInstructModel string `json:"code_instruct_model"` 43 | ChatApiBase string `json:"chat_api_base"` 44 | ChatApiKey string `json:"chat_api_key"` 45 | ChatApiOrganization string `json:"chat_api_organization"` 46 | ChatApiProject string `json:"chat_api_project"` 47 | ChatMaxTokens int `json:"chat_max_tokens"` 48 | ChatModelDefault string `json:"chat_model_default"` 49 | ChatModelMap map[string]string `json:"chat_model_map"` 50 | ChatLocale string `json:"chat_locale"` 51 | AuthToken string `json:"auth_token"` 52 | } 53 | 54 | func readConfig() *config { 55 | var configPath string 56 | if len(os.Args) > 1 { 57 | configPath = os.Args[1] 58 | } else { 59 | configPath = "config.json" 60 | } 61 | content, err := os.ReadFile(configPath) 62 | if nil != err { 63 | log.Fatal(err) 64 | } 65 | 66 | _cfg := &config{} 67 | err = json.Unmarshal(content, &_cfg) 68 | if nil != err { 69 | log.Fatal(err) 70 | } 71 | 72 | v := reflect.ValueOf(_cfg).Elem() 73 | t := v.Type() 74 | 75 | for i := 0; i < v.NumField(); i++ { 76 | field := v.Field(i) 77 | tag := t.Field(i).Tag.Get("json") 78 | if tag == "" { 79 | continue 80 | } 81 | 82 | value, exists := os.LookupEnv("OVERRIDE_" + strings.ToUpper(tag)) 83 | if !exists { 84 | continue 85 | } 86 | 87 | switch field.Kind() { 88 | case reflect.String: 89 | field.SetString(value) 90 | case reflect.Bool: 91 | if boolValue, err := strconv.ParseBool(value); err == nil { 92 | field.SetBool(boolValue) 93 | } 94 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 95 | if intValue, err := strconv.ParseInt(value, 10, 64); err == nil { 96 | field.SetInt(intValue) 97 | } 98 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 99 | if uintValue, err := strconv.ParseUint(value, 10, 64); err == nil { 100 | field.SetUint(uintValue) 101 | } 102 | case reflect.Float32, reflect.Float64: 103 | if floatValue, err := strconv.ParseFloat(value, field.Type().Bits()); err == nil { 104 | field.SetFloat(floatValue) 105 | } 106 | } 107 | } 108 | if _cfg.CodeInstructModel == "" { 109 | _cfg.CodeInstructModel = DefaultInstructModel 110 | } 111 | 112 | if _cfg.CodexMaxTokens == 0 { 113 | _cfg.CodexMaxTokens = 500 114 | } 115 | 116 | if _cfg.ChatMaxTokens == 0 { 117 | _cfg.ChatMaxTokens = 4096 118 | } 119 | 120 | return _cfg 121 | } 122 | 123 | func getClient(cfg *config) (*http.Client, error) { 124 | transport := &http.Transport{ 125 | ForceAttemptHTTP2: true, 126 | DisableKeepAlives: false, 127 | } 128 | 129 | err := http2.ConfigureTransport(transport) 130 | if nil != err { 131 | return nil, err 132 | } 133 | 134 | if "" != cfg.ProxyUrl { 135 | proxyUrl, err := url.Parse(cfg.ProxyUrl) 136 | if nil != err { 137 | return nil, err 138 | } 139 | 140 | transport.Proxy = http.ProxyURL(proxyUrl) 141 | } 142 | 143 | client := &http.Client{ 144 | Transport: transport, 145 | Timeout: time.Duration(cfg.Timeout) * time.Second, 146 | } 147 | 148 | return client, nil 149 | } 150 | 151 | func abortCodex(c *gin.Context, status int) { 152 | c.Header("Content-Type", "text/event-stream") 153 | 154 | c.String(status, "data: [DONE]\n") 155 | c.Abort() 156 | } 157 | 158 | func closeIO(c io.Closer) { 159 | err := c.Close() 160 | if nil != err { 161 | log.Println(err) 162 | } 163 | } 164 | 165 | type ProxyService struct { 166 | cfg *config 167 | client *http.Client 168 | } 169 | 170 | func NewProxyService(cfg *config) (*ProxyService, error) { 171 | client, err := getClient(cfg) 172 | if nil != err { 173 | return nil, err 174 | } 175 | 176 | return &ProxyService{ 177 | cfg: cfg, 178 | client: client, 179 | }, nil 180 | } 181 | func AuthMiddleware(authToken string) gin.HandlerFunc { 182 | return func(c *gin.Context) { 183 | token := c.Param("token") 184 | if token != authToken { 185 | c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) 186 | c.Abort() 187 | return 188 | } 189 | c.Next() 190 | } 191 | } 192 | 193 | func (s *ProxyService) InitRoutes(e *gin.Engine) { 194 | e.GET("/_ping", s.pong) 195 | e.GET("/models", s.models) 196 | e.GET("/v1/models", s.models) 197 | authToken := s.cfg.AuthToken // replace with your dynamic value as needed 198 | if authToken != "" { 199 | // 鉴权 200 | v1 := e.Group("/:token/v1/", AuthMiddleware(authToken)) 201 | { 202 | v1.POST("/chat/completions", s.completions) 203 | v1.POST("/engines/copilot-codex/completions", s.codeCompletions) 204 | 205 | v1.POST("/v1/chat/completions", s.completions) 206 | v1.POST("/v1/engines/copilot-codex/completions", s.codeCompletions) 207 | } 208 | } else { 209 | e.POST("/v1/chat/completions", s.completions) 210 | e.POST("/v1/engines/copilot-codex/completions", s.codeCompletions) 211 | 212 | e.POST("/v1/v1/chat/completions", s.completions) 213 | e.POST("/v1/v1/engines/copilot-codex/completions", s.codeCompletions) 214 | } 215 | } 216 | 217 | type Pong struct { 218 | Now int `json:"now"` 219 | Status string `json:"status"` 220 | Ns1 string `json:"ns1"` 221 | } 222 | 223 | func (s *ProxyService) pong(c *gin.Context) { 224 | c.JSON(http.StatusOK, Pong{ 225 | Now: time.Now().Second(), 226 | Status: "ok", 227 | Ns1: "200 OK", 228 | }) 229 | } 230 | 231 | func (s *ProxyService) models(c *gin.Context) { 232 | c.JSON(http.StatusOK, gin.H{ 233 | "data": []gin.H{ 234 | { 235 | "capabilities": gin.H{ 236 | "family": "gpt-3.5-turbo", 237 | "limits": gin.H{"max_prompt_tokens": 12288}, 238 | "object": "model_capabilities", 239 | "supports": gin.H{"tool_calls": true}, 240 | "tokenizer": "cl100k_base", 241 | "type": "chat", 242 | }, 243 | "id": "gpt-3.5-turbo", 244 | "name": "GPT 3.5 Turbo", 245 | "object": "model", 246 | "version": "gpt-3.5-turbo-0613", 247 | }, 248 | { 249 | "capabilities": gin.H{ 250 | "family": "gpt-3.5-turbo", 251 | "limits": gin.H{"max_prompt_tokens": 12288}, 252 | "object": "model_capabilities", 253 | "supports": gin.H{"tool_calls": true}, 254 | "tokenizer": "cl100k_base", 255 | "type": "chat", 256 | }, 257 | "id": "gpt-3.5-turbo-0613", 258 | "name": "GPT 3.5 Turbo", 259 | "object": "model", 260 | "version": "gpt-3.5-turbo-0613", 261 | }, 262 | { 263 | "capabilities": gin.H{ 264 | "family": "gpt-4", 265 | "limits": gin.H{"max_prompt_tokens": 20000}, 266 | "object": "model_capabilities", 267 | "supports": gin.H{"tool_calls": true}, 268 | "tokenizer": "cl100k_base", 269 | "type": "chat", 270 | }, 271 | "id": "gpt-4", 272 | "name": "GPT 4", 273 | "object": "model", 274 | "version": "gpt-4-0613", 275 | }, 276 | { 277 | "capabilities": gin.H{ 278 | "family": "gpt-4", 279 | "limits": gin.H{"max_prompt_tokens": 20000}, 280 | "object": "model_capabilities", 281 | "supports": gin.H{"tool_calls": true}, 282 | "tokenizer": "cl100k_base", 283 | "type": "chat", 284 | }, 285 | "id": "gpt-4-0613", 286 | "name": "GPT 4", 287 | "object": "model", 288 | "version": "gpt-4-0613", 289 | }, 290 | { 291 | "capabilities": gin.H{ 292 | "family": "gpt-4-turbo", 293 | "limits": gin.H{"max_prompt_tokens": 20000}, 294 | "object": "model_capabilities", 295 | "supports": gin.H{"parallel_tool_calls": true, "tool_calls": true}, 296 | "tokenizer": "cl100k_base", 297 | "type": "chat", 298 | }, 299 | "id": "gpt-4-0125-preview", 300 | "name": "GPT 4 Turbo", 301 | "object": "model", 302 | "version": "gpt-4-0125-preview", 303 | }, 304 | { 305 | "capabilities": gin.H{ 306 | "family": "gpt-4o", 307 | "limits": gin.H{"max_prompt_tokens": 20000}, 308 | "object": "model_capabilities", 309 | "supports": gin.H{"parallel_tool_calls": true, "tool_calls": true}, 310 | "tokenizer": "o200k_base", 311 | "type": "chat", 312 | }, 313 | "id": "gpt-4o", 314 | "name": "GPT 4o", 315 | "object": "model", 316 | "version": "gpt-4o-2024-05-13", 317 | }, 318 | { 319 | "capabilities": gin.H{ 320 | "family": "gpt-4o", 321 | "limits": gin.H{"max_prompt_tokens": 20000}, 322 | "object": "model_capabilities", 323 | "supports": gin.H{"parallel_tool_calls": true, "tool_calls": true}, 324 | "tokenizer": "o200k_base", 325 | "type": "chat", 326 | }, 327 | "id": "gpt-4o-2024-05-13", 328 | "name": "GPT 4o", 329 | "object": "model", 330 | "version": "gpt-4o-2024-05-13", 331 | }, 332 | { 333 | "capabilities": gin.H{ 334 | "family": "gpt-4o", 335 | "limits": gin.H{"max_prompt_tokens": 20000}, 336 | "object": "model_capabilities", 337 | "supports": gin.H{"parallel_tool_calls": true, "tool_calls": true}, 338 | "tokenizer": "o200k_base", 339 | "type": "chat", 340 | }, 341 | "id": "gpt-4-o-preview", 342 | "name": "GPT 4o", 343 | "object": "model", 344 | }, 345 | { 346 | "capabilities": gin.H{ 347 | "family": "text-embedding-ada-002", 348 | "limits": gin.H{"max_inputs": 256}, 349 | "object": "model_capabilities", 350 | "supports": gin.H{}, 351 | "tokenizer": "cl100k_base", 352 | "type": "embeddings", 353 | }, 354 | "id": "text-embedding-ada-002", 355 | "name": "Embedding V2 Ada", 356 | "object": "model", 357 | "version": "text-embedding-ada-002", 358 | }, 359 | { 360 | "capabilities": gin.H{ 361 | "family": "text-embedding-3-small", 362 | "limits": gin.H{"max_inputs": 256}, 363 | "object": "model_capabilities", 364 | "supports": gin.H{"dimensions": true}, 365 | "tokenizer": "cl100k_base", 366 | "type": "embeddings", 367 | }, 368 | "id": "text-embedding-3-small", 369 | "name": "Embedding V3 small", 370 | "object": "model", 371 | "version": "text-embedding-3-small", 372 | }, 373 | { 374 | "capabilities": gin.H{ 375 | "family": "text-embedding-3-small", 376 | "object": "model_capabilities", 377 | "supports": gin.H{"dimensions": true}, 378 | "tokenizer": "cl100k_base", 379 | "type": "embeddings", 380 | }, 381 | "id": "text-embedding-3-small-inference", 382 | "name": "Embedding V3 small (Inference)", 383 | "object": "model", 384 | "version": "text-embedding-3-small", 385 | }, 386 | }, 387 | "object": "list", 388 | }) 389 | } 390 | 391 | func (s *ProxyService) completions(c *gin.Context) { 392 | ctx := c.Request.Context() 393 | 394 | body, err := io.ReadAll(c.Request.Body) 395 | if nil != err { 396 | c.AbortWithStatus(http.StatusBadRequest) 397 | return 398 | } 399 | 400 | model := gjson.GetBytes(body, "model").String() 401 | if mapped, ok := s.cfg.ChatModelMap[model]; ok { 402 | model = mapped 403 | } else { 404 | model = s.cfg.ChatModelDefault 405 | } 406 | body, _ = sjson.SetBytes(body, "model", model) 407 | 408 | if !gjson.GetBytes(body, "function_call").Exists() { 409 | messages := gjson.GetBytes(body, "messages").Array() 410 | for i, msg := range messages { 411 | toolCalls := msg.Get("tool_calls").Array() 412 | if len(toolCalls) == 0 { 413 | body, _ = sjson.DeleteBytes(body, fmt.Sprintf("messages.%d.tool_calls", i)) 414 | } 415 | } 416 | lastIndex := len(messages) - 1 417 | if !strings.Contains(messages[lastIndex].Get("content").String(), "Respond in the following locale") { 418 | locale := s.cfg.ChatLocale 419 | if locale == "" { 420 | locale = "zh_CN" 421 | } 422 | body, _ = sjson.SetBytes(body, "messages."+strconv.Itoa(lastIndex)+".content", messages[lastIndex].Get("content").String()+"Respond in the following locale: "+locale+".") 423 | } 424 | } 425 | 426 | body, _ = sjson.DeleteBytes(body, "intent") 427 | body, _ = sjson.DeleteBytes(body, "intent_threshold") 428 | body, _ = sjson.DeleteBytes(body, "intent_content") 429 | 430 | if int(gjson.GetBytes(body, "max_tokens").Int()) > s.cfg.ChatMaxTokens { 431 | body, _ = sjson.SetBytes(body, "max_tokens", s.cfg.ChatMaxTokens) 432 | } 433 | 434 | proxyUrl := s.cfg.ChatApiBase + "/chat/completions" 435 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, proxyUrl, io.NopCloser(bytes.NewBuffer(body))) 436 | if nil != err { 437 | c.AbortWithStatus(http.StatusInternalServerError) 438 | return 439 | } 440 | 441 | req.Header.Set("Content-Type", "application/json") 442 | req.Header.Set("Authorization", "Bearer "+s.cfg.ChatApiKey) 443 | if "" != s.cfg.ChatApiOrganization { 444 | req.Header.Set("OpenAI-Organization", s.cfg.ChatApiOrganization) 445 | } 446 | if "" != s.cfg.ChatApiProject { 447 | req.Header.Set("OpenAI-Project", s.cfg.ChatApiProject) 448 | } 449 | 450 | resp, err := s.client.Do(req) 451 | if nil != err { 452 | if errors.Is(err, context.Canceled) { 453 | c.AbortWithStatus(http.StatusRequestTimeout) 454 | return 455 | } 456 | 457 | log.Println("request conversation failed:", err.Error()) 458 | c.AbortWithStatus(http.StatusInternalServerError) 459 | return 460 | } 461 | defer closeIO(resp.Body) 462 | 463 | if resp.StatusCode != http.StatusOK { // log 464 | body, _ := io.ReadAll(resp.Body) 465 | log.Println("request completions failed:", string(body)) 466 | 467 | resp.Body = io.NopCloser(bytes.NewBuffer(body)) 468 | } 469 | 470 | c.Status(resp.StatusCode) 471 | 472 | contentType := resp.Header.Get("Content-Type") 473 | if "" != contentType { 474 | c.Header("Content-Type", contentType) 475 | } 476 | 477 | _, _ = io.Copy(c.Writer, resp.Body) 478 | } 479 | 480 | func contains(arr []string, str string) bool { 481 | return strings.Contains(strings.Join(arr, ","), str) 482 | } 483 | 484 | func (s *ProxyService) codeCompletions(c *gin.Context) { 485 | ctx := c.Request.Context() 486 | 487 | time.Sleep(200 * time.Millisecond) 488 | if ctx.Err() != nil { 489 | abortCodex(c, http.StatusRequestTimeout) 490 | return 491 | } 492 | 493 | body, err := io.ReadAll(c.Request.Body) 494 | if nil != err { 495 | abortCodex(c, http.StatusBadRequest) 496 | return 497 | } 498 | 499 | body = ConstructRequestBody(body, s.cfg) 500 | 501 | proxyUrl := s.cfg.CodexApiBase + "/completions" 502 | req, err := http.NewRequestWithContext(ctx, http.MethodPost, proxyUrl, io.NopCloser(bytes.NewBuffer(body))) 503 | if nil != err { 504 | abortCodex(c, http.StatusInternalServerError) 505 | return 506 | } 507 | 508 | req.Header.Set("Content-Type", "application/json") 509 | req.Header.Set("Authorization", "Bearer " + getRandomApiKey(s.cfg.CodexApiKey)) 510 | if "" != s.cfg.CodexApiOrganization { 511 | req.Header.Set("OpenAI-Organization", s.cfg.CodexApiOrganization) 512 | } 513 | if "" != s.cfg.CodexApiProject { 514 | req.Header.Set("OpenAI-Project", s.cfg.CodexApiProject) 515 | } 516 | 517 | resp, err := s.client.Do(req) 518 | if nil != err { 519 | if errors.Is(err, context.Canceled) { 520 | abortCodex(c, http.StatusRequestTimeout) 521 | return 522 | } 523 | 524 | log.Println("request completions failed:", err.Error()) 525 | abortCodex(c, http.StatusInternalServerError) 526 | return 527 | } 528 | defer closeIO(resp.Body) 529 | 530 | if resp.StatusCode != http.StatusOK { 531 | body, _ := io.ReadAll(resp.Body) 532 | log.Println("request completions failed:", string(body)) 533 | 534 | abortCodex(c, resp.StatusCode) 535 | return 536 | } 537 | 538 | c.Status(resp.StatusCode) 539 | 540 | contentType := resp.Header.Get("Content-Type") 541 | if "" != contentType { 542 | c.Header("Content-Type", contentType) 543 | } 544 | 545 | _, _ = io.Copy(c.Writer, resp.Body) 546 | } 547 | 548 | // 随机取一个apiKey 549 | func getRandomApiKey(paramStr string) string { 550 | params := strings.Split(paramStr, ",") 551 | rand.Seed(time.Now().UnixNano()) 552 | randomIndex := rand.Intn(len(params)) 553 | fmt.Println("Code completion API Key index:", randomIndex) 554 | fmt.Println("Code completion API Key:", strings.TrimSpace(params[randomIndex])) 555 | return strings.TrimSpace(params[randomIndex]) 556 | } 557 | 558 | func ConstructRequestBody(body []byte, cfg *config) []byte { 559 | body, _ = sjson.DeleteBytes(body, "extra") 560 | body, _ = sjson.DeleteBytes(body, "nwo") 561 | body, _ = sjson.SetBytes(body, "model", cfg.CodeInstructModel) 562 | 563 | if int(gjson.GetBytes(body, "max_tokens").Int()) > cfg.CodexMaxTokens { 564 | body, _ = sjson.SetBytes(body, "max_tokens", cfg.CodexMaxTokens) 565 | } 566 | 567 | if strings.Contains(cfg.CodeInstructModel, StableCodeModelPrefix) { 568 | return constructWithStableCodeModel(body) 569 | } else if strings.HasPrefix(cfg.CodeInstructModel, DeepSeekCoderModel) || contains(SiliconflowModels, cfg.CodeInstructModel) { 570 | if gjson.GetBytes(body, "n").Int() > 1 { 571 | body, _ = sjson.SetBytes(body, "n", 1) 572 | } 573 | } 574 | 575 | if strings.HasSuffix(cfg.ChatApiBase, "chat") { 576 | // @Todo constructWithChatModel 577 | // 如果code base以chat结尾则构建chatModel,暂时没有好的prompt 578 | } 579 | 580 | return body 581 | } 582 | 583 | func constructWithStableCodeModel(body []byte) []byte { 584 | suffix := gjson.GetBytes(body, "suffix") 585 | prompt := gjson.GetBytes(body, "prompt") 586 | content := fmt.Sprintf("%s%s", prompt, suffix) 587 | 588 | // 创建新的 JSON 对象并添加到 body 中 589 | messages := []map[string]string{ 590 | { 591 | "role": "user", 592 | "content": content, 593 | }, 594 | } 595 | return constructWithChatModel(body, messages) 596 | } 597 | 598 | func constructWithChatModel(body []byte, messages interface{}) []byte { 599 | 600 | body, _ = sjson.SetBytes(body, "messages", messages) 601 | 602 | // fmt.Printf("Request Body: %s\n", body) 603 | // 2. 将转义的字符替换回原来的字符 604 | jsonStr := string(body) 605 | jsonStr = strings.ReplaceAll(jsonStr, "\\u003c", "<") 606 | jsonStr = strings.ReplaceAll(jsonStr, "\\u003e", ">") 607 | return []byte(jsonStr) 608 | } 609 | 610 | func main() { 611 | cfg := readConfig() 612 | 613 | gin.SetMode(gin.ReleaseMode) 614 | r := gin.Default() 615 | 616 | proxyService, err := NewProxyService(cfg) 617 | if nil != err { 618 | log.Fatal(err) 619 | return 620 | } 621 | 622 | proxyService.InitRoutes(r) 623 | 624 | err = r.Run(cfg.Bind) 625 | if nil != err { 626 | log.Fatal(err) 627 | return 628 | } 629 | 630 | } 631 | -------------------------------------------------------------------------------- /scripts/install-all-users.vbs: -------------------------------------------------------------------------------- 1 | If Not WScript.Arguments.Named.Exists("elevate") Then 2 | CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 10 3 | WScript.Quit 4 | End If 5 | 6 | Set oShell = CreateObject("WScript.Shell") 7 | Set oEnvSystem = oShell.Environment("SYSTEM") 8 | Set oFS = CreateObject("Scripting.FileSystemObject") 9 | 10 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 11 | 12 | Dim baseUrl 13 | baseUrl = "http://127.0.0.1:8181" 14 | 15 | Sub RemoveEnv(env) 16 | On Error Resume Next 17 | 18 | env.Remove("AGENT_DEBUG_OVERRIDE_PROXY_URL") 19 | env.Remove("GITHUB_COPILOT_OVERRIDE_PROXY_URL") 20 | env.Remove("AGENT_DEBUG_OVERRIDE_CAPI_URL") 21 | env.Remove("GITHUB_COPILOT_OVERRIDE_CAPI_URL") 22 | End Sub 23 | 24 | RemoveEnv oShell.Environment("USER") 25 | 26 | oEnvSystem("AGENT_DEBUG_OVERRIDE_PROXY_URL") = baseUrl 27 | oEnvSystem("GITHUB_COPILOT_OVERRIDE_PROXY_URL") = baseUrl 28 | oEnvSystem("AGENT_DEBUG_OVERRIDE_CAPI_URL") = baseUrl & "/v1" 29 | oEnvSystem("GITHUB_COPILOT_OVERRIDE_CAPI_URL") = baseUrl & "/v1" 30 | 31 | MsgBox "Done." 32 | -------------------------------------------------------------------------------- /scripts/install-current-user.vbs: -------------------------------------------------------------------------------- 1 | Set oShell = CreateObject("WScript.Shell") 2 | Set oEnv = oShell.Environment("USER") 3 | Set oFS = CreateObject("Scripting.FileSystemObject") 4 | 5 | Dim baseUrl 6 | baseUrl = "http://127.0.0.1:8181" 7 | 8 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 9 | 10 | oEnv("AGENT_DEBUG_OVERRIDE_PROXY_URL") = baseUrl 11 | oEnv("GITHUB_COPILOT_OVERRIDE_PROXY_URL") = baseUrl 12 | oEnv("AGENT_DEBUG_OVERRIDE_CAPI_URL") = baseUrl & "/v1" 13 | oEnv("GITHUB_COPILOT_OVERRIDE_CAPI_URL") = baseUrl & "/v1" 14 | 15 | MsgBox "Done." 16 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | OS_NAME=$(uname -s) 6 | BASE_URL="http://127.0.0.1:8181" 7 | 8 | KDE_ENV_DIR="${HOME}/.config/plasma-workspace/env" 9 | LAUNCH_AGENTS_DIR="${HOME}/Library/LaunchAgents" 10 | 11 | PROFILE_PATH="${HOME}/.profile" 12 | ZSH_PROFILE_PATH="${HOME}/.zshrc" 13 | PLIST_PATH="${LAUNCH_AGENTS_DIR}/copilot.override.plist" 14 | 15 | if [ "$OS_NAME" = "Darwin" ]; then 16 | BASH_PROFILE_PATH="${HOME}/.bash_profile" 17 | 18 | mkdir -p "${LAUNCH_AGENTS_DIR}" 19 | echo 'Labelcopilot.overrideProgramArgumentssh-c' >"${PLIST_PATH}" 20 | else 21 | BASH_PROFILE_PATH="${HOME}/.bashrc" 22 | mkdir -p "${KDE_ENV_DIR}" 23 | fi 24 | 25 | touch "${PROFILE_PATH}" 26 | touch "${BASH_PROFILE_PATH}" 27 | touch "${ZSH_PROFILE_PATH}" 28 | 29 | GH_OVERRIDE_SHELL_NAME="copilot.override.sh" 30 | GH_OVERRIDE_SHELL_FILE="${HOME}/.${GH_OVERRIDE_SHELL_NAME}" 31 | echo '#!/bin/sh' >"${GH_OVERRIDE_SHELL_FILE}" 32 | 33 | # shellcheck disable=SC2016 34 | EXEC_LINE='___GH_OVERRIDE_SHELL_FILE="${HOME}/.copilot.override.sh"; if [ -f "${___GH_OVERRIDE_SHELL_FILE}" ]; then . "${___GH_OVERRIDE_SHELL_FILE}"; fi' 35 | 36 | # shellcheck disable=SC2129 37 | echo "export AGENT_DEBUG_OVERRIDE_PROXY_URL=\"${BASE_URL}\"" >>"${GH_OVERRIDE_SHELL_FILE}" 38 | echo "export GITHUB_COPILOT_OVERRIDE_PROXY_URL=\"${BASE_URL}\"" >>"${GH_OVERRIDE_SHELL_FILE}" 39 | echo "export AGENT_DEBUG_OVERRIDE_CAPI_URL=\"${BASE_URL}/v1\"" >>"${GH_OVERRIDE_SHELL_FILE}" 40 | echo "export GITHUB_COPILOT_OVERRIDE_CAPI_URL=\"${BASE_URL}/v1\"" >>"${GH_OVERRIDE_SHELL_FILE}" 41 | 42 | if [ "$OS_NAME" = "Darwin" ]; then 43 | launchctl setenv "AGENT_DEBUG_OVERRIDE_PROXY_URL" "${BASE_URL}" 44 | launchctl setenv "GITHUB_COPILOT_OVERRIDE_PROXY_URL" "${BASE_URL}" 45 | launchctl setenv "AGENT_DEBUG_OVERRIDE_CAPI_URL" "${BASE_URL}/v1" 46 | launchctl setenv "GITHUB_COPILOT_OVERRIDE_CAPI_URL" "${BASE_URL}/v1" 47 | 48 | # shellcheck disable=SC2129 49 | echo "launchctl setenv \"AGENT_DEBUG_OVERRIDE_PROXY_URL\" \"${BASE_URL}\"" >>"${PLIST_PATH}" 50 | echo "launchctl setenv \"GITHUB_COPILOT_OVERRIDE_PROXY_URL\" \"${BASE_URL}\"" >>"${PLIST_PATH}" 51 | echo "launchctl setenv \"AGENT_DEBUG_OVERRIDE_CAPI_URL\" \"${BASE_URL}/v1\"" >>"${PLIST_PATH}" 52 | echo "launchctl setenv \"GITHUB_COPILOT_OVERRIDE_CAPI_URL\" \"${BASE_URL}/v1\"" >>"${PLIST_PATH}" 53 | fi 54 | 55 | if [ "$OS_NAME" = "Darwin" ]; then 56 | # shellcheck disable=SC2016 57 | sed -i '' '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 58 | # shellcheck disable=SC2016 59 | sed -i '' '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 60 | # shellcheck disable=SC2016 61 | sed -i '' '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 62 | 63 | echo 'RunAtLoad' >>"${PLIST_PATH}" 64 | else 65 | # shellcheck disable=SC2016 66 | sed -i '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 67 | # shellcheck disable=SC2016 68 | sed -i '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 69 | # shellcheck disable=SC2016 70 | sed -i '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 71 | fi 72 | 73 | echo "${EXEC_LINE}" >>"${PROFILE_PATH}" 74 | echo "${EXEC_LINE}" >>"${BASH_PROFILE_PATH}" 75 | echo "${EXEC_LINE}" >>"${ZSH_PROFILE_PATH}" 76 | 77 | if [ "$OS_NAME" = "Darwin" ]; then 78 | echo 'done. the "kill Dock" command can fix the crash issue.' 79 | else 80 | ln -sf "${GH_OVERRIDE_SHELL_FILE}" "${KDE_ENV_DIR}/${GH_OVERRIDE_SHELL_NAME}" 81 | echo "done. you'd better log off first!" 82 | fi 83 | -------------------------------------------------------------------------------- /scripts/replace_max_tokens.vbs: -------------------------------------------------------------------------------- 1 | ' VBScript to change max tokens to 2048 2 | 3 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 4 | 5 | Const ForReading = 1 6 | Const ForWriting = 2 7 | 8 | ' Subpath of the file to be replaced 9 | subpath = "dist\extension.js" 10 | 11 | pattern = "\.maxPromptCompletionTokens\(([a-zA-Z0-9_]+),([0-9]+)\)" 12 | replacement = ".maxPromptCompletionTokens($1,2048)" 13 | 14 | ' Iterate over all github copilot directories 15 | Set objFSO = CreateObject("Scripting.FileSystemObject") 16 | Set objShell = CreateObject("WScript.Shell") 17 | Set colExtensions = objFSO.GetFolder(objShell.ExpandEnvironmentStrings("%USERPROFILE%") & "\.vscode\extensions").SubFolders 18 | 19 | For Each objExtension In colExtensions 20 | extension_path = objExtension.Path & "\" & subpath 21 | If objFSO.FileExists(extension_path) Then 22 | backupfile = extension_path & ".bak" 23 | 24 | ' Delete if backup file exists 25 | If objFSO.FileExists(backupfile) Then 26 | objFSO.DeleteFile backupfile, True 27 | End If 28 | 29 | ' Backup 30 | objFSO.CopyFile extension_path, backupfile 31 | 32 | ' Do search and replace with pattern 33 | Set objFile = objFSO.OpenTextFile(extension_path, ForReading) 34 | strContent = objFile.ReadAll 35 | objFile.Close 36 | 37 | Set objRegEx = New RegExp 38 | objRegEx.Global = True 39 | objRegEx.IgnoreCase = True 40 | objRegEx.Pattern = pattern 41 | strContent = objRegEx.Replace(strContent, replacement) 42 | 43 | Set objFile = objFSO.OpenTextFile(extension_path, ForWriting) 44 | objFile.Write strContent 45 | objFile.Close 46 | End If 47 | Next 48 | 49 | MsgBox "Max tokens modification completed" 50 | -------------------------------------------------------------------------------- /scripts/restore_max_tokens.vbs: -------------------------------------------------------------------------------- 1 | ' VBScript to recovery max tokens 2 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 3 | 4 | Const ForReading = 1 5 | Const ForWriting = 2 6 | 7 | ' Subpath of the file to be recovery 8 | subpath = "dist\extension.js" 9 | 10 | ' Iterate over all github copilot directories 11 | Set objFSO = CreateObject("Scripting.FileSystemObject") 12 | Set objShell = CreateObject("WScript.Shell") 13 | Set colExtensions = objFSO.GetFolder(objShell.ExpandEnvironmentStrings("%USERPROFILE%") & "\.vscode\extensions").SubFolders 14 | 15 | For Each objExtension In colExtensions 16 | extension_path = objExtension.Path & "\" & subpath 17 | backupfile = extension_path & ".bak" 18 | 19 | If objFSO.FileExists(backupfile) Then 20 | ' Delete if exist extension file 21 | If objFSO.FileExists(extension_path) Then 22 | objFSO.DeleteFile extension_path, True 23 | End If 24 | 25 | ' Replace 26 | objFSO.MoveFile backupfile, extension_path 27 | End If 28 | Next 29 | 30 | MsgBox "Restore max tokens to default successed" 31 | -------------------------------------------------------------------------------- /scripts/uninstall-all-users.vbs: -------------------------------------------------------------------------------- 1 | If Not WScript.Arguments.Named.Exists("elevate") Then 2 | CreateObject("Shell.Application").ShellExecute WScript.FullName, """" & WScript.ScriptFullName & """ /elevate", "", "runas", 10 3 | WScript.Quit 4 | End If 5 | 6 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 7 | 8 | Sub RemoveEnv(env) 9 | On Error Resume Next 10 | 11 | env.Remove("AGENT_DEBUG_OVERRIDE_PROXY_URL") 12 | env.Remove("GITHUB_COPILOT_OVERRIDE_PROXY_URL") 13 | env.Remove("AGENT_DEBUG_OVERRIDE_CAPI_URL") 14 | env.Remove("GITHUB_COPILOT_OVERRIDE_CAPI_URL") 15 | End Sub 16 | 17 | Set oShell = CreateObject("WScript.Shell") 18 | 19 | RemoveEnv oShell.Environment("USER") 20 | RemoveEnv oShell.Environment("SYSTEM") 21 | 22 | MsgBox "Done." 23 | -------------------------------------------------------------------------------- /scripts/uninstall-current-user.vbs: -------------------------------------------------------------------------------- 1 | Set oShell = CreateObject("WScript.Shell") 2 | Set oEnv = oShell.Environment("USER") 3 | 4 | MsgBox "It may take a few seconds to execute this script." & vbCrLf & vbCrLf & "Click 'OK' button and wait for the prompt of 'Done.' to pop up!" 5 | 6 | oEnv.Remove("AGENT_DEBUG_OVERRIDE_PROXY_URL") 7 | oEnv.Remove("GITHUB_COPILOT_OVERRIDE_PROXY_URL") 8 | oEnv.Remove("AGENT_DEBUG_OVERRIDE_CAPI_URL") 9 | oEnv.Remove("GITHUB_COPILOT_OVERRIDE_CAPI_URL") 10 | 11 | MsgBox "Done." 12 | -------------------------------------------------------------------------------- /scripts/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | OS_NAME=$(uname -s) 6 | 7 | KDE_ENV_DIR="${HOME}/.config/plasma-workspace/env" 8 | 9 | PROFILE_PATH="${HOME}/.profile" 10 | ZSH_PROFILE_PATH="${HOME}/.zshrc" 11 | PLIST_PATH="${HOME}/Library/LaunchAgents/copilot.override.plist" 12 | 13 | if [ "$OS_NAME" = "Darwin" ]; then 14 | BASH_PROFILE_PATH="${HOME}/.bash_profile" 15 | else 16 | BASH_PROFILE_PATH="${HOME}/.bashrc" 17 | fi 18 | 19 | touch "${PROFILE_PATH}" 20 | touch "${BASH_PROFILE_PATH}" 21 | touch "${ZSH_PROFILE_PATH}" 22 | 23 | GH_OVERRIDE_SHELL_NAME="copilot.override.sh" 24 | GH_OVERRIDE_SHELL_FILE="${HOME}/.${GH_OVERRIDE_SHELL_NAME}" 25 | 26 | rm -rf "${GH_OVERRIDE_SHELL_FILE}" 27 | 28 | if [ "$OS_NAME" = "Darwin" ]; then 29 | launchctl unsetenv "AGENT_DEBUG_OVERRIDE_PROXY_URL" 30 | launchctl unsetenv "GITHUB_COPILOT_OVERRIDE_PROXY_URL" 31 | launchctl unsetenv "AGENT_DEBUG_OVERRIDE_CAPI_URL" 32 | launchctl unsetenv "GITHUB_COPILOT_OVERRIDE_CAPI_URL" 33 | 34 | rm -rf "${PLIST_PATH}" 35 | 36 | # shellcheck disable=SC2016 37 | sed -i '' '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 38 | # shellcheck disable=SC2016 39 | sed -i '' '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 40 | # shellcheck disable=SC2016 41 | sed -i '' '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 42 | 43 | echo 'done.' 44 | else 45 | # shellcheck disable=SC2016 46 | sed -i '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${PROFILE_PATH}" >/dev/null 2>&1 47 | # shellcheck disable=SC2016 48 | sed -i '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${BASH_PROFILE_PATH}" >/dev/null 2>&1 49 | # shellcheck disable=SC2016 50 | sed -i '/___GH_OVERRIDE_SHELL_FILE="${HOME}\/\.copilot\.override\.sh"; if /d' "${ZSH_PROFILE_PATH}" >/dev/null 2>&1 51 | 52 | # shellcheck disable=SC2115 53 | rm -rf "${KDE_ENV_DIR}/${GH_OVERRIDE_SHELL_NAME}" 54 | echo "done. you'd better log off first!" 55 | fi 56 | --------------------------------------------------------------------------------