From 1b36c0a2b819889371c0287cb9316f859bbb5dd3 Mon Sep 17 00:00:00 2001 From: CH3CHO Date: Thu, 2 Jan 2025 17:54:12 +0800 Subject: [PATCH] fix: Fix some issues in AI console 1. Add usage info. 2. Fix key-auth.global_auth flag. 3. Fix model mapping function. 4. Enable retryOnFailure if failover is enabled in a provider. 5. Fix the display issue of the provider's failover switch --- .../constant/plugin/config/AiProxyConfig.java | 3 + .../constant/plugin/config/KeyAuthConfig.java | 1 + .../ai/AbstractLlmProviderHandler.java | 5 + .../consumer/KeyAuthCredentialHandler.java | 2 + frontend/package-lock.json | 198 ++++++++++++++++-- frontend/package.json | 3 +- frontend/src/locales/en-US/translation.json | 3 +- frontend/src/locales/zh-CN/translation.json | 3 +- .../ai/components/ProviderForm/index.tsx | 41 ++-- .../pages/ai/components/RouteForm/index.tsx | 9 +- frontend/src/pages/ai/route.tsx | 47 ++++- 11 files changed, 251 insertions(+), 64 deletions(-) diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiProxyConfig.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiProxyConfig.java index 9525b472..c4fd2ee9 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiProxyConfig.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/AiProxyConfig.java @@ -30,4 +30,7 @@ public class AiProxyConfig { public static final String FAILOVER_HEALTH_CHECK_INTERVAL = "healthCheckInterval"; public static final String FAILOVER_HEALTH_CHECK_TIMEOUT = "healthCheckTimeout"; public static final String FAILOVER_HEALTH_CHECK_MODEL = "healthCheckModel"; + + public static final String RETRY_ON_FAILURE = "retryOnFailure"; + public static final String RETRY_ENABLED = "enabled"; } \ No newline at end of file diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/KeyAuthConfig.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/KeyAuthConfig.java index 2454ac2d..9c1f3f43 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/KeyAuthConfig.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/constant/plugin/config/KeyAuthConfig.java @@ -23,4 +23,5 @@ public class KeyAuthConfig { public static final String IN_QUERY = "in_query"; public static final String ALLOW = "allow"; + public static final String GLOBAL_AUTH = "global_auth"; } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AbstractLlmProviderHandler.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AbstractLlmProviderHandler.java index 20500f20..ec00c684 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AbstractLlmProviderHandler.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/ai/AbstractLlmProviderHandler.java @@ -23,6 +23,8 @@ import static com.alibaba.higress.sdk.constant.plugin.config.AiProxyConfig.PROVIDER_API_TOKENS; import static com.alibaba.higress.sdk.constant.plugin.config.AiProxyConfig.PROVIDER_ID; import static com.alibaba.higress.sdk.constant.plugin.config.AiProxyConfig.PROVIDER_TYPE; +import static com.alibaba.higress.sdk.constant.plugin.config.AiProxyConfig.RETRY_ENABLED; +import static com.alibaba.higress.sdk.constant.plugin.config.AiProxyConfig.RETRY_ON_FAILURE; import java.util.ArrayList; import java.util.HashMap; @@ -109,10 +111,13 @@ public void saveConfig(LlmProvider provider, Map configurations) TokenFailoverConfig failoverConfig = provider.getTokenFailoverConfig(); if (failoverConfig == null) { configurations.remove(FAILOVER); + configurations.remove(RETRY_ON_FAILURE); } else { Map failoverMap = new HashMap<>(); saveTokenFailoverConfig(failoverConfig, failoverMap); configurations.put(FAILOVER, failoverMap); + Map retryOnFailureMap = Map.of(RETRY_ENABLED, failoverConfig.getEnabled()); + configurations.put(RETRY_ON_FAILURE, retryOnFailureMap); } } diff --git a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/consumer/KeyAuthCredentialHandler.java b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/consumer/KeyAuthCredentialHandler.java index 9590a988..98c6186c 100644 --- a/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/consumer/KeyAuthCredentialHandler.java +++ b/backend/sdk/src/main/java/com/alibaba/higress/sdk/service/consumer/KeyAuthCredentialHandler.java @@ -15,6 +15,7 @@ import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.ALLOW; import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.CONSUMERS; import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.CONSUMER_CREDENTIAL; +import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.GLOBAL_AUTH; import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.IN_HEADER; import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.IN_QUERY; import static com.alibaba.higress.sdk.constant.plugin.config.KeyAuthConfig.KEYS; @@ -185,6 +186,7 @@ public boolean saveConsumer(WasmPluginInstance instance, Consumer consumer) { consumerConfig.put(CONSUMER_CREDENTIAL, credential); configurations.put(CONSUMERS, consumers); + configurations.put(GLOBAL_AUTH, false); return true; } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0c18ef05..4bca321a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,7 +27,8 @@ "react": "^18.2.0", "react-chatgpt-modal": "^0.1.8", "react-dom": "^18.2.0", - "react-i18next": "^12.1.4" + "react-i18next": "^12.1.4", + "react-syntax-highlighter": "^15.6.1" }, "devDependencies": { "@ice/app": "^3.0.0", @@ -7463,6 +7464,14 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/@types/history": { "version": "4.7.11", "resolved": "https://registry.npmmirror.com/@types/history/-/history-4.7.11.tgz", @@ -7710,8 +7719,7 @@ "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.6.tgz", - "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", - "dev": true + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==" }, "node_modules/@types/vfile": { "version": "3.0.2", @@ -10553,8 +10561,7 @@ "node_modules/character-entities": { "version": "1.2.4", "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "dev": true + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" }, "node_modules/character-entities-html4": { "version": "1.1.4", @@ -10565,14 +10572,12 @@ "node_modules/character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "dev": true + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" }, "node_modules/character-reference-invalid": { "version": "1.1.4", "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "dev": true + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" }, "node_modules/chardet": { "version": "0.7.0", @@ -11011,6 +11016,15 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "9.5.0", "resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz", @@ -14533,6 +14547,18 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -14876,6 +14902,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -15431,6 +15465,31 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", @@ -15439,6 +15498,19 @@ "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==" + }, "node_modules/history": { "version": "5.3.0", "resolved": "https://registry.npmmirror.com/history/-/history-5.3.0.tgz", @@ -16522,8 +16594,7 @@ "node_modules/is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "dev": true + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" }, "node_modules/is-alphanumeric": { "version": "1.0.0", @@ -16538,7 +16609,6 @@ "version": "1.0.4", "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "dev": true, "dependencies": { "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0" @@ -16649,8 +16719,7 @@ "node_modules/is-decimal": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "dev": true + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" }, "node_modules/is-docker": { "version": "2.2.1", @@ -16701,8 +16770,7 @@ "node_modules/is-hexadecimal": { "version": "1.0.4", "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "dev": true + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" }, "node_modules/is-ip": { "version": "3.1.0", @@ -18827,6 +18895,19 @@ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", "dev": true }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", @@ -21987,6 +22068,14 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/probe.gl": { "version": "3.6.0", "resolved": "https://registry.npmmirror.com/probe.gl/-/probe.gl-3.6.0.tgz", @@ -22050,6 +22139,18 @@ "react-is": "^16.13.1" } }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "resolved": "https://registry.npmmirror.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", @@ -23609,6 +23710,22 @@ "react": "^16.3.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-syntax-highlighter": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz", + "integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==", + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.27.0", + "refractor": "^3.6.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, "node_modules/react-universal-interface": { "version": "0.6.2", "resolved": "https://registry.npmmirror.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz", @@ -23835,6 +23952,45 @@ "resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "engines": { + "node": ">=6" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -25015,6 +25171,15 @@ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", "deprecated": "Please use @jridgewell/sourcemap-codec instead" }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -28811,7 +28976,6 @@ "version": "4.0.2", "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, "engines": { "node": ">=0.4" } diff --git a/frontend/package.json b/frontend/package.json index 98a0bb9d..857be851 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,8 @@ "react": "^18.2.0", "react-chatgpt-modal": "^0.1.8", "react-dom": "^18.2.0", - "react-i18next": "^12.1.4" + "react-i18next": "^12.1.4", + "react-syntax-highlighter": "^15.6.1" }, "devDependencies": { "@ice/app": "^3.0.0", diff --git a/frontend/src/locales/en-US/translation.json b/frontend/src/locales/en-US/translation.json index e30d4422..7583f7d1 100644 --- a/frontend/src/locales/en-US/translation.json +++ b/frontend/src/locales/en-US/translation.json @@ -204,7 +204,8 @@ }, "createDomain": "Create a domain name", "howToUse": "How to use", - "aiRouteUsage": "How to use AI routing" + "aiRouteUsage": "How to use AI routing", + "aiRouteUsageText": "You can send a test request with the command below:" }, "create": "Create AI service provider", "edit": "Edit AI service provider", diff --git a/frontend/src/locales/zh-CN/translation.json b/frontend/src/locales/zh-CN/translation.json index 28fe49ef..4565b30b 100644 --- a/frontend/src/locales/zh-CN/translation.json +++ b/frontend/src/locales/zh-CN/translation.json @@ -208,7 +208,8 @@ }, "createDomain": "创建域名", "howToUse": "使用方法", - "aiRouteUsage": "AI路由使用方法" + "aiRouteUsage": "AI路由使用方法", + "aiRouteUsageText": "可使用以下命令发送请求:" }, "create": "创建AI服务提供者", "edit": "编辑AI服务提供者", diff --git a/frontend/src/pages/ai/components/ProviderForm/index.tsx b/frontend/src/pages/ai/components/ProviderForm/index.tsx index 8c267c87..87f7e609 100644 --- a/frontend/src/pages/ai/components/ProviderForm/index.tsx +++ b/frontend/src/pages/ai/components/ProviderForm/index.tsx @@ -16,7 +16,7 @@ const protocolList = [ const ProviderForm: React.FC = forwardRef((props: { value: any }, ref) => { const { t } = useTranslation(); const [form] = Form.useForm(); - const [enabled, setEnabled] = useState(false); + const [failoverEnabled, setFailoverEnabled] = useState(false); useEffect(() => { form.resetFields(); @@ -26,7 +26,6 @@ const ProviderForm: React.FC = forwardRef((props: { value: any }, ref) => { type, protocol, tokens, - // modelMapping = {}, tokenFailoverConfig = {}, } = props.value; const { @@ -37,14 +36,14 @@ const ProviderForm: React.FC = forwardRef((props: { value: any }, ref) => { healthCheckModel, } = tokenFailoverConfig ?? {}; - setEnabled(tokenFailoverConfig?.enabled || false); + const localFailoverEnabled = tokenFailoverConfig?.enabled || false; + setFailoverEnabled(localFailoverEnabled); form.setFieldsValue({ name, type, protocol, tokens, - // modelMapping: getModelText(modelMapping), - enabled, + failoverEnabled: localFailoverEnabled, failureThreshold, successThreshold, healthCheckInterval, @@ -54,7 +53,7 @@ const ProviderForm: React.FC = forwardRef((props: { value: any }, ref) => { } return () => { - setEnabled(false); + setFailoverEnabled(false); } }, [props.value]); @@ -69,15 +68,14 @@ const ProviderForm: React.FC = forwardRef((props: { value: any }, ref) => { type: values.type, name: values.name, tokens: values.tokens, - version: 0, // 资源版本号。进行创建或强制更新操作时需设置为 0。 / 1 表示强制更新 + version: 0, // 资源版本号。进行创建或强制更新操作时需设置为 0 protocol: values.protocol, - // modelMapping: getModelMapping(values.modelMapping), tokenFailoverConfig: { - enabled: values.enabled, + enabled: values.failoverEnabled, }, } - if (values.enabled) { + if (values.failoverEnabled) { result.tokenFailoverConfig['failureThreshold'] = values.failureThreshold; result.tokenFailoverConfig['successThreshold'] = values.successThreshold; result.tokenFailoverConfig['healthCheckInterval'] = values.healthCheckInterval; @@ -224,33 +222,19 @@ const ProviderForm: React.FC = forwardRef((props: { value: any }, ref) => { )} - {/* 模型映射 */} - {/* - - */} - {/* 令牌降级 */} - setEnabled(e)} /> + setFailoverEnabled(e)} /> { - enabled ? + failoverEnabled ? <> {/* 令牌不可用时需满足的最小连续请求失败次数 */} : null } - ); }); diff --git a/frontend/src/pages/ai/components/RouteForm/index.tsx b/frontend/src/pages/ai/components/RouteForm/index.tsx index cfa9c459..74189d66 100644 --- a/frontend/src/pages/ai/components/RouteForm/index.tsx +++ b/frontend/src/pages/ai/components/RouteForm/index.tsx @@ -74,7 +74,7 @@ const ConsumerForm: React.FC = forwardRef((props: { value: any }, ref) => { if (_fallbackConfig_enabled && value?.fallbackConfig?.upstreams) { fallbackInitValues['fallbackConfig_upstreams'] = value?.fallbackConfig?.upstreams?.[0]?.provider; try { - fallbackInitValues['fallbackConfig_modelNames'] = Object.keys(value?.fallbackConfig?.upstreams?.[0]?.modelMapping)[0]; + fallbackInitValues['fallbackConfig_modelNames'] = value?.fallbackConfig?.upstreams?.[0]?.modelMapping['*']; } catch (err) { fallbackInitValues['fallbackConfig_modelNames'] = ''; } @@ -102,8 +102,7 @@ const ConsumerForm: React.FC = forwardRef((props: { value: any }, ref) => { weight: item.weight, }; if (item.modelMapping) { - const _modelMapping = Object.keys(item.modelMapping); - obj["modelMapping"] = _modelMapping + obj["modelMapping"] = item.modelMapping["*"] || null; } return obj; }); @@ -161,7 +160,7 @@ const ConsumerForm: React.FC = forwardRef((props: { value: any }, ref) => { payload["upstreams"] = upstreams.map(({ provider, weight, modelMapping }) => { const obj = { provider, weight, modelMapping: {} }; if (modelMapping) { - obj["modelMapping"][modelMapping] = provider; + obj["modelMapping"]["*"] = modelMapping; } return obj; }); @@ -309,7 +308,7 @@ const ConsumerForm: React.FC = forwardRef((props: { value: any }, ref) => { noStyle rules={[{ required: true, message: t("llmProvider.matchValueRequired") }]} > - + { diff --git a/frontend/src/pages/ai/route.tsx b/frontend/src/pages/ai/route.tsx index b54c9c29..297fb7dd 100644 --- a/frontend/src/pages/ai/route.tsx +++ b/frontend/src/pages/ai/route.tsx @@ -3,11 +3,12 @@ import { addAiRoute, deleteAiRoute, getAiRoutes, updateAiRoute } from '@/service import { ArrowRightOutlined, ExclamationCircleOutlined, RedoOutlined } from '@ant-design/icons'; import { PageContainer } from '@ant-design/pro-layout'; import { useRequest } from 'ahooks'; -import { Button, Col, Drawer, Form, Modal, Row, Space, Table } from 'antd'; +import { Button, Col, Drawer, Form, Modal, Row, Space, Table, Typography } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import RouteForm from './components/RouteForm'; import { HistoryButton } from './components/RouteForm/Components'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; interface FormRef { reset: () => void; @@ -84,7 +85,7 @@ const AiRouteList: React.FC = () => { align: 'center', render: (_, record) => ( - onUse(record)}>{t('llmProvider.providerForm.howToUse')} + onUsageDrawer(record)}>{t('llmProvider.providerForm.howToUse')} onEditDrawer(record)}>{t('misc.edit')} onShowModal(record)}>{t('misc.delete')} @@ -101,7 +102,8 @@ const AiRouteList: React.FC = () => { const [loadingapi, setLoading] = useState(false); const [openModal, setOpenModal] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false); - const [useDrawer, setUseDrawer] = useState(false) + const [usageDrawer, setUsageDrawer] = useState(false) + const [usageCommand, setUsageCommand] = useState('') const { loading, run, refresh } = useRequest(getAiRoutes, { manual: true, @@ -118,14 +120,36 @@ const AiRouteList: React.FC = () => { run(); }, []); - const onUse = (aiRoute: AiRoute) => { - setCurrentAiRoute(aiRoute); - setUseDrawer(true); + const buildUsageCommand = (aiRoute: AiRoute): string => { + let command = `curl -sv http:///v1/chat/completions \\ + -X POST \\ + -H 'Content-Type: application/json'`; + if (aiRoute.domains && aiRoute.domains.length) { + command += ` \\ + -H 'Host: ${aiRoute.domains[0]}'`; + } + command += ` \\ + -d \\ +'{ + "model": "", + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] +}'`; + return command; + }; + + const onUsageDrawer = (aiRoute: AiRoute) => { + setUsageCommand(buildUsageCommand(aiRoute)); + setUsageDrawer(true); }; const closeUsage = () => { - setCurrentAiRoute(null); - setUseDrawer(false); + setUsageCommand(null); + setUsageDrawer(false); } const onEditDrawer = (aiRoute: AiRoute) => { @@ -250,7 +274,7 @@ const AiRouteList: React.FC = () => { { , ]} > - TBD + {t("llmProvider.providerForm.aiRouteUsageText")} + + {usageCommand} + {t('misc.delete')}}