Enabling checkstyle on spring-ai-openai
This commit is contained in:
committed by
Mark Pollack
parent
5d8c032bb7
commit
cdc1cecb57
@@ -36,6 +36,10 @@
|
||||
<developerConnection>git@github.com:spring-projects/spring-ai.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<disable.checks>false</disable.checks>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- production dependencies -->
|
||||
|
||||
@@ -132,7 +132,7 @@ public class OpenAiAudioSpeechModel implements SpeechModel, StreamingSpeechModel
|
||||
var speech = speechEntity.getBody();
|
||||
|
||||
if (speech == null) {
|
||||
this.logger.warn("No speech response returned for speechRequest: {}", speechRequest);
|
||||
logger.warn("No speech response returned for speechRequest: {}", speechRequest);
|
||||
return new SpeechResponse(new Speech(new byte[0]));
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ public class OpenAiAudioTranscriptionModel implements Model<AudioTranscriptionPr
|
||||
var transcription = transcriptionEntity.getBody();
|
||||
|
||||
if (transcription == null) {
|
||||
this.logger.warn("No transcription returned for request: {}", audioResource);
|
||||
logger.warn("No transcription returned for request: {}", audioResource);
|
||||
return new AudioTranscriptionResponse(null);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class OpenAiAudioTranscriptionModel implements Model<AudioTranscriptionPr
|
||||
var transcription = transcriptionEntity.getBody();
|
||||
|
||||
if (transcription == null) {
|
||||
this.logger.warn("No transcription returned for request: {}", audioResource);
|
||||
logger.warn("No transcription returned for request: {}", audioResource);
|
||||
return new AudioTranscriptionResponse(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,37 +122,48 @@ public class OpenAiAudioTranscriptionOptions implements AudioTranscriptionOption
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
if (this == obj) {
|
||||
return true;
|
||||
if (obj == null)
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
OpenAiAudioTranscriptionOptions other = (OpenAiAudioTranscriptionOptions) obj;
|
||||
if (this.model == null) {
|
||||
if (other.model != null)
|
||||
if (other.model != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.model.equals(other.model))
|
||||
else if (!this.model.equals(other.model)) {
|
||||
return false;
|
||||
}
|
||||
if (this.prompt == null) {
|
||||
if (other.prompt != null)
|
||||
if (other.prompt != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.prompt.equals(other.prompt))
|
||||
else if (!this.prompt.equals(other.prompt)) {
|
||||
return false;
|
||||
}
|
||||
if (this.language == null) {
|
||||
if (other.language != null)
|
||||
if (other.language != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.language.equals(other.language))
|
||||
else if (!this.language.equals(other.language)) {
|
||||
return false;
|
||||
}
|
||||
if (this.responseFormat == null) {
|
||||
if (other.responseFormat != null)
|
||||
if (other.responseFormat != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.responseFormat.equals(other.responseFormat))
|
||||
else if (!this.responseFormat.equals(other.responseFormat)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -307,8 +307,7 @@ public class OpenAiChatModel extends AbstractToolCallSupport implements ChatMode
|
||||
@SuppressWarnings("null")
|
||||
String id = chatCompletion2.id();
|
||||
|
||||
List<Generation> generations = chatCompletion2.choices().stream().map(choice -> {// @formatter:off
|
||||
|
||||
List<Generation> generations = chatCompletion2.choices().stream().map(choice -> { // @formatter:off
|
||||
if (choice.message().role() != null) {
|
||||
roleMap.putIfAbsent(id, choice.message().role().name());
|
||||
}
|
||||
@@ -347,9 +346,7 @@ public class OpenAiChatModel extends AbstractToolCallSupport implements ChatMode
|
||||
}
|
||||
})
|
||||
.doOnError(observation::error)
|
||||
.doFinally(s -> {
|
||||
observation.stop();
|
||||
})
|
||||
.doFinally(s -> observation.stop())
|
||||
.contextWrite(ctx -> ctx.put(ObservationThreadLocalAccessor.KEY, observation));
|
||||
// @formatter:on
|
||||
|
||||
@@ -454,10 +451,8 @@ public class OpenAiChatModel extends AbstractToolCallSupport implements ChatMode
|
||||
else if (message.getMessageType() == MessageType.TOOL) {
|
||||
ToolResponseMessage toolMessage = (ToolResponseMessage) message;
|
||||
|
||||
toolMessage.getResponses().forEach(response -> {
|
||||
Assert.isTrue(response.id() != null, "ToolResponseMessage must have an id");
|
||||
});
|
||||
|
||||
toolMessage.getResponses()
|
||||
.forEach(response -> Assert.isTrue(response.id() != null, "ToolResponseMessage must have an id"));
|
||||
return toolMessage.getResponses()
|
||||
.stream()
|
||||
.map(tr -> new ChatCompletionMessage(tr.responseData(), ChatCompletionMessage.Role.TOOL, tr.name(),
|
||||
|
||||
@@ -169,10 +169,11 @@ public class OpenAiImageModel implements ImageModel {
|
||||
return new ImageResponse(List.of());
|
||||
}
|
||||
|
||||
List<ImageGeneration> imageGenerationList = imageApiResponse.data().stream().map(entry -> {
|
||||
return new ImageGeneration(new Image(entry.url(), entry.b64Json()),
|
||||
new OpenAiImageGenerationMetadata(entry.revisedPrompt()));
|
||||
}).toList();
|
||||
List<ImageGeneration> imageGenerationList = imageApiResponse.data()
|
||||
.stream()
|
||||
.map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()),
|
||||
new OpenAiImageGenerationMetadata(entry.revisedPrompt())))
|
||||
.toList();
|
||||
|
||||
ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created());
|
||||
return new ImageResponse(imageGenerationList, openAiImageResponseMetadata);
|
||||
|
||||
@@ -234,7 +234,7 @@ public class OpenAiImageOptions implements ImageOptions {
|
||||
+ ", user='" + this.user + '\'' + '}';
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
public static final class Builder {
|
||||
|
||||
private final OpenAiImageOptions options;
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ public class OpenAiModerationModel implements ModerationModel {
|
||||
OpenAiModerationApi.OpenAiModerationRequest openAiModerationRequest) {
|
||||
OpenAiModerationApi.OpenAiModerationResponse moderationApiResponse = moderationResponseEntity.getBody();
|
||||
if (moderationApiResponse == null) {
|
||||
this.logger.warn("No moderation response returned for request: {}", openAiModerationRequest);
|
||||
logger.warn("No moderation response returned for request: {}", openAiModerationRequest);
|
||||
return new ModerationResponse(new Generation());
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ public class OpenAiModerationOptions implements ModerationOptions {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
public static final class Builder {
|
||||
|
||||
private final OpenAiModerationOptions options;
|
||||
|
||||
|
||||
@@ -48,12 +48,15 @@ public class OpenAiRuntimeHints implements RuntimeHintsRegistrar {
|
||||
@Override
|
||||
public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
var mcs = MemberCategory.values();
|
||||
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiApi.class)))
|
||||
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiApi.class))) {
|
||||
hints.reflection().registerType(tr, mcs);
|
||||
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiAudioApi.class)))
|
||||
}
|
||||
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiAudioApi.class))) {
|
||||
hints.reflection().registerType(tr, mcs);
|
||||
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiImageApi.class)))
|
||||
}
|
||||
for (var tr : eval(findJsonAnnotatedClassesInPackage(OpenAiImageApi.class))) {
|
||||
hints.reflection().registerType(tr, mcs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public class OpenAiApi {
|
||||
this.webClient = webClientBuilder
|
||||
.baseUrl(baseUrl)
|
||||
.defaultHeaders(finalHeaders)
|
||||
.build();// @formatter:on
|
||||
.build(); // @formatter:on
|
||||
}
|
||||
|
||||
public static String getTextContent(List<ChatCompletionMessage.MediaContent> content) {
|
||||
@@ -556,7 +556,8 @@ public class OpenAiApi {
|
||||
/**
|
||||
* Function tool type.
|
||||
*/
|
||||
@JsonProperty("function") FUNCTION
|
||||
@JsonProperty("function")
|
||||
FUNCTION
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -585,7 +586,7 @@ public class OpenAiApi {
|
||||
this(description, name, ModelOptionsUtils.jsonToMap(jsonSchema));
|
||||
}
|
||||
}
|
||||
}// @formatter:on
|
||||
} // @formatter:on
|
||||
|
||||
/**
|
||||
* Creates a model response for the given chat conversation.
|
||||
@@ -779,7 +780,7 @@ public class OpenAiApi {
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public record ResponseFormat(
|
||||
@JsonProperty("type") Type type,
|
||||
@JsonProperty("json_schema") JsonSchema jsonSchema ) {
|
||||
@JsonProperty("json_schema") JsonSchema jsonSchema) {
|
||||
|
||||
public ResponseFormat(Type type) {
|
||||
this(type, (JsonSchema) null);
|
||||
@@ -790,7 +791,7 @@ public class OpenAiApi {
|
||||
}
|
||||
|
||||
public ResponseFormat(Type type, String name, String schema, Boolean strict) {
|
||||
this(type, StringUtils.hasText(schema)? new JsonSchema(name, schema, strict): null);
|
||||
this(type, StringUtils.hasText(schema) ? new JsonSchema(name, schema, strict) : null);
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
@@ -833,7 +834,7 @@ public class OpenAiApi {
|
||||
}
|
||||
|
||||
public JsonSchema(String name, String schema, Boolean strict) {
|
||||
this(StringUtils.hasText(name)? name : "custom_schema", ModelOptionsUtils.jsonToMap(schema), strict);
|
||||
this(StringUtils.hasText(name) ? name : "custom_schema", ModelOptionsUtils.jsonToMap(schema), strict);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -852,7 +853,7 @@ public class OpenAiApi {
|
||||
|
||||
public static StreamOptions INCLUDE_USAGE = new StreamOptions(true);
|
||||
}
|
||||
}// @formatter:on
|
||||
} // @formatter:on
|
||||
|
||||
/**
|
||||
* Message comprising the conversation.
|
||||
@@ -876,7 +877,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("name") String name,
|
||||
@JsonProperty("tool_call_id") String toolCallId,
|
||||
@JsonProperty("tool_calls") List<ToolCall> toolCalls,
|
||||
@JsonProperty("refusal") String refusal) {// @formatter:on
|
||||
@JsonProperty("refusal") String refusal) { // @formatter:on
|
||||
|
||||
/**
|
||||
* Create a chat completion message with the given content and role. All other
|
||||
@@ -995,7 +996,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("index") Integer index,
|
||||
@JsonProperty("id") String id,
|
||||
@JsonProperty("type") String type,
|
||||
@JsonProperty("function") ChatCompletionFunction function) {// @formatter:on
|
||||
@JsonProperty("function") ChatCompletionFunction function) { // @formatter:on
|
||||
|
||||
public ToolCall(String id, String type, ChatCompletionFunction function) {
|
||||
this(null, id, type, function);
|
||||
@@ -1013,7 +1014,7 @@ public class OpenAiApi {
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public record ChatCompletionFunction(// @formatter:off
|
||||
@JsonProperty("name") String name,
|
||||
@JsonProperty("arguments") String arguments) {// @formatter:on
|
||||
@JsonProperty("arguments") String arguments) { // @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1042,7 +1043,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("model") String model,
|
||||
@JsonProperty("system_fingerprint") String systemFingerprint,
|
||||
@JsonProperty("object") String object,
|
||||
@JsonProperty("usage") Usage usage) {// @formatter:on
|
||||
@JsonProperty("usage") Usage usage) { // @formatter:on
|
||||
|
||||
/**
|
||||
* Chat completion choice.
|
||||
@@ -1057,7 +1058,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("finish_reason") ChatCompletionFinishReason finishReason,
|
||||
@JsonProperty("index") Integer index,
|
||||
@JsonProperty("message") ChatCompletionMessage message,
|
||||
@JsonProperty("logprobs") LogProbs logprobs) {// @formatter:on
|
||||
@JsonProperty("logprobs") LogProbs logprobs) { // @formatter:on
|
||||
|
||||
}
|
||||
|
||||
@@ -1090,7 +1091,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("token") String token,
|
||||
@JsonProperty("logprob") Float logprob,
|
||||
@JsonProperty("bytes") List<Integer> probBytes,
|
||||
@JsonProperty("top_logprobs") List<TopLogProbs> topLogprobs) {// @formatter:on
|
||||
@JsonProperty("top_logprobs") List<TopLogProbs> topLogprobs) { // @formatter:on
|
||||
|
||||
/**
|
||||
* The most likely tokens and their log probability, at this token position.
|
||||
@@ -1107,7 +1108,7 @@ public class OpenAiApi {
|
||||
public record TopLogProbs(// @formatter:off
|
||||
@JsonProperty("token") String token,
|
||||
@JsonProperty("logprob") Float logprob,
|
||||
@JsonProperty("bytes") List<Integer> probBytes) {// @formatter:on
|
||||
@JsonProperty("bytes") List<Integer> probBytes) { // @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1133,7 +1134,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("prompt_tokens") Integer promptTokens,
|
||||
@JsonProperty("total_tokens") Integer totalTokens,
|
||||
@JsonProperty("prompt_tokens_details") PromptTokensDetails promptTokensDetails,
|
||||
@JsonProperty("completion_tokens_details") CompletionTokenDetails completionTokenDetails) {// @formatter:on
|
||||
@JsonProperty("completion_tokens_details") CompletionTokenDetails completionTokenDetails) { // @formatter:on
|
||||
|
||||
public Usage(Integer completionTokens, Integer promptTokens, Integer totalTokens) {
|
||||
this(completionTokens, promptTokens, totalTokens, null, null);
|
||||
@@ -1146,7 +1147,7 @@ public class OpenAiApi {
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public record PromptTokensDetails(// @formatter:off
|
||||
@JsonProperty("cached_tokens") Integer cachedTokens) {// @formatter:on
|
||||
@JsonProperty("cached_tokens") Integer cachedTokens) { // @formatter:on
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1156,7 +1157,7 @@ public class OpenAiApi {
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public record CompletionTokenDetails(// @formatter:off
|
||||
@JsonProperty("reasoning_tokens") Integer reasoningTokens) {// @formatter:on
|
||||
@JsonProperty("reasoning_tokens") Integer reasoningTokens) { // @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1186,7 +1187,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("model") String model,
|
||||
@JsonProperty("system_fingerprint") String systemFingerprint,
|
||||
@JsonProperty("object") String object,
|
||||
@JsonProperty("usage") Usage usage) {// @formatter:on
|
||||
@JsonProperty("usage") Usage usage) { // @formatter:on
|
||||
|
||||
/**
|
||||
* Chat completion choice.
|
||||
@@ -1201,7 +1202,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("finish_reason") ChatCompletionFinishReason finishReason,
|
||||
@JsonProperty("index") Integer index,
|
||||
@JsonProperty("delta") ChatCompletionMessage delta,
|
||||
@JsonProperty("logprobs") LogProbs logprobs) {// @formatter:on
|
||||
@JsonProperty("logprobs") LogProbs logprobs) { // @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1218,7 +1219,7 @@ public class OpenAiApi {
|
||||
public record Embedding(// @formatter:off
|
||||
@JsonProperty("index") Integer index,
|
||||
@JsonProperty("embedding") float[] embedding,
|
||||
@JsonProperty("object") String object) {// @formatter:on
|
||||
@JsonProperty("object") String object) { // @formatter:on
|
||||
|
||||
/**
|
||||
* Create an embedding with the given index, embedding and object type set to
|
||||
@@ -1255,7 +1256,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("model") String model,
|
||||
@JsonProperty("encoding_format") String encodingFormat,
|
||||
@JsonProperty("dimensions") Integer dimensions,
|
||||
@JsonProperty("user") String user) {// @formatter:on
|
||||
@JsonProperty("user") String user) { // @formatter:on
|
||||
|
||||
/**
|
||||
* Create an embedding request with the given input, model and encoding format set
|
||||
@@ -1292,7 +1293,7 @@ public class OpenAiApi {
|
||||
@JsonProperty("object") String object,
|
||||
@JsonProperty("data") List<T> data,
|
||||
@JsonProperty("model") String model,
|
||||
@JsonProperty("usage") Usage usage) {// @formatter:on
|
||||
@JsonProperty("usage") Usage usage) { // @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -256,11 +256,13 @@ public class OpenAiAudioApi {
|
||||
/**
|
||||
* The latest text to speech model, optimized for speed.
|
||||
*/
|
||||
@JsonProperty("tts-1") TTS_1("tts-1"),
|
||||
@JsonProperty("tts-1")
|
||||
TTS_1("tts-1"),
|
||||
/**
|
||||
* The latest text to speech model, optimized for quality.
|
||||
*/
|
||||
@JsonProperty("tts-1-hd") TTS_1_HD("tts-1-hd");
|
||||
@JsonProperty("tts-1-hd")
|
||||
TTS_1_HD("tts-1-hd");
|
||||
// @formatter:on
|
||||
|
||||
public final String value;
|
||||
@@ -286,7 +288,8 @@ public class OpenAiAudioApi {
|
||||
public enum WhisperModel {
|
||||
|
||||
// @formatter:off
|
||||
@JsonProperty("whisper-1") WHISPER_1("whisper-1");
|
||||
@JsonProperty("whisper-1")
|
||||
WHISPER_1("whisper-1");
|
||||
// @formatter:on
|
||||
|
||||
public final String value;
|
||||
@@ -308,11 +311,16 @@ public class OpenAiAudioApi {
|
||||
public enum TranscriptResponseFormat {
|
||||
|
||||
// @formatter:off
|
||||
@JsonProperty("json") JSON("json", StructuredResponse.class),
|
||||
@JsonProperty("text") TEXT("text", String.class),
|
||||
@JsonProperty("srt") SRT("srt", String.class),
|
||||
@JsonProperty("verbose_json") VERBOSE_JSON("verbose_json", StructuredResponse.class),
|
||||
@JsonProperty("vtt") VTT("vtt", String.class);
|
||||
@JsonProperty("json")
|
||||
JSON("json", StructuredResponse.class),
|
||||
@JsonProperty("text")
|
||||
TEXT("text", String.class),
|
||||
@JsonProperty("srt")
|
||||
SRT("srt", String.class),
|
||||
@JsonProperty("verbose_json")
|
||||
VERBOSE_JSON("verbose_json", StructuredResponse.class),
|
||||
@JsonProperty("vtt")
|
||||
VTT("vtt", String.class);
|
||||
// @formatter:on
|
||||
|
||||
public final String value;
|
||||
@@ -373,17 +381,23 @@ public class OpenAiAudioApi {
|
||||
public enum Voice {
|
||||
|
||||
// @formatter:off
|
||||
@JsonProperty("alloy") ALLOY("alloy"),
|
||||
@JsonProperty("echo") ECHO("echo"),
|
||||
@JsonProperty("fable") FABLE("fable"),
|
||||
@JsonProperty("onyx") ONYX("onyx"),
|
||||
@JsonProperty("nova") NOVA("nova"),
|
||||
@JsonProperty("shimmer") SHIMMER("shimmer");
|
||||
@JsonProperty("alloy")
|
||||
ALLOY("alloy"),
|
||||
@JsonProperty("echo")
|
||||
ECHO("echo"),
|
||||
@JsonProperty("fable")
|
||||
FABLE("fable"),
|
||||
@JsonProperty("onyx")
|
||||
ONYX("onyx"),
|
||||
@JsonProperty("nova")
|
||||
NOVA("nova"),
|
||||
@JsonProperty("shimmer")
|
||||
SHIMMER("shimmer");
|
||||
// @formatter:on
|
||||
|
||||
public final String value;
|
||||
|
||||
private Voice(String value) {
|
||||
Voice(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@@ -400,10 +414,14 @@ public class OpenAiAudioApi {
|
||||
public enum AudioResponseFormat {
|
||||
|
||||
// @formatter:off
|
||||
@JsonProperty("mp3") MP3("mp3"),
|
||||
@JsonProperty("opus") OPUS("opus"),
|
||||
@JsonProperty("aac") AAC("aac"),
|
||||
@JsonProperty("flac") FLAC("flac");
|
||||
@JsonProperty("mp3")
|
||||
MP3("mp3"),
|
||||
@JsonProperty("opus")
|
||||
OPUS("opus"),
|
||||
@JsonProperty("aac")
|
||||
AAC("aac"),
|
||||
@JsonProperty("flac")
|
||||
FLAC("flac");
|
||||
// @formatter:on
|
||||
|
||||
public final String value;
|
||||
@@ -511,8 +529,10 @@ public class OpenAiAudioApi {
|
||||
public enum GranularityType {
|
||||
|
||||
// @formatter:off
|
||||
@JsonProperty("word") WORD("word"),
|
||||
@JsonProperty("segment") SEGMENT("segment");
|
||||
@JsonProperty("word")
|
||||
WORD("word"),
|
||||
@JsonProperty("segment")
|
||||
SEGMENT("segment");
|
||||
// @formatter:on
|
||||
|
||||
public final String value;
|
||||
|
||||
@@ -139,7 +139,7 @@ public class OpenAiImageApi {
|
||||
|
||||
// @formatter:off
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public record OpenAiImageRequest (
|
||||
public record OpenAiImageRequest(
|
||||
@JsonProperty("prompt") String prompt,
|
||||
@JsonProperty("model") String model,
|
||||
@JsonProperty("n") Integer n,
|
||||
|
||||
@@ -77,7 +77,7 @@ public class OpenAiModerationApi {
|
||||
|
||||
// @formatter:off
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public record OpenAiModerationRequest (
|
||||
public record OpenAiModerationRequest(
|
||||
@JsonProperty("input") String prompt,
|
||||
@JsonProperty("model") String model
|
||||
) {
|
||||
|
||||
@@ -31,4 +31,4 @@ public class OpenAiApiClientErrorException extends RuntimeException {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,8 @@ public final class OpenAiApiConstants {
|
||||
|
||||
public static final String PROVIDER_NAME = AiProvider.OPENAI.value();
|
||||
|
||||
private OpenAiApiConstants() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,4 +51,4 @@ public interface StreamingSpeechModel extends StreamingModel<SpeechPrompt, Speec
|
||||
@Override
|
||||
Flux<SpeechResponse> stream(SpeechPrompt prompt);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,10 +47,14 @@ import static org.springframework.ai.openai.metadata.support.OpenAiApiResponseHe
|
||||
* @author Christian Tzolov
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class OpenAiResponseHeaderExtractor {
|
||||
public final class OpenAiResponseHeaderExtractor {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OpenAiResponseHeaderExtractor.class);
|
||||
|
||||
private OpenAiResponseHeaderExtractor() {
|
||||
|
||||
}
|
||||
|
||||
public static RateLimit extractAiResponseHeaders(ResponseEntity<?> response) {
|
||||
|
||||
Long requestsLimit = getHeaderAsLong(response, REQUESTS_LIMIT_HEADER.getName());
|
||||
|
||||
@@ -70,7 +70,7 @@ public class ChatCompletionRequestTests {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName(TOOL_FUNCTION_NAME)
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build()),
|
||||
false);
|
||||
@@ -97,7 +97,7 @@ public class ChatCompletionRequestTests {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName(TOOL_FUNCTION_NAME)
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build());
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.springframework.ai.aot.AiRuntimeHints.findJsonAnnotatedClassesInPackage;
|
||||
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;
|
||||
|
||||
class OpenAiRuntimeHintsTests {
|
||||
|
||||
@@ -36,9 +34,11 @@ class OpenAiRuntimeHintsTests {
|
||||
OpenAiRuntimeHints openAiRuntimeHints = new OpenAiRuntimeHints();
|
||||
openAiRuntimeHints.registerHints(runtimeHints, null);
|
||||
|
||||
Set<TypeReference> jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage(OpenAiApi.class);
|
||||
Set<TypeReference> jsonAnnotatedClasses = org.springframework.ai.aot.AiRuntimeHints
|
||||
.findJsonAnnotatedClassesInPackage(OpenAiApi.class);
|
||||
for (TypeReference jsonAnnotatedClass : jsonAnnotatedClasses) {
|
||||
assertThat(runtimeHints).matches(reflection().onType(jsonAnnotatedClass));
|
||||
assertThat(runtimeHints).matches(org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection()
|
||||
.onType(jsonAnnotatedClass));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class MockWeatherService implements Function<MockWeatherService.Request,
|
||||
*/
|
||||
public final String unitName;
|
||||
|
||||
private Unit(String text) {
|
||||
Unit(String text) {
|
||||
this.unitName = text;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public class OpenAiApiToolFunctionCallIT {
|
||||
ResponseEntity<ChatCompletion> chatCompletion2 = this.completionApi
|
||||
.chatCompletionEntity(functionResponseRequest);
|
||||
|
||||
this.logger.info("Final response: " + chatCompletion2.getBody());
|
||||
logger.info("Final response: " + chatCompletion2.getBody());
|
||||
|
||||
assertThat(chatCompletion2.getBody().choices()).isNotEmpty();
|
||||
|
||||
@@ -147,11 +147,8 @@ public class OpenAiApiToolFunctionCallIT {
|
||||
.containsAnyOf("30.0°C", "30°C");
|
||||
assertThat(chatCompletion2.getBody().choices().get(0).message().content()).contains("Tokyo")
|
||||
.containsAnyOf("10.0°C", "10°C");
|
||||
;
|
||||
assertThat(chatCompletion2.getBody().choices().get(0).message().content()).contains("Paris")
|
||||
.containsAnyOf("15.0°C", "15°C");
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -130,24 +130,24 @@ public class OpenAiTranscriptionModelWithTranscriptionResponseMetadataTests {
|
||||
private String getJson() {
|
||||
return """
|
||||
{
|
||||
"id": "chatcmpl-123",
|
||||
"object": "chat.completion",
|
||||
"created": 1677652288,
|
||||
"model": "gpt-3.5-turbo-0613",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "I surrender!"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {
|
||||
"prompt_tokens": 9,
|
||||
"completion_tokens": 12,
|
||||
"total_tokens": 21
|
||||
}
|
||||
}
|
||||
"id": "chatcmpl-123",
|
||||
"object": "chat.completion",
|
||||
"created": 1677652288,
|
||||
"model": "gpt-3.5-turbo-0613",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "I surrender!"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {
|
||||
"prompt_tokens": 9,
|
||||
"completion_tokens": 12,
|
||||
"total_tokens": 21
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
|
||||
@@ -29,12 +29,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link TranscriptionModel}.
|
||||
@@ -54,11 +53,11 @@ class TranscriptionModelTests {
|
||||
|
||||
// Create a mock Transcript
|
||||
AudioTranscription transcript = Mockito.mock(AudioTranscription.class);
|
||||
when(transcript.getOutput()).thenReturn(mockTranscription);
|
||||
given(transcript.getOutput()).willReturn(mockTranscription);
|
||||
|
||||
// Create a mock TranscriptionResponse with the mock Transcript
|
||||
AudioTranscriptionResponse response = Mockito.mock(AudioTranscriptionResponse.class);
|
||||
when(response.getResult()).thenReturn(transcript);
|
||||
given(response.getResult()).willReturn(transcript);
|
||||
|
||||
// Transcript transcript = spy(new Transcript(responseMessage));
|
||||
// TranscriptionResponse response = spy(new
|
||||
@@ -66,16 +65,14 @@ class TranscriptionModelTests {
|
||||
|
||||
doCallRealMethod().when(mockClient).call(any(Resource.class));
|
||||
|
||||
doAnswer(invocationOnMock -> {
|
||||
|
||||
AudioTranscriptionPrompt transcriptionRequest = invocationOnMock.getArgument(0);
|
||||
given(mockClient.call(any(AudioTranscriptionPrompt.class))).will(invocation -> {
|
||||
AudioTranscriptionPrompt transcriptionRequest = invocation.getArgument(0);
|
||||
|
||||
assertThat(transcriptionRequest).isNotNull();
|
||||
assertThat(transcriptionRequest.getInstructions()).isEqualTo(mockAudioFile);
|
||||
|
||||
return response;
|
||||
|
||||
}).when(mockClient).call(any(AudioTranscriptionPrompt.class));
|
||||
});
|
||||
|
||||
assertThat(mockClient.call(mockAudioFile)).isEqualTo(mockTranscription);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
@@ -65,7 +65,7 @@ public class MessageTypeContentTests {
|
||||
ArgumentCaptor<MultiValueMap<String, String>> headersCaptor;
|
||||
|
||||
Flux<ChatCompletionChunk> fluxResponse = Flux
|
||||
.generate(() -> new ChatCompletionChunk("id", List.of(), 0l, "model", "fp", "object", null), (state, sink) -> {
|
||||
.generate(() -> new ChatCompletionChunk("id", List.of(), 0L, "model", "fp", "object", null), (state, sink) -> {
|
||||
sink.next(state);
|
||||
sink.complete();
|
||||
return state;
|
||||
@@ -79,8 +79,8 @@ public class MessageTypeContentTests {
|
||||
@Test
|
||||
public void systemMessageSimpleContentType() {
|
||||
|
||||
when(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.thenReturn(Mockito.mock(ResponseEntity.class));
|
||||
given(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.willReturn(Mockito.mock(ResponseEntity.class));
|
||||
|
||||
this.chatModel.call(new Prompt(List.of(new SystemMessage("test message"))));
|
||||
|
||||
@@ -91,8 +91,8 @@ public class MessageTypeContentTests {
|
||||
@Test
|
||||
public void userMessageSimpleContentType() {
|
||||
|
||||
when(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.thenReturn(Mockito.mock(ResponseEntity.class));
|
||||
given(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.willReturn(Mockito.mock(ResponseEntity.class));
|
||||
|
||||
this.chatModel.call(new Prompt(List.of(new UserMessage("test message"))));
|
||||
|
||||
@@ -102,8 +102,8 @@ public class MessageTypeContentTests {
|
||||
@Test
|
||||
public void streamUserMessageSimpleContentType() {
|
||||
|
||||
when(this.openAiApi.chatCompletionStream(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.thenReturn(this.fluxResponse);
|
||||
given(this.openAiApi.chatCompletionStream(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.willReturn(this.fluxResponse);
|
||||
|
||||
this.chatModel.stream(new Prompt(List.of(new UserMessage("test message")))).subscribe();
|
||||
|
||||
@@ -122,8 +122,8 @@ public class MessageTypeContentTests {
|
||||
@Test
|
||||
public void userMessageWithMediaType() throws MalformedURLException {
|
||||
|
||||
when(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.thenReturn(Mockito.mock(ResponseEntity.class));
|
||||
given(this.openAiApi.chatCompletionEntity(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.willReturn(Mockito.mock(ResponseEntity.class));
|
||||
|
||||
URL mediaUrl = new URL("http://test");
|
||||
this.chatModel.call(new Prompt(
|
||||
@@ -135,8 +135,8 @@ public class MessageTypeContentTests {
|
||||
@Test
|
||||
public void streamUserMessageWithMediaType() throws MalformedURLException {
|
||||
|
||||
when(this.openAiApi.chatCompletionStream(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.thenReturn(this.fluxResponse);
|
||||
given(this.openAiApi.chatCompletionStream(this.pomptCaptor.capture(), this.headersCaptor.capture()))
|
||||
.willReturn(this.fluxResponse);
|
||||
|
||||
URL mediaUrl = new URL("http://test");
|
||||
this.chatModel
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
@@ -48,9 +48,8 @@ public class OpenAiChatModeAdditionalHttpHeadersIT {
|
||||
@Test
|
||||
void additionalApiKeyHeader() {
|
||||
|
||||
assertThrows(NonTransientAiException.class, () -> {
|
||||
this.openAiChatModel.call("Tell me a joke");
|
||||
});
|
||||
assertThatThrownBy(() -> this.openAiChatModel.call("Tell me a joke"))
|
||||
.isInstanceOf(NonTransientAiException.class);
|
||||
|
||||
// Use the additional headers to override the Api Key.
|
||||
// Mind that you have to prefix the Api Key with the "Bearer " prefix.
|
||||
|
||||
@@ -66,7 +66,7 @@ class OpenAiChatModelFunctionCallingIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build());
|
||||
}
|
||||
@@ -102,7 +102,7 @@ class OpenAiChatModelFunctionCallingIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(biFunction)
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.withToolContext(Map.of("sessionId", "123"))
|
||||
.build());
|
||||
@@ -128,7 +128,7 @@ class OpenAiChatModelFunctionCallingIT {
|
||||
.withFunctionCallbacks(List.of((FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build())))
|
||||
.build());
|
||||
}
|
||||
@@ -163,7 +163,7 @@ class OpenAiChatModelFunctionCallingIT {
|
||||
.withFunctionCallbacks(List.of((FunctionCallbackWrapper.builder(biFunction)
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build())))
|
||||
.withToolContext(Map.of("sessionId", "123"))
|
||||
.build();
|
||||
|
||||
@@ -121,9 +121,7 @@ public class OpenAiChatModelIT extends AbstractIT {
|
||||
});
|
||||
chatResponseFlux.subscribe();
|
||||
assertThat(latch.await(120, TimeUnit.SECONDS)).isTrue();
|
||||
IntStream.rangeClosed(1, 1000).forEach(n -> {
|
||||
assertThat(answer).contains(String.valueOf(n));
|
||||
});
|
||||
IntStream.rangeClosed(1, 1000).forEach(n -> assertThat(answer).contains(String.valueOf(n)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -333,7 +331,7 @@ public class OpenAiChatModelIT extends AbstractIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@@ -358,7 +356,7 @@ public class OpenAiChatModelIT extends AbstractIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public class OpenAiChatModelResponseFormatIT {
|
||||
|
||||
String content = response.getResult().getOutput().getContent();
|
||||
|
||||
this.logger.info("Response content: {}", content);
|
||||
logger.info("Response content: {}", content);
|
||||
|
||||
assertThat(isValidJson(content)).isTrue();
|
||||
}
|
||||
@@ -132,7 +132,7 @@ public class OpenAiChatModelResponseFormatIT {
|
||||
|
||||
String content = response.getResult().getOutput().getContent();
|
||||
|
||||
this.logger.info("Response content: {}", content);
|
||||
logger.info("Response content: {}", content);
|
||||
|
||||
assertThat(isValidJson(content)).isTrue();
|
||||
}
|
||||
@@ -172,7 +172,7 @@ public class OpenAiChatModelResponseFormatIT {
|
||||
|
||||
String content = response.getResult().getOutput().getContent();
|
||||
|
||||
this.logger.info("Response content: {}", content);
|
||||
logger.info("Response content: {}", content);
|
||||
|
||||
MathReasoning mathReasoning = outputConverter.convert(content);
|
||||
|
||||
|
||||
@@ -143,23 +143,23 @@ public class OpenAiChatModelWithChatResponseMetadataTests {
|
||||
private String getJson() {
|
||||
return """
|
||||
{
|
||||
"id": "chatcmpl-123",
|
||||
"object": "chat.completion",
|
||||
"created": 1677652288,
|
||||
"model": "gpt-3.5-turbo-0613",
|
||||
"choices": [{
|
||||
"id": "chatcmpl-123",
|
||||
"object": "chat.completion",
|
||||
"created": 1677652288,
|
||||
"model": "gpt-3.5-turbo-0613",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "I surrender!"
|
||||
"role": "assistant",
|
||||
"content": "I surrender!"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {
|
||||
}],
|
||||
"usage": {
|
||||
"prompt_tokens": 9,
|
||||
"completion_tokens": 12,
|
||||
"total_tokens": 21
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
@@ -52,8 +52,6 @@ public class OpenAiCompatibleChatModelIT {
|
||||
return OpenAiChatOptions.builder().withModel(modelName).build();
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
static Stream<ChatModel> openAiCompatibleApis() {
|
||||
Stream.Builder<ChatModel> builder = Stream.builder();
|
||||
|
||||
|
||||
@@ -155,19 +155,19 @@ public class OpenAiPaymentTransactionIT {
|
||||
}
|
||||
|
||||
private AdvisedRequest before(AdvisedRequest request) {
|
||||
this.logger.info("System text: \n" + request.systemText());
|
||||
this.logger.info("System params: " + request.systemParams());
|
||||
this.logger.info("User text: \n" + request.userText());
|
||||
this.logger.info("User params:" + request.userParams());
|
||||
this.logger.info("Function names: " + request.functionNames());
|
||||
logger.info("System text: \n" + request.systemText());
|
||||
logger.info("System params: " + request.systemParams());
|
||||
logger.info("User text: \n" + request.userText());
|
||||
logger.info("User params:" + request.userParams());
|
||||
logger.info("Function names: " + request.functionNames());
|
||||
|
||||
this.logger.info("Options: " + request.chatOptions().toString());
|
||||
logger.info("Options: " + request.chatOptions().toString());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private void observeAfter(AdvisedResponse advisedResponse) {
|
||||
this.logger.info("Response: " + advisedResponse.response());
|
||||
logger.info("Response: " + advisedResponse.response());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
@@ -124,13 +124,13 @@ public class OpenAiRetryTests {
|
||||
|
||||
var choice = new ChatCompletion.Choice(ChatCompletionFinishReason.STOP, 0,
|
||||
new ChatCompletionMessage("Response", Role.ASSISTANT), null);
|
||||
ChatCompletion expectedChatCompletion = new ChatCompletion("id", List.of(choice), 666l, "model", null, null,
|
||||
ChatCompletion expectedChatCompletion = new ChatCompletion("id", List.of(choice), 666L, "model", null, null,
|
||||
new OpenAiApi.Usage(10, 10, 10));
|
||||
|
||||
when(this.openAiApi.chatCompletionEntity(isA(ChatCompletionRequest.class), any()))
|
||||
.thenThrow(new TransientAiException("Transient Error 1"))
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedChatCompletion)));
|
||||
given(this.openAiApi.chatCompletionEntity(isA(ChatCompletionRequest.class), any()))
|
||||
.willThrow(new TransientAiException("Transient Error 1"))
|
||||
.willThrow(new TransientAiException("Transient Error 2"))
|
||||
.willReturn(ResponseEntity.of(Optional.of(expectedChatCompletion)));
|
||||
|
||||
var result = this.chatModel.call(new Prompt("text"));
|
||||
|
||||
@@ -142,8 +142,8 @@ public class OpenAiRetryTests {
|
||||
|
||||
@Test
|
||||
public void openAiChatNonTransientError() {
|
||||
when(this.openAiApi.chatCompletionEntity(isA(ChatCompletionRequest.class), any()))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
given(this.openAiApi.chatCompletionEntity(isA(ChatCompletionRequest.class), any()))
|
||||
.willThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> this.chatModel.call(new Prompt("text")));
|
||||
}
|
||||
|
||||
@@ -153,13 +153,13 @@ public class OpenAiRetryTests {
|
||||
|
||||
var choice = new ChatCompletionChunk.ChunkChoice(ChatCompletionFinishReason.STOP, 0,
|
||||
new ChatCompletionMessage("Response", Role.ASSISTANT), null);
|
||||
ChatCompletionChunk expectedChatCompletion = new ChatCompletionChunk("id", List.of(choice), 666l, "model", null,
|
||||
ChatCompletionChunk expectedChatCompletion = new ChatCompletionChunk("id", List.of(choice), 666L, "model", null,
|
||||
null, null);
|
||||
|
||||
when(this.openAiApi.chatCompletionStream(isA(ChatCompletionRequest.class), any()))
|
||||
.thenThrow(new TransientAiException("Transient Error 1"))
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(Flux.just(expectedChatCompletion));
|
||||
given(this.openAiApi.chatCompletionStream(isA(ChatCompletionRequest.class), any()))
|
||||
.willThrow(new TransientAiException("Transient Error 1"))
|
||||
.willThrow(new TransientAiException("Transient Error 2"))
|
||||
.willReturn(Flux.just(expectedChatCompletion));
|
||||
|
||||
var result = this.chatModel.stream(new Prompt("text"));
|
||||
|
||||
@@ -172,8 +172,8 @@ public class OpenAiRetryTests {
|
||||
@Test
|
||||
@Disabled("Currently stream() does not implmement retry")
|
||||
public void openAiChatStreamNonTransientError() {
|
||||
when(this.openAiApi.chatCompletionStream(isA(ChatCompletionRequest.class), any()))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
given(this.openAiApi.chatCompletionStream(isA(ChatCompletionRequest.class), any()))
|
||||
.willThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> this.chatModel.stream(new Prompt("text")).subscribe());
|
||||
}
|
||||
|
||||
@@ -183,10 +183,10 @@ public class OpenAiRetryTests {
|
||||
EmbeddingList<Embedding> expectedEmbeddings = new EmbeddingList<>("list",
|
||||
List.of(new Embedding(0, new float[] { 9.9f, 8.8f })), "model", new OpenAiApi.Usage(10, 10, 10));
|
||||
|
||||
when(this.openAiApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.thenThrow(new TransientAiException("Transient Error 1"))
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedEmbeddings)));
|
||||
given(this.openAiApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.willThrow(new TransientAiException("Transient Error 1"))
|
||||
.willThrow(new TransientAiException("Transient Error 2"))
|
||||
.willReturn(ResponseEntity.of(Optional.of(expectedEmbeddings)));
|
||||
|
||||
var result = this.embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null));
|
||||
@@ -199,8 +199,8 @@ public class OpenAiRetryTests {
|
||||
|
||||
@Test
|
||||
public void openAiEmbeddingNonTransientError() {
|
||||
when(this.openAiApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.thenThrow(new RuntimeException("Non Transient Error"));
|
||||
given(this.openAiApi.embeddings(isA(EmbeddingRequest.class)))
|
||||
.willThrow(new RuntimeException("Non Transient Error"));
|
||||
assertThrows(RuntimeException.class, () -> this.embeddingModel
|
||||
.call(new org.springframework.ai.embedding.EmbeddingRequest(List.of("text1", "text2"), null)));
|
||||
}
|
||||
@@ -210,10 +210,10 @@ public class OpenAiRetryTests {
|
||||
|
||||
var expectedResponse = new StructuredResponse("nl", 6.7f, "Transcription Text", List.of(), List.of());
|
||||
|
||||
when(this.openAiAudioApi.createTranscription(isA(TranscriptionRequest.class), isA(Class.class)))
|
||||
.thenThrow(new TransientAiException("Transient Error 1"))
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedResponse)));
|
||||
given(this.openAiAudioApi.createTranscription(isA(TranscriptionRequest.class), isA(Class.class)))
|
||||
.willThrow(new TransientAiException("Transient Error 1"))
|
||||
.willThrow(new TransientAiException("Transient Error 2"))
|
||||
.willReturn(ResponseEntity.of(Optional.of(expectedResponse)));
|
||||
|
||||
AudioTranscriptionResponse result = this.audioTranscriptionModel
|
||||
.call(new AudioTranscriptionPrompt(new ClassPathResource("speech/jfk.flac")));
|
||||
@@ -226,8 +226,8 @@ public class OpenAiRetryTests {
|
||||
|
||||
@Test
|
||||
public void openAiAudioTranscriptionNonTransientError() {
|
||||
when(this.openAiAudioApi.createTranscription(isA(TranscriptionRequest.class), isA(Class.class)))
|
||||
.thenThrow(new RuntimeException("Transient Error 1"));
|
||||
given(this.openAiAudioApi.createTranscription(isA(TranscriptionRequest.class), isA(Class.class)))
|
||||
.willThrow(new RuntimeException("Transient Error 1"));
|
||||
assertThrows(RuntimeException.class, () -> this.audioTranscriptionModel
|
||||
.call(new AudioTranscriptionPrompt(new ClassPathResource("speech/jfk.flac"))));
|
||||
}
|
||||
@@ -235,12 +235,12 @@ public class OpenAiRetryTests {
|
||||
@Test
|
||||
public void openAiImageTransientError() {
|
||||
|
||||
var expectedResponse = new OpenAiImageResponse(678l, List.of(new Data("url678", "b64", "prompt")));
|
||||
var expectedResponse = new OpenAiImageResponse(678L, List.of(new Data("url678", "b64", "prompt")));
|
||||
|
||||
when(this.openAiImageApi.createImage(isA(OpenAiImageRequest.class)))
|
||||
.thenThrow(new TransientAiException("Transient Error 1"))
|
||||
.thenThrow(new TransientAiException("Transient Error 2"))
|
||||
.thenReturn(ResponseEntity.of(Optional.of(expectedResponse)));
|
||||
given(this.openAiImageApi.createImage(isA(OpenAiImageRequest.class)))
|
||||
.willThrow(new TransientAiException("Transient Error 1"))
|
||||
.willThrow(new TransientAiException("Transient Error 2"))
|
||||
.willReturn(ResponseEntity.of(Optional.of(expectedResponse)));
|
||||
|
||||
var result = this.imageModel.call(new ImagePrompt(List.of(new ImageMessage("Image Message"))));
|
||||
|
||||
@@ -252,8 +252,8 @@ public class OpenAiRetryTests {
|
||||
|
||||
@Test
|
||||
public void openAiImageNonTransientError() {
|
||||
when(this.openAiImageApi.createImage(isA(OpenAiImageRequest.class)))
|
||||
.thenThrow(new RuntimeException("Transient Error 1"));
|
||||
given(this.openAiImageApi.createImage(isA(OpenAiImageRequest.class)))
|
||||
.willThrow(new RuntimeException("Transient Error 1"));
|
||||
assertThrows(RuntimeException.class,
|
||||
() -> this.imageModel.call(new ImagePrompt(List.of(new ImageMessage("Image Message")))));
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@ class OpenAiChatClientIT extends AbstractIT {
|
||||
.user(u -> u.text("List five {subject}")
|
||||
.param("subject", "ice cream flavors"))
|
||||
.call()
|
||||
.entity(new ParameterizedTypeReference<List<String>>() {});
|
||||
.entity(new ParameterizedTypeReference<List<String>>() {
|
||||
});
|
||||
// @formatter:on
|
||||
|
||||
logger.info(collection.toString());
|
||||
|
||||
@@ -190,8 +190,8 @@ class OpenAiChatClientMultipleFunctionCallsIT extends AbstractIT {
|
||||
// @formatter:off
|
||||
String response = ChatClient.builder(this.chatModel)
|
||||
.defaultFunction("getCurrentWeather", "Get the weather in location", biFunction)
|
||||
.defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
.build()
|
||||
.defaultUser(u -> u.text("What's the weather like in San Francisco, Tokyo, and Paris?"))
|
||||
.build()
|
||||
.prompt()
|
||||
.toolContext(Map.of("sessionId", "123"))
|
||||
.call().content();
|
||||
|
||||
@@ -253,7 +253,7 @@ class GroqWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@@ -276,7 +276,7 @@ class GroqWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ class MistralWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@@ -279,7 +279,7 @@ class MistralWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ class NvidiaWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@@ -272,7 +272,7 @@ class NvidiaWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ class OllamaWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@@ -296,7 +296,7 @@ class OllamaWithOpenAiChatModelIT {
|
||||
.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
|
||||
.withName("getCurrentWeather")
|
||||
.withDescription("Get the weather in location")
|
||||
.withResponseConverter((response) -> "" + response.temp() + response.unit())
|
||||
.withResponseConverter(response -> "" + response.temp() + response.unit())
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -78,11 +78,11 @@ class EmbeddingIT extends AbstractIT {
|
||||
void embeddingBatchDocumentsThatExceedTheLimit() throws Exception {
|
||||
assertThat(this.embeddingModel).isNotNull();
|
||||
String contentAsString = this.resource.getContentAsString(StandardCharsets.UTF_8);
|
||||
assertThatThrownBy(() -> {
|
||||
this.embeddingModel.embed(List.of(new Document("Hello World"), new Document(contentAsString)),
|
||||
OpenAiEmbeddingOptions.builder().withModel(OpenAiApi.DEFAULT_EMBEDDING_MODEL).build(),
|
||||
new TokenCountBatchingStrategy());
|
||||
}).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(
|
||||
() -> this.embeddingModel.embed(List.of(new Document("Hello World"), new Document(contentAsString)),
|
||||
OpenAiEmbeddingOptions.builder().withModel(OpenAiApi.DEFAULT_EMBEDDING_MODEL).build(),
|
||||
new TokenCountBatchingStrategy()))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -113,17 +113,17 @@ public class OpenAiImageModelWithImageResponseMetadataTests {
|
||||
|
||||
private String getJson() {
|
||||
return """
|
||||
{
|
||||
"created": 1589478378,
|
||||
"data": [
|
||||
{
|
||||
"created": 1589478378,
|
||||
"data": [
|
||||
{
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/4/4e/Mini_Golden_Doodle.jpg"
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/4/4e/Mini_Golden_Doodle.jpg"
|
||||
},
|
||||
{
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/8/85/Goldendoodle_puppy_Marty.jpg"
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/8/85/Goldendoodle_puppy_Marty.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
|
||||
@@ -139,40 +139,40 @@ public class OpenAiModerationModelTests {
|
||||
|
||||
private String getJson() {
|
||||
return """
|
||||
{
|
||||
"id": "modr-XXXXX",
|
||||
"model": "text-moderation-005",
|
||||
"results": [
|
||||
{
|
||||
"flagged": true,
|
||||
"categories": {
|
||||
"sexual": false,
|
||||
"hate": false,
|
||||
"harassment": false,
|
||||
"self-harm": false,
|
||||
"sexual/minors": false,
|
||||
"hate/threatening": false,
|
||||
"violence/graphic": false,
|
||||
"self-harm/intent": false,
|
||||
"self-harm/instructions": false,
|
||||
"harassment/threatening": true,
|
||||
"violence": true
|
||||
},
|
||||
"category_scores": {
|
||||
"sexual": 1.2282071e-06,
|
||||
"hate": 0.010696256,
|
||||
"harassment": 0.29842457,
|
||||
"self-harm": 1.5236925e-08,
|
||||
"sexual/minors": 5.7246268e-08,
|
||||
"hate/threatening": 0.0060676364,
|
||||
"violence/graphic": 4.435014e-06,
|
||||
"self-harm/intent": 8.098441e-10,
|
||||
"self-harm/instructions": 2.8498655e-11,
|
||||
"harassment/threatening": 0.63055265,
|
||||
"violence": 0.99011886
|
||||
}
|
||||
}
|
||||
]
|
||||
{
|
||||
"id": "modr-XXXXX",
|
||||
"model": "text-moderation-005",
|
||||
"results": [
|
||||
{
|
||||
"flagged": true,
|
||||
"categories": {
|
||||
"sexual": false,
|
||||
"hate": false,
|
||||
"harassment": false,
|
||||
"self-harm": false,
|
||||
"sexual/minors": false,
|
||||
"hate/threatening": false,
|
||||
"violence/graphic": false,
|
||||
"self-harm/intent": false,
|
||||
"self-harm/instructions": false,
|
||||
"harassment/threatening": true,
|
||||
"violence": true
|
||||
},
|
||||
"category_scores": {
|
||||
"sexual": 1.2282071e-06,
|
||||
"hate": 0.010696256,
|
||||
"harassment": 0.29842457,
|
||||
"self-harm": 1.5236925e-08,
|
||||
"sexual/minors": 5.7246268e-08,
|
||||
"hate/threatening": 0.0060676364,
|
||||
"violence/graphic": 4.435014e-06,
|
||||
"self-harm/intent": 8.098441e-10,
|
||||
"self-harm/instructions": 2.8498655e-11,
|
||||
"harassment/threatening": 0.63055265,
|
||||
"violence": 0.99011886
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
@@ -112,4 +112,4 @@ public abstract class AbstractIT {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck" />
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStaticImportCheck">
|
||||
<property name="excludes"
|
||||
value="org.springframework.ai.image.observation.ImageModelObservationDocumentation.*, org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation.*, org.springframework.aot.hint.predicate.RuntimeHintsPredicates.*, org.springframework.ai.vectorstore.filter.Filter.ExpressionType.*, org.springframework.ai.chat.observation.ChatModelObservationDocumentation.*, org.assertj.core.api.AssertionsForClassTypes.*, org.junit.jupiter.api.Assertions.*, org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.Matchers.*, org.mockito.ArgumentMatchers.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*, org.springframework.web.reactive.function.server.RequestPredicates.*, org.springframework.web.reactive.function.server.RouterFunctions.*, org.springframework.test.web.servlet.setup.MockMvcBuilders.*" />
|
||||
value="org.springframework.ai.aot.AiRuntimeHints.*, org.springframework.ai.openai.metadata.support.OpenAiApiResponseHeaders.*, org.springframework.ai.image.observation.ImageModelObservationDocumentation.*, org.springframework.ai.embedding.observation.EmbeddingModelObservationDocumentation.*, org.springframework.aot.hint.predicate.RuntimeHintsPredicates.*, org.springframework.ai.vectorstore.filter.Filter.ExpressionType.*, org.springframework.ai.chat.observation.ChatModelObservationDocumentation.*, org.assertj.core.api.AssertionsForClassTypes.*, org.junit.jupiter.api.Assertions.*, org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.Matchers.*, org.mockito.ArgumentMatchers.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*, org.springframework.web.reactive.function.server.RequestPredicates.*, org.springframework.web.reactive.function.server.RouterFunctions.*, org.springframework.test.web.servlet.setup.MockMvcBuilders.*" />
|
||||
</module>
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck" />
|
||||
<module name="com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck" />
|
||||
|
||||
Reference in New Issue
Block a user