feat(impl): 添加百炼模型服务实现
- 新增 BaiLianModelService 接口及其实现类 BaiLianModelServiceImpl- 添加与阿里云 DashScope API 交互的适配器 DashScopeAdapter - 新增百炼提供商 BaiLianProvider - 更新 ChatController 以支持流式聊天- 添加必要的配置类和属性文件
This commit is contained in:
parent
3c9796524f
commit
05a2a78dfd
@ -4,6 +4,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.plugin.spring)
|
alias(libs.plugins.kotlin.plugin.spring)
|
||||||
alias(libs.plugins.spring.boot)
|
alias(libs.plugins.spring.boot)
|
||||||
alias(libs.plugins.spring.dependency.management)
|
alias(libs.plugins.spring.dependency.management)
|
||||||
|
alias(libs.plugins.jibLocalPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "org.jcnc"
|
group = "org.jcnc"
|
||||||
@ -91,7 +92,7 @@ subprojects {
|
|||||||
// springCloudBom
|
// springCloudBom
|
||||||
if (project.getPropertyByBoolean(ProjectFlags.USE_SPRING_CLOUD_BOM)) {
|
if (project.getPropertyByBoolean(ProjectFlags.USE_SPRING_CLOUD_BOM)) {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.springCloudDependencies.bom)
|
implementation(platform(libs.springCloudDependencies.bom))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,6 +100,7 @@ subprojects {
|
|||||||
apply {
|
apply {
|
||||||
plugin(libs.plugins.java.get().pluginId)
|
plugin(libs.plugins.java.get().pluginId)
|
||||||
plugin(libs.plugins.kotlin.jvm.get().pluginId)
|
plugin(libs.plugins.kotlin.jvm.get().pluginId)
|
||||||
|
plugin(libs.plugins.jibLocalPlugin.get().pluginId)
|
||||||
}
|
}
|
||||||
println(project.name + ":" + project.getPropertyByBoolean(ProjectFlags.USE_SPRING_BOOT))
|
println(project.name + ":" + project.getPropertyByBoolean(ProjectFlags.USE_SPRING_BOOT))
|
||||||
|
|
||||||
@ -107,6 +109,13 @@ subprojects {
|
|||||||
freeCompilerArgs.addAll("-Xjsr305=strict")
|
freeCompilerArgs.addAll("-Xjsr305=strict")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
jibConfig{
|
||||||
|
project{
|
||||||
|
projectName = "llmhub-core-service"
|
||||||
|
ports = listOf("9002")
|
||||||
|
environment = mapOf("SPRING_PROFILES_ACTIVE" to "prod")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
|
|||||||
@ -6,10 +6,17 @@ plugins {
|
|||||||
alias(libs.plugins.javaGradle.plugin)
|
alias(libs.plugins.javaGradle.plugin)
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// 导入 jib 插件依赖
|
||||||
|
implementation(libs.jib.gradlePlugin)
|
||||||
}
|
}
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
plugins {
|
plugins {
|
||||||
|
register("jib-plugin") {
|
||||||
|
id = "org.jcnc.llmhub.plugin.jib"
|
||||||
|
implementationClass = "org.jcnc.llmhub.plugin.jib.JibPlugin"
|
||||||
|
description =
|
||||||
|
"提供简单的配置构建镜像"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
package org.jcnc.llmhub.plugin.jib
|
||||||
|
|
||||||
|
|
||||||
|
import org.jcnc.llmhub.plugin.jib.entity.JibProject
|
||||||
|
import com.google.cloud.tools.jib.gradle.JibExtension
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.kotlin.dsl.configure
|
||||||
|
|
||||||
|
/**
|
||||||
|
*JIB插件
|
||||||
|
*
|
||||||
|
* @since 2025-03-30 12:11:23
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
class JibPlugin : Plugin<Project> {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
// 创建扩展
|
||||||
|
val extension = project.extensions.create("jibConfig", JibPluginExtension::class.java)
|
||||||
|
project.afterEvaluate {
|
||||||
|
// 只有匹配的模块才会实际应用JIB插件
|
||||||
|
if (extension.projects.any { it.projectName == project.name }) {
|
||||||
|
project.plugins.apply("com.google.cloud.tools.jib")
|
||||||
|
// 调用配置逻辑
|
||||||
|
project.configureJib(extension.projects)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增扩展类
|
||||||
|
open class JibPluginExtension {
|
||||||
|
val projects = mutableListOf<JibProject>()
|
||||||
|
fun project(configure: JibProject.() -> Unit) {
|
||||||
|
projects.add(JibProject().apply(configure))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*项目扩展
|
||||||
|
*
|
||||||
|
* @since 2025-03-30 01:40:10
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
private fun Project.configureJib(jibProjects: List<JibProject>) {
|
||||||
|
val jibProject = jibProjects.find { it.projectName == project.name }
|
||||||
|
jibProject?.let {
|
||||||
|
extensions.configure<JibExtension> {
|
||||||
|
from {
|
||||||
|
image = jibProject.baseImage
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
image =
|
||||||
|
"${System.getenv("LUKE_SERVER_DOCKER_REGISTRY_URL")}/${jibProject.imageName}:${jibProject.version}"
|
||||||
|
auth {
|
||||||
|
username = "root"
|
||||||
|
password = System.getenv("LUKE_SERVER_DOCKER_REGISTRY_PASSWORD")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 动态配置容器参数
|
||||||
|
container {
|
||||||
|
ports = jibProject.ports
|
||||||
|
environment = jibProject.environment
|
||||||
|
if (jibProject.entrypoint.isNotEmpty()) {
|
||||||
|
entrypoint = jibProject.entrypoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态配置额外目录
|
||||||
|
extraDirectories {
|
||||||
|
setPaths(jibProject.paths)
|
||||||
|
permissions.putAll(jibProject.permissions)
|
||||||
|
}
|
||||||
|
// 将动态部分移到任务配置中
|
||||||
|
tasks.named("jib").configure {
|
||||||
|
doFirst {
|
||||||
|
// 只有在实际执行jib任务时才会打印日志
|
||||||
|
println("jibProject: $jibProject")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package org.jcnc.llmhub.plugin.jib.entity
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jib 项目配置类
|
||||||
|
* @author gewuyou
|
||||||
|
* @date 2025/03/30
|
||||||
|
* @constructor 创建[JibProject]
|
||||||
|
* @param [projectName] 项目名称
|
||||||
|
* @param [ports] 监听端口
|
||||||
|
* @param [environment] 环境
|
||||||
|
* @param [entrypoint] 入口点(用于自定义启动命令)
|
||||||
|
* @param [imageName] 图像名称
|
||||||
|
* @param [version] 版本
|
||||||
|
* @param [permissions] 权限
|
||||||
|
*/
|
||||||
|
data class JibProject(
|
||||||
|
var projectName: String = "",
|
||||||
|
var ports: List<String> = listOf("8080"),
|
||||||
|
var environment: Map<String, String> = mapOf("SPRING_PROFILES_ACTIVE" to "prod"),
|
||||||
|
var entrypoint: List<String> = emptyList(),
|
||||||
|
var paths: List<String> = listOf("llmhub-base/scripts/entrypoint.sh"),
|
||||||
|
var imageName: String = "",
|
||||||
|
var version: String = "latest",
|
||||||
|
var permissions: Map<String, String> = mapOf("/scripts/entrypoint.sh" to "755"),
|
||||||
|
var baseImage: String = "docker://bellsoft/liberica-openjdk-debian:21"
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
if (imageName.isEmpty()) {
|
||||||
|
imageName = projectName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
llmhub-base/docker/docker-compose.master.yml
Normal file
54
llmhub-base/docker/docker-compose.master.yml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
services:
|
||||||
|
nacos:
|
||||||
|
image: nacos/nacos-server:latest
|
||||||
|
container_name: nacos-server
|
||||||
|
ports:
|
||||||
|
- "9001:8848" # Nacos控制台
|
||||||
|
- "9848:9848" # gRPC (nacos2.0以后内部使用)
|
||||||
|
- "9849:9849" # gRPC (nacos2.0以后内部使用)
|
||||||
|
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
|
||||||
|
networks:
|
||||||
|
- llmhub-net
|
||||||
|
volumes:
|
||||||
|
- nacos-conf-volume:/home/nacos/conf
|
||||||
|
- 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
|
||||||
|
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
|
||||||
|
volumes:
|
||||||
|
nacos-conf-volume:
|
||||||
|
nacos-data-volume:
|
||||||
|
nacos-logs-volume:
|
||||||
|
llmhub-core-service-volume:
|
||||||
|
llmhub-impl-baiLian-volume:
|
||||||
@ -1,12 +1,13 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin-version = "2.0.0"
|
kotlin-version = "2.0.0"
|
||||||
spring-cloud-version = "2024.0.1"
|
spring-cloud-version = "2023.0.5"
|
||||||
spring-cloud-starter-version = "4.2.1"
|
spring-boot-version = "3.2.4"
|
||||||
spring-boot-version = "3.4.4"
|
|
||||||
spring-dependency-management-version = "1.1.7"
|
spring-dependency-management-version = "1.1.7"
|
||||||
aliyun-bailian-version = "2.0.0"
|
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.3.2"
|
||||||
forgeBoot-version = "1.0.0"
|
forgeBoot-version = "1.0.0"
|
||||||
|
okHttp-version = "4.12.0"
|
||||||
|
jib-version = "3.4.2"
|
||||||
[plugins]
|
[plugins]
|
||||||
# 应用 Java 插件,提供基本的 Java 代码编译和构建能力
|
# 应用 Java 插件,提供基本的 Java 代码编译和构建能力
|
||||||
java = { id = "java" }
|
java = { id = "java" }
|
||||||
@ -24,9 +25,12 @@ spring-dependency-management = { id = "io.spring.dependency-management", version
|
|||||||
# 应用 Spring Boot 插件,提供 Spring Boot 应用的开发和运行能力
|
# 应用 Spring Boot 插件,提供 Spring Boot 应用的开发和运行能力
|
||||||
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot-version" }
|
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot-version" }
|
||||||
|
|
||||||
|
jib = { id = "com.google.cloud.tools.jib", version.ref = "jib-version" }
|
||||||
|
jibLocalPlugin = { id = "org.jcnc.llmhub.plugin.jib" }
|
||||||
[libraries]
|
[libraries]
|
||||||
|
jib-gradlePlugin = { module = "com.google.cloud.tools.jib:com.google.cloud.tools.jib.gradle.plugin", version.ref = "jib-version" }
|
||||||
# bom
|
# bom
|
||||||
springCloudDependencies-bom = { module = "org.springframework.cloud:spring-cloud-dependencies", version.ref = "spring-cloud-version" }
|
springCloudDependencies-bom = { group ="org.springframework.cloud",name = "spring-cloud-dependencies", version.ref = "spring-cloud-version" }
|
||||||
# kotlinx
|
# kotlinx
|
||||||
# 响应式协程库
|
# 响应式协程库
|
||||||
kotlinx-coruntes-reactor = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-reactor" }
|
kotlinx-coruntes-reactor = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-reactor" }
|
||||||
@ -36,7 +40,7 @@ aliyun-bailian = { group = "com.aliyun", name = "bailian20231229", version.ref =
|
|||||||
|
|
||||||
# SrpingCloud
|
# SrpingCloud
|
||||||
springCloudStarter-alibaba-nacos-discovery = { group = "com.alibaba.cloud", name = "spring-cloud-starter-alibaba-nacos-discovery", version.ref = "spring-cloud-starter-alibaba-nacos-discovery-version" }
|
springCloudStarter-alibaba-nacos-discovery = { group = "com.alibaba.cloud", name = "spring-cloud-starter-alibaba-nacos-discovery", version.ref = "spring-cloud-starter-alibaba-nacos-discovery-version" }
|
||||||
springCloudStarter-loadbalancer = { group = "org.springframework.cloud", name = "spring-cloud-starter-loadbalancer" ,version.ref="spring-cloud-starter-version"}
|
springCloudStarter-loadbalancer = { group = "org.springframework.cloud", name = "spring-cloud-starter-loadbalancer" }
|
||||||
|
|
||||||
# SpringBootStarter
|
# SpringBootStarter
|
||||||
springBootStarter-webflux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux" }
|
springBootStarter-webflux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux" }
|
||||||
@ -45,6 +49,8 @@ springBootStarter-test = { group = "org.springframework.boot", name = "spring-bo
|
|||||||
|
|
||||||
junitPlatform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
|
junitPlatform-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
|
||||||
|
|
||||||
|
# OkHttp
|
||||||
|
okHttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okHttp-version" }
|
||||||
# forgeBoot
|
# forgeBoot
|
||||||
forgeBoot-webmvc-version-springBootStarter = { group = "com.gewuyou.forgeboot", name = "forgeboot-webmvc-version-spring-boot-starter", version.ref = "forgeBoot-version" }
|
forgeBoot-webmvc-version-springBootStarter = { group = "com.gewuyou.forgeboot", name = "forgeboot-webmvc-version-spring-boot-starter", version.ref = "forgeBoot-version" }
|
||||||
forgeBoot-core-extension = { group = "com.gewuyou.forgeboot", name = "forgeboot-core-extension", version.ref = "forgeBoot-version" }
|
forgeBoot-core-extension = { group = "com.gewuyou.forgeboot", name = "forgeboot-core-extension", version.ref = "forgeBoot-version" }
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
extra {
|
extra {
|
||||||
// 开启springboot
|
// 开启springboot
|
||||||
setProperty(ProjectFlags.USE_SPRING_BOOT, true)
|
setProperty(ProjectFlags.USE_SPRING_BOOT, true)
|
||||||
|
setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true)
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
val libs = rootProject.libs
|
val libs = rootProject.libs
|
||||||
@ -11,6 +12,7 @@ dependencies {
|
|||||||
implementation(libs.springBootStarter.webflux)
|
implementation(libs.springBootStarter.webflux)
|
||||||
implementation(libs.springCloudStarter.loadbalancer)
|
implementation(libs.springCloudStarter.loadbalancer)
|
||||||
|
|
||||||
|
|
||||||
implementation(project(Modules.Core.SPI))
|
implementation(project(Modules.Core.SPI))
|
||||||
|
|
||||||
// Kotlin Coroutines
|
// Kotlin Coroutines
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
package org.jcnc.llmhub.core.service
|
package org.jcnc.llmhub.core.service
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableDiscoveryClient
|
||||||
class LlmhubCoreServiceApplication
|
class LlmhubCoreServiceApplication
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package org.jcnc.llmhub.core.service.config
|
package org.jcnc.llmhub.core.service.config
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*模型属性
|
*模型属性
|
||||||
@ -9,7 +10,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties
|
|||||||
* @author gewuyou
|
* @author gewuyou
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = "llmhub.model-route")
|
@ConfigurationProperties(prefix = "llmhub.model-route")
|
||||||
class ModelProperties {
|
@RefreshScope
|
||||||
|
open class ModelProperties {
|
||||||
/**
|
/**
|
||||||
* 模型名前缀 -> 服务名映射
|
* 模型名前缀 -> 服务名映射
|
||||||
* 该映射表存储了模型名前缀与服务名的对应关系,用于快速查找模型对应的服务
|
* 该映射表存储了模型名前缀与服务名的对应关系,用于快速查找模型对应的服务
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
package org.jcnc.llmhub.core.service.controller
|
package org.jcnc.llmhub.core.service.controller
|
||||||
|
|
||||||
import com.gewuyou.forgeboot.webmvc.version.annotation.ApiVersion
|
import com.gewuyou.forgeboot.webmvc.version.annotation.ApiVersion
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.jcnc.llmhub.core.service.service.impl.LLMServiceImpl
|
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.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
@ -17,5 +21,18 @@ import org.springframework.web.bind.annotation.RestController
|
|||||||
class ChatController(
|
class ChatController(
|
||||||
private val llmServiceImpl: LLMServiceImpl
|
private val llmServiceImpl: LLMServiceImpl
|
||||||
) {
|
) {
|
||||||
|
/**
|
||||||
|
* 聊天功能入口
|
||||||
|
*
|
||||||
|
* 该函数负责调用语言模型服务实现类中的聊天方法,为用户提供与AI聊天的功能
|
||||||
|
* 它接收一个ChatRequest对象作为参数,该对象包含了聊天所需的参数和用户信息
|
||||||
|
* 函数返回一个Flow流,流中包含了部分聊天响应,这种设计允许用户逐步接收聊天结果,提高用户体验
|
||||||
|
*
|
||||||
|
* @param request 聊天请求对象,包含了发起聊天所需的各种参数和用户信息
|
||||||
|
* @return 返回一个Flow流,流中依次提供了聊天响应的部分数据,允许异步处理和逐步消费响应内容
|
||||||
|
*/
|
||||||
|
@PostMapping("/stream")
|
||||||
|
fun chat(request: ChatRequest): Flow<ChatResponsePart> {
|
||||||
|
return llmServiceImpl.chat(request)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package org.jcnc.llmhub.core.service.manager
|
package org.jcnc.llmhub.core.service.manager
|
||||||
|
|
||||||
import org.jcnc.llmhub.core.service.config.ModelProperties
|
import org.jcnc.llmhub.core.service.config.ModelProperties
|
||||||
import org.springframework.cloud.context.config.annotation.RefreshScope
|
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,7 +13,6 @@ import org.springframework.stereotype.Component
|
|||||||
* @author gewuyou
|
* @author gewuyou
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@RefreshScope // 支持Nacos热刷新,使得配置更新时无需重启应用即可生效
|
|
||||||
class ModelRouteManager(
|
class ModelRouteManager(
|
||||||
private val modelProperties: ModelProperties
|
private val modelProperties: ModelProperties
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -3,11 +3,20 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
|
access-key: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTc0NTc1NjkwOH0.86TmeN27gXbpw55jMmOVNz42B9u8dXtGwCvyGlWbYVo
|
||||||
|
username: nacos
|
||||||
|
password: L4s6f9y3
|
||||||
|
server-addr: 49.235.96.75:8848
|
||||||
discovery:
|
discovery:
|
||||||
server-addr: 49.235.96.75:8848 # Nacos 服务地址
|
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:
|
llmhub:
|
||||||
model-route:
|
model-route:
|
||||||
modelServiceMap:
|
modelServiceMap:
|
||||||
openai: llmhub-impl-openai
|
qwen-turbo: llmhub-impl-baiLian
|
||||||
anotherModel: llmhub-impl-another
|
qwen-max: llmhub-impl-baiLian
|
||||||
|
qwen-plus: llmhub-impl-baiLian
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,25 @@
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: llmhub-core-service
|
name: llmhub-core-service
|
||||||
profiles:
|
cloud:
|
||||||
active: dev
|
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
|
||||||
@ -4,7 +4,6 @@ apply {
|
|||||||
plugin(libs.plugins.kotlin.plugin.spring.get().pluginId)
|
plugin(libs.plugins.kotlin.plugin.spring.get().pluginId)
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
val libs = rootProject.libs
|
|
||||||
compileOnly(libs.kotlinx.coruntes.reactor)
|
compileOnly(libs.kotlinx.coruntes.reactor)
|
||||||
compileOnly(libs.springBootStarter.web)
|
compileOnly(libs.springBootStarter.web)
|
||||||
}
|
}
|
||||||
@ -13,5 +13,5 @@ package org.jcnc.llmhub.core.spi.entities.request
|
|||||||
data class ChatRequest(
|
data class ChatRequest(
|
||||||
val prompt: String,
|
val prompt: String,
|
||||||
val model: String,
|
val model: String,
|
||||||
val options: Map<String, Any>? = null
|
val options: Map<String, String> = mapOf()
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,7 +1,19 @@
|
|||||||
|
|
||||||
// 开启springboot
|
// 开启springboot
|
||||||
extra[ProjectFlags.USE_SPRING_BOOT] = true
|
extra[ProjectFlags.USE_SPRING_BOOT] = true
|
||||||
|
setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true)
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// Nacos 服务发现和配置
|
||||||
|
implementation(libs.springCloudStarter.alibaba.nacos.discovery)
|
||||||
|
|
||||||
implementation(project(Modules.Core.SPI))
|
implementation(project(Modules.Core.SPI))
|
||||||
|
|
||||||
|
implementation(libs.kotlinx.coruntes.reactor)
|
||||||
|
|
||||||
|
implementation(libs.aliyun.bailian)
|
||||||
|
|
||||||
|
implementation(libs.okHttp)
|
||||||
|
|
||||||
|
implementation(libs.forgeBoot.core.extension)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,8 +2,10 @@ package org.jcnc.llmhub.impl.baiLian
|
|||||||
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableDiscoveryClient
|
||||||
class LlmhubImplBaiLianApplication
|
class LlmhubImplBaiLianApplication
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
runApplication<LlmhubImplBaiLianApplication>(*args)
|
runApplication<LlmhubImplBaiLianApplication>(*args)
|
||||||
|
|||||||
@ -0,0 +1,107 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.adapter
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import okhttp3.Headers.Companion.toHeaders
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import org.jcnc.llmhub.core.spi.entities.response.ChatResponsePart
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 百炼适配器
|
||||||
|
*
|
||||||
|
* 该类负责与百炼API进行交互,提供流式聊天功能。
|
||||||
|
* 它使用OkHttpClient发送请求,并通过Jackson库处理JSON数据。
|
||||||
|
*
|
||||||
|
* @param okHttpClient 用于发送HTTP请求的客户端
|
||||||
|
* @param objectMapper 用于序列化和反序列化JSON数据的映射器
|
||||||
|
* @since 2025-04-27 10:31:47
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
class DashScopeAdapter(
|
||||||
|
private val okHttpClient: OkHttpClient,
|
||||||
|
private val objectMapper: ObjectMapper
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* 发送流式聊天请求
|
||||||
|
*
|
||||||
|
* 本函数构建并发送一个聊天请求,然后以流的形式接收和处理响应。
|
||||||
|
* 它主要用于与DashScope API进行交互,提取并发布聊天响应的部分内容。
|
||||||
|
*
|
||||||
|
* @param url 请求的URL
|
||||||
|
* @param headers 请求的头部信息
|
||||||
|
* @param requestBody 请求的主体内容
|
||||||
|
* @param extractContent 一个函数,用于从JSON响应中提取内容
|
||||||
|
* @param dispatcher 协程调度器,默认为IO调度器
|
||||||
|
* @return 返回一个Flow,发布聊天响应的部分内容
|
||||||
|
*/
|
||||||
|
fun sendStreamChat(
|
||||||
|
url: String,
|
||||||
|
headers: Map<String, String>,
|
||||||
|
requestBody: Any,
|
||||||
|
extractContent: (String) -> String,
|
||||||
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
): Flow<ChatResponsePart> = flow {
|
||||||
|
// 将请求体序列化为JSON格式
|
||||||
|
val requestJson = objectMapper.writeValueAsString(requestBody)
|
||||||
|
// 构建HTTP请求
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.headers(headers.toHeaders())
|
||||||
|
.post(requestJson.toRequestBody("application/json".toMediaType()))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// 发送HTTP请求
|
||||||
|
val call = okHttpClient.newCall(request)
|
||||||
|
|
||||||
|
// 使用指定的调度器执行请求
|
||||||
|
val response = withContext(dispatcher) {
|
||||||
|
call.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查HTTP响应是否成功
|
||||||
|
if (!response.isSuccessful) {
|
||||||
|
throw RuntimeException("DashScope request failed: ${response.code}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取响应体
|
||||||
|
val responseBody = response.body ?: throw RuntimeException("Empty response body from DashScope")
|
||||||
|
// 获取响应体的源
|
||||||
|
val source = responseBody.source().buffer
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 发布聊天响应的部分内容
|
||||||
|
emit(ChatResponsePart(content = content, done = false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布结束信号
|
||||||
|
emit(ChatResponsePart(content = "[END]", done = true))
|
||||||
|
} finally {
|
||||||
|
// 关闭资源
|
||||||
|
source.close()
|
||||||
|
response.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.config
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*客户端配置
|
||||||
|
*
|
||||||
|
* @since 2025-03-28 17:03:48
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
class ClientConfig {
|
||||||
|
/**
|
||||||
|
* OkHttpClient
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
fun createOkHttpClient(): OkHttpClient {
|
||||||
|
return OkHttpClient.Builder()
|
||||||
|
.connectTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(30, TimeUnit.SECONDS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.config
|
||||||
|
|
||||||
|
import org.jcnc.llmhub.impl.baiLian.config.entities.DashScopeProperties
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
|
/**
|
||||||
|
*仪表范围配置
|
||||||
|
*
|
||||||
|
* @since 2025-04-27 10:53:55
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(DashScopeProperties::class)
|
||||||
|
class DashScopeConfig
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.config.entities
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仪表范围属性
|
||||||
|
*
|
||||||
|
* @author gewuyou
|
||||||
|
* @since 2025-03-08 15:39:34
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "aliyun.dash.scope")
|
||||||
|
class DashScopeProperties {
|
||||||
|
/**
|
||||||
|
* 访问密钥ID,用于身份认证。
|
||||||
|
*/
|
||||||
|
var accessKeyId: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问密钥,与访问密钥ID配合使用完成身份认证。
|
||||||
|
*/
|
||||||
|
var accessKeySecret: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务访问端点,例如 https://dash.aliyun.com。
|
||||||
|
*/
|
||||||
|
var endpoint: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作空间ID,标识具体的业务工作空间。
|
||||||
|
*/
|
||||||
|
var workspaceId: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API密钥,用于调用API的身份验证。
|
||||||
|
*/
|
||||||
|
var apiKey: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用ID,标识具体的应用程序。
|
||||||
|
*/
|
||||||
|
var appId: String = ""
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用调用基路径,例如 /api/v1。
|
||||||
|
*/
|
||||||
|
var baseUrl: String = ""
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.controller
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import org.jcnc.llmhub.core.spi.entities.request.ChatRequest
|
||||||
|
import org.jcnc.llmhub.core.spi.entities.request.EmbeddingRequest
|
||||||
|
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.RestController
|
||||||
|
|
||||||
|
/**
|
||||||
|
*百炼提供商
|
||||||
|
*
|
||||||
|
* @since 2025-04-27 10:12:12
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
class BaiLianProvider(
|
||||||
|
private val baiLianModelService: BaiLianModelService
|
||||||
|
): LLMProvider {
|
||||||
|
/**
|
||||||
|
* 初始化与聊天服务的连接,以处理聊天请求
|
||||||
|
*
|
||||||
|
* 此函数接收一个聊天请求对象,并返回一个Flow流,用于接收聊天响应的部分数据
|
||||||
|
* 它主要用于建立聊天通信的通道,而不是发送具体的消息
|
||||||
|
*
|
||||||
|
* @param request 聊天请求对象,包含建立聊天所需的信息,如用户标识、会话标识等
|
||||||
|
* @return 返回一个Flow流,通过该流可以接收到聊天响应的部分数据,如消息、状态更新等
|
||||||
|
*/
|
||||||
|
override fun chat(request: ChatRequest): Flow<ChatResponsePart> {
|
||||||
|
return baiLianModelService.streamChat(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 嵌入功能方法
|
||||||
|
* 该方法允许用户发送嵌入请求,以获取LLM生成的嵌入向量
|
||||||
|
*
|
||||||
|
* @param request 嵌入请求对象,包含需要进行嵌入处理的数据
|
||||||
|
* @return EmbeddingResponse 嵌入响应对象,包含生成的嵌入向量信息
|
||||||
|
*/
|
||||||
|
override fun embedding(request: EmbeddingRequest): EmbeddingResponse {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.service
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import org.jcnc.llmhub.core.spi.entities.request.ChatRequest
|
||||||
|
import org.jcnc.llmhub.core.spi.entities.response.ChatResponsePart
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 百炼模型服务接口
|
||||||
|
* 用于定义与百炼AI模型交互的方法
|
||||||
|
*
|
||||||
|
* @since 2025-04-27 10:45:42
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
interface BaiLianModelService {
|
||||||
|
/**
|
||||||
|
* 使用流式聊天交互
|
||||||
|
*
|
||||||
|
* @param request 聊天请求对象,包含用户输入、上下文等信息
|
||||||
|
* @return 返回一个Flow流,包含部分聊天响应,允许逐步处理和消费响应
|
||||||
|
*/
|
||||||
|
fun streamChat(request: ChatRequest): Flow<ChatResponsePart>
|
||||||
|
}
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
package org.jcnc.llmhub.impl.baiLian.service.impl
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
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.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
|
||||||
|
/**
|
||||||
|
* 百炼模型服务接口实现
|
||||||
|
*
|
||||||
|
* @since 2025-04-27 10:47:07
|
||||||
|
* @author gewuyou
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
class BaiLianModelServiceImpl(
|
||||||
|
private val dashScopeAdapter: DashScopeAdapter,
|
||||||
|
private val dashScopeProperties: DashScopeProperties,
|
||||||
|
private val objectMapper: ObjectMapper
|
||||||
|
) : BaiLianModelService {
|
||||||
|
/**
|
||||||
|
* 使用流式聊天交互
|
||||||
|
*
|
||||||
|
* @param request 聊天请求对象,包含用户输入、上下文等信息
|
||||||
|
* @return 返回一个Flow流,包含部分聊天响应,允许逐步处理和消费响应
|
||||||
|
*/
|
||||||
|
override fun streamChat(request: ChatRequest): Flow<ChatResponsePart> {
|
||||||
|
// 构造请求URL
|
||||||
|
val url = "${dashScopeProperties.baseUrl}${dashScopeProperties.appId}/completion"
|
||||||
|
// 构造请求头,包括授权信息和内容类型
|
||||||
|
val headers = mapOf(
|
||||||
|
"Authorization" to "Bearer ${dashScopeProperties.apiKey}",
|
||||||
|
"Content-Type" to "application/json",
|
||||||
|
"X-DashScope-SSE" to "enable"
|
||||||
|
)
|
||||||
|
// 构造输入参数,主要包括用户的prompt
|
||||||
|
val inputMap = mutableMapOf("prompt" to request.prompt)
|
||||||
|
// 获取会话ID,如果存在,则添加到输入参数中
|
||||||
|
val sessionId = (request.options["session_id"] ?: "").toString()
|
||||||
|
if (StringUtils.hasText(sessionId)) {
|
||||||
|
inputMap["session_id"] = sessionId
|
||||||
|
}
|
||||||
|
// 构造参数地图,包括是否包含思考、模型ID、增量输出等
|
||||||
|
val parametersMap = mutableMapOf(
|
||||||
|
"has_thoughts" to (request.options["has_thoughts"] ?: false),
|
||||||
|
"model_id" to request.model,
|
||||||
|
"incremental_output" to true,
|
||||||
|
"rag_options" to emptyMap<String, Any>()
|
||||||
|
)
|
||||||
|
// 构造RAG选项参数,包括管道ID和文件ID
|
||||||
|
val ragOptionsMap = mutableMapOf<String, Any>()
|
||||||
|
// 解析并添加管道ID
|
||||||
|
val pipelineIdsJson = request.options["pipelineIds"]
|
||||||
|
pipelineIdsJson?.let {
|
||||||
|
val pipelineIds = objectMapper.readValue(it, object : TypeReference<List<String>>() {})
|
||||||
|
if (!CollectionUtils.isEmpty(pipelineIds)) {
|
||||||
|
ragOptionsMap["pipeline_ids"] = pipelineIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 解析并添加文件ID
|
||||||
|
val fileIdsJson = request.options["fileIds"]
|
||||||
|
fileIdsJson?.let {
|
||||||
|
val fileIds = objectMapper.readValue(it, object : TypeReference<List<String>>() {})
|
||||||
|
if (!CollectionUtils.isEmpty(fileIds)) {
|
||||||
|
ragOptionsMap["session_file_ids"] = fileIds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将RAG选项参数添加到参数地图中
|
||||||
|
parametersMap["rag_options"] = ragOptionsMap
|
||||||
|
// 构造请求体,包括输入参数、构造的参数地图和调试信息
|
||||||
|
val body = mapOf(
|
||||||
|
"input" to inputMap,
|
||||||
|
"parameters" to parametersMap,
|
||||||
|
"debug" to emptyMap()
|
||||||
|
)
|
||||||
|
// 发送流式聊天请求,并处理响应
|
||||||
|
return dashScopeAdapter.sendStreamChat(
|
||||||
|
url, headers, body,
|
||||||
|
{ json: String ->
|
||||||
|
// 解析响应JSON,提取输出文本
|
||||||
|
val node = objectMapper.readTree(json)
|
||||||
|
node["output"]?.get("text")?.asText() ?: ""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
server:
|
||||||
|
port: 8082
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: llmhub-impl-baiLian
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: 49.235.96.75:8848 # Nacos 服务地址
|
||||||
|
# 阿里云配置
|
||||||
|
aliyun:
|
||||||
|
# DashScope服务配置
|
||||||
|
dash:
|
||||||
|
# 访问凭证配置
|
||||||
|
scope:
|
||||||
|
access-key-id: LTAI5tHiA2Ry3XTAfoSEJW6z # 阿里云访问密钥ID
|
||||||
|
access-key-secret: K5sf4FxZZuUgLEFnyfepBfMqFGmDcD # 阿里云访问密钥密钥
|
||||||
|
endpoint: bailian.cn-beijing.aliyuncs.com # 阿里云服务端点
|
||||||
|
workspace-id: llm-axfkuqft05uzbjpi # 工作区ID
|
||||||
|
api-key: sk-78af4dd964a94f4cb373851064dbdc12 # API密钥
|
||||||
|
app-id: 3fae0bbab2e54a90a37aa02cd12dd62c # 应用ID
|
||||||
|
base-url: https://dashscope.aliyuncs.com/api/v1/apps/ # 基础API URL
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
server:
|
||||||
|
port: 9003
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: llmhub-impl-baiLian
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: 49.235.96.75:9001 # Nacos 服务地址
|
||||||
|
# 阿里云配置
|
||||||
|
aliyun:
|
||||||
|
# DashScope服务配置
|
||||||
|
dash:
|
||||||
|
# 访问凭证配置
|
||||||
|
scope:
|
||||||
|
access-key-id: LTAI5tHiA2Ry3XTAfoSEJW6z # 阿里云访问密钥ID
|
||||||
|
access-key-secret: K5sf4FxZZuUgLEFnyfepBfMqFGmDcD # 阿里云访问密钥密钥
|
||||||
|
endpoint: bailian.cn-beijing.aliyuncs.com # 阿里云服务端点
|
||||||
|
workspace-id: llm-axfkuqft05uzbjpi # 工作区ID
|
||||||
|
api-key: sk-78af4dd964a94f4cb373851064dbdc12 # API密钥
|
||||||
|
app-id: 3fae0bbab2e54a90a37aa02cd12dd62c # 应用ID
|
||||||
|
base-url: https://dashscope.aliyuncs.com/api/v1/apps/ # 基础API URL
|
||||||
@ -1,3 +1,18 @@
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: llmhub-impl-baiLian
|
name: llmhub-impl-baiLian
|
||||||
|
profiles:
|
||||||
|
active: dev
|
||||||
|
# 阿里云配置
|
||||||
|
aliyun:
|
||||||
|
# DashScope服务配置
|
||||||
|
dash:
|
||||||
|
# 访问凭证配置
|
||||||
|
scope:
|
||||||
|
access-key-id: LTAI5tHiA2Ry3XTAfoSEJW6z # 阿里云访问密钥ID
|
||||||
|
access-key-secret: K5sf4FxZZuUgLEFnyfepBfMqFGmDcD # 阿里云访问密钥密钥
|
||||||
|
endpoint: bailian.cn-beijing.aliyuncs.com # 阿里云服务端点
|
||||||
|
workspace-id: llm-axfkuqft05uzbjpi # 工作区ID
|
||||||
|
api-key: sk-78af4dd964a94f4cb373851064dbdc12 # API密钥
|
||||||
|
app-id: 3fae0bbab2e54a90a37aa02cd12dd62c # 应用ID
|
||||||
|
base-url: https://dashscope.aliyuncs.com/api/v1/apps/ # 基础API URL
|
||||||
99
llmhub-base/scripts/entrypoint.sh
Normal file
99
llmhub-base/scripts/entrypoint.sh
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#set -x # 调试模式,可以启用以打印每行脚本的执行情况
|
||||||
|
|
||||||
|
# 设置默认的等待时间间隔,默认值为2秒
|
||||||
|
: "${SLEEP_SECOND:=2}"
|
||||||
|
# 设置默认的超时时间,默认值为60秒
|
||||||
|
: "${TIMEOUT:=60}"
|
||||||
|
|
||||||
|
# 带时间戳+emoji的小型日志函数
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 等待服务函数
|
||||||
|
wait_for() {
|
||||||
|
local host="$1"
|
||||||
|
local port="$2"
|
||||||
|
local timeout="$TIMEOUT"
|
||||||
|
local start_time
|
||||||
|
local current_time
|
||||||
|
local elapsed_time
|
||||||
|
local attempt=0
|
||||||
|
|
||||||
|
start_time=$(date +%s)
|
||||||
|
log "🔄 开始等待依赖服务 $host:$port 可用 (超时时间: ${timeout}s, 间隔: ${SLEEP_SECOND}s)"
|
||||||
|
|
||||||
|
while ! nc -z "$host" "$port" 2>/dev/null; do
|
||||||
|
current_time=$(date +%s)
|
||||||
|
elapsed_time=$((current_time - start_time))
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
|
||||||
|
if [ "$elapsed_time" -ge "$timeout" ]; then
|
||||||
|
log "🛑 [ERROR] 等待超时!依赖服务 $host:$port 在 ${timeout}s 内未启动"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "🔄 第 ${attempt} 次检测:$host:$port 未就绪,已等待 ${elapsed_time}s..."
|
||||||
|
sleep "$SLEEP_SECOND"
|
||||||
|
done
|
||||||
|
|
||||||
|
total_time=$(( $(date +%s) - start_time ))
|
||||||
|
log "✅ [SUCCESS] 依赖服务 $host:$port 已启动,耗时 ${total_time}s"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 声明变量
|
||||||
|
declare DEPENDS
|
||||||
|
declare CMD
|
||||||
|
|
||||||
|
# 解析参数
|
||||||
|
while getopts "d:c:" arg; do
|
||||||
|
case "$arg" in
|
||||||
|
d)
|
||||||
|
DEPENDS="$OPTARG"
|
||||||
|
;;
|
||||||
|
c)
|
||||||
|
CMD="$OPTARG"
|
||||||
|
;;
|
||||||
|
?)
|
||||||
|
log "🛑 [ERROR] 未知参数"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# 检查依赖
|
||||||
|
if [ -n "$DEPENDS" ]; then
|
||||||
|
log "📦 检测到依赖服务列表: $DEPENDS"
|
||||||
|
for var in ${DEPENDS//,/ }; do
|
||||||
|
host=${var%:*}
|
||||||
|
port=${var#*:}
|
||||||
|
if [ -z "$host" ] || [ -z "$port" ]; then
|
||||||
|
log "🛑 [ERROR] 依赖项格式错误: $var,应为 host:port"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! wait_for "$host" "$port"; then
|
||||||
|
log "❌ [ERROR] 依赖服务 $host:$port 启动失败,终止执行"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
log "⚡️ 未配置依赖服务,跳过依赖检测"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 执行命令
|
||||||
|
if [ -n "$CMD" ]; then
|
||||||
|
log "🚀 准备执行命令: $CMD"
|
||||||
|
eval "$CMD"
|
||||||
|
cmd_exit_code=$?
|
||||||
|
if [ $cmd_exit_code -eq 0 ]; then
|
||||||
|
log "✅ [SUCCESS] 命令执行完成,退出码: $cmd_exit_code"
|
||||||
|
else
|
||||||
|
log "❌ [ERROR] 命令执行失败,退出码: $cmd_exit_code"
|
||||||
|
exit $cmd_exit_code
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "⚠️ [WARNING] 未指定要执行的命令,脚本结束"
|
||||||
|
fi
|
||||||
Loading…
x
Reference in New Issue
Block a user