Update to ResponseMetadata design
* Remove inheritance from HashMap * No more subclasses per model provider * Builder class for ChatResponse * Fix the AbstractResponseMetadata#AI_METADATA_STRING parameter order * ChatResponseMetadata ignore Null values.
This commit is contained in:
committed by
Christian Tzolov
parent
17c44237a5
commit
97f443d615
@@ -23,6 +23,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.ai.chat.client.AdvisedRequest;
|
||||
import org.springframework.ai.chat.client.RequestResponseAdvisor;
|
||||
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.model.Content;
|
||||
@@ -127,15 +128,17 @@ public class QuestionAnswerAdvisor implements RequestResponseAdvisor {
|
||||
|
||||
@Override
|
||||
public ChatResponse adviseResponse(ChatResponse response, Map<String, Object> context) {
|
||||
response.getMetadata().put(RETRIEVED_DOCUMENTS, context.get(RETRIEVED_DOCUMENTS));
|
||||
return response;
|
||||
ChatResponse.Builder chatResponseBuilder = ChatResponse.builder().from(response);
|
||||
chatResponseBuilder.withMetadata(RETRIEVED_DOCUMENTS, context.get(RETRIEVED_DOCUMENTS));
|
||||
return chatResponseBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> adviseResponse(Flux<ChatResponse> fluxResponse, Map<String, Object> context) {
|
||||
return fluxResponse.map(cr -> {
|
||||
cr.getMetadata().put(RETRIEVED_DOCUMENTS, context.get(RETRIEVED_DOCUMENTS));
|
||||
return cr;
|
||||
ChatResponse.Builder chatResponseBuilder = ChatResponse.builder().from(cr);
|
||||
chatResponseBuilder.withMetadata(RETRIEVED_DOCUMENTS, context.get(RETRIEVED_DOCUMENTS));
|
||||
return chatResponseBuilder.build();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,40 +15,51 @@
|
||||
*/
|
||||
package org.springframework.ai.chat.metadata;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ai.model.AbstractResponseMetadata;
|
||||
import org.springframework.ai.model.ResponseMetadata;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Abstract Data Type (ADT) modeling common AI provider metadata returned in an AI
|
||||
* response.
|
||||
* Models common AI provider metadata returned in an AI response.
|
||||
*
|
||||
* @author John Blum
|
||||
* @author Thomas Vitale
|
||||
* @since 0.7.0
|
||||
* @author Mark Pollack
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ChatResponseMetadata extends ResponseMetadata {
|
||||
public class ChatResponseMetadata extends AbstractResponseMetadata implements ResponseMetadata {
|
||||
|
||||
class DefaultChatResponseMetadata extends HashMap<String, Object> implements ChatResponseMetadata {
|
||||
private final static Logger logger = LoggerFactory.getLogger(ChatResponseMetadata.class);
|
||||
|
||||
}
|
||||
private String id = ""; // Set to blank to preserve backward compat with previous
|
||||
// interface default methods
|
||||
|
||||
ChatResponseMetadata NULL = new DefaultChatResponseMetadata();
|
||||
private String model = "";
|
||||
|
||||
private RateLimit rateLimit = new EmptyRateLimit();
|
||||
|
||||
private Usage usage = new EmptyUsage();
|
||||
|
||||
private PromptMetadata promptMetadata = PromptMetadata.empty();
|
||||
|
||||
/**
|
||||
* A unique identifier for the chat completion operation.
|
||||
* @return unique operation identifier.
|
||||
*/
|
||||
default String getId() {
|
||||
return "";
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The model that handled the request.
|
||||
* @return the model that handled the request.
|
||||
*/
|
||||
default String getModel() {
|
||||
return "";
|
||||
public String getModel() {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,8 +67,8 @@ public interface ChatResponseMetadata extends ResponseMetadata {
|
||||
* @return AI provider specific metadata on rate limits.
|
||||
* @see RateLimit
|
||||
*/
|
||||
default RateLimit getRateLimit() {
|
||||
return new EmptyRateLimit();
|
||||
public RateLimit getRateLimit() {
|
||||
return this.rateLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,12 +76,98 @@ public interface ChatResponseMetadata extends ResponseMetadata {
|
||||
* @return AI provider specific metadata on API usage.
|
||||
* @see Usage
|
||||
*/
|
||||
default Usage getUsage() {
|
||||
return new EmptyUsage();
|
||||
public Usage getUsage() {
|
||||
return this.usage;
|
||||
}
|
||||
|
||||
default PromptMetadata getPromptMetadata() {
|
||||
return PromptMetadata.empty();
|
||||
/**
|
||||
* Returns the prompt metadata gathered by the AI during request processing.
|
||||
* @return the prompt metadata.
|
||||
*/
|
||||
public PromptMetadata getPromptMetadata() {
|
||||
return this.promptMetadata;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final ChatResponseMetadata chatResponseMetadata;
|
||||
|
||||
public Builder() {
|
||||
this.chatResponseMetadata = new ChatResponseMetadata();
|
||||
}
|
||||
|
||||
public Builder withMetadata(Map<String, Object> mapToCopy) {
|
||||
this.chatResponseMetadata.map.putAll(mapToCopy);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withKeyValue(String key, Object value) {
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("Key must not be null");
|
||||
}
|
||||
if (value != null) {
|
||||
this.chatResponseMetadata.map.put(key, value);
|
||||
}
|
||||
else {
|
||||
logger.debug("Ignore null value for key [{}]", key);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withId(String id) {
|
||||
this.chatResponseMetadata.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withModel(String model) {
|
||||
this.chatResponseMetadata.model = model;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withRateLimit(RateLimit rateLimit) {
|
||||
this.chatResponseMetadata.rateLimit = rateLimit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withUsage(Usage usage) {
|
||||
this.chatResponseMetadata.usage = usage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withPromptMetadata(PromptMetadata promptMetadata) {
|
||||
this.chatResponseMetadata.promptMetadata = promptMetadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatResponseMetadata build() {
|
||||
return this.chatResponseMetadata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof ChatResponseMetadata that))
|
||||
return false;
|
||||
return Objects.equals(this.id, that.id) && Objects.equals(this.model, that.model)
|
||||
&& Objects.equals(this.rateLimit, that.rateLimit) && Objects.equals(this.usage, that.usage)
|
||||
&& Objects.equals(this.promptMetadata, that.promptMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.id, this.model, this.rateLimit, this.usage, this.promptMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return AI_METADATA_STRING.formatted(getId(), getUsage(), getRateLimit());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
package org.springframework.ai.chat.model;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.ai.model.ModelResponse;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -40,7 +42,7 @@ public class ChatResponse implements ModelResponse<Generation> {
|
||||
* provider.
|
||||
*/
|
||||
public ChatResponse(List<Generation> generations) {
|
||||
this(generations, ChatResponseMetadata.NULL);
|
||||
this(generations, new ChatResponseMetadata());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,4 +109,44 @@ public class ChatResponse implements ModelResponse<Generation> {
|
||||
return Objects.hash(chatResponseMetadata, generations);
|
||||
}
|
||||
|
||||
public static ChatResponse.Builder builder() {
|
||||
return new ChatResponse.Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private List<Generation> generations;
|
||||
|
||||
private ChatResponseMetadata.Builder chatResponseMetadataBuilder;
|
||||
|
||||
private Builder() {
|
||||
this.chatResponseMetadataBuilder = ChatResponseMetadata.builder();
|
||||
}
|
||||
|
||||
public Builder from(ChatResponse other) {
|
||||
this.generations = other.generations;
|
||||
Set<Map.Entry<String, Object>> entries = other.chatResponseMetadata.entrySet();
|
||||
for (Map.Entry<String, Object> entry : entries) {
|
||||
this.chatResponseMetadataBuilder.withKeyValue(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withMetadata(String key, Object value) {
|
||||
this.chatResponseMetadataBuilder.withKeyValue(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withGenerations(List<Generation> generations) {
|
||||
this.generations = generations;
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
public ChatResponse build() {
|
||||
return new ChatResponse(generations, chatResponseMetadataBuilder.build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,24 +15,20 @@
|
||||
*/
|
||||
package org.springframework.ai.embedding;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ai.chat.metadata.EmptyUsage;
|
||||
import org.springframework.ai.chat.metadata.Usage;
|
||||
import org.springframework.ai.model.AbstractResponseMetadata;
|
||||
import org.springframework.ai.model.ResponseMetadata;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Common AI provider metadata returned in an embedding response.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
* @author Thomas Vitale
|
||||
*/
|
||||
public class EmbeddingResponseMetadata extends HashMap<String, Object> implements ResponseMetadata {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
public class EmbeddingResponseMetadata extends AbstractResponseMetadata implements ResponseMetadata {
|
||||
|
||||
private String model;
|
||||
|
||||
@@ -42,12 +38,15 @@ public class EmbeddingResponseMetadata extends HashMap<String, Object> implement
|
||||
}
|
||||
|
||||
public EmbeddingResponseMetadata(String model, Usage usage) {
|
||||
this.model = model;
|
||||
this.usage = usage;
|
||||
this(model, usage, Map.of());
|
||||
}
|
||||
|
||||
public EmbeddingResponseMetadata(Map<String, ?> metadata) {
|
||||
super(metadata);
|
||||
public EmbeddingResponseMetadata(String model, Usage usage, Map<String, Object> metadata) {
|
||||
this.model = model;
|
||||
this.usage = usage;
|
||||
for (Map.Entry<String, Object> entry : metadata.entrySet()) {
|
||||
this.map.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ImageResponse implements ModelResponse<ImageGeneration> {
|
||||
* provider.
|
||||
*/
|
||||
public ImageResponse(List<ImageGeneration> generations) {
|
||||
this(generations, ImageResponseMetadata.NULL);
|
||||
this(generations, new ImageResponseMetadata());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.ai.image;
|
||||
|
||||
import org.springframework.ai.model.MutableResponseMetadata;
|
||||
import org.springframework.ai.model.ResponseMetadata;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -28,16 +29,20 @@ import java.util.HashMap;
|
||||
* @author Thomas Vitale
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ImageResponseMetadata extends ResponseMetadata {
|
||||
public class ImageResponseMetadata extends MutableResponseMetadata {
|
||||
|
||||
class DefaultImageResponseMetadata extends HashMap<String, Object> implements ImageResponseMetadata {
|
||||
private Long created;
|
||||
|
||||
public ImageResponseMetadata() {
|
||||
this.created = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
ImageResponseMetadata NULL = new DefaultImageResponseMetadata();
|
||||
public ImageResponseMetadata(Long created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
default Long getCreated() {
|
||||
return System.currentTimeMillis();
|
||||
public Long getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.springframework.ai.model;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import io.micrometer.common.lang.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class AbstractResponseMetadata {
|
||||
|
||||
protected static final String AI_METADATA_STRING = "{ id: %1$s, usage: %2$s, rateLimit: %3$s }";
|
||||
|
||||
protected final Map<String, Object> map = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Gets an entry from the context. Returns {@code null} when entry is not present.
|
||||
* @param key key
|
||||
* @param <T> value type
|
||||
* @return entry or {@code null} if not present
|
||||
*/
|
||||
@Nullable
|
||||
public <T> T get(String key) {
|
||||
return (T) this.map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entry from the context. Throws exception when entry is not present.
|
||||
* @param key key
|
||||
* @param <T> value type
|
||||
* @return entry
|
||||
* @throws IllegalArgumentException if not present
|
||||
*/
|
||||
@NonNull
|
||||
public <T> T getRequired(Object key) {
|
||||
T object = (T) this.map.get(key);
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("Context does not have an entry for key [" + key + "]");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if context contains a key.
|
||||
* @param key key
|
||||
* @return {@code true} when the context contains the entry with the given key
|
||||
*/
|
||||
public boolean containsKey(Object key) {
|
||||
return this.map.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an element or default if not present.
|
||||
* @param key key
|
||||
* @param defaultObject default object to return
|
||||
* @param <T> value type
|
||||
* @return object or default if not present
|
||||
*/
|
||||
public <T> T getOrDefault(Object key, T defaultObject) {
|
||||
return (T) this.map.getOrDefault(key, defaultObject);
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String, Object>> entrySet() {
|
||||
return Collections.unmodifiableMap(this.map).entrySet();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableSet(this.map.keySet());
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.map.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.springframework.ai.model;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import io.micrometer.common.lang.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MutableResponseMetadata implements ResponseMetadata {
|
||||
|
||||
private final Map<String, Object> map = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Puts an element to the context.
|
||||
* @param key key
|
||||
* @param object value
|
||||
* @param <T> value type
|
||||
* @return this for chaining
|
||||
*/
|
||||
public <T> MutableResponseMetadata put(String key, T object) {
|
||||
this.map.put(key, object);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entry from the context. Returns {@code null} when entry is not present.
|
||||
* @param key key
|
||||
* @param <T> value type
|
||||
* @return entry or {@code null} if not present
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(String key) {
|
||||
return (T) this.map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an entry from the context.
|
||||
* @param key key by which to remove an entry
|
||||
* @return the previous value associated with the key, or null if there was no mapping
|
||||
* for the key
|
||||
*/
|
||||
public Object remove(Object key) {
|
||||
return this.map.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entry from the context. Throws exception when entry is not present.
|
||||
* @param key key
|
||||
* @param <T> value type
|
||||
* @throws IllegalArgumentException if not present
|
||||
* @return entry
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
public <T> T getRequired(Object key) {
|
||||
T object = (T) this.map.get(key);
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("Context does not have an entry for key [" + key + "]");
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if context contains a key.
|
||||
* @param key key
|
||||
* @return {@code true} when the context contains the entry with the given key
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.map.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an element or default if not present.
|
||||
* @param key key
|
||||
* @param defaultObject default object to return
|
||||
* @param <T> value type
|
||||
* @return object or default if not present
|
||||
*/
|
||||
@Override
|
||||
public <T> T getOrDefault(Object key, T defaultObject) {
|
||||
return (T) this.map.getOrDefault(key, defaultObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<String, Object>> entrySet() {
|
||||
return Collections.unmodifiableMap(this.map).entrySet();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableSet(this.map.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an element or calls a mapping function if entry not present. The function
|
||||
* will insert the value to the map.
|
||||
* @param key key
|
||||
* @param mappingFunction mapping function
|
||||
* @param <T> value type
|
||||
* @return object or one derived from the mapping function if not present
|
||||
*/
|
||||
public <T> T computeIfAbsent(String key, Function<Object, ? extends T> mappingFunction) {
|
||||
return (T) this.map.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the entries from the context.
|
||||
*/
|
||||
public void clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
|
||||
public Map<String, Object> getRawMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,18 +15,77 @@
|
||||
*/
|
||||
package org.springframework.ai.model;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import io.micrometer.common.lang.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Interface representing metadata associated with an AI model's response. This interface
|
||||
* is designed to provide additional information about the generative response from an AI
|
||||
* model, including processing details and model-specific data. It serves as a value
|
||||
* object within the core domain, enhancing the understanding and management of AI model
|
||||
* responses in various applications.
|
||||
* Interface representing metadata associated with an AI model's response.
|
||||
*
|
||||
* @author Mark Pollack
|
||||
* @since 0.8.0
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ResponseMetadata extends Map<String, Object> {
|
||||
public interface ResponseMetadata {
|
||||
|
||||
/**
|
||||
* Gets an entry from the context. Returns {@code null} when entry is not present.
|
||||
* @param key key
|
||||
* @param <T> value type
|
||||
* @return entry or {@code null} if not present
|
||||
*/
|
||||
@Nullable
|
||||
<T> T get(String key);
|
||||
|
||||
/**
|
||||
* Gets an entry from the context. Throws exception when entry is not present.
|
||||
* @param key key
|
||||
* @param <T> value type
|
||||
* @throws IllegalArgumentException if not present
|
||||
* @return entry
|
||||
*/
|
||||
@NonNull
|
||||
<T> T getRequired(Object key);
|
||||
|
||||
/**
|
||||
* Checks if context contains a key.
|
||||
* @param key key
|
||||
* @return {@code true} when the context contains the entry with the given key
|
||||
*/
|
||||
boolean containsKey(Object key);
|
||||
|
||||
/**
|
||||
* Returns an element or default if not present.
|
||||
* @param key key
|
||||
* @param defaultObject default object to return
|
||||
* @param <T> value type
|
||||
* @return object or default if not present
|
||||
*/
|
||||
<T> T getOrDefault(Object key, T defaultObject);
|
||||
|
||||
/**
|
||||
* Returns an element or default if not present.
|
||||
* @param key key
|
||||
* @param defaultObjectSupplier supplier for default object to return
|
||||
* @param <T> value type
|
||||
* @return object or default if not present
|
||||
* @since 1.11.0
|
||||
*/
|
||||
default <T> T getOrDefault(String key, Supplier<T> defaultObjectSupplier) {
|
||||
T value = get(key);
|
||||
return value != null ? value : defaultObjectSupplier.get();
|
||||
}
|
||||
|
||||
Set<Map.Entry<String, Object>> entrySet();
|
||||
|
||||
public Set<String> keySet();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this map contains no key-value mappings.
|
||||
* @return {@code true} if this map contains no key-value mappings
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
}
|
||||
|
||||
@@ -136,9 +136,7 @@ public abstract class AbstractFunctionCallSupport<Msg, Req, Resp> {
|
||||
|
||||
// The chat completion tool call requires the complete conversation
|
||||
// history. Including the initial user message.
|
||||
List<Msg> conversationHistory = new ArrayList<>();
|
||||
|
||||
conversationHistory.addAll(this.doGetUserMessages(request));
|
||||
List<Msg> conversationHistory = new ArrayList<>(this.doGetUserMessages(request));
|
||||
|
||||
Msg responseMessage = this.doGetToolResponseMessage(response);
|
||||
|
||||
@@ -164,9 +162,7 @@ public abstract class AbstractFunctionCallSupport<Msg, Req, Resp> {
|
||||
|
||||
// The chat completion tool call requires the complete conversation
|
||||
// history. Including the initial user message.
|
||||
List<Msg> conversationHistory = new ArrayList<>();
|
||||
|
||||
conversationHistory.addAll(this.doGetUserMessages(request));
|
||||
List<Msg> conversationHistory = new ArrayList<>(this.doGetUserMessages(request));
|
||||
|
||||
Msg responseMessage = this.doGetToolResponseMessage(resp);
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.MessageType;
|
||||
import org.springframework.ai.chat.metadata.ChatResponseMetadata;
|
||||
import org.springframework.ai.chat.metadata.ChatResponseMetadata.DefaultChatResponseMetadata;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.model.Generation;
|
||||
@@ -58,8 +57,7 @@ public class ChatClientResponseEntityTests {
|
||||
@Test
|
||||
public void responseEntityTest() {
|
||||
|
||||
ChatResponseMetadata metadata = new DefaultChatResponseMetadata();
|
||||
metadata.put("key1", "value1");
|
||||
ChatResponseMetadata metadata = ChatResponseMetadata.builder().withKeyValue("key1", "value1").build();
|
||||
|
||||
var chatResponse = new ChatResponse(List.of(new Generation("""
|
||||
{"name":"John", "age":30}
|
||||
@@ -75,7 +73,7 @@ public class ChatClientResponseEntityTests {
|
||||
.responseEntity(MyBean.class);
|
||||
|
||||
assertThat(responseEntity.getResponse()).isEqualTo(chatResponse);
|
||||
assertThat(responseEntity.getResponse().getMetadata().get("key1")).isEqualTo("value1");
|
||||
assertThat(responseEntity.getResponse().getMetadata().get("key1").toString()).isEqualTo("value1");
|
||||
|
||||
assertThat(responseEntity.getEntity()).isEqualTo(new MyBean("John", 30));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user