feat(chat): support streaming reply in agent turn

This commit is contained in:
2026-03-31 14:44:58 +08:00
parent b4c44c7d98
commit 81aa4b7933
5 changed files with 139 additions and 53 deletions

View File

@@ -7,6 +7,7 @@ import work.slhaf.partner.api.agent.runtime.config.AgentConfigLoader
import work.slhaf.partner.api.agent.runtime.interaction.flow.RunningFlowContext
import work.slhaf.partner.api.chat.pojo.Message
import work.slhaf.partner.api.chat.runtime.OpenAiChatRuntime
import work.slhaf.partner.api.chat.runtime.StreamChatMessageConsumer
/**
* 模块基类
@@ -52,11 +53,15 @@ interface ActivateModel {
}
fun chat(messages: List<Message>): String {
return runtime.chat(mergeMessages(messages), useStreaming())
return runtime.chat(mergeMessages(messages))
}
fun streamChat(messages: List<Message>, handler: StreamChatMessageConsumer) {
return runtime.streamChat(mergeMessages(messages), handler)
}
fun <T : Any> formattedChat(messages: List<Message>, responseType: Class<T>): T {
return runtime.formattedChat(mergeMessages(messages), useStreaming(), responseType)
return runtime.formattedChat(mergeMessages(messages), responseType)
}
fun mergeMessages(messages: List<Message>): List<Message> {
@@ -81,6 +86,4 @@ interface ActivateModel {
}
fun modulePrompt(): List<Message> = emptyList()
fun useStreaming(): Boolean = false
}

View File

@@ -3,7 +3,6 @@ package work.slhaf.partner.api.chat.runtime;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.helpers.ChatCompletionAccumulator;
import com.openai.models.chat.completions.*;
import work.slhaf.partner.api.chat.pojo.Message;
@@ -24,32 +23,27 @@ public class OpenAiChatRuntime {
this.model = model;
}
public String chat(List<Message> messages, boolean streaming) {
public String chat(List<Message> messages) {
ChatCompletionCreateParams params = buildParams(messages);
if (!streaming) {
return extractText(client.chat().completions().create(params));
}
ChatCompletionAccumulator accumulator = ChatCompletionAccumulator.create();
try (StreamResponse<ChatCompletionChunk> response = client.chat().completions().createStreaming(params)) {
response.stream().forEach(accumulator::accumulate);
}
return extractText(accumulator.chatCompletion());
return extractText(client.chat().completions().create(params));
}
public <T> T formattedChat(List<Message> messages, boolean streaming, Class<T> responseType) {
public void streamChat(List<Message> messages, StreamChatMessageConsumer handler) {
ChatCompletionCreateParams params = buildParams(messages);
try (StreamResponse<ChatCompletionChunk> streamResponse = client.chat().completions().createStreaming(params)) {
streamResponse.stream()
.flatMap(completion -> completion.choices().stream())
.flatMap(choice -> choice.delta().content().stream())
.filter(delta -> !delta.isEmpty())
.forEach(handler::onDelta);
}
}
public <T> T formattedChat(List<Message> messages, Class<T> responseType) {
StructuredChatCompletionCreateParams<T> params = buildParams(messages).toBuilder()
.responseFormat(responseType)
.build();
if (!streaming) {
return extractStructured(client.chat().completions().create(params));
}
ChatCompletionAccumulator accumulator = ChatCompletionAccumulator.create();
try (StreamResponse<ChatCompletionChunk> response = client.chat().completions().createStreaming(params.rawParams())) {
response.stream().forEach(accumulator::accumulate);
}
return extractStructured(accumulator.chatCompletion(responseType));
return extractStructured(client.chat().completions().create(params));
}
private ChatCompletionCreateParams buildParams(List<Message> messages) {

View File

@@ -0,0 +1,16 @@
package work.slhaf.partner.api.chat.runtime;
public abstract class StreamChatMessageConsumer {
private final StringBuilder responseText = new StringBuilder();
public void onDelta(String delta) {
consumeDelta(delta);
responseText.append(delta);
}
public String collectResponse() {
return responseText.toString();
}
protected abstract void consumeDelta(String delta);
}