dev : 合并提交 #41
| @ -19,7 +19,7 @@ configurations.implementation { | ||||
| allprojects { | ||||
|     // 设置全局属性 | ||||
|     ext { | ||||
|         set(ProjectFlags.USE_SPRING_BOOT, false) | ||||
|         set(ProjectFlags.USE_SPRING_BOOT_WEB, false) | ||||
|         set(ProjectFlags.USE_LLM_CORE_SPI, false) | ||||
|         set(ProjectFlags.USE_SPRING_CLOUD_BOM, false) | ||||
|         set(ProjectFlags.IS_ROOT_MODULE, false) | ||||
| @ -67,7 +67,8 @@ allprojects { | ||||
| subprojects { | ||||
|     afterEvaluate { | ||||
|         // springbootWeb | ||||
|         if (project.getPropertyByBoolean(ProjectFlags.USE_SPRING_BOOT)) { | ||||
|         // springbootWeb | ||||
|         if (project.getPropertyByBoolean(ProjectFlags.USE_SPRING_BOOT_WEB)) { | ||||
|             apply { | ||||
|                 plugin(libs.plugins.spring.dependency.management.get().pluginId) | ||||
|                 plugin(libs.plugins.spring.boot.get().pluginId) | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| 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_LLM_CORE_SPI = "useLLMCoreSPI" | ||||
|     const val IS_ROOT_MODULE = "isRootModule" | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| extra { | ||||
|     // 开启springboot | ||||
|     setProperty(ProjectFlags.USE_SPRING_BOOT, true) | ||||
|     setProperty(ProjectFlags.USE_SPRING_BOOT_WEB, true) | ||||
|     setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true) | ||||
| } | ||||
| dependencies { | ||||
|  | ||||
| @ -5,6 +5,7 @@ import kotlinx.coroutines.flow.Flow | ||||
| 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.response.ChatResponsePart | ||||
| import org.springframework.http.MediaType | ||||
| 
 | ||||
| import org.springframework.web.bind.annotation.PostMapping | ||||
| import org.springframework.web.bind.annotation.RequestBody | ||||
| @ -33,7 +34,7 @@ class ChatController( | ||||
|      * @param request 聊天请求对象,包含了发起聊天所需的各种参数和用户信息 | ||||
|      * @return 返回一个Flow流,流中依次提供了聊天响应的部分数据,允许异步处理和逐步消费响应内容 | ||||
|      */ | ||||
|     @PostMapping("/stream") | ||||
|     @PostMapping("/stream", produces = [MediaType.APPLICATION_NDJSON_VALUE]) | ||||
|     fun chat(@RequestBody request: ChatRequest): Flow<ChatResponsePart> { | ||||
|         return llmServiceImpl.chat(request) | ||||
|     } | ||||
|  | ||||
| @ -4,7 +4,7 @@ spring: | ||||
|       username: nacos | ||||
|       password: L4s6f9y3 | ||||
|       server-addr: 49.235.96.75:8848 | ||||
|       ip: 192.168.1.6 | ||||
|       ip: 192.168.1.100 | ||||
|       discovery: | ||||
|         server-addr: ${spring.cloud.nacos.server-addr} | ||||
|         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.response.ChatResponsePart | ||||
| 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.RequestMapping | ||||
| @ -29,7 +30,7 @@ interface LLMProvider { | ||||
|      * @param request 聊天请求对象,包含建立聊天所需的信息,如用户标识、会话标识等 | ||||
|      * @return 返回一个Flow流,通过该流可以接收到聊天响应的部分数据,如消息、状态更新等 | ||||
|      */ | ||||
|     @PostMapping("/chat") | ||||
|     @PostMapping("/chat", produces = [MediaType.APPLICATION_NDJSON_VALUE]) | ||||
|     fun chat(request: ChatRequest): Flow<ChatResponsePart> | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| 
 | ||||
| // 开启springboot | ||||
| extra[ProjectFlags.USE_SPRING_BOOT] = true | ||||
| setProperty(ProjectFlags.USE_SPRING_BOOT_WEB, true) | ||||
| setProperty(ProjectFlags.USE_SPRING_CLOUD_BOM,true) | ||||
| dependencies { | ||||
|     // Nacos 服务发现和配置 | ||||
|  | ||||
| @ -4,6 +4,7 @@ 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.FlowCollector | ||||
| import kotlinx.coroutines.flow.flow | ||||
| import okhttp3.Headers.Companion.toHeaders | ||||
| import okhttp3.MediaType.Companion.toMediaType | ||||
| @ -54,52 +55,81 @@ class DashScopeAdapter( | ||||
|         val requestJson = objectMapper.writeValueAsString(requestBody) | ||||
|         log.info("📤 请求参数: {}", requestJson) | ||||
| 
 | ||||
|         val request = Request.Builder() | ||||
|             .url(url) | ||||
|             .headers(headers.toHeaders()) | ||||
|             .post(requestJson.toRequestBody("application/json".toMediaType())) | ||||
|             .build() | ||||
|         val request = buildRequest(url, headers, requestJson) | ||||
| 
 | ||||
|         val call = okHttpClient.newCall(request) | ||||
|         val response = call.execute() | ||||
|         okHttpClient.newCall(request).execute().use { response -> | ||||
|             if (!response.isSuccessful) { | ||||
|                 throw RuntimeException("❌ DashScope 请求失败: HTTP ${response.code}") | ||||
|             } | ||||
| 
 | ||||
|         if (!response.isSuccessful) { | ||||
|             throw RuntimeException("❌ DashScope 请求失败: HTTP ${response.code}") | ||||
|         } | ||||
|             val responseBody = response.body ?: throw RuntimeException("❌ DashScope 响应体为空") | ||||
| 
 | ||||
|         val responseBody = response.body ?: throw RuntimeException("❌ DashScope 响应体为空") | ||||
| 
 | ||||
|         val bufferedReader: BufferedReader = responseBody.charStream().buffered() | ||||
|         val allContent = StringBuilder() | ||||
| 
 | ||||
|         try { | ||||
|             while (currentCoroutineContext().isActive) { | ||||
|                 val line = withContext(dispatcher) { | ||||
|                     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) | ||||
|                     } | ||||
|             responseBody.charStream().buffered().use { reader -> | ||||
|                 val allContent = StringBuilder() | ||||
|                 try { | ||||
|                     processResponse(dispatcher, reader, extractContent, allContent) | ||||
|                     log.info("📦 完整内容: {}", allContent) | ||||
|                 } catch (e: Exception) { | ||||
|                     log.error("🚨 读取 DashScope 响应流失败: {}", e.message, e) | ||||
|                     throw 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 | ||||
|       password: L4s6f9y3 | ||||
|       server-addr: 49.235.96.75:8848 | ||||
|       ip: 192.168.1.6 | ||||
|       ip: 192.168.1.100 | ||||
|       discovery: | ||||
|         server-addr: ${spring.cloud.nacos.server-addr} | ||||
|         username: ${spring.cloud.nacos.username} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user