mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-18 03:57:06 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c1c6a134b | ||
|
|
e0aa270328 | ||
|
|
6ab537ad4e | ||
|
|
6d7a4e32fe | ||
|
|
eb2c236699 | ||
|
|
86a655fb5d | ||
|
|
c50e7d0606 | ||
|
|
4ec7b4289f | ||
|
|
6bebe37bfc |
+1
-1
@@ -8,7 +8,7 @@ info:
|
|||||||
description: "Cursor助手"
|
description: "Cursor助手"
|
||||||
copyright: "© 2026, Cursor助手"
|
copyright: "© 2026, Cursor助手"
|
||||||
comments: "Cursor助手"
|
comments: "Cursor助手"
|
||||||
version: "0.0.38"
|
version: "0.0.40"
|
||||||
|
|
||||||
dev_mode:
|
dev_mode:
|
||||||
root_path: .
|
root_path: .
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.0.38</string>
|
<string>0.0.39</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>0.0.38</string>
|
<string>0.0.39</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>12.0.0</string>
|
<string>12.0.0</string>
|
||||||
<key>LSUIElement</key>
|
<key>LSUIElement</key>
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.0.38</string>
|
<string>0.0.39</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>0.0.38</string>
|
<string>0.0.39</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>12.0.0</string>
|
<string>12.0.0</string>
|
||||||
<key>LSUIElement</key>
|
<key>LSUIElement</key>
|
||||||
|
|||||||
+27
-14
@@ -6,7 +6,7 @@
|
|||||||
name: "Cursor助手"
|
name: "Cursor助手"
|
||||||
arch: ${GOARCH}
|
arch: ${GOARCH}
|
||||||
platform: "linux"
|
platform: "linux"
|
||||||
version: "0.0.38"
|
version: "0.0.39"
|
||||||
section: "default"
|
section: "default"
|
||||||
priority: "extra"
|
priority: "extra"
|
||||||
maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
|
maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}>
|
||||||
@@ -24,24 +24,24 @@ contents:
|
|||||||
- src: "./build/linux/Cursor助手.desktop"
|
- src: "./build/linux/Cursor助手.desktop"
|
||||||
dst: "/usr/share/applications/Cursor助手.desktop"
|
dst: "/usr/share/applications/Cursor助手.desktop"
|
||||||
|
|
||||||
# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1
|
# Default dependencies for the GTK4 + WebKitGTK 6.0 stack (Ubuntu 24.04+ / Debian 13+)
|
||||||
depends:
|
depends:
|
||||||
- libgtk-3-0
|
- libgtk-4-1
|
||||||
- libwebkit2gtk-4.1-0
|
- libwebkitgtk-6.0-4
|
||||||
|
|
||||||
# Distribution-specific overrides for different package formats and WebKit versions
|
# Distribution-specific overrides for different package formats
|
||||||
overrides:
|
overrides:
|
||||||
# RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0)
|
# RPM packages for Fedora / RHEL / AlmaLinux / Rocky Linux
|
||||||
rpm:
|
rpm:
|
||||||
depends:
|
depends:
|
||||||
- gtk3
|
- gtk4
|
||||||
- webkit2gtk4.1
|
- webkitgtk6.0
|
||||||
|
|
||||||
# Arch Linux packages (WebKit 4.1)
|
# Arch Linux packages
|
||||||
archlinux:
|
archlinux:
|
||||||
depends:
|
depends:
|
||||||
- gtk3
|
- gtk4
|
||||||
- webkit2gtk-4.1
|
- webkitgtk-6.0
|
||||||
|
|
||||||
# scripts section to ensure desktop database is updated after install
|
# scripts section to ensure desktop database is updated after install
|
||||||
scripts:
|
scripts:
|
||||||
@@ -50,13 +50,26 @@ scripts:
|
|||||||
# preremove: "./build/linux/nfpm/scripts/preremove.sh"
|
# preremove: "./build/linux/nfpm/scripts/preremove.sh"
|
||||||
# postremove: "./build/linux/nfpm/scripts/postremove.sh"
|
# postremove: "./build/linux/nfpm/scripts/postremove.sh"
|
||||||
|
|
||||||
|
# If you build your app with -tags gtk3 (legacy WebKit2GTK 4.1 stack — supported through v3.0.x, removed in v3.1),
|
||||||
|
# replace the depends/overrides above with these:
|
||||||
|
#
|
||||||
|
# depends:
|
||||||
|
# - libgtk-3-0
|
||||||
|
# - libwebkit2gtk-4.1-0
|
||||||
|
# overrides:
|
||||||
|
# rpm:
|
||||||
|
# depends:
|
||||||
|
# - gtk3
|
||||||
|
# - webkit2gtk4.1
|
||||||
|
# archlinux:
|
||||||
|
# depends:
|
||||||
|
# - gtk3
|
||||||
|
# - webkit2gtk-4.1
|
||||||
|
#
|
||||||
# replaces:
|
# replaces:
|
||||||
# - foobar
|
# - foobar
|
||||||
# provides:
|
# provides:
|
||||||
# - bar
|
# - bar
|
||||||
# depends:
|
|
||||||
# - gtk3
|
|
||||||
# - libwebkit2gtk
|
|
||||||
# recommends:
|
# recommends:
|
||||||
# - whatever
|
# - whatever
|
||||||
# suggests:
|
# suggests:
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"fixed": {
|
"fixed": {
|
||||||
"file_version": "0.0.38"
|
"file_version": "0.0.39"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"0000": {
|
"0000": {
|
||||||
"ProductVersion": "0.0.38",
|
"ProductVersion": "0.0.39",
|
||||||
"CompanyName": "Cursor助手",
|
"CompanyName": "Cursor助手",
|
||||||
"FileDescription": "Cursor助手",
|
"FileDescription": "Cursor助手",
|
||||||
"LegalCopyright": "© 2026, Cursor助手",
|
"LegalCopyright": "© 2026, Cursor助手",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
!define INFO_PRODUCTNAME "Cursor助手"
|
!define INFO_PRODUCTNAME "Cursor助手"
|
||||||
!endif
|
!endif
|
||||||
!ifndef INFO_PRODUCTVERSION
|
!ifndef INFO_PRODUCTVERSION
|
||||||
!define INFO_PRODUCTVERSION "0.0.38"
|
!define INFO_PRODUCTVERSION "0.0.39"
|
||||||
!endif
|
!endif
|
||||||
!ifndef INFO_COPYRIGHT
|
!ifndef INFO_COPYRIGHT
|
||||||
!define INFO_COPYRIGHT "© 2026, Cursor助手"
|
!define INFO_COPYRIGHT "© 2026, Cursor助手"
|
||||||
@@ -27,9 +27,17 @@
|
|||||||
!endif
|
!endif
|
||||||
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
|
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
|
||||||
|
|
||||||
|
!ifndef WAILS_INSTALL_SCOPE
|
||||||
|
!define WAILS_INSTALL_SCOPE "machine"
|
||||||
|
!endif
|
||||||
|
|
||||||
!ifndef REQUEST_EXECUTION_LEVEL
|
!ifndef REQUEST_EXECUTION_LEVEL
|
||||||
|
!if "${WAILS_INSTALL_SCOPE}" == "user"
|
||||||
|
!define REQUEST_EXECUTION_LEVEL "user"
|
||||||
|
!else
|
||||||
!define REQUEST_EXECUTION_LEVEL "admin"
|
!define REQUEST_EXECUTION_LEVEL "admin"
|
||||||
!endif
|
!endif
|
||||||
|
!endif
|
||||||
|
|
||||||
RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
||||||
|
|
||||||
@@ -115,6 +123,18 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
|||||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
SetRegView 64
|
SetRegView 64
|
||||||
|
!if "${WAILS_INSTALL_SCOPE}" == "user"
|
||||||
|
WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
|
||||||
|
WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
|
||||||
|
WriteRegStr HKCU "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
|
||||||
|
WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
|
||||||
|
WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||||
|
WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||||
|
|
||||||
|
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||||
|
IntFmt $0 "0x%08X" $0
|
||||||
|
WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0"
|
||||||
|
!else
|
||||||
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
|
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
|
||||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
|
||||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
|
||||||
@@ -125,13 +145,18 @@ RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
|
|||||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||||
IntFmt $0 "0x%08X" $0
|
IntFmt $0 "0x%08X" $0
|
||||||
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
|
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
|
||||||
|
!endif
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro wails.deleteUninstaller
|
!macro wails.deleteUninstaller
|
||||||
Delete "$INSTDIR\uninstall.exe"
|
Delete "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
SetRegView 64
|
SetRegView 64
|
||||||
|
!if "${WAILS_INSTALL_SCOPE}" == "user"
|
||||||
|
DeleteRegKey HKCU "${UNINST_KEY}"
|
||||||
|
!else
|
||||||
DeleteRegKey HKLM "${UNINST_KEY}"
|
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||||
|
!endif
|
||||||
!macroend
|
!macroend
|
||||||
|
|
||||||
!macro wails.setShellContext
|
!macro wails.setShellContext
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
<assemblyIdentity type="win32" name="com.cursor.wuxianxubei" version="0.0.38" processorArchitecture="*"/>
|
<assemblyIdentity type="win32" name="com.cursor.wuxianxubei" version="0.0.39" processorArchitecture="*"/>
|
||||||
<dependency>
|
<dependency>
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
createEmptyModelAdapter,
|
createEmptyModelAdapter,
|
||||||
normalizeModelAdapter,
|
normalizeModelAdapter,
|
||||||
OPENAI_ENDPOINT_CHAT_COMPLETIONS,
|
OPENAI_ENDPOINT_CHAT_COMPLETIONS,
|
||||||
|
OPENAI_ENDPOINT_CUSTOM,
|
||||||
OPENAI_ENDPOINT_RESPONSES,
|
OPENAI_ENDPOINT_RESPONSES,
|
||||||
OPENAI_EXTRA_PARAMS_DEFAULT_JSON,
|
OPENAI_EXTRA_PARAMS_DEFAULT_JSON,
|
||||||
} from "@/state/appState";
|
} from "@/state/appState";
|
||||||
@@ -22,6 +23,7 @@ const reasoningEffortOptions = [
|
|||||||
{ label: "中", value: "medium", icon: "icon-[mdi--head-lightbulb-outline]" },
|
{ label: "中", value: "medium", icon: "icon-[mdi--head-lightbulb-outline]" },
|
||||||
{ label: "高", value: "high", icon: "icon-[mdi--brain]" },
|
{ label: "高", value: "high", icon: "icon-[mdi--brain]" },
|
||||||
{ label: "极高", value: "xhigh", icon: "icon-[mdi--head-cog-outline]" },
|
{ label: "极高", value: "xhigh", icon: "icon-[mdi--head-cog-outline]" },
|
||||||
|
{ label: "最高", value: "max", icon: "icon-[mdi--brain]" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const anthropicThinkingEffortOptions = [
|
const anthropicThinkingEffortOptions = [
|
||||||
@@ -35,10 +37,11 @@ const anthropicThinkingEffortOptions = [
|
|||||||
const openAIEndpointOptions = [
|
const openAIEndpointOptions = [
|
||||||
{ label: "/v1/responses", value: OPENAI_ENDPOINT_RESPONSES, icon: "icon-[mdi--api]" },
|
{ label: "/v1/responses", value: OPENAI_ENDPOINT_RESPONSES, icon: "icon-[mdi--api]" },
|
||||||
{ label: "/v1/chat/completions", value: OPENAI_ENDPOINT_CHAT_COMPLETIONS, icon: "icon-[mdi--message-text-outline]" },
|
{ label: "/v1/chat/completions", value: OPENAI_ENDPOINT_CHAT_COMPLETIONS, icon: "icon-[mdi--message-text-outline]" },
|
||||||
|
{ label: "自定义路径", value: OPENAI_ENDPOINT_CUSTOM, icon: "icon-[mdi--pencil-outline]" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const fieldTips = {
|
const fieldTips = {
|
||||||
openAIExtraParams: "开启后会把 JSON 对象合并到 OpenAI 请求体。OpenAI service_tier 支持 auto、default、flex、scale、priority;priority 可用于高优先级/Fast 类场景。",
|
openAIExtraParams: "开启后会把 JSON 对象覆盖到 OpenAI 请求体。同名字段以这里为准。OpenAI service_tier 支持 auto、default、flex、scale、priority;priority 可用于高优先级/Fast 类场景。",
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -274,18 +274,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"1b7d24b212e52c54": {
|
|
||||||
"source": "{0} 的推理强度仅支持 low、medium、high、xhigh",
|
|
||||||
"kind": "template",
|
|
||||||
"placeholders": 1,
|
|
||||||
"refs": [
|
|
||||||
{
|
|
||||||
"file": "src/state/appState.js",
|
|
||||||
"line": 315,
|
|
||||||
"column": 14
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1bc77f5ab979f4c1": {
|
"1bc77f5ab979f4c1": {
|
||||||
"source": "新增模型配置",
|
"source": "新增模型配置",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
@@ -1175,6 +1163,23 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"8672864e90417138": {
|
||||||
|
"source": "最高",
|
||||||
|
"kind": "text",
|
||||||
|
"placeholders": 0,
|
||||||
|
"refs": [
|
||||||
|
{
|
||||||
|
"file": "src/components/ModelAdapterModal.vue",
|
||||||
|
"line": 26,
|
||||||
|
"column": 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "src/views/ModelEditor.vue",
|
||||||
|
"line": 41,
|
||||||
|
"column": 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"86df7ec743047234": {
|
"86df7ec743047234": {
|
||||||
"source": "服务运行中",
|
"source": "服务运行中",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
@@ -2110,6 +2115,18 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"dbb4b5be9b5723dc": {
|
||||||
|
"source": "{0} 的推理强度仅支持 low、medium、high、xhigh、max",
|
||||||
|
"kind": "template",
|
||||||
|
"placeholders": 1,
|
||||||
|
"refs": [
|
||||||
|
{
|
||||||
|
"file": "src/state/appState.js",
|
||||||
|
"line": 435,
|
||||||
|
"column": 14
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"dbee6e7139243362": {
|
"dbee6e7139243362": {
|
||||||
"source": "{0} 的接口地址不能为空",
|
"source": "{0} 的接口地址不能为空",
|
||||||
"kind": "template",
|
"kind": "template",
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"15d124b200ddabed": "Maximum number of context tokens the model can accept in a single request. Leave blank to use the default.",
|
"15d124b200ddabed": "Maximum number of context tokens the model can accept in a single request. Leave blank to use the default.",
|
||||||
"18b7312022cd1840": "Start Service",
|
"18b7312022cd1840": "Start Service",
|
||||||
"1af38868896cf53d": "Routing mode only supports local or upstream",
|
"1af38868896cf53d": "Routing mode only supports local or upstream",
|
||||||
"1b7d24b212e52c54": "{0} reasoning effort only supports low, medium, high, and xhigh",
|
|
||||||
"1bc77f5ab979f4c1": "Add Model Settings",
|
"1bc77f5ab979f4c1": "Add Model Settings",
|
||||||
"1e238093b79b3165": "Uses 65536 by default when left blank",
|
"1e238093b79b3165": "Uses 65536 by default when left blank",
|
||||||
"24343a2096988d42": "Failed to open",
|
"24343a2096988d42": "Failed to open",
|
||||||
@@ -84,6 +83,7 @@
|
|||||||
"7e9e334aeb0bdc07": "Service operation failed",
|
"7e9e334aeb0bdc07": "Service operation failed",
|
||||||
"7f68ebad19ba6bcd": "Check for Updates",
|
"7f68ebad19ba6bcd": "Check for Updates",
|
||||||
"81123c56d5d880d0": "API Key",
|
"81123c56d5d880d0": "API Key",
|
||||||
|
"8672864e90417138": "Max",
|
||||||
"86df7ec743047234": "Service running",
|
"86df7ec743047234": "Service running",
|
||||||
"87ed126f7bd1121e": "Routing Mode",
|
"87ed126f7bd1121e": "Routing Mode",
|
||||||
"899add6275682210": "Uses 200000 by default when left blank",
|
"899add6275682210": "Uses 200000 by default when left blank",
|
||||||
@@ -154,6 +154,7 @@
|
|||||||
"d7da2aabd35772ec": "e.g. 200000 (leave blank to use the default)",
|
"d7da2aabd35772ec": "e.g. 200000 (leave blank to use the default)",
|
||||||
"da590a8fe3ce4de0": "Please select",
|
"da590a8fe3ce4de0": "Please select",
|
||||||
"daede9881787abe7": "Notes",
|
"daede9881787abe7": "Notes",
|
||||||
|
"dbb4b5be9b5723dc": "{0} reasoning effort only supports low, medium, high, xhigh, and max",
|
||||||
"dbee6e7139243362": "{0} base URL cannot be empty",
|
"dbee6e7139243362": "{0} base URL cannot be empty",
|
||||||
"dc82c5e8fb2ab777": "Version: v{0}",
|
"dc82c5e8fb2ab777": "Version: v{0}",
|
||||||
"de8184da1ef88d03": "Configured",
|
"de8184da1ef88d03": "Configured",
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"15d124b200ddabed": "モデルが1回のリクエストで受け取れる最大コンテキスト Token 数。空欄の場合はデフォルト値を使用します。",
|
"15d124b200ddabed": "モデルが1回のリクエストで受け取れる最大コンテキスト Token 数。空欄の場合はデフォルト値を使用します。",
|
||||||
"18b7312022cd1840": "サービスを開始",
|
"18b7312022cd1840": "サービスを開始",
|
||||||
"1af38868896cf53d": "ルーティングモードは local または upstream のみサポートします",
|
"1af38868896cf53d": "ルーティングモードは local または upstream のみサポートします",
|
||||||
"1b7d24b212e52c54": "{0} の推論強度は low、medium、high、xhigh のみサポートします",
|
|
||||||
"1bc77f5ab979f4c1": "モデル設定を追加",
|
"1bc77f5ab979f4c1": "モデル設定を追加",
|
||||||
"1e238093b79b3165": "空欄で 65536",
|
"1e238093b79b3165": "空欄で 65536",
|
||||||
"24343a2096988d42": "開けませんでした",
|
"24343a2096988d42": "開けませんでした",
|
||||||
@@ -84,6 +83,7 @@
|
|||||||
"7e9e334aeb0bdc07": "サービス操作に失敗しました",
|
"7e9e334aeb0bdc07": "サービス操作に失敗しました",
|
||||||
"7f68ebad19ba6bcd": "アップデートを確認",
|
"7f68ebad19ba6bcd": "アップデートを確認",
|
||||||
"81123c56d5d880d0": "API キー",
|
"81123c56d5d880d0": "API キー",
|
||||||
|
"8672864e90417138": "最大",
|
||||||
"86df7ec743047234": "サービス稼働中",
|
"86df7ec743047234": "サービス稼働中",
|
||||||
"87ed126f7bd1121e": "ルーティングモード",
|
"87ed126f7bd1121e": "ルーティングモード",
|
||||||
"899add6275682210": "空欄で 200000",
|
"899add6275682210": "空欄で 200000",
|
||||||
@@ -154,6 +154,7 @@
|
|||||||
"d7da2aabd35772ec": "例: 200000(空欄でデフォルト値)",
|
"d7da2aabd35772ec": "例: 200000(空欄でデフォルト値)",
|
||||||
"da590a8fe3ce4de0": "選択してください",
|
"da590a8fe3ce4de0": "選択してください",
|
||||||
"daede9881787abe7": "メモ",
|
"daede9881787abe7": "メモ",
|
||||||
|
"dbb4b5be9b5723dc": "{0} の推論強度は low、medium、high、xhigh、max のみサポートします",
|
||||||
"dbee6e7139243362": "{0} のベース URL は必須です",
|
"dbee6e7139243362": "{0} のベース URL は必須です",
|
||||||
"dc82c5e8fb2ab777": "バージョン: v{0}",
|
"dc82c5e8fb2ab777": "バージョン: v{0}",
|
||||||
"de8184da1ef88d03": "設定済み",
|
"de8184da1ef88d03": "設定済み",
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"15d124b200ddabed": "模型单次可接受的最大上下文 Token 数。留空时使用默认值。",
|
"15d124b200ddabed": "模型单次可接受的最大上下文 Token 数。留空时使用默认值。",
|
||||||
"18b7312022cd1840": "启动服务",
|
"18b7312022cd1840": "启动服务",
|
||||||
"1af38868896cf53d": "运行模式仅支持 local 或 upstream",
|
"1af38868896cf53d": "运行模式仅支持 local 或 upstream",
|
||||||
"1b7d24b212e52c54": "{0} 的推理强度仅支持 low、medium、high、xhigh",
|
|
||||||
"1bc77f5ab979f4c1": "新增模型配置",
|
"1bc77f5ab979f4c1": "新增模型配置",
|
||||||
"1e238093b79b3165": "留空时默认 65536",
|
"1e238093b79b3165": "留空时默认 65536",
|
||||||
"24343a2096988d42": "打开失败",
|
"24343a2096988d42": "打开失败",
|
||||||
@@ -84,6 +83,7 @@
|
|||||||
"7e9e334aeb0bdc07": "服务操作失败",
|
"7e9e334aeb0bdc07": "服务操作失败",
|
||||||
"7f68ebad19ba6bcd": "检查更新",
|
"7f68ebad19ba6bcd": "检查更新",
|
||||||
"81123c56d5d880d0": "访问密钥",
|
"81123c56d5d880d0": "访问密钥",
|
||||||
|
"8672864e90417138": "最高",
|
||||||
"86df7ec743047234": "服务运行中",
|
"86df7ec743047234": "服务运行中",
|
||||||
"87ed126f7bd1121e": "运行模式",
|
"87ed126f7bd1121e": "运行模式",
|
||||||
"899add6275682210": "留空时默认 200000",
|
"899add6275682210": "留空时默认 200000",
|
||||||
@@ -154,6 +154,7 @@
|
|||||||
"d7da2aabd35772ec": "例如:200000(留空用默认值)",
|
"d7da2aabd35772ec": "例如:200000(留空用默认值)",
|
||||||
"da590a8fe3ce4de0": "请选择",
|
"da590a8fe3ce4de0": "请选择",
|
||||||
"daede9881787abe7": "备注",
|
"daede9881787abe7": "备注",
|
||||||
|
"dbb4b5be9b5723dc": "{0} 的推理强度仅支持 low、medium、high、xhigh、max",
|
||||||
"dbee6e7139243362": "{0} 的接口地址不能为空",
|
"dbee6e7139243362": "{0} 的接口地址不能为空",
|
||||||
"dc82c5e8fb2ab777": "版本:v{0}",
|
"dc82c5e8fb2ab777": "版本:v{0}",
|
||||||
"de8184da1ef88d03": "已配置",
|
"de8184da1ef88d03": "已配置",
|
||||||
|
|||||||
@@ -22,11 +22,12 @@ import {
|
|||||||
const APP_STATE_STORAGE_KEY = "cursor-client:runtime-state:v2";
|
const APP_STATE_STORAGE_KEY = "cursor-client:runtime-state:v2";
|
||||||
const GENERIC_SERVICE_ERROR = "服务错误";
|
const GENERIC_SERVICE_ERROR = "服务错误";
|
||||||
const SUPPORTED_MODEL_ADAPTER_TYPES = new Set(["openai", "anthropic"]);
|
const SUPPORTED_MODEL_ADAPTER_TYPES = new Set(["openai", "anthropic"]);
|
||||||
const SUPPORTED_REASONING_EFFORTS = new Set(["low", "medium", "high", "xhigh"]);
|
const SUPPORTED_REASONING_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
||||||
const SUPPORTED_ANTHROPIC_THINKING_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
const SUPPORTED_ANTHROPIC_THINKING_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
||||||
export const ANTHROPIC_THINKING_EFFORT_DEFAULT = "xhigh";
|
export const ANTHROPIC_THINKING_EFFORT_DEFAULT = "xhigh";
|
||||||
export const OPENAI_ENDPOINT_RESPONSES = "/v1/responses";
|
export const OPENAI_ENDPOINT_RESPONSES = "/v1/responses";
|
||||||
export const OPENAI_ENDPOINT_CHAT_COMPLETIONS = "/v1/chat/completions";
|
export const OPENAI_ENDPOINT_CHAT_COMPLETIONS = "/v1/chat/completions";
|
||||||
|
export const OPENAI_ENDPOINT_CUSTOM = "/custom";
|
||||||
export const OPENAI_EXTRA_PARAMS_DEFAULT_JSON = `{
|
export const OPENAI_EXTRA_PARAMS_DEFAULT_JSON = `{
|
||||||
"service_tier": "priority"
|
"service_tier": "priority"
|
||||||
}`;
|
}`;
|
||||||
@@ -34,7 +35,7 @@ export const EXTRA_PARAMS_DEFAULT_JSON = `{
|
|||||||
}`;
|
}`;
|
||||||
export const CUSTOM_HEADERS_DEFAULT_JSON = `{
|
export const CUSTOM_HEADERS_DEFAULT_JSON = `{
|
||||||
}`;
|
}`;
|
||||||
const SUPPORTED_OPENAI_ENDPOINTS = new Set([OPENAI_ENDPOINT_RESPONSES, OPENAI_ENDPOINT_CHAT_COMPLETIONS]);
|
const SUPPORTED_OPENAI_ENDPOINTS = new Set([OPENAI_ENDPOINT_RESPONSES, OPENAI_ENDPOINT_CHAT_COMPLETIONS, OPENAI_ENDPOINT_CUSTOM]);
|
||||||
const SUPPORTED_ROUTE_MODES = new Set(["local", "upstream"]);
|
const SUPPORTED_ROUTE_MODES = new Set(["local", "upstream"]);
|
||||||
const PROXY_STATE_EVENT = "proxy:state";
|
const PROXY_STATE_EVENT = "proxy:state";
|
||||||
const USER_CONFIG_CHANGED_EVENT = "user-config:changed";
|
const USER_CONFIG_CHANGED_EVENT = "user-config:changed";
|
||||||
@@ -282,6 +283,9 @@ export function createEmptyModelAdapter() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeOpenAIEndpoint 归一化 endpoint 路径。
|
||||||
|
// 支持三个预设值:/v1/responses、/v1/chat/completions、/custom(自定义路径)。
|
||||||
|
// 选 /custom 时,用户需在接口地址栏填写完整请求 URL。
|
||||||
function normalizeOpenAIEndpoint(value) {
|
function normalizeOpenAIEndpoint(value) {
|
||||||
const text = asString(value).toLowerCase();
|
const text = asString(value).toLowerCase();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@@ -290,6 +294,10 @@ function normalizeOpenAIEndpoint(value) {
|
|||||||
return SUPPORTED_OPENAI_ENDPOINTS.has(text) ? text : "";
|
return SUPPORTED_OPENAI_ENDPOINTS.has(text) ? text : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidOpenAIEndpoint(value) {
|
||||||
|
return normalizeOpenAIEndpoint(value) !== "";
|
||||||
|
}
|
||||||
|
|
||||||
function validateJSONObject(value, label) {
|
function validateJSONObject(value, label) {
|
||||||
const text = asString(value);
|
const text = asString(value);
|
||||||
if (!text) {
|
if (!text) {
|
||||||
@@ -424,10 +432,10 @@ export function validateModelAdapters(source) {
|
|||||||
return `${prefix} 的模型标识不能为空`;
|
return `${prefix} 的模型标识不能为空`;
|
||||||
}
|
}
|
||||||
if (adapter.type === "openai" && !SUPPORTED_REASONING_EFFORTS.has(adapter.reasoningEffort)) {
|
if (adapter.type === "openai" && !SUPPORTED_REASONING_EFFORTS.has(adapter.reasoningEffort)) {
|
||||||
return `${prefix} 的推理强度仅支持 low、medium、high、xhigh`;
|
return `${prefix} 的推理强度仅支持 low、medium、high、xhigh、max`;
|
||||||
}
|
}
|
||||||
if (adapter.type === "openai" && !SUPPORTED_OPENAI_ENDPOINTS.has(adapter.openAIEndpoint)) {
|
if (adapter.type === "openai" && !isValidOpenAIEndpoint(adapter.openAIEndpoint)) {
|
||||||
return `${prefix} 的 OpenAI 端点仅支持 /v1/responses 或 /v1/chat/completions`;
|
return `${prefix} 的 OpenAI 端点仅支持 /v1/responses、/v1/chat/completions 或以 / 开头的自定义路径`;
|
||||||
}
|
}
|
||||||
if (adapter.type === "openai" && adapter.openAIExtraParamsEnabled) {
|
if (adapter.type === "openai" && adapter.openAIExtraParamsEnabled) {
|
||||||
const extraParamsError = validateOpenAIExtraParamsJSON(adapter.openAIExtraParamsJSON);
|
const extraParamsError = validateOpenAIExtraParamsJSON(adapter.openAIExtraParamsJSON);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
isModelAdapterTestResultStale,
|
isModelAdapterTestResultStale,
|
||||||
normalizeModelAdapter,
|
normalizeModelAdapter,
|
||||||
OPENAI_ENDPOINT_CHAT_COMPLETIONS,
|
OPENAI_ENDPOINT_CHAT_COMPLETIONS,
|
||||||
|
OPENAI_ENDPOINT_CUSTOM,
|
||||||
OPENAI_ENDPOINT_RESPONSES,
|
OPENAI_ENDPOINT_RESPONSES,
|
||||||
OPENAI_EXTRA_PARAMS_DEFAULT_JSON,
|
OPENAI_EXTRA_PARAMS_DEFAULT_JSON,
|
||||||
runModelAdapterTest,
|
runModelAdapterTest,
|
||||||
@@ -37,6 +38,7 @@ const reasoningEffortOptions = [
|
|||||||
{ label: "中", value: "medium", icon: "icon-[mdi--head-lightbulb-outline]" },
|
{ label: "中", value: "medium", icon: "icon-[mdi--head-lightbulb-outline]" },
|
||||||
{ label: "高", value: "high", icon: "icon-[mdi--brain]" },
|
{ label: "高", value: "high", icon: "icon-[mdi--brain]" },
|
||||||
{ label: "极高", value: "xhigh", icon: "icon-[mdi--head-cog-outline]" },
|
{ label: "极高", value: "xhigh", icon: "icon-[mdi--head-cog-outline]" },
|
||||||
|
{ label: "最高", value: "max", icon: "icon-[mdi--brain]" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const anthropicThinkingEffortOptions = [
|
const anthropicThinkingEffortOptions = [
|
||||||
@@ -50,6 +52,7 @@ const anthropicThinkingEffortOptions = [
|
|||||||
const openAIEndpointOptions = [
|
const openAIEndpointOptions = [
|
||||||
{ label: "/v1/responses", value: OPENAI_ENDPOINT_RESPONSES, icon: "icon-[mdi--api]" },
|
{ label: "/v1/responses", value: OPENAI_ENDPOINT_RESPONSES, icon: "icon-[mdi--api]" },
|
||||||
{ label: "/v1/chat/completions", value: OPENAI_ENDPOINT_CHAT_COMPLETIONS, icon: "icon-[mdi--message-text-outline]" },
|
{ label: "/v1/chat/completions", value: OPENAI_ENDPOINT_CHAT_COMPLETIONS, icon: "icon-[mdi--message-text-outline]" },
|
||||||
|
{ label: "自定义路径(请输入完整请求地址)", value: OPENAI_ENDPOINT_CUSTOM, icon: "icon-[mdi--pencil-outline]" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const editorIndex = ref(-1);
|
const editorIndex = ref(-1);
|
||||||
@@ -128,7 +131,7 @@ const fieldTips = {
|
|||||||
contextWindowTokens: "模型单次可接受的最大上下文 Token 数。留空时使用默认值。",
|
contextWindowTokens: "模型单次可接受的最大上下文 Token 数。留空时使用默认值。",
|
||||||
reasoningEffort: "推理强度仅对部分支持 reasoning_effort 的模型生效,并不是所有模型都支持。越高通常越稳,但也可能更慢。",
|
reasoningEffort: "推理强度仅对部分支持 reasoning_effort 的模型生效,并不是所有模型都支持。越高通常越稳,但也可能更慢。",
|
||||||
maxCompletionTokens: "单次回复允许生成的最大 Token 数。留空时使用默认值。",
|
maxCompletionTokens: "单次回复允许生成的最大 Token 数。留空时使用默认值。",
|
||||||
openAIEndpoint: "OpenAI 兼容接口使用的协议端点。未选择时默认使用 /v1/responses。",
|
openAIEndpoint: "选择接口协议端点。选“自定义路径”时,请在接口地址栏填写完整请求地址(含 /chat/completions 或 /responses 路径后缀),系统会根据末段自动判断协议形态。",
|
||||||
openAIExtraParams: "开启后会把 JSON 对象覆盖到 OpenAI 请求体。同名字段以这里为准。OpenAI service_tier 支持 auto、default、flex、scale、priority。",
|
openAIExtraParams: "开启后会把 JSON 对象覆盖到 OpenAI 请求体。同名字段以这里为准。OpenAI service_tier 支持 auto、default、flex、scale、priority。",
|
||||||
customHeaders: "开启后会把 JSON 对象覆盖到最终请求头。同名请求头以这里为准,值必须是字符串。",
|
customHeaders: "开启后会把 JSON 对象覆盖到最终请求头。同名请求头以这里为准,值必须是字符串。",
|
||||||
anthropicExtraParams: "开启后会把 JSON 对象覆盖到 Anthropic 请求体。同名字段以这里为准。",
|
anthropicExtraParams: "开启后会把 JSON 对象覆盖到 Anthropic 请求体。同名字段以这里为准。",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type openAIRequestBody struct {
|
|||||||
Tools []json.RawMessage `json:"tools,omitempty"`
|
Tools []json.RawMessage `json:"tools,omitempty"`
|
||||||
Messages []map[string]any `json:"messages"`
|
Messages []map[string]any `json:"messages"`
|
||||||
Stream bool `json:"stream"`
|
Stream bool `json:"stream"`
|
||||||
MaxTokens int `json:"max_tokens"`
|
MaxTokens int `json:"max_tokens,omitempty"`
|
||||||
StreamOptions map[string]any `json:"stream_options"`
|
StreamOptions map[string]any `json:"stream_options"`
|
||||||
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
ReasoningEffort string `json:"reasoning_effort,omitempty"`
|
||||||
PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
||||||
@@ -41,7 +41,7 @@ type openAIResponsesRequestBody struct {
|
|||||||
Input []map[string]any `json:"input"`
|
Input []map[string]any `json:"input"`
|
||||||
Tools []map[string]any `json:"tools,omitempty"`
|
Tools []map[string]any `json:"tools,omitempty"`
|
||||||
Stream bool `json:"stream"`
|
Stream bool `json:"stream"`
|
||||||
MaxOutputTokens int `json:"max_output_tokens"`
|
MaxOutputTokens int `json:"max_output_tokens,omitempty"`
|
||||||
Reasoning *openAIResponsesReasoning `json:"reasoning,omitempty"`
|
Reasoning *openAIResponsesReasoning `json:"reasoning,omitempty"`
|
||||||
Include []string `json:"include,omitempty"`
|
Include []string `json:"include,omitempty"`
|
||||||
PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
PromptCacheKey string `json:"prompt_cache_key,omitempty"`
|
||||||
@@ -304,15 +304,67 @@ func OpenAIEndpointURL(baseURL string, endpoint string) string {
|
|||||||
if !strings.HasPrefix(normalizedEndpoint, "/") {
|
if !strings.HasPrefix(normalizedEndpoint, "/") {
|
||||||
normalizedEndpoint = "/" + normalizedEndpoint
|
normalizedEndpoint = "/" + normalizedEndpoint
|
||||||
}
|
}
|
||||||
|
// 规则0:自定义路径模式
|
||||||
|
// - baseURL 已含 endpoint 后缀(/chat/completions 或 /responses)→ 直接用 base
|
||||||
|
// - 否则追加 /chat/completions(默认协议形态,覆盖 Z.AI /v4 等场景)
|
||||||
|
if normalizedEndpoint == modelchannel.OpenAIEndpointCustom {
|
||||||
if OpenAIEndpointFromBaseURL(base) != "" {
|
if OpenAIEndpointFromBaseURL(base) != "" {
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(base, "/v1") && strings.HasPrefix(normalizedEndpoint, "/v1/") {
|
return base + "/chat/completions"
|
||||||
return base + strings.TrimPrefix(normalizedEndpoint, "/v1")
|
|
||||||
}
|
}
|
||||||
|
// 规则1:baseURL 已含 endpoint 后缀 → 直接用 base
|
||||||
|
if OpenAIEndpointFromBaseURL(base) != "" {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
// 规则2:baseURL 以 /vN 结尾时,剥离 endpoint 的版本前缀(/v1/、/v2/ 等)
|
||||||
|
// 这样 base=.../v4 + endpoint=/v1/chat/completions → .../v4/chat/completions
|
||||||
|
if _, ok := trailingVersionSegment(base); ok {
|
||||||
|
if rest, stripped := stripEndpointVersionPrefix(normalizedEndpoint); stripped {
|
||||||
|
return base + rest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 规则3:兜底原样拼接
|
||||||
return base + normalizedEndpoint
|
return base + normalizedEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trailingVersionSegment 检测 URL 末尾是否以 /vN 形式结尾(N 为数字),
|
||||||
|
// 返回版本段(如 "v4")和是否匹配。用于通用版本段去重。
|
||||||
|
func trailingVersionSegment(base string) (string, bool) {
|
||||||
|
idx := strings.LastIndex(base, "/")
|
||||||
|
if idx < 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
seg := base[idx+1:]
|
||||||
|
if len(seg) < 2 || seg[0] != 'v' {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
for i := 1; i < len(seg); i++ {
|
||||||
|
if seg[i] < '0' || seg[i] > '9' {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seg, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripEndpointVersionPrefix 剥离 endpoint 路径开头的版本段前缀(/vN/),
|
||||||
|
// 返回剩余路径和是否成功剥离。
|
||||||
|
// /v1/chat/completions → ("/chat/completions", true)
|
||||||
|
// /chat/completions → ("", false)
|
||||||
|
func stripEndpointVersionPrefix(endpoint string) (string, bool) {
|
||||||
|
if len(endpoint) < 4 || endpoint[0] != '/' || endpoint[1] != 'v' {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
i := 2
|
||||||
|
for i < len(endpoint) && endpoint[i] >= '0' && endpoint[i] <= '9' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == 2 || i >= len(endpoint) || endpoint[i] != '/' {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return endpoint[i:], true
|
||||||
|
}
|
||||||
|
|
||||||
func ResolveOpenAIEndpoint(baseURL string, endpoint string) string {
|
func ResolveOpenAIEndpoint(baseURL string, endpoint string) string {
|
||||||
if endpointFromURL := OpenAIEndpointFromBaseURL(baseURL); endpointFromURL != "" {
|
if endpointFromURL := OpenAIEndpointFromBaseURL(baseURL); endpointFromURL != "" {
|
||||||
return endpointFromURL
|
return endpointFromURL
|
||||||
@@ -377,11 +429,11 @@ func (adapter *OpenAIAdapter) Stream(ctx context.Context, req StreamRequest, sin
|
|||||||
req.OpenAIEndpoint = endpoint
|
req.OpenAIEndpoint = endpoint
|
||||||
if req.RequestKnobs != nil {
|
if req.RequestKnobs != nil {
|
||||||
req.RequestKnobs["openai_endpoint"] = endpoint
|
req.RequestKnobs["openai_endpoint"] = endpoint
|
||||||
if endpoint == modelchannel.OpenAIEndpointResponses {
|
if modelchannel.OpenAIEndpointShape(endpoint) == "responses" {
|
||||||
req.RequestKnobs["max_output_tokens"] = req.MaxTokens
|
req.RequestKnobs["max_output_tokens"] = req.MaxTokens
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if endpoint == modelchannel.OpenAIEndpointResponses {
|
if modelchannel.OpenAIEndpointShape(endpoint) == "responses" {
|
||||||
return adapter.streamResponses(ctx, req, baseURL, apiKey, modelID, sink)
|
return adapter.streamResponses(ctx, req, baseURL, apiKey, modelID, sink)
|
||||||
}
|
}
|
||||||
return adapter.streamChatCompletions(ctx, req, baseURL, apiKey, modelID, sink)
|
return adapter.streamChatCompletions(ctx, req, baseURL, apiKey, modelID, sink)
|
||||||
@@ -403,9 +455,11 @@ func (adapter *OpenAIAdapter) streamChatCompletions(ctx context.Context, req Str
|
|||||||
Model: modelID,
|
Model: modelID,
|
||||||
Messages: normalizedMessages,
|
Messages: normalizedMessages,
|
||||||
Stream: true,
|
Stream: true,
|
||||||
MaxTokens: req.MaxTokens,
|
|
||||||
StreamOptions: map[string]any{"include_usage": true},
|
StreamOptions: map[string]any{"include_usage": true},
|
||||||
}
|
}
|
||||||
|
if shouldSendOpenAIMaxOutputTokens(modelID) {
|
||||||
|
requestBody.MaxTokens = req.MaxTokens
|
||||||
|
}
|
||||||
if key := openAIPromptCacheKey(req, modelID); key != "" {
|
if key := openAIPromptCacheKey(req, modelID); key != "" {
|
||||||
requestBody.PromptCacheKey = key
|
requestBody.PromptCacheKey = key
|
||||||
}
|
}
|
||||||
@@ -425,14 +479,14 @@ func (adapter *OpenAIAdapter) streamChatCompletions(ctx context.Context, req Str
|
|||||||
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
applyOpenAIThinkingDisable(bodyMap, req, baseURL, modelID, modelchannel.OpenAIEndpointChatCompletions)
|
applyOpenAIThinkingDisable(bodyMap, req, baseURL, modelID, req.OpenAIEndpoint)
|
||||||
if err := ApplyOpenAIExtraParams(bodyMap, req.OpenAIExtraParamsEnabled, req.OpenAIExtraParamsJSON); err != nil {
|
if err := ApplyOpenAIExtraParams(bodyMap, req.OpenAIExtraParamsEnabled, req.OpenAIExtraParamsJSON); err != nil {
|
||||||
finishedAt = time.Now().UTC()
|
finishedAt = time.Now().UTC()
|
||||||
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
body = bodyMap
|
body = bodyMap
|
||||||
requestURL := OpenAIEndpointURL(baseURL, modelchannel.OpenAIEndpointChatCompletions)
|
requestURL := OpenAIEndpointURL(baseURL, req.OpenAIEndpoint)
|
||||||
recordLLMRequestArtifact(req, "openai", modelID, "POST", requestURL, body)
|
recordLLMRequestArtifact(req, "openai", modelID, "POST", requestURL, body)
|
||||||
|
|
||||||
payload, err := json.Marshal(body)
|
payload, err := json.Marshal(body)
|
||||||
@@ -866,9 +920,11 @@ func (adapter *OpenAIAdapter) streamResponses(ctx context.Context, req StreamReq
|
|||||||
Instructions: instructions,
|
Instructions: instructions,
|
||||||
Input: input,
|
Input: input,
|
||||||
Stream: true,
|
Stream: true,
|
||||||
MaxOutputTokens: req.MaxTokens,
|
|
||||||
Store: false,
|
Store: false,
|
||||||
}
|
}
|
||||||
|
if shouldSendOpenAIMaxOutputTokens(modelID) {
|
||||||
|
requestBody.MaxOutputTokens = req.MaxTokens
|
||||||
|
}
|
||||||
if key := openAIPromptCacheKey(req, modelID); key != "" {
|
if key := openAIPromptCacheKey(req, modelID); key != "" {
|
||||||
requestBody.PromptCacheKey = key
|
requestBody.PromptCacheKey = key
|
||||||
}
|
}
|
||||||
@@ -901,7 +957,7 @@ func (adapter *OpenAIAdapter) streamResponses(ctx context.Context, req StreamReq
|
|||||||
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
applyOpenAIThinkingDisable(bodyMap, req, baseURL, modelID, modelchannel.OpenAIEndpointResponses)
|
applyOpenAIThinkingDisable(bodyMap, req, baseURL, modelID, req.OpenAIEndpoint)
|
||||||
if err := ApplyOpenAIExtraParams(bodyMap, req.OpenAIExtraParamsEnabled, req.OpenAIExtraParamsJSON); err != nil {
|
if err := ApplyOpenAIExtraParams(bodyMap, req.OpenAIExtraParamsEnabled, req.OpenAIExtraParamsJSON); err != nil {
|
||||||
finishedAt = time.Now().UTC()
|
finishedAt = time.Now().UTC()
|
||||||
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
recordLLMSummaryArtifact(req, buildLLMSummaryPayload(req, "openai", modelID, startedAt, time.Time{}, finishedAt, "", 0, 0, 0, 0, err))
|
||||||
@@ -909,7 +965,7 @@ func (adapter *OpenAIAdapter) streamResponses(ctx context.Context, req StreamReq
|
|||||||
}
|
}
|
||||||
body = bodyMap
|
body = bodyMap
|
||||||
|
|
||||||
requestURL := OpenAIEndpointURL(baseURL, modelchannel.OpenAIEndpointResponses)
|
requestURL := OpenAIEndpointURL(baseURL, req.OpenAIEndpoint)
|
||||||
recordLLMRequestArtifact(req, "openai", modelID, "POST", requestURL, body)
|
recordLLMRequestArtifact(req, "openai", modelID, "POST", requestURL, body)
|
||||||
|
|
||||||
payload, err := json.Marshal(body)
|
payload, err := json.Marshal(body)
|
||||||
@@ -1792,6 +1848,10 @@ func normalizeOpenAIProviderMessages(messages []Message, thinkingEnabled bool) (
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldSendOpenAIMaxOutputTokens(modelID string) bool {
|
||||||
|
return !strings.Contains(strings.ToLower(strings.TrimSpace(modelID)), "gpt")
|
||||||
|
}
|
||||||
|
|
||||||
func shouldIncludeOpenAIReasoningContent(message Message, thinkingEnabled bool) bool {
|
func shouldIncludeOpenAIReasoningContent(message Message, thinkingEnabled bool) bool {
|
||||||
if strings.TrimSpace(message.ReasoningContent) != "" {
|
if strings.TrimSpace(message.ReasoningContent) != "" {
|
||||||
return true
|
return true
|
||||||
@@ -1819,7 +1879,7 @@ func applyOpenAIThinkingDisable(body map[string]any, req StreamRequest, baseURL
|
|||||||
delete(body, "reasoning_effort")
|
delete(body, "reasoning_effort")
|
||||||
setRequestKnob(req, "thinking_disabled_provider_param", "enable_thinking")
|
setRequestKnob(req, "thinking_disabled_provider_param", "enable_thinking")
|
||||||
case "reasoning_none":
|
case "reasoning_none":
|
||||||
if endpoint == modelchannel.OpenAIEndpointResponses {
|
if modelchannel.OpenAIEndpointShape(endpoint) == "responses" {
|
||||||
body["reasoning"] = map[string]any{"effort": "none"}
|
body["reasoning"] = map[string]any{"effort": "none"}
|
||||||
} else {
|
} else {
|
||||||
body["reasoning_effort"] = "none"
|
body["reasoning_effort"] = "none"
|
||||||
|
|||||||
@@ -152,9 +152,9 @@ func NormalizeModelAdapterConfigs(input []ModelAdapterConfig) ([]ModelAdapterCon
|
|||||||
case next.ModelID == "":
|
case next.ModelID == "":
|
||||||
return nil, errors.New("模型适配器 modelID 不能为空")
|
return nil, errors.New("模型适配器 modelID 不能为空")
|
||||||
case next.Type == "openai" && next.ReasoningEffort == "":
|
case next.Type == "openai" && next.ReasoningEffort == "":
|
||||||
return nil, errors.New("模型适配器 reasoningEffort 仅支持 low、medium、high、xhigh")
|
return nil, errors.New("模型适配器 reasoningEffort 仅支持 low、medium、high、xhigh、max")
|
||||||
case next.Type == "openai" && next.OpenAIEndpoint == "":
|
case next.Type == "openai" && next.OpenAIEndpoint == "":
|
||||||
return nil, errors.New("模型适配器 openAIEndpoint 仅支持 /v1/responses 或 /v1/chat/completions")
|
return nil, errors.New("模型适配器 openAIEndpoint 仅支持 /v1/responses、/v1/chat/completions 或 /custom(自定义路径)")
|
||||||
case next.Type == "openai" && next.OpenAIExtraParamsEnabled:
|
case next.Type == "openai" && next.OpenAIExtraParamsEnabled:
|
||||||
if err := validateJSONMap(next.OpenAIExtraParamsJSON, "openAIExtraParamsJSON"); err != nil {
|
if err := validateJSONMap(next.OpenAIExtraParamsJSON, "openAIExtraParamsJSON"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -216,7 +216,7 @@ func normalizeReasoningEffort(value string) string {
|
|||||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||||
case "", "medium":
|
case "", "medium":
|
||||||
return "medium"
|
return "medium"
|
||||||
case "low", "high", "xhigh":
|
case "low", "high", "xhigh", "max":
|
||||||
return strings.ToLower(strings.TrimSpace(value))
|
return strings.ToLower(strings.TrimSpace(value))
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ const (
|
|||||||
bootstrapStatsigDisableTerminalOutputUIStreaming = "disable_terminal_output_ui_streaming"
|
bootstrapStatsigDisableTerminalOutputUIStreaming = "disable_terminal_output_ui_streaming"
|
||||||
bootstrapStatsigBrowserCanvas = "browser_canvas"
|
bootstrapStatsigBrowserCanvas = "browser_canvas"
|
||||||
bootstrapStatsigEnableMultitaskMode = "enable_multitask_mode"
|
bootstrapStatsigEnableMultitaskMode = "enable_multitask_mode"
|
||||||
|
bootstrapStatsigDecomposeAlwaysLocalExtHostGate = "decompose_always_local_ext_host"
|
||||||
|
bootstrapStatsigCursorExtensionsIsolationV2Gate = "cursor_extensions_isolation_v2"
|
||||||
bootstrapStatsigCursorAgentWorkerExtension = "enable_cursor_agent_worker_extension"
|
bootstrapStatsigCursorAgentWorkerExtension = "enable_cursor_agent_worker_extension"
|
||||||
bootstrapStatsigExperimentName = "free_user_model_picker"
|
bootstrapStatsigExperimentName = "free_user_model_picker"
|
||||||
bootstrapStatsigVariantParam = "variant"
|
bootstrapStatsigVariantParam = "variant"
|
||||||
@@ -139,6 +141,8 @@ var bootstrapStatsigTemplate = statsigBootstrapTemplate{
|
|||||||
bootstrapStatsigDisableTerminalOutputUIStreaming: buildEnabledStatsigGate(bootstrapStatsigDisableTerminalOutputUIStreaming),
|
bootstrapStatsigDisableTerminalOutputUIStreaming: buildEnabledStatsigGate(bootstrapStatsigDisableTerminalOutputUIStreaming),
|
||||||
bootstrapStatsigBrowserCanvas: buildEnabledStatsigGate(bootstrapStatsigBrowserCanvas),
|
bootstrapStatsigBrowserCanvas: buildEnabledStatsigGate(bootstrapStatsigBrowserCanvas),
|
||||||
bootstrapStatsigEnableMultitaskMode: buildEnabledStatsigGate(bootstrapStatsigEnableMultitaskMode),
|
bootstrapStatsigEnableMultitaskMode: buildEnabledStatsigGate(bootstrapStatsigEnableMultitaskMode),
|
||||||
|
bootstrapStatsigDecomposeAlwaysLocalExtHostGate: buildDisabledStatsigGate(bootstrapStatsigDecomposeAlwaysLocalExtHostGate),
|
||||||
|
bootstrapStatsigCursorExtensionsIsolationV2Gate: buildDisabledStatsigGate(bootstrapStatsigCursorExtensionsIsolationV2Gate),
|
||||||
bootstrapStatsigCursorAgentWorkerExtension: buildDisabledStatsigGate(bootstrapStatsigCursorAgentWorkerExtension),
|
bootstrapStatsigCursorAgentWorkerExtension: buildDisabledStatsigGate(bootstrapStatsigCursorAgentWorkerExtension),
|
||||||
},
|
},
|
||||||
DynamicConfigs: map[string]statsigDynamicConfigTemplate{
|
DynamicConfigs: map[string]statsigDynamicConfigTemplate{
|
||||||
@@ -742,7 +746,7 @@ func buildThinkingEffortVariantDisplayName(modelDisplayName string, effortValue
|
|||||||
|
|
||||||
func thinkingEffortValuesForAdapter(adapterType string) []string {
|
func thinkingEffortValuesForAdapter(adapterType string) []string {
|
||||||
values := []string{"disabled", "low", "medium", "high", "xhigh"}
|
values := []string{"disabled", "low", "medium", "high", "xhigh"}
|
||||||
if strings.EqualFold(strings.TrimSpace(adapterType), "anthropic") {
|
if adapterType := strings.ToLower(strings.TrimSpace(adapterType)); adapterType == "openai" || adapterType == "anthropic" {
|
||||||
values = append(values, "max")
|
values = append(values, "max")
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
@@ -769,7 +773,7 @@ func defaultThinkingEffortForAdapter(adapter legacyruntime.ModelAdapterConfig) s
|
|||||||
if strings.EqualFold(strings.TrimSpace(adapter.Type), "anthropic") {
|
if strings.EqualFold(strings.TrimSpace(adapter.Type), "anthropic") {
|
||||||
return normalizeAvailableModelThinkingEffort(adapter.AnthropicThinkingEffort, true, "xhigh")
|
return normalizeAvailableModelThinkingEffort(adapter.AnthropicThinkingEffort, true, "xhigh")
|
||||||
}
|
}
|
||||||
return normalizeAvailableModelThinkingEffort(adapter.ReasoningEffort, false, "medium")
|
return normalizeAvailableModelThinkingEffort(adapter.ReasoningEffort, true, "medium")
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeAvailableModelThinkingEffort(raw string, allowMax bool, fallback string) string {
|
func normalizeAvailableModelThinkingEffort(raw string, allowMax bool, fallback string) string {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package upstream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildBootstrapStatsigConfigJSONDisablesAlwaysLocalDecompositionGate(t *testing.T) {
|
||||||
|
payload, err := buildBootstrapStatsigConfigJSON(12345, "test-auth-id")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("build bootstrap statsig config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var decoded statsigBootstrapTemplate
|
||||||
|
if err := json.Unmarshal(payload, &decoded); err != nil {
|
||||||
|
t.Fatalf("decode bootstrap statsig config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gate, ok := decoded.FeatureGates[bootstrapStatsigDecomposeAlwaysLocalExtHostGate]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("missing feature gate %q", bootstrapStatsigDecomposeAlwaysLocalExtHostGate)
|
||||||
|
}
|
||||||
|
if value, _ := gate["value"].(bool); value {
|
||||||
|
t.Fatalf("expected %q to be disabled", bootstrapStatsigDecomposeAlwaysLocalExtHostGate)
|
||||||
|
}
|
||||||
|
if ruleID, _ := gate["rule_id"].(string); ruleID != "local_disabled" {
|
||||||
|
t.Fatalf("unexpected rule_id: %q", ruleID)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -668,7 +668,7 @@ func normalizeModelAdapterTestType(value string) string {
|
|||||||
|
|
||||||
func normalizeModelAdapterTestReasoning(value string) string {
|
func normalizeModelAdapterTestReasoning(value string) string {
|
||||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||||
case "low", "medium", "high", "xhigh":
|
case "low", "medium", "high", "xhigh", "max":
|
||||||
return strings.ToLower(strings.TrimSpace(value))
|
return strings.ToLower(strings.TrimSpace(value))
|
||||||
default:
|
default:
|
||||||
return "medium"
|
return "medium"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cursor
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -23,8 +24,14 @@ const (
|
|||||||
cursorStateDBRelativePath = "Cursor/User/globalStorage/state.vscdb"
|
cursorStateDBRelativePath = "Cursor/User/globalStorage/state.vscdb"
|
||||||
cursorStateDarwinRelativePath = "Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
cursorStateDarwinRelativePath = "Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||||||
cursorStateLinuxRelativePath = ".config/Cursor/User/globalStorage/state.vscdb"
|
cursorStateLinuxRelativePath = ".config/Cursor/User/globalStorage/state.vscdb"
|
||||||
|
cursorStateStatsigBootstrapKey = "workbench.experiments.statsigBootstrap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cursorStateDisabledStatsigGates = []string{
|
||||||
|
"decompose_always_local_ext_host",
|
||||||
|
"cursor_extensions_isolation_v2",
|
||||||
|
}
|
||||||
|
|
||||||
// InjectCursorUserInfo synchronizes the Cursor user-level auth cache used by the
|
// InjectCursorUserInfo synchronizes the Cursor user-level auth cache used by the
|
||||||
// Settings page. It does not modify the installed Cursor app bundle.
|
// Settings page. It does not modify the installed Cursor app bundle.
|
||||||
func InjectCursorUserInfo(email, token string) error {
|
func InjectCursorUserInfo(email, token string) error {
|
||||||
@@ -42,11 +49,12 @@ func InjectCursorUserInfo(email, token string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Infof(
|
logger.Infof(
|
||||||
"injectCursorUserInfo synced path=%s email=%s membership=%s subscription=%s",
|
"injectCursorUserInfo synced path=%s email=%s membership=%s subscription=%s disabled_statsig_gates=%s",
|
||||||
stateDBPath,
|
stateDBPath,
|
||||||
values["cursorAuth/cachedEmail"],
|
values["cursorAuth/cachedEmail"],
|
||||||
values["cursorAuth/stripeMembershipType"],
|
values["cursorAuth/stripeMembershipType"],
|
||||||
values["cursorAuth/stripeSubscriptionStatus"],
|
values["cursorAuth/stripeSubscriptionStatus"],
|
||||||
|
strings.Join(cursorStateDisabledStatsigGates, ","),
|
||||||
)
|
)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -111,6 +119,10 @@ func syncCursorAuthStateDB(path string, values map[string]string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := disableCursorStatsigGates(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
if err := tx.Commit(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -118,6 +130,70 @@ func syncCursorAuthStateDB(path string, values map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func disableCursorStatsigGates(ctx context.Context, tx *sql.Tx) error {
|
||||||
|
var raw []byte
|
||||||
|
err := tx.QueryRowContext(ctx, "SELECT value FROM ItemTable WHERE key = ?", cursorStateStatsigBootstrapKey).Scan(&raw)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload map[string]any
|
||||||
|
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||||
|
return fmt.Errorf("解析 Cursor Statsig bootstrap 失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
featureGates, _ := payload["feature_gates"].(map[string]any)
|
||||||
|
if featureGates == nil {
|
||||||
|
featureGates = map[string]any{}
|
||||||
|
payload["feature_gates"] = featureGates
|
||||||
|
}
|
||||||
|
|
||||||
|
hashUsed, _ := payload["hash_used"].(string)
|
||||||
|
for _, gate := range cursorStateDisabledStatsigGates {
|
||||||
|
disableCursorStatsigGate(featureGates, gate)
|
||||||
|
if strings.EqualFold(hashUsed, "djb2") {
|
||||||
|
disableCursorStatsigGate(featureGates, cursorStateDJB2Hash(gate))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("编码 Cursor Statsig bootstrap 失败: %w", err)
|
||||||
|
}
|
||||||
|
if _, err := tx.ExecContext(ctx, "UPDATE ItemTable SET value = ? WHERE key = ?", updated, cursorStateStatsigBootstrapKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableCursorStatsigGate(featureGates map[string]any, key string) {
|
||||||
|
gate, _ := featureGates[key].(map[string]any)
|
||||||
|
if gate == nil {
|
||||||
|
gate = map[string]any{
|
||||||
|
"name": key,
|
||||||
|
"rule_id": "local_disabled",
|
||||||
|
"ruleID": "local_disabled",
|
||||||
|
"group_name": "local_disabled",
|
||||||
|
"groupName": "local_disabled",
|
||||||
|
"id_type": "userID",
|
||||||
|
"idType": "userID",
|
||||||
|
}
|
||||||
|
featureGates[key] = gate
|
||||||
|
}
|
||||||
|
gate["value"] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func cursorStateDJB2Hash(value string) string {
|
||||||
|
var hash uint32
|
||||||
|
for _, b := range []byte(value) {
|
||||||
|
hash = hash*31 + uint32(b)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d", hash)
|
||||||
|
}
|
||||||
|
|
||||||
func resolveCursorStateDBPath() (string, error) {
|
func resolveCursorStateDBPath() (string, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const ChannelIDHexLength = 16
|
|||||||
const (
|
const (
|
||||||
OpenAIEndpointResponses = "/v1/responses"
|
OpenAIEndpointResponses = "/v1/responses"
|
||||||
OpenAIEndpointChatCompletions = "/v1/chat/completions"
|
OpenAIEndpointChatCompletions = "/v1/chat/completions"
|
||||||
|
OpenAIEndpointCustom = "/custom"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NormalizeBaseURL(raw string) (string, error) {
|
func NormalizeBaseURL(raw string) (string, error) {
|
||||||
@@ -39,20 +40,37 @@ func NormalizeBaseURL(raw string) (string, error) {
|
|||||||
return normalized, nil
|
return normalized, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormalizeOpenAIEndpoint 归一化 OpenAI endpoint 路径。
|
||||||
|
// 支持三个预设值:/v1/responses、/v1/chat/completions、/custom(自定义路径)。
|
||||||
|
// 选 /custom 时,用户需在接口地址栏填写完整请求 URL。
|
||||||
func NormalizeOpenAIEndpoint(providerType string, endpoint string) string {
|
func NormalizeOpenAIEndpoint(providerType string, endpoint string) string {
|
||||||
if strings.TrimSpace(strings.ToLower(providerType)) != "openai" {
|
if strings.TrimSpace(strings.ToLower(providerType)) != "openai" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
switch strings.ToLower(strings.TrimSpace(endpoint)) {
|
normalized := strings.TrimSpace(endpoint)
|
||||||
case "", OpenAIEndpointResponses:
|
switch normalized {
|
||||||
|
case "":
|
||||||
return OpenAIEndpointResponses
|
return OpenAIEndpointResponses
|
||||||
case OpenAIEndpointChatCompletions:
|
case OpenAIEndpointResponses, OpenAIEndpointChatCompletions, OpenAIEndpointCustom:
|
||||||
return OpenAIEndpointChatCompletions
|
return normalized
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAIEndpointShape 根据 endpoint 路径末段推断协议形态。
|
||||||
|
// 返回 "responses"(Responses API)或 "chat/completions"(Chat Completions API)。
|
||||||
|
// 这样 /v1/chat/completions、/v4/chat/completions、/chat/completions 都走同一协议分支。
|
||||||
|
func OpenAIEndpointShape(endpoint string) string {
|
||||||
|
lower := strings.ToLower(strings.TrimSpace(endpoint))
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(lower, "/responses"):
|
||||||
|
return "responses"
|
||||||
|
default:
|
||||||
|
return "chat/completions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BuildLegacyChannelID(baseURL string, modelID string, apiKey string, name string) string {
|
func BuildLegacyChannelID(baseURL string, modelID string, apiKey string, name string) string {
|
||||||
return buildChannelID([]string{
|
return buildChannelID([]string{
|
||||||
strings.TrimSpace(baseURL),
|
strings.TrimSpace(baseURL),
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ func NormalizeModelAdapterConfigs(input []ModelAdapterConfig) ([]ModelAdapterCon
|
|||||||
case next.ModelID == "":
|
case next.ModelID == "":
|
||||||
return nil, errors.New("模型适配器 modelID 不能为空")
|
return nil, errors.New("模型适配器 modelID 不能为空")
|
||||||
case next.Type == "openai" && next.ReasoningEffort == "":
|
case next.Type == "openai" && next.ReasoningEffort == "":
|
||||||
return nil, errors.New("模型适配器 reasoningEffort 仅支持 low、medium、high、xhigh")
|
return nil, errors.New("模型适配器 reasoningEffort 仅支持 low、medium、high、xhigh、max")
|
||||||
case next.Type == "openai" && next.OpenAIEndpoint == "":
|
case next.Type == "openai" && next.OpenAIEndpoint == "":
|
||||||
return nil, errors.New("模型适配器 openAIEndpoint 仅支持 /v1/responses 或 /v1/chat/completions")
|
return nil, errors.New("模型适配器 openAIEndpoint 仅支持 /v1/responses 或 /v1/chat/completions")
|
||||||
case next.Type == "openai" && next.OpenAIExtraParamsEnabled:
|
case next.Type == "openai" && next.OpenAIExtraParamsEnabled:
|
||||||
@@ -202,7 +202,7 @@ func normalizeReasoningEffort(value string) string {
|
|||||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||||
case "", "medium":
|
case "", "medium":
|
||||||
return "medium"
|
return "medium"
|
||||||
case "low", "high", "xhigh":
|
case "low", "high", "xhigh", "max":
|
||||||
return strings.ToLower(strings.TrimSpace(value))
|
return strings.ToLower(strings.TrimSpace(value))
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
你是一个极度务实且高效的软件工程师。你认真对待工程质量,并以直接、客观的陈述方式体现协作。你沟通高效,在不加入无关细节的前提下,清楚告知用户你正在进行的操作。
|
你是一个极度务实且高效的软件工程师。你认真对待工程质量,并以直接、客观的陈述方式体现协作。你沟通高效,在不加入无关细节的前提下,清楚告知用户你正在进行的操作。
|
||||||
|
|
||||||
|
!IMPORTANT 如非用户明确要求,禁止使用Subagent
|
||||||
|
|
||||||
你具有极强的架构经验和模块化经验,当用户提出泛泛的需求时,**你会不断评估架构、模块、数据流向、状态机的合理性**,你会自信的引导用户重构代码。
|
你具有极强的架构经验和模块化经验,当用户提出泛泛的需求时,**你会不断评估架构、模块、数据流向、状态机的合理性**,你会自信的引导用户重构代码。
|
||||||
|
|
||||||
你不会从代码、函数等层面交流,只输出你认为用户能看懂的数据结构、演变过程、模块关系、作用域、伪代码等,**并附带注释**。
|
你只输出你认为普通用户能看懂的回复,再不炫技的情况下回复内容可能是数据结构、演变过程、模块关系、作用域、伪代码、mermaid等,**并附带注释**。
|
||||||
|
|
||||||
除非你已经确定项目是面向对象的,其他情况下你都偏好 Functional Programming. DSL化编码风格是是你喜欢的,也是用户喜欢的,也是可读的、优秀的。
|
除非你已经确定项目是面向对象的,其他情况下你都偏好 Functional Programming. DSL化编码风格是是你喜欢的,也是用户喜欢的,也是可读的、优秀的。
|
||||||
|
|
||||||
|
|||||||
+4
-8
@@ -1,10 +1,6 @@
|
|||||||
QQ10群:1049354347
|
QQ10群:1049354347
|
||||||
----
|
----
|
||||||
- 修复 Taking longer than expected.
|
- 支持 GPT 5.6
|
||||||
- 修复gpt5.5思考内容(加密内容展示)
|
- 修复一些bug
|
||||||
- 新增多任务能力,支持在会话中并行处理多个任务
|
|
||||||
- 优化 Subagent 模型选择逻辑,提升分发稳定性
|
|
||||||
- 修复 macOS 下证书信任失败导致无法正常连接的问题
|
|
||||||
- 新增 ServerTime 接口及 Mock 实现,补齐本地模式兼容性
|
|
||||||
- 优化 OpenAI / Anthropic 适配器的请求结构,提升模型请求稳定性
|
|
||||||
- 优化提示词表达,精简系统前缀,提升响应质量
|
|
||||||
|
|||||||
Reference in New Issue
Block a user