refactor(llmhub): 重构项目并添加 OpenAI 实现- 重命名核心服务模块为 llmhub-core-service
- 新增 OpenAI 实现模块 llmhub-impl-openAi - 更新配置文件和端口设置 - 优化百炼模型服务实现- 调整项目结构和依赖
This commit is contained in:
parent
729a701d9f
commit
c945e5920f
27
llmhub-base/docker/docker-compose.dev.yml
Normal file
27
llmhub-base/docker/docker-compose.dev.yml
Normal file
@ -0,0 +1,27 @@
|
||||
services:
|
||||
llmhub-core-service:
|
||||
image: ${49.235.96.75:5000}/llmhub-core-service
|
||||
container_name: llmhub-core-service
|
||||
ports:
|
||||
- "9002:9002"
|
||||
networks:
|
||||
- llmhub-net
|
||||
volumes:
|
||||
- llmhub-core-service-volume:/app/volume
|
||||
restart: always
|
||||
llmhub-impl-baiLian:
|
||||
image: ${49.235.96.75:5000}/llmhub-impl-baiLian
|
||||
container_name: llmhub-impl-baiLian
|
||||
ports:
|
||||
- "9003:9003"
|
||||
networks:
|
||||
- llmhub-net
|
||||
volumes:
|
||||
- llmhub-impl-baiLian-volume:/app/volume
|
||||
restart: always
|
||||
networks:
|
||||
llmhub-net-dev:
|
||||
driver: bridge
|
||||
volumes:
|
||||
llmhub-core-service-volume:
|
||||
llmhub-impl-baiLian-volume:
|
||||
@ -1,6 +1,6 @@
|
||||
services:
|
||||
nacos:
|
||||
image: nacos/nacos-server:latest
|
||||
image: nacos/nacos-server:v2.3.2
|
||||
container_name: nacos-server
|
||||
ports:
|
||||
- "9001:8848" # Nacos控制台
|
||||
@ -9,12 +9,16 @@ services:
|
||||
environment:
|
||||
- NACOS_AUTH_ENABLE=true
|
||||
- NACOS_AUTH_IDENTITY_KEY=serverIdentity
|
||||
- NACOS_AUTH_IDENTITY_VALUE=security
|
||||
- NACOS_AUTH_TOKEN=L4s6f9y3
|
||||
- MODE=standalone
|
||||
- SPRING_DATASOURCE_PLATFORM=empty
|
||||
- JVM_XMS=256m
|
||||
- JVM_XMX=512m
|
||||
- NACOS_AUTH_IDENTITY_VALUE=gRuycTvLGqWpkXyOHQeqWV+KSpkLZxLvtPyATXGJ00w=
|
||||
- NACOS_AUTH_TOKEN=yCS3rQVXrnJPt7q3vc79wJl76mG3dY++O854NhxVj7g=
|
||||
- MODE=cluster
|
||||
- NACOS_SERVER_PORT=8848
|
||||
- SPRING_DATASOURCE_PLATFORM=mysql
|
||||
- MYSQL_SERVICE_HOST=mysql
|
||||
- MYSQL_SERVICE_PORT=3306
|
||||
- MYSQL_SERVICE_DB_NAME=nacos_config
|
||||
- MYSQL_SERVICE_USER=root
|
||||
- MYSQL_SERVICE_PASSWORD=root
|
||||
networks:
|
||||
- llmhub-net
|
||||
volumes:
|
||||
@ -22,27 +26,41 @@ services:
|
||||
- nacos-data-volume:/home/nacos/data
|
||||
- nacos-logs-volume:/home/nacos/logs
|
||||
restart: always
|
||||
|
||||
llmhub-core-service:
|
||||
image: ${49.235.96.75:5000}/llmhub-core-service
|
||||
container_name: llmhub-core-service
|
||||
ports:
|
||||
- "9002:9002"
|
||||
networks:
|
||||
- llmhub-net
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: nacos-mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: nacos_config
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- llmhub-core-service-volume:/app/volume
|
||||
restart: always
|
||||
llmhub-impl-baiLian:
|
||||
image: ${49.235.96.75:5000}/llmhub-impl-baiLian
|
||||
container_name: llmhub-impl-baiLian
|
||||
- ./mysql-data:/var/lib/mysql
|
||||
- ./nacos-mysql.sql:/docker-entrypoint-initdb.d/nacos-mysql.sql
|
||||
ports:
|
||||
- "9002:9002"
|
||||
networks:
|
||||
- llmhub-net
|
||||
volumes:
|
||||
- llmhub-impl-baiLian-volume:/app/volume
|
||||
- "3306:3306"
|
||||
restart: always
|
||||
networks:
|
||||
- nacos-net
|
||||
# llmhub-core-service:
|
||||
# image: ${49.235.96.75:5000}/llmhub-core-service
|
||||
# container_name: llmhub-core-service
|
||||
# ports:
|
||||
# - "9002:9002"
|
||||
# networks:
|
||||
# - llmhub-net
|
||||
# volumes:
|
||||
# - llmhub-core-service-volume:/app/volume
|
||||
# restart: always
|
||||
# llmhub-impl-baiLian:
|
||||
# image: ${49.235.96.75:5000}/llmhub-impl-baiLian
|
||||
# container_name: llmhub-impl-baiLian
|
||||
# ports:
|
||||
# - "9002:9002"
|
||||
# networks:
|
||||
# - llmhub-net
|
||||
# volumes:
|
||||
# - llmhub-impl-baiLian-volume:/app/volume
|
||||
# restart: always
|
||||
networks:
|
||||
llmhub-net:
|
||||
driver: bridge
|
||||
@ -50,5 +68,5 @@ volumes:
|
||||
nacos-conf-volume:
|
||||
nacos-data-volume:
|
||||
nacos-logs-volume:
|
||||
llmhub-core-service-volume:
|
||||
llmhub-impl-baiLian-volume:
|
||||
# llmhub-core-service-volume:
|
||||
# llmhub-impl-baiLian-volume:
|
||||
178
llmhub-base/docker/init/mysql-schema.sql
Normal file
178
llmhub-base/docker/init/mysql-schema.sql
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = config_info */
|
||||
/******************************************/
|
||||
CREATE TABLE `config_info` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) DEFAULT NULL COMMENT 'group_id',
|
||||
`content` longtext NOT NULL COMMENT 'content',
|
||||
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`src_user` text COMMENT 'source user',
|
||||
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
|
||||
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
|
||||
`c_desc` varchar(256) DEFAULT NULL COMMENT 'configuration description',
|
||||
`c_use` varchar(64) DEFAULT NULL COMMENT 'configuration usage',
|
||||
`effect` varchar(64) DEFAULT NULL COMMENT '配置生效的描述',
|
||||
`type` varchar(64) DEFAULT NULL COMMENT '配置的类型',
|
||||
`c_schema` text COMMENT '配置的模式',
|
||||
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = config_info since 2.5.0 */
|
||||
/******************************************/
|
||||
CREATE TABLE `config_info_gray` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
|
||||
`content` longtext NOT NULL COMMENT 'content',
|
||||
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
|
||||
`src_user` text COMMENT 'src_user',
|
||||
`src_ip` varchar(100) DEFAULT NULL COMMENT 'src_ip',
|
||||
`gmt_create` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_create',
|
||||
`gmt_modified` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'gmt_modified',
|
||||
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
|
||||
`gray_name` varchar(128) NOT NULL COMMENT 'gray_name',
|
||||
`gray_rule` text NOT NULL COMMENT 'gray_rule',
|
||||
`encrypted_data_key` varchar(256) NOT NULL DEFAULT '' COMMENT 'encrypted_data_key',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_configinfogray_datagrouptenantgray` (`data_id`,`group_id`,`tenant_id`,`gray_name`),
|
||||
KEY `idx_dataid_gmt_modified` (`data_id`,`gmt_modified`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='config_info_gray';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = config_tags_relation */
|
||||
/******************************************/
|
||||
CREATE TABLE `config_tags_relation` (
|
||||
`id` bigint(20) NOT NULL COMMENT 'id',
|
||||
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
|
||||
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
|
||||
`nid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增长标识',
|
||||
PRIMARY KEY (`nid`),
|
||||
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
|
||||
KEY `idx_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = group_capacity */
|
||||
/******************************************/
|
||||
CREATE TABLE `group_capacity` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
|
||||
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
|
||||
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
|
||||
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
|
||||
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_group_id` (`group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = his_config_info */
|
||||
/******************************************/
|
||||
CREATE TABLE `his_config_info` (
|
||||
`id` bigint(20) unsigned NOT NULL COMMENT 'id',
|
||||
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'nid, 自增标识',
|
||||
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
|
||||
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
|
||||
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
|
||||
`content` longtext NOT NULL COMMENT 'content',
|
||||
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`src_user` text COMMENT 'source user',
|
||||
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
|
||||
`op_type` char(10) DEFAULT NULL COMMENT 'operation type',
|
||||
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
|
||||
`encrypted_data_key` varchar(1024) NOT NULL DEFAULT '' COMMENT '密钥',
|
||||
`publish_type` varchar(50) DEFAULT 'formal' COMMENT 'publish type gray or formal',
|
||||
`gray_name` varchar(50) DEFAULT NULL COMMENT 'gray name',
|
||||
`ext_info` longtext DEFAULT NULL COMMENT 'ext info',
|
||||
PRIMARY KEY (`nid`),
|
||||
KEY `idx_gmt_create` (`gmt_create`),
|
||||
KEY `idx_gmt_modified` (`gmt_modified`),
|
||||
KEY `idx_did` (`data_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
|
||||
|
||||
|
||||
/******************************************/
|
||||
/* 表名称 = tenant_capacity */
|
||||
/******************************************/
|
||||
CREATE TABLE `tenant_capacity` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
|
||||
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
|
||||
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
|
||||
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
|
||||
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
|
||||
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
|
||||
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
|
||||
|
||||
|
||||
CREATE TABLE `tenant_info` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`kp` varchar(128) NOT NULL COMMENT 'kp',
|
||||
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
|
||||
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
|
||||
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
|
||||
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
|
||||
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
|
||||
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
|
||||
|
||||
CREATE TABLE `users` (
|
||||
`username` varchar(50) NOT NULL PRIMARY KEY COMMENT 'username',
|
||||
`password` varchar(500) NOT NULL COMMENT 'password',
|
||||
`enabled` boolean NOT NULL COMMENT 'enabled'
|
||||
);
|
||||
|
||||
CREATE TABLE `roles` (
|
||||
`username` varchar(50) NOT NULL COMMENT 'username',
|
||||
`role` varchar(50) NOT NULL COMMENT 'role',
|
||||
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
|
||||
);
|
||||
|
||||
CREATE TABLE `permissions` (
|
||||
`role` varchar(50) NOT NULL COMMENT 'role',
|
||||
`resource` varchar(128) NOT NULL COMMENT 'resource',
|
||||
`action` varchar(8) NOT NULL COMMENT 'action',
|
||||
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
|
||||
);
|
||||
@ -4,7 +4,7 @@ spring-cloud-version = "2023.0.5"
|
||||
spring-boot-version = "3.2.4"
|
||||
spring-dependency-management-version = "1.1.7"
|
||||
aliyun-bailian-version = "2.0.0"
|
||||
spring-cloud-starter-alibaba-nacos-discovery-version = "2023.0.3.2"
|
||||
spring-cloud-starter-alibaba-nacos-discovery-version = "2023.0.1.0"
|
||||
forgeBoot-version = "1.1.0-SNAPSHOT"
|
||||
okHttp-version = "4.12.0"
|
||||
jib-version = "3.4.2"
|
||||
@ -53,5 +53,12 @@ junitPlatform-launcher = { group = "org.junit.platform", name = "junit-platform-
|
||||
okHttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okHttp-version" }
|
||||
# forgeBoot
|
||||
forgeBoot-webmvc-version-springBootStarter = { group = "com.gewuyou.forgeboot", name = "forgeboot-webmvc-version-spring-boot-starter", version.ref = "forgeBoot-version" }
|
||||
forgeBoot-i18n-springBootStarter = { group = "com.gewuyou.forgeboot", name = "forgeboot-i18n-spring-boot-starter", version.ref = "forgeBoot-version" }
|
||||
forgeBoot-core-extension = { group = "com.gewuyou.forgeboot", name = "forgeboot-core-extension", version.ref = "forgeBoot-version" }
|
||||
|
||||
jackson-core={group="com.fasterxml.jackson.core", name="jackson-core"}
|
||||
jackson-databind={group="com.fasterxml.jackson.core", name="jackson-databind"}
|
||||
jackson-annotations={group="com.fasterxml.jackson.core", name="jackson-annotations"}
|
||||
jackson-datatype-jsr310={group="com.fasterxml.jackson.datatype", name="jackson-datatype-jsr310"}
|
||||
jackson-module-kotlin={group="com.fasterxml.jackson.module", name="jackson-module-kotlin"}
|
||||
[bundles]
|
||||
|
||||
@ -21,4 +21,6 @@ dependencies {
|
||||
implementation(libs.forgeBoot.webmvc.version.springBootStarter)
|
||||
implementation(libs.forgeBoot.core.extension)
|
||||
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package org.jcnc.llmhub.core.service
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
|
||||
|
||||
@ -6,6 +6,7 @@ import org.jcnc.llmhub.core.service.service.impl.LLMServiceImpl
|
||||
import org.jcnc.llmhub.core.spi.entities.request.ChatRequest
|
||||
import org.jcnc.llmhub.core.spi.entities.response.ChatResponsePart
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@ -32,7 +33,7 @@ class ChatController(
|
||||
* @return 返回一个Flow流,流中依次提供了聊天响应的部分数据,允许异步处理和逐步消费响应内容
|
||||
*/
|
||||
@PostMapping("/stream")
|
||||
fun chat(request: ChatRequest): Flow<ChatResponsePart> {
|
||||
fun chat(@RequestBody request: ChatRequest): Flow<ChatResponsePart> {
|
||||
return llmServiceImpl.chat(request)
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ class LLMServiceImpl(
|
||||
val serviceName = modelRouteManager.resolveServiceName(request.model)
|
||||
val webClient = webClientBuilder.build()
|
||||
return webClient.post()
|
||||
.uri("http://$serviceName/provider/chat/stream")
|
||||
.uri("http://$serviceName/provider/chat")
|
||||
.bodyValue(request)
|
||||
.retrieve()
|
||||
.bodyToFlux(ChatResponsePart::class.java)
|
||||
|
||||
@ -1,17 +1,8 @@
|
||||
server:
|
||||
port: 8081
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
access-key: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTc0NTc1NjkwOH0.86TmeN27gXbpw55jMmOVNz42B9u8dXtGwCvyGlWbYVo
|
||||
username: nacos
|
||||
password: L4s6f9y3
|
||||
server-addr: 49.235.96.75:8848
|
||||
discovery:
|
||||
server-addr: ${spring.cloud.nacos.server-addr}
|
||||
access-key: ${spring.cloud.nacos.access-key}
|
||||
username: ${spring.cloud.nacos.username}
|
||||
password: ${spring.cloud.nacos.password}
|
||||
config:
|
||||
import: classpath:bootstrap-dev.yml
|
||||
llmhub:
|
||||
model-route:
|
||||
modelServiceMap:
|
||||
|
||||
@ -1,25 +1,5 @@
|
||||
spring:
|
||||
application:
|
||||
name: llmhub-core-service
|
||||
cloud:
|
||||
nacos:
|
||||
access-key: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTc0NTc1NjkwOH0.86TmeN27gXbpw55jMmOVNz42B9u8dXtGwCvyGlWbYVo
|
||||
username: nacos
|
||||
password: L4s6f9y3
|
||||
server-addr: 49.235.96.75:8848
|
||||
discovery:
|
||||
server-addr: ${spring.cloud.nacos.server-addr}
|
||||
access-key: ${spring.cloud.nacos.access-key}
|
||||
username: ${spring.cloud.nacos.username}
|
||||
password: ${spring.cloud.nacos.password}
|
||||
llmhub:
|
||||
model-route:
|
||||
modelServiceMap:
|
||||
qwen-turbo: llmhub-impl-baiLian
|
||||
qwen-max: llmhub-impl-baiLian
|
||||
qwen-plus: llmhub-impl-baiLian
|
||||
|
||||
# profiles:
|
||||
# active: dev
|
||||
server:
|
||||
port: 8081
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: L4s6f9y3
|
||||
server-addr: 49.235.96.75:8848
|
||||
ip: 192.168.1.6
|
||||
discovery:
|
||||
server-addr: ${spring.cloud.nacos.server-addr}
|
||||
username: ${spring.cloud.nacos.username}
|
||||
password: ${spring.cloud.nacos.password}
|
||||
ip: ${spring.cloud.nacos.ip}
|
||||
@ -15,7 +15,7 @@ package org.jcnc.llmhub.core.spi.entities.response
|
||||
*/
|
||||
data class ChatResponsePart(
|
||||
val content: String,
|
||||
val other: String? = null,
|
||||
val other: Map<String, Any>? = mapOf(),
|
||||
val done: Boolean = false,
|
||||
val usage: Usage? = null
|
||||
)
|
||||
|
||||
@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping
|
||||
* @since 2025-04-25 16:13:00
|
||||
* @author gewuyou
|
||||
*/
|
||||
@RequestMapping("/provider/chat")
|
||||
@RequestMapping("/provider")
|
||||
interface LLMProvider {
|
||||
|
||||
/**
|
||||
|
||||
@ -15,5 +15,9 @@ dependencies {
|
||||
implementation(libs.okHttp)
|
||||
|
||||
implementation(libs.forgeBoot.core.extension)
|
||||
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
|
||||
implementation(libs.forgeBoot.core.extension)
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.jcnc.llmhub.impl.baiLian.adapter
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
@ -11,6 +12,7 @@ import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.jcnc.llmhub.core.spi.entities.response.ChatResponsePart
|
||||
import org.springframework.stereotype.Component
|
||||
import java.io.BufferedReader
|
||||
|
||||
/**
|
||||
* 百炼适配器
|
||||
@ -45,62 +47,57 @@ class DashScopeAdapter(
|
||||
url: String,
|
||||
headers: Map<String, String>,
|
||||
requestBody: Any,
|
||||
extractContent: (String) -> String,
|
||||
extractContent: (String) -> ChatResponsePart,
|
||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
): Flow<ChatResponsePart> = flow {
|
||||
// 将请求体序列化为JSON格式
|
||||
val requestJson = objectMapper.writeValueAsString(requestBody)
|
||||
// 构建HTTP请求
|
||||
log.info("📤 请求参数: {}", requestJson)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.headers(headers.toHeaders())
|
||||
.post(requestJson.toRequestBody("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
// 发送HTTP请求
|
||||
val call = okHttpClient.newCall(request)
|
||||
val response = call.execute()
|
||||
|
||||
// 使用指定的调度器执行请求
|
||||
val response = withContext(dispatcher) {
|
||||
call.execute()
|
||||
}
|
||||
|
||||
// 检查HTTP响应是否成功
|
||||
if (!response.isSuccessful) {
|
||||
throw RuntimeException("DashScope request failed: ${response.code}")
|
||||
throw RuntimeException("❌ DashScope 请求失败: HTTP ${response.code}")
|
||||
}
|
||||
|
||||
// 获取响应体
|
||||
val responseBody = response.body ?: throw RuntimeException("Empty response body from DashScope")
|
||||
// 获取响应体的源
|
||||
val source = responseBody.source().buffer
|
||||
val responseBody = response.body ?: throw RuntimeException("❌ DashScope 响应体为空")
|
||||
|
||||
val bufferedReader: BufferedReader = responseBody.charStream().buffered()
|
||||
val allContent = StringBuilder()
|
||||
|
||||
try {
|
||||
// 读取响应体,直到数据结束或协程被取消
|
||||
while (!source.exhausted() && currentCoroutineContext().isActive) {
|
||||
// 读取一行数据
|
||||
val line = source.readUtf8Line()
|
||||
// 忽略空行
|
||||
if (line.isNullOrBlank()) {
|
||||
continue
|
||||
}
|
||||
// 处理以"data:"开头的行
|
||||
while (currentCoroutineContext().isActive) {
|
||||
val line = withContext(dispatcher) {
|
||||
bufferedReader.readLine()
|
||||
} ?: break
|
||||
log.info("📥 接收到行: {}", line)
|
||||
|
||||
if (line.startsWith("data:")) {
|
||||
// 提取JSON部分
|
||||
val jsonPart = line.removePrefix("data:").trim()
|
||||
// 提取内容
|
||||
val content = extractContent(jsonPart)
|
||||
|
||||
// 发布聊天响应的部分内容
|
||||
emit(ChatResponsePart(content = content, done = false))
|
||||
try {
|
||||
val part = extractContent(jsonPart)
|
||||
allContent.append(part.content)
|
||||
log.info("✅ 提取内容: {}", part)
|
||||
emit(part)
|
||||
} catch (e: Exception) {
|
||||
log.warn("⚠️ 无法解析 JSON: {}", jsonPart, e)
|
||||
}
|
||||
}
|
||||
|
||||
// 发布结束信号
|
||||
emit(ChatResponsePart(content = "[END]", done = true))
|
||||
}
|
||||
log.info("📦 完整内容: {}", allContent)
|
||||
} catch (e: Exception) {
|
||||
log.error("🚨 读取 DashScope 响应流失败: {}", e.message, e)
|
||||
throw e
|
||||
} finally {
|
||||
// 关闭资源
|
||||
source.close()
|
||||
withContext(dispatcher) {
|
||||
bufferedReader.close()
|
||||
}
|
||||
response.close()
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import org.jcnc.llmhub.core.spi.entities.response.ChatResponsePart
|
||||
import org.jcnc.llmhub.core.spi.entities.response.EmbeddingResponse
|
||||
import org.jcnc.llmhub.core.spi.provider.LLMProvider
|
||||
import org.jcnc.llmhub.impl.baiLian.service.BaiLianModelService
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
/**
|
||||
@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController
|
||||
* @author gewuyou
|
||||
*/
|
||||
@RestController
|
||||
//@RequestMapping("/provider")
|
||||
class BaiLianProvider(
|
||||
private val baiLianModelService: BaiLianModelService
|
||||
): LLMProvider {
|
||||
@ -28,7 +30,8 @@ class BaiLianProvider(
|
||||
* @param request 聊天请求对象,包含建立聊天所需的信息,如用户标识、会话标识等
|
||||
* @return 返回一个Flow流,通过该流可以接收到聊天响应的部分数据,如消息、状态更新等
|
||||
*/
|
||||
override fun chat(request: ChatRequest): Flow<ChatResponsePart> {
|
||||
// @PostMapping("/chat")
|
||||
override fun chat(@RequestBody request: ChatRequest): Flow<ChatResponsePart> {
|
||||
return baiLianModelService.streamChat(request)
|
||||
}
|
||||
|
||||
|
||||
@ -2,15 +2,18 @@ package org.jcnc.llmhub.impl.baiLian.service.impl
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.jcnc.llmhub.core.spi.entities.request.ChatRequest
|
||||
import org.jcnc.llmhub.core.spi.entities.response.ChatResponsePart
|
||||
import org.jcnc.llmhub.core.spi.entities.response.Usage
|
||||
import org.jcnc.llmhub.impl.baiLian.adapter.DashScopeAdapter
|
||||
import org.jcnc.llmhub.impl.baiLian.config.entities.DashScopeProperties
|
||||
import org.jcnc.llmhub.impl.baiLian.service.BaiLianModelService
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.util.CollectionUtils
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
/**
|
||||
* 百炼模型服务接口实现
|
||||
*
|
||||
@ -32,12 +35,14 @@ class BaiLianModelServiceImpl(
|
||||
override fun streamChat(request: ChatRequest): Flow<ChatResponsePart> {
|
||||
// 构造请求URL
|
||||
val url = "${dashScopeProperties.baseUrl}${dashScopeProperties.appId}/completion"
|
||||
log.info("请求URL: $url")
|
||||
// 构造请求头,包括授权信息和内容类型
|
||||
val headers = mapOf(
|
||||
"Authorization" to "Bearer ${dashScopeProperties.apiKey}",
|
||||
"Content-Type" to "application/json",
|
||||
"X-DashScope-SSE" to "enable"
|
||||
)
|
||||
log.info("请求头: $headers")
|
||||
// 构造输入参数,主要包括用户的prompt
|
||||
val inputMap = mutableMapOf("prompt" to request.prompt)
|
||||
// 获取会话ID,如果存在,则添加到输入参数中
|
||||
@ -82,9 +87,38 @@ class BaiLianModelServiceImpl(
|
||||
return dashScopeAdapter.sendStreamChat(
|
||||
url, headers, body,
|
||||
{ json: String ->
|
||||
// 解析响应JSON,提取输出文本
|
||||
val node = objectMapper.readTree(json)
|
||||
node["output"]?.get("text")?.asText() ?: ""
|
||||
val output = node["output"]
|
||||
val usage = node["usage"]
|
||||
val requestId = node["request_id"]?.asText() ?: ""
|
||||
// 提取输出文本
|
||||
val text = output?.get("text")?.asText() ?: ""
|
||||
|
||||
// 判断是否完成(finish_reason 通常为 "stop",但你这里是字符串 "null")
|
||||
val finishReason = output?.get("finish_reason")?.asText()
|
||||
val done = finishReason == "stop"
|
||||
|
||||
// 提取使用情况
|
||||
val usageNode = usage?.get("models")?.firstOrNull()
|
||||
val promptTokens = usageNode?.get("input_tokens")?.asInt() ?: 0
|
||||
val completionTokens = usageNode?.get("output_tokens")?.asInt() ?: 0
|
||||
val totalTokens = promptTokens + completionTokens
|
||||
val responseSessionId = output?.get("session_id")?.asText() ?: ""
|
||||
ChatResponsePart(
|
||||
content = text,
|
||||
done = done,
|
||||
usage = Usage(
|
||||
promptTokens = promptTokens,
|
||||
completionTokens = completionTokens,
|
||||
totalTokens = totalTokens
|
||||
),
|
||||
other = mapOf(
|
||||
"request_id" to requestId,
|
||||
"session_id" to responseSessionId,
|
||||
"model" to request.model,
|
||||
"response" to json
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
server:
|
||||
port: 8082
|
||||
spring:
|
||||
config:
|
||||
import: classpath:bootstrap-dev.yml
|
||||
application:
|
||||
name: llmhub-impl-baiLian
|
||||
cloud:
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: L4s6f9y3
|
||||
server-addr: 49.235.96.75:8848
|
||||
ip: 192.168.1.6
|
||||
discovery:
|
||||
server-addr: ${spring.cloud.nacos.server-addr}
|
||||
username: ${spring.cloud.nacos.username}
|
||||
password: ${spring.cloud.nacos.password}
|
||||
ip: ${spring.cloud.nacos.ip}
|
||||
3
llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitattributes
vendored
Normal file
3
llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/gradlew text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.jar binary
|
||||
40
llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitignore
vendored
Normal file
40
llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitignore
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
HELP.md
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Kotlin ###
|
||||
.kotlin
|
||||
@ -0,0 +1,7 @@
|
||||
// 开启springboot
|
||||
extra[ProjectFlags.USE_SPRING_BOOT] = true
|
||||
setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true)
|
||||
dependencies {
|
||||
// Nacos 服务发现和配置
|
||||
implementation(libs.springCloudStarter.alibaba.nacos.discovery)
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package org.jcnc.llmhub.impl.openAi
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.runApplication
|
||||
|
||||
@SpringBootApplication
|
||||
class LlmhubImplOpenAiApplication
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
runApplication<LlmhubImplOpenAiApplication>(*args)
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package org.jcnc.llmhub.impl.openAi
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.test.context.SpringBootTest
|
||||
|
||||
@SpringBootTest
|
||||
class LlmhubImplOpenAiApplicationTests {
|
||||
|
||||
@Test
|
||||
fun contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,5 +14,7 @@ project(":llmhub-core:llmhub-core-spi").name = "llmhub-core-spi"
|
||||
include(
|
||||
"llmhub-impl",
|
||||
"llmhub-impl:llmhub-impl-baiLian",
|
||||
"llmhub-impl:llmhub-impl-openAi",
|
||||
)
|
||||
project(":llmhub-impl:llmhub-impl-baiLian").name = "llmhub-impl-baiLian"
|
||||
project(":llmhub-impl:llmhub-impl-openAi").name = "llmhub-impl-openAi"
|
||||
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="GENERAL_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Loading…
x
Reference in New Issue
Block a user