diff --git a/llmhub-base/docker/docker-compose.dev.yml b/llmhub-base/docker/docker-compose.dev.yml new file mode 100644 index 0000000..fa56b6d --- /dev/null +++ b/llmhub-base/docker/docker-compose.dev.yml @@ -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: \ No newline at end of file diff --git a/llmhub-base/docker/docker-compose.master.yml b/llmhub-base/docker/docker-compose.master.yml index 55d8212..548b936 100644 --- a/llmhub-base/docker/docker-compose.master.yml +++ b/llmhub-base/docker/docker-compose.master.yml @@ -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: \ No newline at end of file + # llmhub-core-service-volume: + # llmhub-impl-baiLian-volume: \ No newline at end of file diff --git a/llmhub-base/docker/init/mysql-schema.sql b/llmhub-base/docker/init/mysql-schema.sql new file mode 100644 index 0000000..5fbe7e1 --- /dev/null +++ b/llmhub-base/docker/init/mysql-schema.sql @@ -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 +); \ No newline at end of file diff --git a/llmhub-base/gradle/libs.versions.toml b/llmhub-base/gradle/libs.versions.toml index 87b636e..0f54e0f 100644 --- a/llmhub-base/gradle/libs.versions.toml +++ b/llmhub-base/gradle/libs.versions.toml @@ -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] diff --git a/llmhub-base/llmhub-core/llmhub-core-service/build.gradle.kts b/llmhub-base/llmhub-core/llmhub-core-service/build.gradle.kts index c64a8e4..c35e025 100644 --- a/llmhub-base/llmhub-core/llmhub-core-service/build.gradle.kts +++ b/llmhub-base/llmhub-core/llmhub-core-service/build.gradle.kts @@ -21,4 +21,6 @@ dependencies { implementation(libs.forgeBoot.webmvc.version.springBootStarter) implementation(libs.forgeBoot.core.extension) + implementation(libs.jackson.module.kotlin) + } diff --git a/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/LlmhubCoreServiceApplication.kt b/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/LlmhubCoreServiceApplication.kt index 133d495..0baf478 100644 --- a/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/LlmhubCoreServiceApplication.kt +++ b/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/LlmhubCoreServiceApplication.kt @@ -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 diff --git a/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/controller/ChatController.kt b/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/controller/ChatController.kt index 31e65ea..662fe46 100644 --- a/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/controller/ChatController.kt +++ b/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/controller/ChatController.kt @@ -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 { + fun chat(@RequestBody request: ChatRequest): Flow { return llmServiceImpl.chat(request) } } \ No newline at end of file diff --git a/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/service/impl/LLMServiceImpl.kt b/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/service/impl/LLMServiceImpl.kt index 786d643..51b1aeb 100644 --- a/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/service/impl/LLMServiceImpl.kt +++ b/llmhub-base/llmhub-core/llmhub-core-service/src/main/kotlin/org/jcnc/llmhub/core/service/service/impl/LLMServiceImpl.kt @@ -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) diff --git a/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application-dev.yml b/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application-dev.yml index 4c5ecd0..f6ea2d1 100644 --- a/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application-dev.yml +++ b/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application-dev.yml @@ -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: diff --git a/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application.yml b/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application.yml index da53d98..4e5448d 100644 --- a/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application.yml +++ b/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/application.yml @@ -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 \ No newline at end of file + profiles: + active: dev diff --git a/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/bootstrap-dev.yml b/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/bootstrap-dev.yml new file mode 100644 index 0000000..d1e2592 --- /dev/null +++ b/llmhub-base/llmhub-core/llmhub-core-service/src/main/resources/bootstrap-dev.yml @@ -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} \ No newline at end of file diff --git a/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/entities/response/ChatResponse.kt b/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/entities/response/ChatResponse.kt index 47ac65a..508ea61 100644 --- a/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/entities/response/ChatResponse.kt +++ b/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/entities/response/ChatResponse.kt @@ -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? = mapOf(), val done: Boolean = false, val usage: Usage? = null ) diff --git a/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/provider/LLMProvider.kt b/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/provider/LLMProvider.kt index 08c08bf..e7919e8 100644 --- a/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/provider/LLMProvider.kt +++ b/llmhub-base/llmhub-core/llmhub-core-spi/src/main/kotlin/org/jcnc/llmhub/core/spi/provider/LLMProvider.kt @@ -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 { /** diff --git a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/build.gradle.kts b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/build.gradle.kts index c4a9aa4..12471f2 100644 --- a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/build.gradle.kts +++ b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/build.gradle.kts @@ -15,5 +15,9 @@ dependencies { implementation(libs.okHttp) implementation(libs.forgeBoot.core.extension) + + implementation(libs.jackson.module.kotlin) + + implementation(libs.forgeBoot.core.extension) } diff --git a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/adapter/DashScopeAdapter.kt b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/adapter/DashScopeAdapter.kt index 682f027..439569e 100644 --- a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/adapter/DashScopeAdapter.kt +++ b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/adapter/DashScopeAdapter.kt @@ -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, requestBody: Any, - extractContent: (String) -> String, + extractContent: (String) -> ChatResponsePart, dispatcher: CoroutineDispatcher = Dispatchers.IO ): Flow = 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:"开头的行 - if (line.startsWith("data:")) { - // 提取JSON部分 - val jsonPart = line.removePrefix("data:").trim() - // 提取内容 - val content = extractContent(jsonPart) + while (currentCoroutineContext().isActive) { + val line = withContext(dispatcher) { + bufferedReader.readLine() + } ?: break + log.info("📥 接收到行: {}", line) - // 发布聊天响应的部分内容 - emit(ChatResponsePart(content = content, done = false)) + if (line.startsWith("data:")) { + val jsonPart = line.removePrefix("data:").trim() + 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() } } diff --git a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/controller/BaiLianProvider.kt b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/controller/BaiLianProvider.kt index f3c622b..2ec4889 100644 --- a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/controller/BaiLianProvider.kt +++ b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/controller/BaiLianProvider.kt @@ -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 { +// @PostMapping("/chat") + override fun chat(@RequestBody request: ChatRequest): Flow { return baiLianModelService.streamChat(request) } diff --git a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/service/impl/BaiLianModelServiceImpl.kt b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/service/impl/BaiLianModelServiceImpl.kt index dd6f459..5634567 100644 --- a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/service/impl/BaiLianModelServiceImpl.kt +++ b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/kotlin/org/jcnc/llmhub/impl/baiLian/service/impl/BaiLianModelServiceImpl.kt @@ -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 { // 构造请求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 + ) + ) } ) } diff --git a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/application-dev.yml b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/application-dev.yml index c964936..7bb8cd6 100644 --- a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/application-dev.yml +++ b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/application-dev.yml @@ -1,6 +1,8 @@ server: port: 8082 spring: + config: + import: classpath:bootstrap-dev.yml application: name: llmhub-impl-baiLian cloud: diff --git a/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/bootstrap-dev.yml b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/bootstrap-dev.yml new file mode 100644 index 0000000..d1e2592 --- /dev/null +++ b/llmhub-base/llmhub-impl/llmhub-impl-baiLian/src/main/resources/bootstrap-dev.yml @@ -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} \ No newline at end of file diff --git a/llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitattributes b/llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitignore b/llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/llmhub-base/llmhub-impl/llmhub-impl-openAi/.gitignore @@ -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 diff --git a/llmhub-base/llmhub-impl/llmhub-impl-openAi/build.gradle.kts b/llmhub-base/llmhub-impl/llmhub-impl-openAi/build.gradle.kts new file mode 100644 index 0000000..24b7a0c --- /dev/null +++ b/llmhub-base/llmhub-impl/llmhub-impl-openAi/build.gradle.kts @@ -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) +} \ No newline at end of file diff --git a/llmhub-base/llmhub-impl/llmhub-impl-openAi/src/main/kotlin/org/jcnc/llmhub/impl/openAi/LlmhubImplOpenAiApplication.kt b/llmhub-base/llmhub-impl/llmhub-impl-openAi/src/main/kotlin/org/jcnc/llmhub/impl/openAi/LlmhubImplOpenAiApplication.kt new file mode 100644 index 0000000..41c112e --- /dev/null +++ b/llmhub-base/llmhub-impl/llmhub-impl-openAi/src/main/kotlin/org/jcnc/llmhub/impl/openAi/LlmhubImplOpenAiApplication.kt @@ -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) { + runApplication(*args) +} diff --git a/llmhub-base/llmhub-impl/llmhub-impl-openAi/src/test/kotlin/org/jcnc/llmhub/impl/openAi/LlmhubImplOpenAiApplicationTests.kt b/llmhub-base/llmhub-impl/llmhub-impl-openAi/src/test/kotlin/org/jcnc/llmhub/impl/openAi/LlmhubImplOpenAiApplicationTests.kt new file mode 100644 index 0000000..9cc6b05 --- /dev/null +++ b/llmhub-base/llmhub-impl/llmhub-impl-openAi/src/test/kotlin/org/jcnc/llmhub/impl/openAi/LlmhubImplOpenAiApplicationTests.kt @@ -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() { + } + +} diff --git a/llmhub-base/settings.gradle.kts b/llmhub-base/settings.gradle.kts index c63b789..b2723e1 100644 --- a/llmhub-base/settings.gradle.kts +++ b/llmhub-base/settings.gradle.kts @@ -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" \ No newline at end of file +project(":llmhub-impl:llmhub-impl-baiLian").name = "llmhub-impl-baiLian" +project(":llmhub-impl:llmhub-impl-openAi").name = "llmhub-impl-openAi" \ No newline at end of file diff --git a/llmhub-demo.iml b/llmhub-demo.iml deleted file mode 100644 index 9a5cfce..0000000 --- a/llmhub-demo.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file