feat: Add OpenRouter AI integration with config, service, and tests

This commit is contained in:
2025-02-14 18:52:57 +03:00
parent 99ef7161b9
commit a4269a5d8c
7 changed files with 161 additions and 1 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ dependencies {
runtimeOnly group: 'org.flywaydb', name: 'flyway-database-postgresql', version: '11.3.1'
// https://mvnrepository.com/artifact/org.postgresql/postgresql
implementation group: 'org.postgresql', name: 'postgresql', version: '42.7.5'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
@@ -0,0 +1,16 @@
package ru.cathub.telegabot.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "openrouter.api")
@Getter
@Setter
public class OpenRouterConfig {
private String key;
private String url;
private String model;
}
@@ -0,0 +1,11 @@
package ru.cathub.telegabot.exception;
public class OpenRouterException extends Exception {
public OpenRouterException(String message) {
super(message);
}
public OpenRouterException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,7 @@
package ru.cathub.telegabot.service;
import ru.cathub.telegabot.exception.OpenRouterException;
public interface OpenRouterService {
String getChatResponse(String message) throws OpenRouterException;
}
@@ -0,0 +1,65 @@
package ru.cathub.telegabot.service.impl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import ru.cathub.telegabot.config.OpenRouterConfig;
import ru.cathub.telegabot.exception.OpenRouterException;
import ru.cathub.telegabot.service.OpenRouterService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
@RequiredArgsConstructor
@Slf4j
public class OpenRouterServiceImpl implements OpenRouterService {
private final OpenRouterConfig config;
private final RestTemplate restTemplate;
@Override
public String getChatResponse(String message) throws OpenRouterException {
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(config.getKey());
headers.add("HTTP-Referer", "https://github.com/your-username/telegabot");
headers.add("X-Title", "TelegaBot");
Map<String, Object> request = new HashMap<>();
request.put("model", config.getModel());
request.put("messages", List.of(Map.of(
"role", "user",
"content", message
)));
try {
ResponseEntity<Map> response = restTemplate.postForEntity(
config.getUrl() + "/chat/completions",
new HttpEntity<>(request, headers),
Map.class
);
return extractResponse(response.getBody());
} catch (RestClientException e) {
log.error("OpenRouter API error: {}", e.getMessage());
throw new OpenRouterException("API request failed: " + e.getMessage());
}
}
private String extractResponse(Map<String, Object> response) {
try {
List<Map<String, Object>> choices = (List<Map<String, Object>>) response.get("choices");
Map<String, Object> firstChoice = choices.get(0);
Map<String, String> message = (Map<String, String>) firstChoice.get("message");
return message.get("content");
} catch (Exception e) {
throw new OpenRouterException("Error parsing API response");
}
}
}
@@ -37,3 +37,9 @@ logging.level.io.hypersistence.utils.hibernate.query=DEBUG
# ??????????? ????
logging.level.org.springframework.cache=INFO
# OpenRouter AI
openrouter.api.url=https://openrouter.ai/api/v1
openrouter.api.model=gryphe/mythomax-l2-13b
# Для безопасности лучше установить через переменную окружения:
openrouter.api.key=${OPENROUTER_API_KEY}
@@ -0,0 +1,55 @@
package ru.cathub.telegabot.service;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import ru.cathub.telegabot.exception.OpenRouterException;
import ru.cathub.telegabot.service.impl.OpenRouterServiceImpl;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class OpenRouterServiceTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private OpenRouterServiceImpl openRouterService;
@Test
void shouldReturnValidResponse() throws OpenRouterException {
// Mock response
Map<String, Object> mockResponse = Map.of(
"choices", List.of(Map.of(
"message", Map.of("content", "Test response")
))
);
when(restTemplate.postForEntity(anyString(), any(), eq(Map.class)))
.thenReturn(new ResponseEntity<>(mockResponse, HttpStatus.OK));
String response = openRouterService.getChatResponse("Test message");
assertEquals("Test response", response);
}
@Test
void shouldThrowExceptionOnApiError() {
when(restTemplate.postForEntity(anyString(), any(), eq(Map.class)))
.thenThrow(new RestClientException("API error"));
assertThrows(OpenRouterException.class,
() -> openRouterService.getChatResponse("Test message"));
}
}