Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,31 @@ Server: `https://api.jobdri.site`
| **Mail / Realtime** | JavaMailSender(Gmail SMTP), SSE |
| **Docs / Build** | SpringDoc Swagger, Gradle, GitHub Actions |

## Cohere Embed API 로컬 검증

Cohere 임베딩 클라이언트는 `COHERE_API_KEY` 환경변수를 통해 API key를 읽습니다. 값이 없어도 애플리케이션 기동은 실패하지 않지만, 실제 임베딩 호출 시 명확한 예외가 발생합니다.

```bash
export COHERE_API_KEY='실제_API_키'
```

기본 설정:

- endpoint: `POST https://api.cohere.com/v2/embed`
- model: `embed-v4.0`
- output dimension: `1024`
- document input type: `search_document`
- query input type: `search_query`
- embedding type: `float`

초기 검증용 텍스트:

```text
Spring Boot 기반 REST API 개발 및 PostgreSQL 성능 최적화
```

기본 테스트 스위트는 실제 Cohere API를 호출하지 않습니다. 수동 검증이 필요하면 `COHERE_API_KEY`를 설정한 뒤 Spring 컨텍스트에서 `CohereEmbeddingClient.embedDocuments(...)` 또는 `embedQuery(...)`를 호출해 반환 벡터 차원이 1024인지 확인합니다. 전체 embedding 값이나 API key는 로그에 남기지 않습니다.

## ✨ Key Features

### 1. 인증 및 사용자 관리
Expand Down
21 changes: 21 additions & 0 deletions evaluation/evaluation_nlg_judge_missing_keyword_provenance.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
caseId,sourceResultFile,analysisCount,averageRelevance,averageProblemValidity,averageSentenceTypeConsistency,averageReasonCorrectness,averageContextAwareness,averageFaithfulness,averageTenseConsistency,averageUsability,averageNonMeta,averageMeaningPreservation,noAnalysisAppropriateness,strengthsPrecision,strengthsCoverage,missingKeywordsPrecision,missingKeywordsCoverage,overallUsefulness,errorCodes,shortRationale,judgeInputTokens,judgeOutputTokens,judgeLatencyMs,failureStage
EV-01,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,1,4.0,3.0,4.0,3.0,3.0,4.0,5.0,4.0,4.0,4.0,4,5,5,5,5,4,"[""NONE""]","전반적으로 경험이 잘 드러나지만, 구체적인 절차와 결과 수치가 부족합니다.",6566,280,4806,
EV-02,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,5,5,5,5,5,4,"[""NONE""]","답변은 강점이 잘 드러나지만, 구체적인 행동과 방법론이 부족하여 일부 아쉬움이 있습니다.",6004,130,2873,
EV-03,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,3.0,3.0,3.0,3.0,3.0,4.0,4.0,4.0,4.0,4.0,4,5,5,5,5,4,"[""NONE""]","전반적으로 분석 역량과 경험이 잘 드러나지만, 구체적인 행동과 결과를 더 강조할 필요가 있습니다.",7600,450,6170,
EV-04,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,1,5,5,5,5,2,"[""MISSED_ANALYSIS""]",명확한 문제 문장이 존재하나 분석이 없어서 중요한 첨삭 대상을 놓쳤습니다.,7672,133,2355,
EV-05,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,4.0,3.0,3.0,4.0,3.0,4.0,5.0,4.0,4.0,4.0,4,5,5,5,5,4,"[""NONE""]","지원 동기와 포부가 구체적이지 않아 개선 여지가 있으며, 전반적으로 유용한 분석이 제공되었습니다.",6473,430,6139,
EV-06,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,1,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,4,5,5,5,5,5,"[""NONE""]","전반적으로 구체적인 성과와 실행 방법이 잘 드러나 있으며, 분석의 정확성이 높아 유용한 첨삭 자료로 활용될 수 있습니다.",9172,331,7780,
EV-07,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,4,5,5,5,5,3,"[""NONE""]","답변에 명확한 문제 문장이 없고, 강점 및 누락 키워드도 적절하여 분석 부재가 대체로 적합합니다.",5550,136,2995,
EV-08,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,4.0,3.0,3.0,4.0,3.0,4.0,4.0,3.0,4.0,4.0,4,5,5,5,5,4,"[""NONE""]","전반적으로 자기소개서의 강점이 잘 드러나지만, 구체적인 실행 방안과 방법론이 부족하여 개선 여지가 있습니다.",7503,431,5298,
EV-09,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,4.0,3.0,4.0,4.0,3.0,4.0,5.0,4.0,4.0,4.0,5,5,5,5,5,4,"[""NONE""]","전반적으로 구체적인 행동과 방법론이 부족하지만, 경험을 잘 설명하고 있어 유용한 분석입니다.",6496,418,12493,
EV-10,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,4,5,5,5,5,3,"[""NONE""]","답변에 명확한 문제 문장이 없고, 강점 및 누락 키워드도 적절하여 분석 부재가 대체로 적합합니다.",6709,136,2661,
EV-11,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,2,3,3,2,2,2,[],답변에서 JD의 핵심 경험 요구사항인 복지 프로그램 기획과 행정 지원이 누락되었습니다.,6763,267,4588,
EV-12,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,5.0,4.0,5.0,4.0,4.0,5.0,5.0,4.0,5.0,5.0,4,5,5,5,5,4,"[""NONE""]","전반적으로 직무 적합성을 잘 드러내고 있으나, 구체적인 행동이나 방법이 부족한 부분이 있습니다.",6990,424,6968,
EV-13,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,4.0,3.0,4.0,4.0,3.0,4.0,5.0,4.0,4.0,4.0,4,5,5,5,5,4,"[""NONE""]","전반적으로 경험과 방법론이 잘 서술되었으나, 구체성이 부족한 부분이 있어 개선 여지가 있습니다.",6620,440,5346,
EV-14,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,4,5,5,5,5,3,"[""NONE""]",지원 동기에서 구체적인 행동이나 방법이 부족하여 명확한 메시지를 전달하지 못하고 있습니다.,6673,125,2601,
EV-15,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,1,3,3,2,2,2,"[""MISSED_ANALYSIS""]","지원 동기와 직무 적합성에 대한 구체적인 사례가 부족하며, 중요한 경험 키워드가 누락되었습니다.",5595,217,4432,
EV-16,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,1,5,5,5,5,2,"[""MISSED_ANALYSIS""]","명확한 행동 계획과 방법론이 부족한 문장이 존재하나, 분석이 없어서 중요한 첨삭 대상을 놓쳤습니다.",6670,141,2356,
EV-17,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,4.0,3.0,4.0,4.0,3.0,4.0,5.0,4.0,4.0,4.0,4,5,5,5,5,4,"[""NONE""]","전반적으로 경험이 잘 서술되었으나, 구체적인 행동이나 방법이 부족하여 개선 여지가 있습니다.",6002,408,4717,
EV-18,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,5,3,3,2,2,3,[],"답변에서 JD의 핵심 경험 요구사항인 사내 일반 행정 지원이 누락되어 있으며, 전반적으로 구체적인 경험이 부족합니다.",6023,170,8328,
EV-19,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,2,3.0,3.0,4.0,3.0,3.0,4.0,4.0,3.0,4.0,4.0,3,5,5,5,5,3,"[""NONE""]",지원 동기와 강점에 대한 구체성이 부족하여 개선 여지가 있습니다.,6176,396,6194,
EV-20,evaluation/evaluation_ai_results_two_pass_provenance_strength_fix.csv,0,,,,,,,,,,,1,5,5,5,5,2,"[""MISSED_ANALYSIS""]",명확한 문제 문장이 존재하나 분석이 없어서 중요한 첨삭 대상을 놓쳤습니다.,6018,133,2254,
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
sourceResultFile,caseCount,successCount,judgeFailedCount,averageRelevance,averageProblemValidity,averageSentenceTypeConsistency,averageReasonCorrectness,averageContextAwareness,averageFaithfulness,averageTenseConsistency,averageUsability,averageNonMeta,averageMeaningPreservation,noAnalysisAppropriateness,strengthsPrecision,strengthsCoverage,missingKeywordsPrecision,missingKeywordsCoverage,overallUsefulness,averageJudgeInputTokens,averageJudgeOutputTokens,averageJudgeLatencyMs,averageAnalysisCount,metaImprovementRate,unsupportedFactRate,falsePositiveAnalysisRate,fatalErrorRate,errorCodeCounts
evaluation/evaluation_nlg_judge_policy_alignment.csv,20,20,0,3.85,3.35,3.85,4.1,3.35,4.1,4.6,4.0,4.1,4.1,3.35,4.7,4.7,4.55,4.55,3.45,6412.75,258.95,7965.95,0.9,0.0,0.0,0.0,0.0,"{""MISSED_ANALYSIS"":5,""MISSED_MISSING_KEYWORD"":3,""NONE"":14}"
evaluation/evaluation_nlg_judge_missing_keyword_provenance.csv,20,20,0,4.0,3.3,3.9,3.8,3.3,4.2,4.7,3.9,4.2,4.2,3.4,4.7,4.7,4.55,4.55,3.3,6663.75,279.8,5067.7,0.9,0.0,0.0,0.0,0.0,"{""MISSED_ANALYSIS"":4,""NONE"":14}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.jobdri.jobdri_api.global.cohere;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(CohereProperties.class)
public class CohereConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package com.jobdri.jobdri_api.global.cohere;

import com.jobdri.jobdri_api.global.apiPayload.code.GeneralErrorCode;
import com.jobdri.jobdri_api.global.apiPayload.exception.GeneralException;
import com.jobdri.jobdri_api.global.cohere.dto.CohereEmbeddingRequest;
import com.jobdri.jobdri_api.global.cohere.dto.CohereEmbeddingResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;

import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class CohereEmbeddingClient {
private static final int MAX_TEXTS_PER_REQUEST = 96;
private static final String INPUT_TYPE_SEARCH_DOCUMENT = "search_document";
private static final String INPUT_TYPE_SEARCH_QUERY = "search_query";
private static final List<String> FLOAT_EMBEDDING_TYPE = List.of("float");

private final CohereProperties properties;
private final RestClient restClient;

public CohereEmbeddingClient(CohereProperties properties, RestClient.Builder restClientBuilder) {
this.properties = properties;
this.restClient = restClientBuilder
.baseUrl(properties.baseUrl())
.requestFactory(requestFactory(properties))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
Comment on lines +31 to +38

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

SimpleClientHttpRequestFactory는 커넥션 풀링을 제공하지 않습니다.

SimpleClientHttpRequestFactory는 JDK HttpURLConnection 기반으로 요청마다 새 연결을 열고 닫는 구조라 TCP/TLS 핸드셰이크 비용이 매번 발생하고, 커넥션 풀 크기 등을 세밀하게 제어할 수 없습니다. corpus 적재처럼 대량으로 embedDocuments를 호출하는 시나리오에서는 처리량 저하 요인이 될 수 있습니다. HttpComponentsClientHttpRequestFactory(Apache HttpClient) 등 풀링을 지원하는 팩토리로 교체하는 것을 권장합니다.

As per path instructions, "외부 API 호출 성능: connection pooling ... 대량 요청 시 처리량 저하 가능성"을 우선순위 높게 검토해야 합니다.

♻️ 커넥션 풀링을 지원하는 팩토리로 교체하는 예시
private static ClientHttpRequestFactory requestFactory(CohereProperties properties) {
    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
    connectionManager.setMaxTotal(50);
    connectionManager.setDefaultMaxPerRoute(20);

    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(Timeout.ofMilliseconds(properties.embedding().connectTimeout().toMillis()))
            .setResponseTimeout(Timeout.ofMilliseconds(properties.embedding().readTimeout().toMillis()))
            .build();

    CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .setDefaultRequestConfig(requestConfig)
            .build();

    return new HttpComponentsClientHttpRequestFactory(httpClient);
}

Also applies to: 164-169

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/jobdri/jobdri_api/global/cohere/CohereEmbeddingClient.java`
around lines 31 - 38, Update the requestFactory used by CohereEmbeddingClient to
use a connection-pooling HTTP client such as
HttpComponentsClientHttpRequestFactory instead of
SimpleClientHttpRequestFactory. Configure pooled connection limits and preserve
the existing embedding connect and read timeout values from CohereProperties.

Source: Path instructions


public List<float[]> embedDocuments(List<String> texts) {
return embed(texts, INPUT_TYPE_SEARCH_DOCUMENT);
}

public float[] embedQuery(String text) {
if (!StringUtils.hasText(text)) {
throw invalidParameter("검색 질의 텍스트는 필수입니다.");
}
List<float[]> embeddings = embed(List.of(text), INPUT_TYPE_SEARCH_QUERY);
if (embeddings.isEmpty()) {
throw unavailable("Cohere 검색 질의 임베딩 응답이 비어 있습니다.");
}
return embeddings.getFirst();
}

private List<float[]> embed(List<String> texts, String inputType) {
validateApiKey();
List<String> normalizedTexts = validateTexts(texts);
CohereEmbeddingRequest request = new CohereEmbeddingRequest(
properties.embedding().model(),
normalizedTexts,
inputType,
FLOAT_EMBEDDING_TYPE,
properties.embedding().dimension()
);

CohereEmbeddingResponse response = callCohere(request);
return validateResponse(response, normalizedTexts.size());
}

private CohereEmbeddingResponse callCohere(CohereEmbeddingRequest request) {
try {
return restClient.post()
.uri("/v2/embed")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + properties.apiKey())
.body(request)
.retrieve()
.onStatus(
status -> status.value() == 429 || status.is5xxServerError(),
(ignoredRequest, ignoredResponse) -> {
throw unavailable("Cohere Embed API가 일시적으로 응답할 수 없습니다.");
}
)
.onStatus(
status -> status.value() == 400
|| status.value() == 401
|| status.value() == 403
|| status.is4xxClientError(),
(ignoredRequest, ignoredResponse) -> {
throw invalidParameter("Cohere Embed API 요청 또는 설정이 올바르지 않습니다.");
}
)
Comment on lines +77 to +91

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

429/5xx에 대한 재시도(backoff) 정책이 없습니다.

현재는 429/5xx 응답을 즉시 SERVICE_UNAVAILABLE로 변환해 실패시킵니다. Cohere는 일시적인 rate limit(429)에 대해 지수 백오프 재시도를 권장하는 사례가 흔한데, 이 구현은 상위 호출자에게 즉시 예외를 전파합니다. 1차 연동 범위상 당장 필수는 아니지만, 향후 실호출 트래픽이 늘어나면 429에서 재시도(가능하면 Retry-After 헤더 반영)를 추가하는 것을 권장합니다.

As per path instructions, "외부 API 호출 성능: ... 재시도 정책, rate limit 대응"을 우선순위 높게 검토해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/jobdri/jobdri_api/global/cohere/CohereEmbeddingClient.java`
around lines 77 - 91, Update the Cohere request flow around the 429/5xx handler
to retry transient failures with bounded exponential backoff before converting
them to SERVICE_UNAVAILABLE. Honor the Retry-After response header when present,
while preserving the existing invalidParameter handling for 4xx client errors
and the final unavailable error after retries are exhausted.

Source: Path instructions

.body(CohereEmbeddingResponse.class);
} catch (GeneralException e) {
throw e;
} catch (ResourceAccessException e) {
log.warn("Cohere Embed API access failed. reason=resource_access_failure, message={}", e.getMessage());
throw new GeneralException(
GeneralErrorCode.EXTERNAL_SERVICE_TIMEOUT,
"Cohere Embed API 응답이 지연되었거나 연결할 수 없습니다.",
e
);
} catch (RestClientException e) {
log.warn("Cohere Embed API call failed. reason=rest_client_failure, message={}", e.getMessage());
throw unavailable("Cohere Embed API 호출에 실패했습니다.", e);
}
}

private List<float[]> validateResponse(CohereEmbeddingResponse response, int expectedCount) {
if (response == null || response.embeddings() == null || response.embeddings().floatValues() == null
|| response.embeddings().floatValues().isEmpty()) {
throw unavailable("Cohere 임베딩 응답이 비어 있습니다.");
}

List<List<Double>> embeddings = response.embeddings().floatValues();
if (embeddings.size() != expectedCount) {
throw unavailable("Cohere 임베딩 응답 개수가 요청 개수와 일치하지 않습니다.");
}

List<float[]> result = new ArrayList<>();
for (List<Double> embedding : embeddings) {
if (embedding == null || embedding.size() != properties.embedding().dimension()) {
throw unavailable("Cohere 임베딩 차원이 설정값과 일치하지 않습니다.");
}
float[] vector = new float[embedding.size()];
for (int i = 0; i < embedding.size(); i++) {
Double value = embedding.get(i);
if (value == null) {
throw unavailable("Cohere 임베딩 벡터에 비어 있는 값이 포함되어 있습니다.");
}
vector[i] = value.floatValue();
}
result.add(vector);
}
return result;
}

private void validateApiKey() {
if (!properties.hasApiKey()) {
throw new GeneralException(
GeneralErrorCode.SERVICE_UNAVAILABLE,
"Cohere API 키가 설정되지 않았습니다."
);
}
}

private List<String> validateTexts(List<String> texts) {
if (texts == null || texts.isEmpty()) {
throw invalidParameter("임베딩할 텍스트는 1개 이상이어야 합니다.");
}
if (texts.size() > MAX_TEXTS_PER_REQUEST) {
throw invalidParameter("Cohere 임베딩은 한 번에 최대 96개 텍스트만 요청할 수 있습니다.");
}

List<String> normalizedTexts = new ArrayList<>();
for (String text : texts) {
if (!StringUtils.hasText(text)) {
throw invalidParameter("임베딩할 텍스트는 비어 있을 수 없습니다.");
}
normalizedTexts.add(text.trim());
}
return List.copyOf(normalizedTexts);
}

private static SimpleClientHttpRequestFactory requestFactory(CohereProperties properties) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(properties.embedding().connectTimeout());
requestFactory.setReadTimeout(properties.embedding().readTimeout());
return requestFactory;
}

private GeneralException invalidParameter(String message) {
return new GeneralException(GeneralErrorCode.INVALID_PARAMETER, message);
}

private GeneralException unavailable(String message) {
return new GeneralException(GeneralErrorCode.SERVICE_UNAVAILABLE, message);
}

private GeneralException unavailable(String message, Throwable cause) {
return new GeneralException(GeneralErrorCode.SERVICE_UNAVAILABLE, message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.jobdri.jobdri_api.global.cohere;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.time.Duration;

@ConfigurationProperties(prefix = "cohere")
public record CohereProperties(
String apiKey,
String baseUrl,
Embedding embedding
) {
private static final String DEFAULT_BASE_URL = "https://api.cohere.com";

public CohereProperties {
baseUrl = hasText(baseUrl) ? baseUrl : DEFAULT_BASE_URL;
embedding = embedding == null ? new Embedding(null, null, null, null) : embedding;
}

boolean hasApiKey() {
return hasText(apiKey);
}

public record Embedding(
String model,
Integer dimension,
Duration connectTimeout,
Duration readTimeout
) {
private static final String DEFAULT_MODEL = "embed-v4.0";
private static final int DEFAULT_DIMENSION = 1024;
private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(3);
private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(15);

public Embedding {
model = hasText(model) ? model : DEFAULT_MODEL;
dimension = dimension == null ? DEFAULT_DIMENSION : dimension;
connectTimeout = connectTimeout == null ? DEFAULT_CONNECT_TIMEOUT : connectTimeout;
readTimeout = readTimeout == null ? DEFAULT_READ_TIMEOUT : readTimeout;
}
}

private static boolean hasText(String value) {
return value != null && !value.isBlank();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.jobdri.jobdri_api.global.cohere.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

public record CohereEmbeddingRequest(
String model,
List<String> texts,
@JsonProperty("input_type")
String inputType,
@JsonProperty("embedding_types")
List<String> embeddingTypes,
@JsonProperty("output_dimension")
Integer outputDimension
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.jobdri.jobdri_api.global.cohere.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

public record CohereEmbeddingResponse(
Embeddings embeddings
) {
public record Embeddings(
@JsonProperty("float")
List<List<Double>> floatValues
) {
}
}
11 changes: 11 additions & 0 deletions src/main/resources/application-analysis-eval.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ evaluation:
output: ""
review-output: ""

cohere:
api-key: ${COHERE_API_KEY:}
base-url: ${COHERE_BASE_URL:https://api.cohere.com}
embedding:
model: ${COHERE_EMBEDDING_MODEL:embed-v4.0}
dimension: ${COHERE_EMBEDDING_DIMENSION:1024}
connect-timeout: ${COHERE_EMBEDDING_CONNECT_TIMEOUT:3s}
read-timeout: ${COHERE_EMBEDDING_READ_TIMEOUT:15s}
api:
key: ${COHERE_API_KEY:}

jwt:
secret:
key: ${JWT_SECRET_KEY:am9iZHJpLWFuYWx5c2lzLWV2YWwtbG9jYWwtc2VjcmV0LWtleQ==}
Expand Down
Loading
Loading