dev : 合并提交 #41
| @ -19,7 +19,7 @@ configurations.implementation { | |||||||
| allprojects { | allprojects { | ||||||
|     // 设置全局属性 |     // 设置全局属性 | ||||||
|     ext { |     ext { | ||||||
|         set(ProjectFlags.USE_SPRING_BOOT, false) |         set(ProjectFlags.USE_SPRING_BOOT_WEB, false) | ||||||
|         set(ProjectFlags.USE_LLM_CORE_SPI, false) |         set(ProjectFlags.USE_LLM_CORE_SPI, false) | ||||||
|         set(ProjectFlags.USE_SPRING_CLOUD_BOM, false) |         set(ProjectFlags.USE_SPRING_CLOUD_BOM, false) | ||||||
|         set(ProjectFlags.IS_ROOT_MODULE, false) |         set(ProjectFlags.IS_ROOT_MODULE, false) | ||||||
| @ -67,7 +67,8 @@ allprojects { | |||||||
| subprojects { | subprojects { | ||||||
|     afterEvaluate { |     afterEvaluate { | ||||||
|         // springbootWeb |         // springbootWeb | ||||||
|         if (project.getPropertyByBoolean(ProjectFlags.USE_SPRING_BOOT)) { |         // springbootWeb | ||||||
|  |         if (project.getPropertyByBoolean(ProjectFlags.USE_SPRING_BOOT_WEB)) { | ||||||
|             apply { |             apply { | ||||||
|                 plugin(libs.plugins.spring.dependency.management.get().pluginId) |                 plugin(libs.plugins.spring.dependency.management.get().pluginId) | ||||||
|                 plugin(libs.plugins.spring.boot.get().pluginId) |                 plugin(libs.plugins.spring.boot.get().pluginId) | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| object ProjectFlags { | object ProjectFlags { | ||||||
|     const val USE_SPRING_BOOT = "useSpringBoot" |     const val USE_SPRING_BOOT_WEB = "useSpringBootWeb" | ||||||
|     const val USE_SPRING_CLOUD_BOM = "useSpringCloudBom" |     const val USE_SPRING_CLOUD_BOM = "useSpringCloudBom" | ||||||
|     const val USE_LLM_CORE_SPI = "useLLMCoreSPI" |     const val USE_LLM_CORE_SPI = "useLLMCoreSPI" | ||||||
|     const val IS_ROOT_MODULE = "isRootModule" |     const val IS_ROOT_MODULE = "isRootModule" | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| extra { | extra { | ||||||
|     // 开启springboot |     // 开启springboot | ||||||
|     setProperty(ProjectFlags.USE_SPRING_BOOT, true) |     setProperty(ProjectFlags.USE_SPRING_BOOT_WEB, true) | ||||||
|     setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true) |     setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true) | ||||||
| } | } | ||||||
| dependencies { | dependencies { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import kotlinx.coroutines.flow.Flow | |||||||
| import org.jcnc.llmx.core.service.service.impl.LLMServiceImpl | import org.jcnc.llmx.core.service.service.impl.LLMServiceImpl | ||||||
| import org.jcnc.llmx.core.spi.entities.request.ChatRequest | import org.jcnc.llmx.core.spi.entities.request.ChatRequest | ||||||
| import org.jcnc.llmx.core.spi.entities.response.ChatResponsePart | import org.jcnc.llmx.core.spi.entities.response.ChatResponsePart | ||||||
|  | import org.springframework.http.MediaType | ||||||
| 
 | 
 | ||||||
| import org.springframework.web.bind.annotation.PostMapping | import org.springframework.web.bind.annotation.PostMapping | ||||||
| import org.springframework.web.bind.annotation.RequestBody | import org.springframework.web.bind.annotation.RequestBody | ||||||
| @ -33,7 +34,7 @@ class ChatController( | |||||||
|      * @param request 聊天请求对象,包含了发起聊天所需的各种参数和用户信息 |      * @param request 聊天请求对象,包含了发起聊天所需的各种参数和用户信息 | ||||||
|      * @return 返回一个Flow流,流中依次提供了聊天响应的部分数据,允许异步处理和逐步消费响应内容 |      * @return 返回一个Flow流,流中依次提供了聊天响应的部分数据,允许异步处理和逐步消费响应内容 | ||||||
|      */ |      */ | ||||||
|     @PostMapping("/stream") |     @PostMapping("/stream", produces = [MediaType.APPLICATION_NDJSON_VALUE]) | ||||||
|     fun chat(@RequestBody request: ChatRequest): Flow<ChatResponsePart> { |     fun chat(@RequestBody request: ChatRequest): Flow<ChatResponsePart> { | ||||||
|         return llmServiceImpl.chat(request) |         return llmServiceImpl.chat(request) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ spring: | |||||||
|       username: nacos |       username: nacos | ||||||
|       password: L4s6f9y3 |       password: L4s6f9y3 | ||||||
|       server-addr: 49.235.96.75:8848 |       server-addr: 49.235.96.75:8848 | ||||||
|       ip: 192.168.1.6 |       ip: 192.168.1.100 | ||||||
|       discovery: |       discovery: | ||||||
|         server-addr: ${spring.cloud.nacos.server-addr} |         server-addr: ${spring.cloud.nacos.server-addr} | ||||||
|         username: ${spring.cloud.nacos.username} |         username: ${spring.cloud.nacos.username} | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import org.jcnc.llmx.core.spi.entities.request.ChatRequest | |||||||
| import org.jcnc.llmx.core.spi.entities.request.EmbeddingRequest | import org.jcnc.llmx.core.spi.entities.request.EmbeddingRequest | ||||||
| import org.jcnc.llmx.core.spi.entities.response.ChatResponsePart | import org.jcnc.llmx.core.spi.entities.response.ChatResponsePart | ||||||
| import org.jcnc.llmx.core.spi.entities.response.EmbeddingResponse | import org.jcnc.llmx.core.spi.entities.response.EmbeddingResponse | ||||||
|  | import org.springframework.http.MediaType | ||||||
| 
 | 
 | ||||||
| import org.springframework.web.bind.annotation.PostMapping | import org.springframework.web.bind.annotation.PostMapping | ||||||
| import org.springframework.web.bind.annotation.RequestMapping | import org.springframework.web.bind.annotation.RequestMapping | ||||||
| @ -29,7 +30,7 @@ interface LLMProvider { | |||||||
|      * @param request 聊天请求对象,包含建立聊天所需的信息,如用户标识、会话标识等 |      * @param request 聊天请求对象,包含建立聊天所需的信息,如用户标识、会话标识等 | ||||||
|      * @return 返回一个Flow流,通过该流可以接收到聊天响应的部分数据,如消息、状态更新等 |      * @return 返回一个Flow流,通过该流可以接收到聊天响应的部分数据,如消息、状态更新等 | ||||||
|      */ |      */ | ||||||
|     @PostMapping("/chat") |     @PostMapping("/chat", produces = [MediaType.APPLICATION_NDJSON_VALUE]) | ||||||
|     fun chat(request: ChatRequest): Flow<ChatResponsePart> |     fun chat(request: ChatRequest): Flow<ChatResponsePart> | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| 
 | 
 | ||||||
| // 开启springboot | // 开启springboot | ||||||
| extra[ProjectFlags.USE_SPRING_BOOT] = true | setProperty(ProjectFlags.USE_SPRING_BOOT_WEB, true) | ||||||
| setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true) | setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true) | ||||||
| dependencies { | dependencies { | ||||||
|     // Nacos 服务发现和配置 |     // Nacos 服务发现和配置 | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper | |||||||
| import com.gewuyou.forgeboot.core.extension.log | import com.gewuyou.forgeboot.core.extension.log | ||||||
| import kotlinx.coroutines.* | import kotlinx.coroutines.* | ||||||
| import kotlinx.coroutines.flow.Flow | import kotlinx.coroutines.flow.Flow | ||||||
|  | import kotlinx.coroutines.flow.FlowCollector | ||||||
| import kotlinx.coroutines.flow.flow | import kotlinx.coroutines.flow.flow | ||||||
| import okhttp3.Headers.Companion.toHeaders | import okhttp3.Headers.Companion.toHeaders | ||||||
| import okhttp3.MediaType.Companion.toMediaType | import okhttp3.MediaType.Companion.toMediaType | ||||||
| @ -54,52 +55,81 @@ class DashScopeAdapter( | |||||||
|         val requestJson = objectMapper.writeValueAsString(requestBody) |         val requestJson = objectMapper.writeValueAsString(requestBody) | ||||||
|         log.info("📤 请求参数: {}", requestJson) |         log.info("📤 请求参数: {}", requestJson) | ||||||
| 
 | 
 | ||||||
|         val request = Request.Builder() |         val request = buildRequest(url, headers, requestJson) | ||||||
|             .url(url) |  | ||||||
|             .headers(headers.toHeaders()) |  | ||||||
|             .post(requestJson.toRequestBody("application/json".toMediaType())) |  | ||||||
|             .build() |  | ||||||
| 
 | 
 | ||||||
|         val call = okHttpClient.newCall(request) |         okHttpClient.newCall(request).execute().use { response -> | ||||||
|         val response = call.execute() |             if (!response.isSuccessful) { | ||||||
|  |                 throw RuntimeException("❌ DashScope 请求失败: HTTP ${response.code}") | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|         if (!response.isSuccessful) { |             val responseBody = response.body ?: throw RuntimeException("❌ DashScope 响应体为空") | ||||||
|             throw RuntimeException("❌ DashScope 请求失败: HTTP ${response.code}") |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         val responseBody = response.body ?: throw RuntimeException("❌ DashScope 响应体为空") |             responseBody.charStream().buffered().use { reader -> | ||||||
| 
 |                 val allContent = StringBuilder() | ||||||
|         val bufferedReader: BufferedReader = responseBody.charStream().buffered() |                 try { | ||||||
|         val allContent = StringBuilder() |                     processResponse(dispatcher, reader, extractContent, allContent) | ||||||
| 
 |                     log.info("📦 完整内容: {}", allContent) | ||||||
|         try { |                 } catch (e: Exception) { | ||||||
|             while (currentCoroutineContext().isActive) { |                     log.error("🚨 读取 DashScope 响应流失败: {}", e.message, e) | ||||||
|                 val line = withContext(dispatcher) { |                     throw e | ||||||
|                     bufferedReader.readLine() |  | ||||||
|                 } ?: break |  | ||||||
|                 log.info("📥 接收到行: {}", line) |  | ||||||
| 
 |  | ||||||
|                 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) |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             log.info("📦 完整内容: {}", allContent) |  | ||||||
|         } catch (e: Exception) { |  | ||||||
|             log.error("🚨 读取 DashScope 响应流失败: {}", e.message, e) |  | ||||||
|             throw e |  | ||||||
|         } finally { |  | ||||||
|             withContext(dispatcher) { |  | ||||||
|                 bufferedReader.close() |  | ||||||
|             } |  | ||||||
|             response.close() |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 处理响应流 | ||||||
|  |      * | ||||||
|  |      * 本函数读取HTTP响应中的数据行,解析并提取内容。 | ||||||
|  |      * 它在循环中读取每一行,并使用提供的函数提取内容。 | ||||||
|  |      * | ||||||
|  |      * @param dispatcher 协程调度器 | ||||||
|  |      * @param reader 响应体的BufferedReader | ||||||
|  |      * @param extractContent 一个函数,用于从JSON响应中提取内容 | ||||||
|  |      * @param allContent 保存所有提取内容的StringBuilder | ||||||
|  |      */ | ||||||
|  |     private suspend fun FlowCollector<ChatResponsePart>.processResponse( | ||||||
|  |         dispatcher: CoroutineDispatcher, | ||||||
|  |         reader: BufferedReader, | ||||||
|  |         extractContent: (String) -> ChatResponsePart, | ||||||
|  |         allContent: StringBuilder | ||||||
|  |     ) { | ||||||
|  |         while (currentCoroutineContext().isActive) { | ||||||
|  |             val line = withContext(dispatcher) { | ||||||
|  |                 reader.readLine() | ||||||
|  |             } ?: break | ||||||
|  | 
 | ||||||
|  |             log.debug("📥 接收到行: {}", line) | ||||||
|  | 
 | ||||||
|  |             if (line.startsWith("data:")) { | ||||||
|  |                 val jsonPart = line.removePrefix("data:").trim() | ||||||
|  |                 try { | ||||||
|  |                     val part = extractContent(jsonPart) | ||||||
|  |                     allContent.append(part.content) | ||||||
|  |                     log.debug("✅ 提取内容: {}", part) | ||||||
|  |                     emit(part) | ||||||
|  |                 } catch (e: Exception) { | ||||||
|  |                     log.warn("⚠️ 无法解析 JSON: {}", jsonPart, e) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 构建请求 | ||||||
|  |      * | ||||||
|  |      * 本函数构建一个OkHttp请求对象,用于发送聊天请求。 | ||||||
|  |      * | ||||||
|  |      * @param url 请求的URL | ||||||
|  |      * @param headers 请求的头部信息 | ||||||
|  |      * @param json 请求的主体内容的JSON字符串 | ||||||
|  |      * @return 返回构建好的Request对象 | ||||||
|  |      */ | ||||||
|  |     private fun buildRequest(url: String, headers: Map<String, String>, json: String): Request { | ||||||
|  |         return Request.Builder() | ||||||
|  |             .url(url) | ||||||
|  |             .headers(headers.toHeaders()) | ||||||
|  |             .post(json.toRequestBody("application/json".toMediaType())) | ||||||
|  |             .build() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ spring: | |||||||
|       username: nacos |       username: nacos | ||||||
|       password: L4s6f9y3 |       password: L4s6f9y3 | ||||||
|       server-addr: 49.235.96.75:8848 |       server-addr: 49.235.96.75:8848 | ||||||
|       ip: 192.168.1.6 |       ip: 192.168.1.100 | ||||||
|       discovery: |       discovery: | ||||||
|         server-addr: ${spring.cloud.nacos.server-addr} |         server-addr: ${spring.cloud.nacos.server-addr} | ||||||
|         username: ${spring.cloud.nacos.username} |         username: ${spring.cloud.nacos.username} | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user