WIP
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package org.springframework.ai.openai.chat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.FluentChatClient;
|
||||
import org.springframework.ai.openai.OpenAiTestConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
public class OpenAiChatClientSpecIT {
|
||||
|
||||
|
||||
@Autowired
|
||||
private ChatClient chatClient;
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatClientSpecIT.class);
|
||||
|
||||
|
||||
@Test
|
||||
void simpleTest() {
|
||||
FluentChatClient fluentChatClient = new FluentChatClient(chatClient);
|
||||
|
||||
ActorsFilmsRecord actorsFilms = fluentChatClient.chat()
|
||||
.user()
|
||||
.text("Generate the filmography of 5 movies for {actor}.")
|
||||
.param("actor", "Tom Hanks")
|
||||
.and()
|
||||
.and()
|
||||
.execute();
|
||||
|
||||
|
||||
// ActorsFilmsRecord actorsFilms = new FluentChatClient().chat(
|
||||
// chatSpec -> chatSpec
|
||||
// .user(userSpec -> userSpec.text("Generate the filmography of 5 movies for {actor}.",
|
||||
// textSpec -> textSpec.param("actor", "Tom Hanks"))
|
||||
// )
|
||||
// .execute());
|
||||
|
||||
// ActorsFilmsRecord actorsFilms = new FluentChatClient().chat(chatSpec ->
|
||||
// chatSpec.user(userSpec ->
|
||||
// userSpec.text("Generate the filmography of 5 movies for {actor}.",
|
||||
// textSpec -> textSpec.param("actor", "Tom Hanks")
|
||||
// )
|
||||
// )
|
||||
// ).execute();
|
||||
|
||||
|
||||
|
||||
// ActorsFilmsRecord actorsFilms = fluentChatClient.chat()
|
||||
// .user()
|
||||
// .text("Generate the filmography of 5 movies for {actor}.")
|
||||
// .param("actor", "Tom Hanks")
|
||||
// .and()
|
||||
// .and()
|
||||
// .execute();
|
||||
//
|
||||
//
|
||||
//
|
||||
// ActorsFilmsRecord actorsFilms = new FluentChatClient().chat()
|
||||
// .user(userSpec -> userSpec
|
||||
// .text("Generate the filmography of 5 movies for {actor}.",
|
||||
// textSpec -> textSpec.param("actor", "Tom Hanks").params(Map.of()) ) })
|
||||
// .media(mediaSpec -> {
|
||||
// mediaSpec.param(myImage, MimeTypeUtils.IMAGE_PNG);
|
||||
// })
|
||||
// )
|
||||
// .execute();
|
||||
//
|
||||
// System.out.println(actorsFilms);
|
||||
|
||||
}
|
||||
|
||||
record ActorsFilmsRecord(String actor, List<String> movies) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.springframework.ai.openai.chat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.FluentChatClientLambda;
|
||||
import org.springframework.ai.openai.OpenAiTestConfiguration;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest(classes = OpenAiTestConfiguration.class)
|
||||
@EnabledIfEnvironmentVariable(named = "OPENAI_API_KEY", matches = ".+")
|
||||
public class OpenAiChatClientSpecLambdaIT {
|
||||
|
||||
|
||||
@Autowired
|
||||
private ChatClient chatClient;
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiChatClientSpecLambdaIT.class);
|
||||
|
||||
|
||||
@Test
|
||||
void simpleTest() {
|
||||
FluentChatClientLambda fluentChatClient = new FluentChatClientLambda(chatClient);
|
||||
|
||||
ActorsFilmsRecord2 actorsFilms =
|
||||
fluentChatClient.chat(chatSpec ->
|
||||
chatSpec.user(chatUserSpec ->
|
||||
chatUserSpec.text("Generate the filmograph of 5 movies for {actor}",
|
||||
chatParamSpec -> chatParamSpec.param("actor", "Tom")))
|
||||
|
||||
// .user()
|
||||
// .text("Generate the filmography of 5 movies for {actor}.")
|
||||
// .param("actor", "Tom Hanks")
|
||||
// .and()
|
||||
// .and()
|
||||
// .execute();
|
||||
|
||||
|
||||
// ActorsFilmsRecord actorsFilms = new FluentChatClient().chat(
|
||||
// chatSpec -> chatSpec
|
||||
// .user(userSpec -> userSpec.text("Generate the filmography of 5 movies for {actor}.",
|
||||
// textSpec -> textSpec.param("actor", "Tom Hanks"))
|
||||
// )
|
||||
// .execute());
|
||||
|
||||
// ActorsFilmsRecord actorsFilms = new FluentChatClient().chat(chatSpec ->
|
||||
// chatSpec.user(userSpec ->
|
||||
// userSpec.text("Generate the filmography of 5 movies for {actor}.",
|
||||
// textSpec -> textSpec.param("actor", "Tom Hanks")
|
||||
// )
|
||||
// )
|
||||
// ).execute();
|
||||
|
||||
|
||||
|
||||
// ActorsFilmsRecord actorsFilms = fluentChatClient.chat()
|
||||
// .user()
|
||||
// .text("Generate the filmography of 5 movies for {actor}.")
|
||||
// .param("actor", "Tom Hanks")
|
||||
// .and()
|
||||
// .and()
|
||||
// .execute();
|
||||
//
|
||||
//
|
||||
//
|
||||
// ActorsFilmsRecord actorsFilms = new FluentChatClient().chat()
|
||||
// .user(userSpec -> userSpec
|
||||
// .text("Generate the filmography of 5 movies for {actor}.",
|
||||
// textSpec -> textSpec.param("actor", "Tom Hanks").params(Map.of()) ) })
|
||||
// .media(mediaSpec -> {
|
||||
// mediaSpec.param(myImage, MimeTypeUtils.IMAGE_PNG);
|
||||
// })
|
||||
// )
|
||||
// .execute();
|
||||
//
|
||||
// System.out.println(actorsFilms);
|
||||
|
||||
// }
|
||||
|
||||
record ActorsFilmsRecord2(String actor, List<String> movies) {
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,18 @@
|
||||
*/
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.model.ModelClient;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ChatClient extends ModelClient<Prompt, ChatResponse> {
|
||||
|
||||
default String call(String message) {
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.ai.converter.BeanOutputConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DefaultChatSpec implements FluentChatClient.ChatSpec {
|
||||
|
||||
private String userText;
|
||||
|
||||
private ChatClient chatClient;
|
||||
|
||||
private Map<String, Object> textParameters = new HashMap<>();
|
||||
|
||||
public DefaultChatSpec(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatUserSpec user() {
|
||||
return new DefaultChatUserSpec(this);
|
||||
}
|
||||
|
||||
public void addUser(String userText, Map<String, Object> textParameters) {
|
||||
this.userText = userText;
|
||||
this.textParameters.putAll(textParameters);
|
||||
}
|
||||
|
||||
public <T> T execute(T... varargsOfT) {
|
||||
|
||||
Assert.state(varargsOfT.length == 0, "you should not provide any values for T!");
|
||||
Assert.state(varargsOfT.getClass().isArray(), "this needs to be an array parameter");
|
||||
Class<?> componentClass = varargsOfT.getClass().getComponentType();
|
||||
|
||||
List<Message> messageList = new ArrayList<>();
|
||||
String userTextToUse = userText + System.lineSeparator() + "{format}";
|
||||
var converter = new BeanOutputConverter(componentClass);
|
||||
|
||||
this.textParameters.put("format", converter.getFormat());
|
||||
doCreateUserMessage(messageList, userTextToUse, this.textParameters);
|
||||
|
||||
Prompt prompt = new Prompt(messageList);
|
||||
ChatResponse chatResponse = this.chatClient.call(prompt);
|
||||
String stringResponse = chatResponse.getResult().getOutput().getContent();
|
||||
|
||||
T convertedResponse = (T) converter.convert(stringResponse);
|
||||
return convertedResponse;
|
||||
}
|
||||
|
||||
private void doCreateUserMessage(List<Message> messageList, String userText, Map<String, Object> textParameters) {
|
||||
PromptTemplate userPromptTemplate = new PromptTemplate(userText);
|
||||
messageList.add(userPromptTemplate.createMessage(textParameters));
|
||||
|
||||
}
|
||||
|
||||
private class DefaultChatUserSpec implements FluentChatClient.ChatUserSpec {
|
||||
|
||||
private DefaultChatSpec chatSpec;
|
||||
|
||||
private String text;
|
||||
|
||||
private Map<String, Object> textParameters = new HashMap<>();
|
||||
|
||||
public DefaultChatUserSpec(DefaultChatSpec chatSpec) {
|
||||
this.chatSpec = chatSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatParamSpec text(String text) {
|
||||
this.text = text;
|
||||
return new DefaultChatParamSpec(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatMediaParamSpec media() {
|
||||
return new DefaultChatMediaParamSpec(this);
|
||||
}
|
||||
|
||||
public void addTextParams(Map<String, Object> textParameters) {
|
||||
this.textParameters.putAll(textParameters);
|
||||
}
|
||||
|
||||
public FluentChatClient.ChatSpec and() {
|
||||
this.chatSpec.addUser(this.text, this.textParameters);
|
||||
return this.chatSpec;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DefaultChatMediaParamSpec implements FluentChatClient.ChatMediaParamSpec {
|
||||
|
||||
private final DefaultChatUserSpec chatUserSpec;
|
||||
|
||||
public DefaultChatMediaParamSpec(DefaultChatUserSpec chatUserSpec) {
|
||||
this.chatUserSpec = chatUserSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatMediaParamSpec param(List<Media> mediaList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatMediaParamSpec param(Media media) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatMediaParamSpec param(MimeType mimeType, Object data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatUserSpec and() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DefaultChatParamSpec implements FluentChatClient.ChatParamSpec {
|
||||
|
||||
private final DefaultChatUserSpec chatUserSpec;
|
||||
|
||||
private Map<String, Object> parameters = new HashMap<>();
|
||||
|
||||
public DefaultChatParamSpec(DefaultChatUserSpec chatUserSpec) {
|
||||
this.chatUserSpec = chatUserSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatParamSpec param(String name, Object value) {
|
||||
this.parameters.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatParamSpec params(Map<String, ?> paramMap) {
|
||||
this.parameters.putAll(paramMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClient.ChatUserSpec and() {
|
||||
chatUserSpec.addTextParams(this.parameters);
|
||||
return this.chatUserSpec;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.ai.converter.BeanOutputConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class DefaultChatSpecLambda implements FluentChatClientLambda.ChatSpec {
|
||||
|
||||
private String userText;
|
||||
|
||||
private ChatClient chatClient;
|
||||
|
||||
private Map<String, Object> textParameters = new HashMap<>();
|
||||
|
||||
public DefaultChatSpecLambda(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatUserSpec user() {
|
||||
return new DefaultChatUserSpec(this);
|
||||
}
|
||||
|
||||
public void addUser(String userText, Map<String, Object> textParameters) {
|
||||
this.userText = userText;
|
||||
this.textParameters.putAll(textParameters);
|
||||
}
|
||||
|
||||
public <T> T execute(T... varargsOfT) {
|
||||
|
||||
Assert.state(varargsOfT.length == 0, "you should not provide any values for T!");
|
||||
Assert.state(varargsOfT.getClass().isArray(), "this needs to be an array parameter");
|
||||
Class<?> componentClass = varargsOfT.getClass().getComponentType();
|
||||
|
||||
List<Message> messageList = new ArrayList<>();
|
||||
String userTextToUse = userText + System.lineSeparator() + "{format}";
|
||||
var converter = new BeanOutputConverter(componentClass);
|
||||
|
||||
this.textParameters.put("format", converter.getFormat());
|
||||
doCreateUserMessage(messageList, userTextToUse, this.textParameters);
|
||||
|
||||
Prompt prompt = new Prompt(messageList);
|
||||
ChatResponse chatResponse = this.chatClient.call(prompt);
|
||||
String stringResponse = chatResponse.getResult().getOutput().getContent();
|
||||
|
||||
T convertedResponse = (T) converter.convert(stringResponse);
|
||||
return convertedResponse;
|
||||
}
|
||||
|
||||
private void doCreateUserMessage(List<Message> messageList, String userText, Map<String, Object> textParameters) {
|
||||
PromptTemplate userPromptTemplate = new PromptTemplate(userText);
|
||||
messageList.add(userPromptTemplate.createMessage(textParameters));
|
||||
|
||||
}
|
||||
|
||||
private class DefaultChatUserSpec implements FluentChatClientLambda.ChatUserSpec {
|
||||
|
||||
private DefaultChatSpecLambda chatSpec;
|
||||
|
||||
private String text;
|
||||
|
||||
private Map<String, Object> textParameters = new HashMap<>();
|
||||
|
||||
public DefaultChatUserSpec(DefaultChatSpecLambda chatSpec) {
|
||||
this.chatSpec = chatSpec;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public FluentChatClientLambda.ChatParamSpec text(String text) {
|
||||
// this.text = text;
|
||||
// return new DefaultChatParamSpec(this);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public FluentChatClientLambda.ChatMediaParamSpec media() {
|
||||
// return new DefaultChatMediaParamSpec(this);
|
||||
// }
|
||||
|
||||
public void addTextParams(Map<String, Object> textParameters) {
|
||||
this.textParameters.putAll(textParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatParamSpec text(String text,
|
||||
Consumer<FluentChatClientLambda.ChatParamSpec> chatParamSpecConsumer) {
|
||||
this.text = text;
|
||||
return chatParamSpecConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatMediaParamSpec media(
|
||||
Consumer<FluentChatClientLambda.ChatMediaParamSpec> chatMediaParamSpecConsumer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DefaultChatMediaParamSpec implements FluentChatClientLambda.ChatMediaParamSpec {
|
||||
|
||||
private final DefaultChatUserSpec chatUserSpec;
|
||||
|
||||
public DefaultChatMediaParamSpec(DefaultChatUserSpec chatUserSpec) {
|
||||
this.chatUserSpec = chatUserSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatMediaParamSpec param(List<Media> mediaList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatMediaParamSpec param(Media media) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatMediaParamSpec param(MimeType mimeType, Object data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DefaultChatParamSpec implements FluentChatClientLambda.ChatParamSpec {
|
||||
|
||||
private final DefaultChatUserSpec chatUserSpec;
|
||||
|
||||
private Map<String, Object> parameters = new HashMap<>();
|
||||
|
||||
public DefaultChatParamSpec(DefaultChatUserSpec chatUserSpec) {
|
||||
this.chatUserSpec = chatUserSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatParamSpec param(String name, Object value) {
|
||||
this.parameters.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluentChatClientLambda.ChatParamSpec params(Map<String, ?> paramMap) {
|
||||
this.parameters.putAll(paramMap);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FluentChatClient {
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public FluentChatClient(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
public ChatSpec chat() {
|
||||
return new DefaultChatSpec(this.chatClient);
|
||||
}
|
||||
|
||||
public interface ChatSpec {
|
||||
|
||||
ChatUserSpec user();
|
||||
|
||||
<T> T execute(T... varargsOfT);
|
||||
|
||||
}
|
||||
|
||||
public interface ChatUserSpec {
|
||||
|
||||
ChatParamSpec text(String text);
|
||||
|
||||
ChatMediaParamSpec media();
|
||||
|
||||
ChatSpec and();
|
||||
|
||||
}
|
||||
|
||||
public interface ChatMediaParamSpec {
|
||||
|
||||
ChatMediaParamSpec param(List<Media> mediaList);
|
||||
|
||||
ChatMediaParamSpec param(Media media);
|
||||
|
||||
ChatMediaParamSpec param(MimeType mimeType, Object data);
|
||||
|
||||
ChatUserSpec and();
|
||||
|
||||
}
|
||||
|
||||
public interface ChatParamSpec {
|
||||
|
||||
ChatParamSpec param(String name, Object value);
|
||||
|
||||
ChatParamSpec params(Map<String, ?> paramMap);
|
||||
|
||||
ChatUserSpec and();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springframework.ai.chat;
|
||||
|
||||
import org.springframework.ai.chat.messages.Media;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FluentChatClientLambda {
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public FluentChatClientLambda(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
public void chat(Consumer<FluentChatClientLambda.ChatSpec> chatSpecConsumer) {
|
||||
|
||||
}
|
||||
|
||||
public interface ChatSpec {
|
||||
|
||||
ChatUserSpec user(Consumer<FluentChatClientLambda.ChatUserSpec> chatUserSpecConsumer);
|
||||
|
||||
<T> T execute(T... varargsOfT);
|
||||
|
||||
}
|
||||
|
||||
public interface ChatUserSpec {
|
||||
|
||||
ChatParamSpec text(String text, Consumer<ChatParamSpec> chatParamSpecConsumer);
|
||||
|
||||
ChatMediaParamSpec media(Consumer<FluentChatClientLambda.ChatMediaParamSpec> chatMediaParamSpecConsumer);
|
||||
|
||||
}
|
||||
|
||||
public interface ChatMediaParamSpec {
|
||||
|
||||
ChatMediaParamSpec param(List<Media> mediaList);
|
||||
|
||||
ChatMediaParamSpec param(Media media);
|
||||
|
||||
ChatMediaParamSpec param(MimeType mimeType, Object data);
|
||||
|
||||
}
|
||||
|
||||
public interface ChatParamSpec {
|
||||
|
||||
ChatParamSpec param(String name, Object value);
|
||||
|
||||
ChatParamSpec params(Map<String, ?> paramMap);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user