package com.homme.demo.service; import java.time.Duration; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClientResponseException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import reactor.util.retry.Retry; @Service public class AzureMistralService { @Value("${azure.mistral.endpoint}") private String endPoint; @Value("${azure.mistral.api-key}") private String apiKey; @Value("${azure.mistral.deployment}") private String deployment; @Value("${azure.mistral.api-version}") private String apiVersion; @Autowired private WebClient webClient; @Autowired private ObjectMapper objectMapper; public String askMistral(String systemPrompt, String userPrompt) { int estimatedTokens = Math.min(8000, Math.max(512, userPrompt.length() / 2)); return askMistral(systemPrompt, userPrompt, estimatedTokens); } public String askMistral(String systemPrompt, String userPrompt, int maxTokens) { try { String url = endPoint + "/openai/v1/chat/completions"; System.out.println(">>> MODELL = " + deployment + " | URL = " + url); Map requstBody = Map.of( "model", "gpt-5-mini-datazone", "messages", List.of( Map.of("role", "system","content",systemPrompt), Map.of("role", "user","content", userPrompt) ), "reasoning_effort", "minimal", "max_completion_tokens", maxTokens ); long c0 = System.currentTimeMillis(); String response = webClient.post() .uri(url) .header("api-key", apiKey) .contentType(MediaType.APPLICATION_JSON) .bodyValue(requstBody) .retrieve() .bodyToMono(String.class) .retryWhen( Retry.backoff(2, Duration.ofSeconds(5)) .filter(ex -> ex instanceof WebClientResponseException.TooManyRequests) .onRetryExhaustedThrow((spec, sig) -> sig.failure()) ) .block(); System.out.println(" askMistral daurte = "+ (System.currentTimeMillis()- c0) + "ms"); if(response == null || response.isBlank()) { throw new RuntimeException("Leere Antwort von Azure Mistral"); } JsonNode root = objectMapper.readTree(response); return root .path("choices") .get(0) .get("message") .path("content") .asText(); }catch(WebClientResponseException e) { throw new RuntimeException("Azure Mistral request failed: "+e.getStatusCode() + " Retry-After = "+ e.getHeaders().getFirst("Retry-After") + " Body = "+ e.getResponseBodyAsString()); }catch (Exception e) { throw new RuntimeException("Azure Mistral request failed: "+ e.getMessage()); } } public String askMistral(List> messages, int maxTokens) { try { String url = endPoint + "/openai/v1/chat/completions"; System.out.println(">>> MODELL = " + deployment + " | URL = " + url); Map requstBody = Map.of( "model", "gpt-5-mini-datazone", "messages", messages, "reasoning_effort", "minimal", "max_completion_tokens", maxTokens ); long c0 = System.currentTimeMillis(); String response = webClient.post() .uri(url) .header("api-key", apiKey) .contentType(MediaType.APPLICATION_JSON) .bodyValue(requstBody) .retrieve() .bodyToMono(String.class) .retryWhen( Retry.backoff(2, Duration.ofSeconds(5)) .filter(ex -> ex instanceof WebClientResponseException.TooManyRequests) .onRetryExhaustedThrow((spec, sig) -> sig.failure()) ) .block(); System.out.println(" askMistral daurte = " + (System.currentTimeMillis() - c0) + "ms"); if (response == null || response.isBlank()) { throw new RuntimeException("Leere Antwort von Azure Mistral"); } JsonNode root = objectMapper.readTree(response); return root.path("choices").get(0).get("message").path("content").asText(); } catch (WebClientResponseException e) { throw new RuntimeException("Azure Mistral request failed: " + e.getStatusCode() + " Retry-After = " + e.getHeaders().getFirst("Retry-After") + " Body = " + e.getResponseBodyAsString()); } catch (Exception e) { throw new RuntimeException("Azure Mistral request failed: " + e.getMessage()); } } }