This is an automated email from the ASF dual-hosted git repository.
hefengen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new ae1702bcfa [refactor] refactor ai plugins (#5986)
ae1702bcfa is described below
commit ae1702bcfaf2d4b595becff9e3b683d785d9eece
Author: aias00 <[email protected]>
AuthorDate: Mon Apr 14 23:28:38 2025 +0800
[refactor] refactor ai plugins (#5986)
* [refactor] refactor ai plugins
* [refactor] refactor ai plugins
* [refactor] refactor ai plugins
* [refactor] refactor ai plugins
* [refactor] refactor ai plugins
* [refactor] refactor ai plugins
* [refactor] refactor ai plugins
---
db/init/mysql/schema.sql | 2 +-
db/init/ob/schema.sql | 2 +-
db/init/og/create-table.sql | 2 +-
db/init/oracle/schema.sql | 2 +-
db/init/pg/create-table.sql | 2 +-
db/upgrade/2.7.0-upgrade-2.7.1-mysql.sql | 2 +-
db/upgrade/2.7.0-upgrade-2.7.1-ob.sql | 2 +-
db/upgrade/2.7.0-upgrade-2.7.1-og.sql | 2 +-
db/upgrade/2.7.0-upgrade-2.7.1-oracle.sql | 2 +-
db/upgrade/2.7.0-upgrade-2.7.1-pg.sql | 2 +-
.../apache/shenyu/common/constant/Constants.java | 26 +++--
.../dto/convert/rule/AiTokenLimiterHandle.java | 2 +-
.../shenyu/common/enums/AiTokenLimiterEnum.java | 6 +-
shenyu-plugin/pom.xml | 4 +-
.../resources/META-INF/scripts/check-limit.lua | 28 -----
.../resources/META-INF/scripts/increment-token.lua | 35 ------
.../pom.xml | 32 ++++--
.../shenyu-plugin-ai-common}/pom.xml | 4 +-
.../plugin/ai/common/config/AiCommonConfig.java | 8 +-
.../shenyu/plugin/ai/common}/strategy/AiModel.java | 21 +++-
.../plugin/ai/common}/strategy/AiModelFactory.java | 8 +-
.../plugin/ai/common}/strategy/openai/OpenAI.java | 49 +++++++--
.../shenyu-plugin-ai-prompt/pom.xml | 2 +-
.../shenyu/plugin/ai/prompt/AiPromptPlugin.java | 0
.../prompt/handler/AiPromptPluginDataHandler.java | 0
.../shenyu-plugin-ai-proxy/pom.xml | 7 +-
.../shenyu/plugin/ai/proxy/AiProxyPlugin.java | 38 ++++---
.../ai/proxy/handler/AiProxyPluginHandler.java | 8 +-
.../shenyu-plugin-ai-token-limiter/pom.xml | 9 +-
.../ai/token/limiter/AiTokenLimiterPlugin.java | 121 ++++++---------------
.../handler/AiTokenLimiterPluginHandler.java | 0
.../token/limiter/redis/RedisConfigProperties.java | 0
.../limiter/redis/RedisConnectionFactory.java | 0
.../limiter/redis/ShenyuReactiveRedisTemplate.java | 0
.../redis/ShenyuReactiveScriptExecutor.java | 0
.../redis/serializer/ByteArrayRedisSerializer.java | 0
.../ShenyuRedisSerializationContext.java | 0
.../shenyu/plugin/api/result/ShenyuResultEnum.java | 5 +
38 files changed, 191 insertions(+), 242 deletions(-)
diff --git a/db/init/mysql/schema.sql b/db/init/mysql/schema.sql
index 56cff6b938..535fe31fd6 100644
--- a/db/init/mysql/schema.sql
+++ b/db/init/mysql/schema.sql
@@ -2189,7 +2189,7 @@ INSERT INTO `shenyu_dict` VALUES ('1679002911061737581',
'preRole', 'ROLE_TYPE_U
INSERT INTO `shenyu_dict` VALUES ('1679002911061737582', 'postRole',
'ROLE_TYPE_SYSTEM', 'SYSTEM', 'system', 'system', 0, 1, '2024-02-07 14:31:49',
'2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737583', 'postRole',
'ROLE_TYPE_USER', 'USER', 'user', 'user', 1, 1, '2024-02-07 14:31:49',
'2024-02-07 14:31:49');
-INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitType',
'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit by default', 0, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitType',
'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath', 'Rate limit by
contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737491', 'aiTokenLimitType',
'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip', 1, 1, '2024-02-07
14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737492', 'aiTokenLimitType',
'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request uri', 2, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737493', 'aiTokenLimitType',
'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by request header', 3,
1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/init/ob/schema.sql b/db/init/ob/schema.sql
index 98bd70b693..c056107121 100644
--- a/db/init/ob/schema.sql
+++ b/db/init/ob/schema.sql
@@ -2504,7 +2504,7 @@ INSERT INTO `shenyu_dict` VALUES ('1679002911061737583',
'postRole', 'ROLE_TYPE_
INSERT INTO `namespace_plugin_rel` (`id`,`namespace_id`,`plugin_id`, `config`,
`sort`, `enabled`, `date_created`, `date_updated`) VALUES
('1801816010882822189','649330b6-c2d7-4edc-be8e-8a54df9eb385','52', NULL, 171,
0, '2022-05-25 18:02:53.000', '2022-05-25 18:02:53.000');
-INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitType',
'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit by default', 0, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitType',
'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath', 'Rate limit by
contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737491', 'aiTokenLimitType',
'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip', 1, 1, '2024-02-07
14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737492', 'aiTokenLimitType',
'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request uri', 2, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737493', 'aiTokenLimitType',
'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by request header', 3,
1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/init/og/create-table.sql b/db/init/og/create-table.sql
index 07f68a55c6..9a9e033d3b 100644
--- a/db/init/og/create-table.sql
+++ b/db/init/og/create-table.sql
@@ -2887,7 +2887,7 @@ INSERT INTO "public"."permission" VALUES
('1697146860569542758', '13463585604272
INSERT INTO "public"."permission" VALUES ('1697146860569542759',
'1346358560427216896', '1844026099075534867', '2023-08-31 07:18:37',
'2023-08-31 07:18:37');
INSERT INTO "public"."permission" VALUES ('1697146860569542760',
'1346358560427216896', '1844026099075534868', '2023-08-31 07:18:37',
'2023-08-31 07:18:37');
-INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit
by default', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath',
'Rate limit by contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07
14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737491',
'aiTokenLimitType', 'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip',
1, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737492',
'aiTokenLimitType', 'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request
uri', 2, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737493',
'aiTokenLimitType', 'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by
request header', 3, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/init/oracle/schema.sql b/db/init/oracle/schema.sql
index 6cb3a29c09..bc0efd09e8 100644
--- a/db/init/oracle/schema.sql
+++ b/db/init/oracle/schema.sql
@@ -3351,7 +3351,7 @@ INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(id))
*/ INTO plugin_handle (
VALUES ('1722804548510507057', '51', 'maxWait', 'maxWait', 3, 3, 10,
'{"required":"0","defaultValue":"-1","rule":""}', sysdate, sysdate);
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(id)) */ INTO shenyu_dict
(id, type, dict_code, dict_name, dict_value, "desc", sort, enabled,
date_created, date_updated)
-VALUES ('1679002911061737490', 'aiTokenLimitType', 'DEFAULT_KEY_RESOLVER',
'default', 'default', 'Rate limit by default', 0, 1, sysdate, sysdate);
+VALUES ('1679002911061737490', 'aiTokenLimitType',
'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath', 'Rate limit by
contextPath', 0, 1, sysdate, sysdate);
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(id)) */ INTO shenyu_dict
(id, type, dict_code, dict_name, dict_value, "desc", sort, enabled,
date_created, date_updated)
VALUES ('1679002911061737491', 'aiTokenLimitType', 'IP_KEY_RESOLVER', 'ip',
'ip', 'Rate limit by request ip', 1, 1, sysdate, sysdate);
diff --git a/db/init/pg/create-table.sql b/db/init/pg/create-table.sql
index 55b0a40d81..0eeae43530 100644
--- a/db/init/pg/create-table.sql
+++ b/db/init/pg/create-table.sql
@@ -3019,7 +3019,7 @@ INSERT INTO "public"."permission" VALUES
('1697146860569542758', '13463585604272
INSERT INTO "public"."permission" VALUES ('1697146860569542759',
'1346358560427216896', '1844026099075534867', '2023-08-31 07:18:37',
'2023-08-31 07:18:37');
INSERT INTO "public"."permission" VALUES ('1697146860569542760',
'1346358560427216896', '1844026099075534868', '2023-08-31 07:18:37',
'2023-08-31 07:18:37');
-INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit
by default', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath',
'Rate limit by contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07
14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737491',
'aiTokenLimitType', 'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip',
1, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737492',
'aiTokenLimitType', 'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request
uri', 2, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737493',
'aiTokenLimitType', 'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by
request header', 3, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/upgrade/2.7.0-upgrade-2.7.1-mysql.sql
b/db/upgrade/2.7.0-upgrade-2.7.1-mysql.sql
index 7336447859..c3293ccdb8 100755
--- a/db/upgrade/2.7.0-upgrade-2.7.1-mysql.sql
+++ b/db/upgrade/2.7.0-upgrade-2.7.1-mysql.sql
@@ -143,7 +143,7 @@ INSERT INTO `plugin_handle` VALUES ('1722804548510507055',
'51', 'minIdle', 'min
INSERT INTO `plugin_handle` VALUES ('1722804548510507056', '51', 'maxActive',
'maxActive', 1, 3, 9,
'{\"required\":\"0\",\"defaultValue\":\"8\",\"rule\":\"\"}', '2022-05-25
18:02:53', '2022-05-25 18:02:53');
INSERT INTO `plugin_handle` VALUES ('1722804548510507057', '51', 'maxWait',
'maxWait', 3, 3, 10,
'{\"required\":\"0\",\"defaultValue\":\"-1\",\"rule\":\"\"}', '2022-05-25
18:02:53', '2022-05-25 18:02:53');
-INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitKey',
'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit by default', 0, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitKey',
'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath', 'Rate limit by
contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737491', 'aiTokenLimitKey',
'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip', 1, 1, '2024-02-07
14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737492', 'aiTokenLimitKey',
'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request uri', 2, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737493', 'aiTokenLimitKey',
'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by request header', 3,
1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/upgrade/2.7.0-upgrade-2.7.1-ob.sql
b/db/upgrade/2.7.0-upgrade-2.7.1-ob.sql
index 9cd7280b91..009687bfe3 100755
--- a/db/upgrade/2.7.0-upgrade-2.7.1-ob.sql
+++ b/db/upgrade/2.7.0-upgrade-2.7.1-ob.sql
@@ -141,7 +141,7 @@ INSERT INTO `plugin_handle` VALUES ('1722804548510507055',
'51', 'minIdle', 'min
INSERT INTO `plugin_handle` VALUES ('1722804548510507056', '51', 'maxActive',
'maxActive', 1, 3, 9,
'{\"required\":\"0\",\"defaultValue\":\"8\",\"rule\":\"\"}', '2022-05-25
18:02:53', '2022-05-25 18:02:53');
INSERT INTO `plugin_handle` VALUES ('1722804548510507057', '51', 'maxWait',
'maxWait', 3, 3, 10,
'{\"required\":\"0\",\"defaultValue\":\"-1\",\"rule\":\"\"}', '2022-05-25
18:02:53', '2022-05-25 18:02:53');
-INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitKey',
'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit by default', 0, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO `shenyu_dict` VALUES ('1679002911061737490', 'aiTokenLimitKey',
'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath', 'Rate limit by
contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737491', 'aiTokenLimitKey',
'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip', 1, 1, '2024-02-07
14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737492', 'aiTokenLimitKey',
'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request uri', 2, 1,
'2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO `shenyu_dict` VALUES ('1679002911061737493', 'aiTokenLimitKey',
'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by request header', 3,
1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/upgrade/2.7.0-upgrade-2.7.1-og.sql
b/db/upgrade/2.7.0-upgrade-2.7.1-og.sql
index 085c732a86..6894823a00 100644
--- a/db/upgrade/2.7.0-upgrade-2.7.1-og.sql
+++ b/db/upgrade/2.7.0-upgrade-2.7.1-og.sql
@@ -142,7 +142,7 @@ INSERT INTO "public"."plugin_handle" VALUES
('1722804548510507056', '51', 'maxAc
INSERT INTO "public"."plugin_handle" VALUES ('1722804548510507057', '51',
'maxWait', 'maxWait', 3, 3, 10,
'{"required":"0","defaultValue":"-1","rule":""}', '2022-05-25 18:02:53',
'2022-05-25 18:02:53');
-INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit
by default', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath',
'Rate limit by contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07
14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737491',
'aiTokenLimitType', 'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip',
1, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737492',
'aiTokenLimitType', 'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request
uri', 2, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737493',
'aiTokenLimitType', 'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by
request header', 3, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git a/db/upgrade/2.7.0-upgrade-2.7.1-oracle.sql
b/db/upgrade/2.7.0-upgrade-2.7.1-oracle.sql
index 6973e732a7..06a95b8415 100755
--- a/db/upgrade/2.7.0-upgrade-2.7.1-oracle.sql
+++ b/db/upgrade/2.7.0-upgrade-2.7.1-oracle.sql
@@ -329,7 +329,7 @@ INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(plugin_handle(id)) */
INTO plugin_handle (
VALUES ('1722804548510507057', '51', 'maxWait', 'maxWait', 3, 3, 10,
'{"required":"0","defaultValue":"-1","rule":""}', sysdate, sysdate);
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(id)) */ INTO shenyu_dict
(id, type, dict_code, dict_name, dict_value, "desc", sort, enabled,
date_created, date_updated)
-VALUES ('1679002911061737490', 'aiTokenLimitType', 'DEFAULT_KEY_RESOLVER',
'default', 'default', 'Rate limit by default', 0, 1, sysdate, sysdate);
+VALUES ('1679002911061737490', 'aiTokenLimitType',
'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath', 'Rate limit by
contextPath', 0, 1, sysdate, sysdate);
INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(shenyu_dict(id)) */ INTO shenyu_dict
(id, type, dict_code, dict_name, dict_value, "desc", sort, enabled,
date_created, date_updated)
VALUES ('1679002911061737491', 'aiTokenLimitType', 'IP_KEY_RESOLVER', 'ip',
'ip', 'Rate limit by request ip', 1, 1, sysdate, sysdate);
diff --git a/db/upgrade/2.7.0-upgrade-2.7.1-pg.sql
b/db/upgrade/2.7.0-upgrade-2.7.1-pg.sql
index db0f090635..eb87cfe36b 100755
--- a/db/upgrade/2.7.0-upgrade-2.7.1-pg.sql
+++ b/db/upgrade/2.7.0-upgrade-2.7.1-pg.sql
@@ -140,7 +140,7 @@ INSERT INTO "public"."plugin_handle" VALUES
('1722804548510507055', '51', 'minId
INSERT INTO "public"."plugin_handle" VALUES ('1722804548510507056', '51',
'maxActive', 'maxActive', 1, 3, 9,
'{"required":"0","defaultValue":"8","rule":""}', '2022-05-25 18:02:53',
'2022-05-25 18:02:53');
INSERT INTO "public"."plugin_handle" VALUES ('1722804548510507057', '51',
'maxWait', 'maxWait', 3, 3, 10,
'{"required":"0","defaultValue":"-1","rule":""}', '2022-05-25 18:02:53',
'2022-05-25 18:02:53');
-INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'DEFAULT_KEY_RESOLVER', 'default', 'default', 'Rate limit
by default', 0, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
+INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737490',
'aiTokenLimitType', 'CONTEXT_PATH_KEY_RESOLVER', 'contextPath', 'contextPath',
'Rate limit by contextPath', 0, 1, '2024-02-07 14:31:49', '2024-02-07
14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737491',
'aiTokenLimitType', 'IP_KEY_RESOLVER', 'ip', 'ip', 'Rate limit by request ip',
1, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737492',
'aiTokenLimitType', 'URI_KEY_RESOLVER', 'uri', 'uri', 'Rate limit by request
uri', 2, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
INSERT INTO "public"."shenyu_dict" VALUES ('1679002911061737493',
'aiTokenLimitType', 'HEADER_KEY_RESOLVER', 'header', 'header', 'Rate limit by
request header', 3, 1, '2024-02-07 14:31:49', '2024-02-07 14:31:49');
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
index 62bfd1c399..1341bb4f74 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
@@ -950,16 +950,6 @@ public interface Constants {
* The constant messages.
*/
String MESSAGES = "messages";
-
- /**
- * The constant usedTokens.
- */
- String USED_TOKENS = "used-tokens";
-
- /**
- * The constant X-Client-ID.
- */
- String CLIENT_ID = "X-Client-ID";
/**
* The constant Content-Encoding.
@@ -980,9 +970,25 @@ public interface Constants {
*/
String ROLE = "role";
+ /**
+ * The constant USAGE.
+ */
+ String USAGE = "usage";
+
+ /**
+ * The constant COMPLETION_TOKENS.
+ */
+ String COMPLETION_TOKENS = "completion_tokens";
+
+ /**
+ * The constant AI_MODEL.
+ */
+ String AI_MODEL = "ai_model";
+
/**
* String q.
*/
default void findConstants() {
}
+
}
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/rule/AiTokenLimiterHandle.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/rule/AiTokenLimiterHandle.java
index fad7ac46fd..52fc3b5ab7 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/rule/AiTokenLimiterHandle.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/rule/AiTokenLimiterHandle.java
@@ -125,7 +125,7 @@ public class AiTokenLimiterHandle {
*/
public static AiTokenLimiterHandle newDefaultInstance() {
AiTokenLimiterHandle aiTokenLimiterHandle = new AiTokenLimiterHandle();
-
aiTokenLimiterHandle.setAiTokenLimitType(AiTokenLimiterEnum.DEFAULT.getName());
+
aiTokenLimiterHandle.setAiTokenLimitType(AiTokenLimiterEnum.CONTEXT_PATH.getName());
aiTokenLimiterHandle.setTimeWindowSeconds(TimeWindowEnum.MINUTE.getSeconds());
aiTokenLimiterHandle.setKeyName("default");
aiTokenLimiterHandle.setTokenLimit(100L);
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/AiTokenLimiterEnum.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/AiTokenLimiterEnum.java
index 4ac259f3fd..134972c438 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/AiTokenLimiterEnum.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/AiTokenLimiterEnum.java
@@ -47,9 +47,9 @@ public enum AiTokenLimiterEnum {
COOKIE("cookie"),
/**
- * Default key resolver enum.
+ * contextPath key resolver enum.
*/
- DEFAULT("default");
+ CONTEXT_PATH("contextPath");
private final String name;
@@ -83,6 +83,6 @@ public enum AiTokenLimiterEnum {
return value;
}
}
- return DEFAULT;
+ return CONTEXT_PATH;
}
}
diff --git a/shenyu-plugin/pom.xml b/shenyu-plugin/pom.xml
index 5bd15e5325..9e540ec17f 100644
--- a/shenyu-plugin/pom.xml
+++ b/shenyu-plugin/pom.xml
@@ -49,9 +49,7 @@
<module>shenyu-plugin-proxy</module>
<module>shenyu-plugin-security</module>
<module>shenyu-plugin-fault-tolerance</module>
- <module>shenyu-plugin-ai-proxy</module>
- <module>shenyu-plugin-ai-prompt</module>
- <module>shenyu-plugin-ai-token-limiter</module>
+ <module>shenyu-plugin-ai</module>
</modules>
<dependencies>
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/resources/META-INF/scripts/check-limit.lua
b/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/resources/META-INF/scripts/check-limit.lua
deleted file mode 100644
index a66888afbf..0000000000
---
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/resources/META-INF/scripts/check-limit.lua
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
--- check-limit.lua
--- KEYS[1]: user's token count key
--- ARGV[1]: the limit of tokens
-
-local key = KEYS[1]
-local currentTokens = redis.call('GET', key)
-if currentTokens and tonumber(currentTokens) >= tonumber(ARGV[1]) then
- return 0 -- over limit, return 0
-else
- return 1 -- not over limit, return 1
-end
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/resources/META-INF/scripts/increment-token.lua
b/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/resources/META-INF/scripts/increment-token.lua
deleted file mode 100644
index e3cbe119b7..0000000000
---
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/resources/META-INF/scripts/increment-token.lua
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
--- increment-token.lua
--- KEYS[1]: user's token count key
--- ARGV[1]: increment value
--- ARGV[2]: time window in seconds
-
-local key = KEYS[1]
-local increment = tonumber(ARGV[1])
-local ttl = tonumber(ARGV[2])
-local currentTokens = redis.call('GET', key)
-
-if currentTokens then
- redis.call('INCRBY', key, increment)
- redis.call('EXPIRE', key, ttl) -- fresh the expiration time
-else
- redis.call('SETEX', key, ttl, increment) -- set initial value and
expiration time
-end
-
-return redis.call('GET', key) -- return the current token count
diff --git a/shenyu-plugin/shenyu-plugin-ai-prompt/pom.xml
b/shenyu-plugin/shenyu-plugin-ai/pom.xml
similarity index 57%
copy from shenyu-plugin/shenyu-plugin-ai-prompt/pom.xml
copy to shenyu-plugin/shenyu-plugin-ai/pom.xml
index e9a66ada67..8ae190877e 100644
--- a/shenyu-plugin/shenyu-plugin-ai-prompt/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-ai/pom.xml
@@ -16,21 +16,29 @@
~ limitations under the License.
-->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>pom</packaging>
<parent>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-plugin</artifactId>
<version>2.7.1-SNAPSHOT</version>
</parent>
- <modelVersion>4.0.0</modelVersion>
- <artifactId>shenyu-plugin-ai-prompt</artifactId>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin-base</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
+
+ <artifactId>shenyu-plugin-ai</artifactId>
+
+ <properties>
+ <maven.compiler.source>18</maven.compiler.source>
+ <maven.compiler.target>18</maven.compiler.target>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
-</project>
+ <modules>
+ <module>shenyu-plugin-ai-common</module>
+ <module>shenyu-plugin-ai-proxy</module>
+ <module>shenyu-plugin-ai-prompt</module>
+ <module>shenyu-plugin-ai-token-limiter</module>
+ </modules>
+</project>
\ No newline at end of file
diff --git a/shenyu-plugin/shenyu-plugin-ai-proxy/pom.xml
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/pom.xml
similarity index 94%
copy from shenyu-plugin/shenyu-plugin-ai-proxy/pom.xml
copy to shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/pom.xml
index 510a4b9b4c..0fea45bd48 100644
--- a/shenyu-plugin/shenyu-plugin-ai-proxy/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/pom.xml
@@ -19,11 +19,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin</artifactId>
+ <artifactId>shenyu-plugin-ai</artifactId>
<version>2.7.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
- <artifactId>shenyu-plugin-ai-proxy</artifactId>
+ <artifactId>shenyu-plugin-ai-common</artifactId>
<dependencies>
<dependency>
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/plugin/AiProxyConfig.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/config/AiCommonConfig.java
similarity index 96%
rename from
shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/plugin/AiProxyConfig.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/config/AiCommonConfig.java
index f057c39301..18c022c5fd 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/plugin/AiProxyConfig.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/config/AiCommonConfig.java
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-package org.apache.shenyu.common.dto.convert.plugin;
+package org.apache.shenyu.plugin.ai.common.config;
import java.util.Objects;
/**
- * this is Ai Proxy plugin config.
+ * this is Ai plugin common config.
*/
-public class AiProxyConfig {
+public class AiCommonConfig {
/**
* provider.
@@ -193,7 +193,7 @@ public class AiProxyConfig {
if (Objects.isNull(o) || getClass() != o.getClass()) {
return false;
}
- AiProxyConfig that = (AiProxyConfig) o;
+ AiCommonConfig that = (AiCommonConfig) o;
return Objects.equals(provider, that.provider)
&& Objects.equals(baseUrl, that.baseUrl)
&& Objects.equals(apiKey, that.apiKey)
diff --git
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/AiModel.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/AiModel.java
similarity index 68%
rename from
shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/AiModel.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/AiModel.java
index b30adf4a55..3ba32faac0 100644
---
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/AiModel.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/AiModel.java
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.ai.proxy.strategy;
+package org.apache.shenyu.plugin.ai.common.strategy;
-import org.apache.shenyu.common.dto.convert.plugin.AiProxyConfig;
+import org.apache.shenyu.plugin.ai.common.config.AiCommonConfig;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.web.server.ServerWebExchange;
@@ -31,13 +31,24 @@ import java.util.List;
public interface AiModel {
/**
- * Invoke mono.
+ * Invoke ai.
*
- * @param aiProxyConfig the ai proxy config
+ * @param aiCommonConfig the ai config
* @param exchange the exchange
* @param chain the chain
* @param messageReaders the message readers
* @return the mono
*/
- Mono<Void> invoke(AiProxyConfig aiProxyConfig, ServerWebExchange exchange,
ShenyuPluginChain chain, List<HttpMessageReader<?>> messageReaders);
+ Mono<Void> invoke(AiCommonConfig aiCommonConfig,
+ ServerWebExchange exchange,
+ ShenyuPluginChain chain,
+ List<HttpMessageReader<?>> messageReaders);
+
+ /**
+ * getCompletionTokens.
+ *
+ * @param responseBody the response body
+ * @return the completion tokens
+ */
+ Long getCompletionTokens(String responseBody);
}
diff --git
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/AiModelFactory.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/AiModelFactory.java
similarity index 85%
rename from
shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/AiModelFactory.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/AiModelFactory.java
index 324ace7d92..523e76a81a 100644
---
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/AiModelFactory.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/AiModelFactory.java
@@ -15,10 +15,10 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.ai.proxy.strategy;
+package org.apache.shenyu.plugin.ai.common.strategy;
import org.apache.shenyu.common.enums.AiModelProviderEnum;
-import org.apache.shenyu.plugin.ai.proxy.strategy.openai.OpenAI;
+import org.apache.shenyu.plugin.ai.common.strategy.openai.OpenAI;
import java.util.Objects;
@@ -31,14 +31,14 @@ public final class AiModelFactory {
}
/**
- * Create ai model.
+ * Create ai model instance.
*
* @param provider the provider
* @return the ai model provider
*/
public static AiModel createAiModel(final AiModelProviderEnum provider) {
if (Objects.isNull(provider)) {
- return null;
+ throw new IllegalArgumentException("not supported provider");
}
return switch (provider) {
case OPEN_AI, DEEP_SEEK, ALIYUN, OPEN_API, MOONSHOT -> new
OpenAI();
diff --git
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/openai/OpenAI.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/openai/OpenAI.java
similarity index 64%
rename from
shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/openai/OpenAI.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/openai/OpenAI.java
index 93b0797fcb..a81416c755 100644
---
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/strategy/openai/OpenAI.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/strategy/openai/OpenAI.java
@@ -15,16 +15,20 @@
* limitations under the License.
*/
-package org.apache.shenyu.plugin.ai.proxy.strategy.openai;
+package org.apache.shenyu.plugin.ai.common.strategy.openai;
+import com.fasterxml.jackson.databind.JsonNode;
import org.apache.shenyu.common.constant.Constants;
-import org.apache.shenyu.common.dto.convert.plugin.AiProxyConfig;
+import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.plugin.ai.common.config.AiCommonConfig;
import org.apache.shenyu.common.utils.GsonUtils;
-import org.apache.shenyu.plugin.ai.proxy.strategy.AiModel;
+import org.apache.shenyu.plugin.ai.common.strategy.AiModel;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.exception.ResponsiveException;
import org.apache.shenyu.plugin.api.utils.WebFluxResultUtils;
import org.apache.shenyu.plugin.base.utils.ServerWebExchangeUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
@@ -33,24 +37,27 @@ import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* The OpenAI model.
*/
public class OpenAI implements AiModel {
+ private static final Logger LOG = LoggerFactory.getLogger(OpenAI.class);
+
@Override
- public Mono<Void> invoke(final AiProxyConfig aiProxyConfig, final
ServerWebExchange exchange,
+ public Mono<Void> invoke(final AiCommonConfig aiCommonConfig, final
ServerWebExchange exchange,
final ShenyuPluginChain chain,
final List<HttpMessageReader<?>> messageReaders) {
ServerWebExchange modifiedExchange = exchange.mutate()
.request(originalRequest -> originalRequest
- .headers(httpHeaders -> convertHeader(httpHeaders,
aiProxyConfig))
+ .headers(httpHeaders -> convertHeader(httpHeaders,
aiCommonConfig))
.method(exchange.getRequest().getMethod())
)
.build();
return ServerWebExchangeUtils.rewriteRequestBody(modifiedExchange,
messageReaders, originalBody ->
- Mono.just(convertBody(originalBody, aiProxyConfig))
+ Mono.just(convertBody(originalBody, aiCommonConfig))
).flatMap(chain::execute)
.onErrorResume(error -> {
if (error instanceof ResponsiveException) {
@@ -60,22 +67,40 @@ public class OpenAI implements AiModel {
});
}
+ @Override
+ public Long getCompletionTokens(final String responseBody) {
+ try {
+ JsonNode root = JsonUtils.toJsonNode(responseBody);
+ JsonNode usage = root.get(Constants.USAGE);
+ if (Objects.nonNull(usage)) {
+ JsonNode totalTokens = usage.get(Constants.COMPLETION_TOKENS);
+ if (Objects.nonNull(totalTokens)) {
+ return totalTokens.asLong();
+ }
+ }
+ } catch (Exception e) {
+ // Handle parsing exceptions
+ LOG.error("Failed to parse response body: {}", responseBody, e);
+ }
+ return 0L;
+ }
+
- private static void convertHeader(final HttpHeaders httpHeaders, final
AiProxyConfig aiProxyConfig) {
+ private static void convertHeader(final HttpHeaders httpHeaders, final
AiCommonConfig aiCommonConfig) {
if (!httpHeaders.containsKey("Authorization")) {
- httpHeaders.add("Authorization", "Bearer " +
aiProxyConfig.getApiKey());
+ httpHeaders.add("Authorization", "Bearer " +
aiCommonConfig.getApiKey());
}
- if (aiProxyConfig.getStream()) {
+ if (aiCommonConfig.getStream()) {
httpHeaders.add(HttpHeaders.CONTENT_TYPE,
MediaType.TEXT_EVENT_STREAM_VALUE);
httpHeaders.add(HttpHeaders.CACHE_CONTROL, "no-cache");
httpHeaders.add(HttpHeaders.CONNECTION, "keep-alive");
}
}
- private String convertBody(final String originalBody, final AiProxyConfig
aiProxyConfig) {
+ private String convertBody(final String originalBody, final AiCommonConfig
aiCommonConfig) {
Map<String, Object> requestBodyMap =
GsonUtils.getInstance().convertToMap(originalBody);
- requestBodyMap.put(Constants.MODEL, aiProxyConfig.getModel());
- requestBodyMap.put(Constants.STREAM, aiProxyConfig.getStream());
+ requestBodyMap.put(Constants.MODEL, aiCommonConfig.getModel());
+ requestBodyMap.put(Constants.STREAM, aiCommonConfig.getStream());
return GsonUtils.getInstance().toJson(requestBodyMap);
}
diff --git a/shenyu-plugin/shenyu-plugin-ai-prompt/pom.xml
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/pom.xml
similarity index 96%
rename from shenyu-plugin/shenyu-plugin-ai-prompt/pom.xml
rename to shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/pom.xml
index e9a66ada67..f3a3a2faad 100644
--- a/shenyu-plugin/shenyu-plugin-ai-prompt/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/pom.xml
@@ -19,7 +19,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin</artifactId>
+ <artifactId>shenyu-plugin-ai</artifactId>
<version>2.7.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
diff --git
a/shenyu-plugin/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/AiPromptPlugin.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/AiPromptPlugin.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/AiPromptPlugin.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/AiPromptPlugin.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/handler/AiPromptPluginDataHandler.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/handler/AiPromptPluginDataHandler.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/handler/AiPromptPluginDataHandler.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-prompt/src/main/java/org/apache/shenyu/plugin/ai/prompt/handler/AiPromptPluginDataHandler.java
diff --git a/shenyu-plugin/shenyu-plugin-ai-proxy/pom.xml
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/pom.xml
similarity index 88%
rename from shenyu-plugin/shenyu-plugin-ai-proxy/pom.xml
rename to shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/pom.xml
index 510a4b9b4c..3070bc9e59 100644
--- a/shenyu-plugin/shenyu-plugin-ai-proxy/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/pom.xml
@@ -19,7 +19,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin</artifactId>
+ <artifactId>shenyu-plugin-ai</artifactId>
<version>2.7.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -31,6 +31,11 @@
<artifactId>shenyu-plugin-base</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-plugin-ai-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>io.netty</groupId>
diff --git
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/AiProxyPlugin.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/AiProxyPlugin.java
similarity index 76%
rename from
shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/AiProxyPlugin.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/AiProxyPlugin.java
index e51f955f5f..931276b382 100644
---
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/AiProxyPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/AiProxyPlugin.java
@@ -20,15 +20,15 @@ package org.apache.shenyu.plugin.ai.proxy;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
-import org.apache.shenyu.common.dto.convert.plugin.AiProxyConfig;
import org.apache.shenyu.common.dto.convert.rule.AiProxyHandle;
import org.apache.shenyu.common.enums.AiModelProviderEnum;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.enums.RpcTypeEnum;
import org.apache.shenyu.common.utils.Singleton;
+import org.apache.shenyu.plugin.ai.common.config.AiCommonConfig;
+import org.apache.shenyu.plugin.ai.common.strategy.AiModel;
+import org.apache.shenyu.plugin.ai.common.strategy.AiModelFactory;
import org.apache.shenyu.plugin.ai.proxy.handler.AiProxyPluginHandler;
-import org.apache.shenyu.plugin.ai.proxy.strategy.AiModel;
-import org.apache.shenyu.plugin.ai.proxy.strategy.AiModelFactory;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.context.ShenyuContext;
import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
@@ -58,9 +58,9 @@ public class AiProxyPlugin extends AbstractShenyuPlugin {
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain,
final SelectorData selector, final RuleData
rule) {
- AiProxyConfig aiProxyConfig = Singleton.INST.get(AiProxyConfig.class);
- if (Objects.isNull(aiProxyConfig)) {
- aiProxyConfig = new AiProxyConfig();
+ AiCommonConfig aiCommonConfig =
Singleton.INST.get(AiCommonConfig.class);
+ if (Objects.isNull(aiCommonConfig)) {
+ aiCommonConfig = new AiCommonConfig();
}
final ShenyuContext shenyuContext =
exchange.getAttribute(Constants.CONTEXT);
Objects.requireNonNull(shenyuContext);
@@ -71,16 +71,16 @@ public class AiProxyPlugin extends AbstractShenyuPlugin {
// Create final config with selector handle taking precedence
if (Objects.nonNull(selectorHandle)) {
- aiProxyConfig.setProvider(selectorHandle.getProvider());
- aiProxyConfig.setBaseUrl(selectorHandle.getBaseUrl());
- aiProxyConfig.setApiKey(selectorHandle.getApiKey());
- aiProxyConfig.setModel(selectorHandle.getModel());
- aiProxyConfig.setTemperature(selectorHandle.getTemperature());
- aiProxyConfig.setMaxTokens(selectorHandle.getMaxTokens());
- aiProxyConfig.setStream(selectorHandle.getStream());
+ aiCommonConfig.setProvider(selectorHandle.getProvider());
+ aiCommonConfig.setBaseUrl(selectorHandle.getBaseUrl());
+ aiCommonConfig.setApiKey(selectorHandle.getApiKey());
+ aiCommonConfig.setModel(selectorHandle.getModel());
+ aiCommonConfig.setTemperature(selectorHandle.getTemperature());
+ aiCommonConfig.setMaxTokens(selectorHandle.getMaxTokens());
+ aiCommonConfig.setStream(selectorHandle.getStream());
}
- if (Objects.isNull(aiProxyConfig.getBaseUrl())) {
+ if (Objects.isNull(aiCommonConfig.getBaseUrl())) {
LOG.error("AI proxy plugin: baseUrl is null");
return chain.execute(exchange);
}
@@ -89,21 +89,25 @@ public class AiProxyPlugin extends AbstractShenyuPlugin {
exchange.getAttributes().put(Constants.CONTEXT, shenyuContext);
// set domain
- exchange.getAttributes().put(Constants.HTTP_DOMAIN,
aiProxyConfig.getBaseUrl());
+ exchange.getAttributes().put(Constants.HTTP_DOMAIN,
aiCommonConfig.getBaseUrl());
// set the http timeout
exchange.getAttributes().put(Constants.HTTP_TIME_OUT, 60 * 3000L);
exchange.getAttributes().put(Constants.HTTP_RETRY, 0);
- String provider = aiProxyConfig.getProvider();
+ String provider = aiCommonConfig.getProvider();
AiModelProviderEnum providerEnum =
AiModelProviderEnum.getByName(provider);
if (Objects.isNull(providerEnum)) {
return Mono.error(new IllegalArgumentException("Invalid AI model
provider"));
}
+ // Create AI model instance
AiModel aiModel = AiModelFactory.createAiModel(providerEnum);
if (Objects.isNull(aiModel)) {
return Mono.error(new IllegalStateException("Failed to create AI
model"));
}
- return aiModel.invoke(aiProxyConfig, exchange, chain, messageReaders);
+
+ exchange.getAttributes().put(Constants.AI_MODEL, aiModel);
+
+ return aiModel.invoke(aiCommonConfig, exchange, chain, messageReaders);
}
diff --git
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/handler/AiProxyPluginHandler.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/handler/AiProxyPluginHandler.java
similarity index 89%
rename from
shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/handler/AiProxyPluginHandler.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/handler/AiProxyPluginHandler.java
index ad9b49d843..e3f5d34426 100644
---
a/shenyu-plugin/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/handler/AiProxyPluginHandler.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/handler/AiProxyPluginHandler.java
@@ -20,11 +20,11 @@ package org.apache.shenyu.plugin.ai.proxy.handler;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.PluginData;
import org.apache.shenyu.common.dto.SelectorData;
-import org.apache.shenyu.common.dto.convert.plugin.AiProxyConfig;
import org.apache.shenyu.common.dto.convert.rule.AiProxyHandle;
import org.apache.shenyu.common.enums.PluginEnum;
import org.apache.shenyu.common.utils.GsonUtils;
import org.apache.shenyu.common.utils.Singleton;
+import org.apache.shenyu.plugin.ai.common.config.AiCommonConfig;
import org.apache.shenyu.plugin.base.cache.CommonHandleCache;
import org.apache.shenyu.plugin.base.handler.PluginDataHandler;
import org.apache.shenyu.plugin.base.utils.BeanHolder;
@@ -43,11 +43,11 @@ public class AiProxyPluginHandler implements
PluginDataHandler {
@Override
public void handlerPlugin(final PluginData pluginData) {
if (Objects.nonNull(pluginData) && pluginData.getEnabled()) {
- AiProxyConfig aiProxyConfig =
GsonUtils.getInstance().fromJson(pluginData.getConfig(), AiProxyConfig.class);
- if (Objects.isNull(aiProxyConfig)) {
+ AiCommonConfig aiCommonConfig =
GsonUtils.getInstance().fromJson(pluginData.getConfig(), AiCommonConfig.class);
+ if (Objects.isNull(aiCommonConfig)) {
return;
}
- Singleton.INST.single(AiProxyConfig.class, aiProxyConfig);
+ Singleton.INST.single(AiCommonConfig.class, aiCommonConfig);
}
}
diff --git a/shenyu-plugin/shenyu-plugin-ai-token-limiter/pom.xml
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/pom.xml
similarity index 84%
rename from shenyu-plugin/shenyu-plugin-ai-token-limiter/pom.xml
rename to shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/pom.xml
index 9addf5a86b..82e713b5f6 100644
--- a/shenyu-plugin/shenyu-plugin-ai-token-limiter/pom.xml
+++ b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/pom.xml
@@ -19,7 +19,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin</artifactId>
+ <artifactId>shenyu-plugin-ai</artifactId>
<version>2.7.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,9 +27,14 @@
<artifactId>shenyu-plugin-ai-token-limiter</artifactId>
<dependencies>
+<!-- <dependency>-->
+<!-- <groupId>org.apache.shenyu</groupId>-->
+<!-- <artifactId>shenyu-plugin-logging-common</artifactId>-->
+<!-- <version>${project.version}</version>-->
+<!-- </dependency>-->
<dependency>
<groupId>org.apache.shenyu</groupId>
- <artifactId>shenyu-plugin-logging-common</artifactId>
+ <artifactId>shenyu-plugin-ai-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java
similarity index 71%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java
index fcb5912d4e..8781885c7d 100644
---
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java
@@ -17,8 +17,6 @@
package org.apache.shenyu.plugin.ai.token.limiter;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.common.constant.Constants;
import org.apache.shenyu.common.dto.RuleData;
@@ -26,7 +24,7 @@ import org.apache.shenyu.common.dto.SelectorData;
import org.apache.shenyu.common.dto.convert.rule.AiTokenLimiterHandle;
import org.apache.shenyu.common.enums.AiTokenLimiterEnum;
import org.apache.shenyu.common.enums.PluginEnum;
-import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.plugin.ai.common.strategy.AiModel;
import
org.apache.shenyu.plugin.ai.token.limiter.handler.AiTokenLimiterPluginHandler;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
import org.apache.shenyu.plugin.api.result.ShenyuResultEnum;
@@ -37,18 +35,14 @@ import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
-import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.http.HttpCookie;
-import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
-import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
@@ -62,7 +56,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
-import java.util.List;
+import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -78,10 +72,6 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
private static final String REDIS_KEY_PREFIX = "SHENYU:AI:TOKENLIMIT:";
- private static final String CHECK_LIMIT_SCRIPT = "check-limit.lua";
-
- private static final String INCREMENT_TOKEN_SCRIPT = "increment-token.lua";
-
@Override
protected Mono<Void> doExecute(final ServerWebExchange exchange, final
ShenyuPluginChain chain,
final SelectorData selector, final RuleData
rule) {
@@ -96,14 +86,14 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
Assert.notNull(reactiveRedisTemplate, "reactiveRedisTemplate is null");
// generate redis key
- String keyResolverName = aiTokenLimiterHandle.getAiTokenLimitType();
+ String tokenLimitType = aiTokenLimiterHandle.getAiTokenLimitType();
String keyName = aiTokenLimiterHandle.getKeyName();
Long tokenLimit = aiTokenLimiterHandle.getTokenLimit();
Long timeWindowSeconds = aiTokenLimiterHandle.getTimeWindowSeconds();
- String cacheKey = REDIS_KEY_PREFIX +
aiTokenLimiterHandle.getAiTokenLimitType() + ":" + getCacheKey(exchange,
keyResolverName, keyName);
+ String cacheKey = REDIS_KEY_PREFIX + getCacheKey(exchange,
tokenLimitType, keyName);
- final AiStatisticServerHttpResponse loggingServerHttpResponse = new
AiStatisticServerHttpResponse(exchange.getResponse(),
+ final AiStatisticServerHttpResponse loggingServerHttpResponse = new
AiStatisticServerHttpResponse(exchange, exchange.getResponse(),
tokens -> recordTokensUsage(reactiveRedisTemplate,
cacheKey,
tokens,
@@ -116,7 +106,7 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
final Consumer<HttpStatusCode> consumer =
exchange.getAttribute(Constants.METRICS_RATE_LIMITER);
Optional.ofNullable(consumer).ifPresent(c ->
c.accept(exchange.getResponse().getStatusCode()));
- Object error = ShenyuResultWrap.error(exchange,
ShenyuResultEnum.TOO_MANY_REQUESTS);
+ Object error = ShenyuResultWrap.error(exchange,
ShenyuResultEnum.RUN_OUT_OF_TOKENS);
return WebFluxResultUtils.result(exchange, error);
}
// record tokens usage
@@ -138,36 +128,32 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
* @return whether the request is allowed
*/
private Mono<Boolean> isAllowed(final ReactiveRedisTemplate
reactiveRedisTemplate, final String cacheKey, final Long tokenLimit) {
- DefaultRedisScript checkLimitScript = new DefaultRedisScript<>();
- String scriptPath = Constants.SCRIPT_PATH + CHECK_LIMIT_SCRIPT;
- checkLimitScript.setScriptSource(new ResourceScriptSource(new
ClassPathResource(scriptPath)));
- checkLimitScript.setResultType(Long.class);
- return Mono.from(reactiveRedisTemplate.execute(
- checkLimitScript,
- Lists.newArrayList(cacheKey),
- Lists.newArrayList(tokenLimit.toString()))
- .map(result -> {
- // Convert result to String since StatusOutput doesn't
support long
- // Return true if result is "1", false otherwise
- return (Long) result == 1L;
- }));
+
+ return reactiveRedisTemplate.opsForValue().get(cacheKey)
+ .defaultIfEmpty(0L)
+ .flatMap(currentTokens -> {
+ if (Long.parseLong(currentTokens.toString()) >=
tokenLimit) {
+ return Mono.just(false);
+ }
+ return Mono.just(true);
+ });
}
/**
* Get the cache key based on the configured key resolver type.
*
* @param exchange the server web exchange
- * @param keyResolverName the name of the key resolver
+ * @param tokenLimitType the type of token limit
* @param keyName the name of the key
* @return the cache key
*/
- private String getCacheKey(final ServerWebExchange exchange, final String
keyResolverName, final String keyName) {
+ private String getCacheKey(final ServerWebExchange exchange, final String
tokenLimitType, final String keyName) {
ServerHttpRequest request = exchange.getRequest();
String key;
// Determine the key based on the configured key resolver type
- AiTokenLimiterEnum keyResolverEnum =
AiTokenLimiterEnum.getByName(keyResolverName);
+ AiTokenLimiterEnum tokenLimiterEnum =
AiTokenLimiterEnum.getByName(tokenLimitType);
- switch (keyResolverEnum) {
+ switch (tokenLimiterEnum) {
case IP:
key =
Objects.requireNonNull(request.getRemoteAddress()).getHostString();
break;
@@ -184,46 +170,20 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
HttpCookie cookie = request.getCookies().getFirst(keyName);
key = Objects.nonNull(cookie) ? cookie.getValue() : "";
break;
+ case CONTEXT_PATH:
default:
- key = exchange.getAttribute(Constants.CONTEXT_PATH)
- + ":"
- + exchange.getRequest().getURI().getPath();
+ key = exchange.getAttribute(Constants.CONTEXT_PATH);
}
return StringUtils.isBlank(key) ? "" : key;
}
- private String extractClientId(final ServerWebExchange exchange) {
- ServerHttpRequest request = exchange.getRequest();
- // Get client identifier from request headers first
- String clientId = request.getHeaders().getFirst(Constants.CLIENT_ID);
-
- if (StringUtils.isBlank(clientId)) {
- // If not present, try to get it from the exchange attributes
- if (exchange.getAttributes().containsKey(Constants.CLIENT_ID)) {
- clientId =
exchange.getAttributes().get(Constants.CLIENT_ID).toString();
- }
- }
-
- if (StringUtils.isBlank(clientId)) {
- // Finally, try using the combination of IP and User-Agent
- String ip =
request.getRemoteAddress().getAddress().getHostAddress();
- String userAgent =
request.getHeaders().getFirst(HttpHeaders.USER_AGENT);
- clientId = ip + "|" + (Objects.nonNull(userAgent) ? userAgent :
"unknown");
- }
- exchange.getAttributes().put(Constants.CLIENT_ID, clientId);
-
- return clientId;
- }
-
private void recordTokensUsage(final ReactiveRedisTemplate
reactiveRedisTemplate, final String cacheKey, final Long tokens, final Long
windowSeconds) {
- DefaultRedisScript incrementTokenScript = new DefaultRedisScript<>();
- String scriptPath = Constants.SCRIPT_PATH + INCREMENT_TOKEN_SCRIPT;
- incrementTokenScript.setScriptSource(new ResourceScriptSource(new
ClassPathResource(scriptPath)));
- incrementTokenScript.setResultType(List.class);
- reactiveRedisTemplate.execute(incrementTokenScript,
- Lists.newArrayList(cacheKey),
- Lists.newArrayList(tokens.toString(),
windowSeconds.toString())).subscribe();
+ // Record token usage with expiration
+ reactiveRedisTemplate.opsForValue()
+ .increment(cacheKey, tokens)
+ .flatMap(currentValue ->
reactiveRedisTemplate.expire(cacheKey, Duration.ofSeconds(windowSeconds)))
+ .subscribe();
}
@Override
@@ -238,12 +198,15 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
static class AiStatisticServerHttpResponse extends
ServerHttpResponseDecorator {
+ private final ServerWebExchange exchange;
+
private final ServerHttpResponse serverHttpResponse;
private final Consumer<Long> tokensRecorder;
- AiStatisticServerHttpResponse(final ServerHttpResponse delegate, final
Consumer<Long> tokensRecorder) {
+ AiStatisticServerHttpResponse(final ServerWebExchange exchange, final
ServerHttpResponse delegate, final Consumer<Long> tokensRecorder) {
super(delegate);
+ this.exchange = exchange;
this.serverHttpResponse = delegate;
this.tokensRecorder = tokensRecorder;
}
@@ -263,7 +226,7 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
bufferIterator.forEachRemaining(byteBuffer -> {
// Handle gzip encoded response
if
(serverHttpResponse.getHeaders().containsKey(Constants.CONTENT_ENCODING)
- &&
serverHttpResponse.getHeaders().getFirst(Constants.CONTENT_ENCODING).contains("gzip"))
{
+ &&
serverHttpResponse.getHeaders().getFirst(Constants.CONTENT_ENCODING).contains(Constants.HTTP_ACCEPT_ENCODING_GZIP))
{
try {
ByteBuffer readOnlyBuffer =
byteBuffer.asReadOnlyBuffer();
byte[] compressed = new
byte[readOnlyBuffer.remaining()];
@@ -284,7 +247,8 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
}
}).doFinally(signal -> {
String responseBody = writer.output();
- long tokens = extractTokensFromResponse(responseBody);
+ AiModel aiModel = exchange.getAttribute(Constants.AI_MODEL);
+ long tokens =
Objects.requireNonNull(aiModel).getCompletionTokens(responseBody);
tokensRecorder.accept(tokens);
});
}
@@ -301,25 +265,6 @@ public class AiTokenLimiterPlugin extends
AbstractShenyuPlugin {
}
}
- private long extractTokensFromResponse(final String responseBody) {
- // 根据 OpenAI API 响应格式解析出 tokens 使用量
- // 不同的 API 端点响应格式可能不同,需要相应调整
- try {
- JsonNode root = JsonUtils.toJsonNode(responseBody);
- JsonNode usage = root.get("usage");
- if (Objects.nonNull(usage)) {
- JsonNode totalTokens = usage.get("total_tokens");
- if (Objects.nonNull(totalTokens)) {
- return totalTokens.asLong();
- }
- }
- } catch (Exception e) {
- // 处理解析异常
- LOG.error("Failed to parse response body: {}", responseBody,
e);
- }
- return 0;
- }
-
}
static class BodyWriter {
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/handler/AiTokenLimiterPluginHandler.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/handler/AiTokenLimiterPluginHandler.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/handler/AiTokenLimiterPluginHandler.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/handler/AiTokenLimiterPluginHandler.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConfigProperties.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConfigProperties.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConfigProperties.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConfigProperties.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConnectionFactory.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConnectionFactory.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConnectionFactory.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/RedisConnectionFactory.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveRedisTemplate.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveRedisTemplate.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveRedisTemplate.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveRedisTemplate.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveScriptExecutor.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveScriptExecutor.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveScriptExecutor.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/ShenyuReactiveScriptExecutor.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ByteArrayRedisSerializer.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ByteArrayRedisSerializer.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ByteArrayRedisSerializer.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ByteArrayRedisSerializer.java
diff --git
a/shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ShenyuRedisSerializationContext.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ShenyuRedisSerializationContext.java
similarity index 100%
rename from
shenyu-plugin/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ShenyuRedisSerializationContext.java
rename to
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/redis/serializer/ShenyuRedisSerializationContext.java
diff --git
a/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/result/ShenyuResultEnum.java
b/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/result/ShenyuResultEnum.java
index c957a80c21..a52976c5a8 100644
---
a/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/result/ShenyuResultEnum.java
+++
b/shenyu-plugin/shenyu-plugin-api/src/main/java/org/apache/shenyu/plugin/api/result/ShenyuResultEnum.java
@@ -52,6 +52,11 @@ public enum ShenyuResultEnum {
*/
TOO_MANY_REQUESTS(429, "You have been restricted, please try again
later!"),
+ /**
+ * run out of tokens shenyu result enum.
+ */
+ RUN_OUT_OF_TOKENS(429, "You have reach your tokens limit, please try again
later!"),
+
/**
* Hystrix plugin fallback, due to a circuit break.
*/