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
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ public ResponseEntity<DiagnosisAlgorithmMapping> getDiagnosis(@RequestBody Diagn
@Operation(summary = "1차 시스템 목록 조회", description = "자가진단 알고리즘에서 사용 가능한 'system' 리스트를 언어 코드 기준으로 조회합니다.")
@GetMapping("/systems")
public ResponseEntity<List<String>> getSystems(
@Parameter(description = "언어 코드 (ko 또는 en)", example = "en")
@Parameter(description = "언어 코드 (ko, en 등)", example = "en")
@RequestParam(defaultValue = "en") String languageCode) {
return ResponseEntity.ok(diagnosisService.getUniqueSystems(languageCode));
}

@Operation(summary = "2차 증상 목록 조회", description = "선택한 'system'에 해당하는 'symptom' 리스트를 조회합니다.")
@GetMapping("/symptoms")
public ResponseEntity<List<String>> getSymptoms(
@Parameter(description = "언어 코드 (ko 또는 en)", example = "en")
@Parameter(description = "언어 코드 (ko, en 등)", example = "en")
@RequestParam(defaultValue = "en") String languageCode,

@Parameter(description = "1차 시스템 이름", example = "Digestive")
Expand All @@ -63,7 +63,7 @@ public ResponseEntity<List<String>> getSymptoms(
@Operation(summary = "3차 조건 목록 조회", description = "'system'과 'symptom' 조합에 해당하는 'condition' 리스트를 조회합니다. 'three-step' 항목일 경우에만 존재합니다.")
@GetMapping("/conditions")
public ResponseEntity<List<String>> getConditions(
@Parameter(description = "언어 코드 (ko 또는 en)", example = "en")
@Parameter(description = "언어 코드 (ko, en 등)", example = "en")
@RequestParam(defaultValue = "en") String languageCode,

@Parameter(description = "1차 시스템 이름", example = "Digestive")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ public class MedicalVisitGuideStep {
public static class LocalizedText {
private String ko;
private String en;
private String zh;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public class OuchGuideEntry {
public static class LocalizedText {
private String ko;
private String en;
private String zh;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ public class AllDepartmentResponse {
private Long code;
private String nameKr;
private String nameEn;
private String nameZh;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,34 @@ public class DiagnosisAlgorithmMapping {
public static class DiagnosisAlgorithmSystem {
private String ko;
private String en;
private String zh;
}

@Getter
public static class DiagnosisAlgorithmSymptom {
private String ko;
private String en;
private String zh;
}

@Getter
public static class DiagnosisAlgorithmCondition {
private String ko;
private String zh;
private String en;
}

@Getter
public static class DiagnosisAlgorithmDepartment {
private String ko;
private String zh;
private String en;
}

@Getter
public static class DiagnosisAlgorithmNote {
private String ko;
private String zh;
private String en;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ public class RealtimeSessionController {
@Value("${openai.api-key}")
private String openaiApiKey;

@PostMapping("/session")
public ResponseEntity<String> createEphemeralKey() {
@PostMapping("/session/en")
public ResponseEntity<String> createEphemeralEnKey() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + openaiApiKey);
headers.set("Content-Type", "application/json");

String instructions = "한국 병원에서 영어를 사용하는 환자와 한국어를 사용하는 병원 관계자의 대화가 입력될거야. 둘이 언어가 통하지 않으니 영어와 한국어로 통시 통역이 필요해. 그래서 한국 말은 영어로, 영어는 한국어로 동시 통역을 해 줘. 너는 개인적인 대답을 하지 말고 오직 번역만 진행하면 돼. 너한테 말걸어도 문장 그대로 번역만 해. 천천히 친절하게 대답해.";
//한국 병원에서 영어를 사용하는 환자와 한국어를 사용하는 병원 관계자의 대화가 입력될거야
String instructions = "영어를 사용하는 사람과 한국어를 사용하는 사람이 대화를 하는 상황이므로 영어와 한국어로 통역이 필요해. "
+ "따라서 한국 말은 영어로, 영어는 한국어로 통역을 해 줘. "
+ "너는 절대 개인적인 대답이나 조언을 하지 말고 번역만 진행하면 돼. "
+ "너한테 말걸어도 문장 그대로 번역만 해. 천천히 친절하게 대답해.";

// "You are a strict translation assistant specializing exclusively in medical scenarios for a Korean hospital setting. "
// + "- Your SOLE task is strict translation. Under NO circumstances should you answer questions, provide advice, or respond to any input other than translating. "
Expand All @@ -30,7 +34,7 @@ public ResponseEntity<String> createEphemeralKey() {
// + "- The output must ONLY be the translated text, delivered slowly, gently, and in a reassuring manner appropriate for patients. Absolutely NO other communication is permitted.";

String requestBody = "{"
+ "\"model\": \"gpt-4o-mini-realtime-preview\","
+ "\"model\": \"gpt-4o-mini-realtime-preview-2024-12-17\","
+ "\"modalities\": [\"audio\", \"text\"],"
+ "\"instructions\": \"" + instructions + "\","
+ "\"voice\": \"sage\","
Expand All @@ -49,19 +53,67 @@ public ResponseEntity<String> createEphemeralKey() {
+ "\"silence_duration_ms\": 1000,"
+ "\"prefix_padding_ms\": 300,"
+ "\"threshold\": 0.6"
+ "}" //,제외함 밑에 각주처리해서

// + "\"tool_choice\": \"auto\","
// + "\"tools\": ["
// + "{"
// + "\"type\": \"function\","
// + "\"name\": \"get_patient_info\","
// + "\"description\": \"Fetch detailed patient information from the hospital database.\","
// + "\"parameters\": {"
// + "\"type\": \"object\","
// + "\"properties\": {}"
// + "}"
// + "}]"
+ "}";

HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);

ResponseEntity<String> response = restTemplate.exchange(
"https://api.openai.com/v1/realtime/sessions",
HttpMethod.POST,
entity,
String.class
);

return response;
}

@PostMapping("/session/zh")
public ResponseEntity<String> createEphemeralZhKey() {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + openaiApiKey);
headers.set("Content-Type", "application/json");

//한국 병원에서 영어를 사용하는 환자와 한국어를 사용하는 병원 관계자의 대화가 입력될거야
String instructions = "중국어를 사용하는 사람과 한국어를 사용하는 사람이 대화를 하는 상황이므로 중국어와 한국어로 통역이 필요해. "
+ "따라서 한국 말은 중국어로, 중국어는 한국어로 통역을 해 줘. "
+ "너는 절대 개인적인 대답이나 조언을 하지 말고 번역만 진행하면 돼. "
+ "너한테 말걸어도 문장 그대로 번역만 해. 천천히 친절하게 대답해.";

String requestBody = "{"
+ "\"model\": \"gpt-4o-mini-realtime-preview-2024-12-17\","
+ "\"modalities\": [\"audio\", \"text\"],"
+ "\"instructions\": \"" + instructions + "\","
+ "\"voice\": \"sage\","
+ "\"input_audio_format\": \"pcm16\","
+ "\"output_audio_format\": \"pcm16\","
+ "\"temperature\": 0.6,"

+ "\"input_audio_transcription\": {" //새로 나온 모델 추가
+ "\"model\": \"gpt-4o-mini-transcribe\","
// + "\"language\": \"\","
+ "\"prompt\": \"This audio input may contain both Korean and Chinese words mixed together. Please transcribe both languages accurately.\""
+ "},"

+ "\"tool_choice\": \"auto\","
+ "\"tools\": ["
+ "{"
+ "\"type\": \"function\","
+ "\"name\": \"get_patient_info\","
+ "\"description\": \"Fetch detailed patient information from the hospital database.\","
+ "\"parameters\": {"
+ "\"type\": \"object\","
+ "\"properties\": {}"
+ "\"turn_detection\": {"
+ "\"type\": \"server_vad\","
+ "\"silence_duration_ms\": 1000,"
+ "\"prefix_padding_ms\": 300,"
+ "\"threshold\": 0.6"
+ "}"
+ "}]"
+ "}";

HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
Expand All @@ -75,5 +127,4 @@ public ResponseEntity<String> createEphemeralKey() {

return response;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.onebridge.ouch.apiPayload.code.error.CommonErrorCode;
import com.onebridge.ouch.apiPayload.code.error.ErrorCode;
import com.onebridge.ouch.apiPayload.exception.OuchException;
import com.onebridge.ouch.dto.guide.OuchGuideEntry;
import jakarta.annotation.PostConstruct;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

Expand All @@ -34,23 +32,37 @@ public List<OuchGuideEntry> getAll() {
public List<OuchGuideEntry> getByCategory(String category, String languageCode) {
return allGuides.stream()
.filter(e -> {
String localizedCategory = "en".equalsIgnoreCase(languageCode)
? e.getCategory().getEn()
: e.getCategory().getKo();
String localizedCategory;
if ("en".equalsIgnoreCase(languageCode)) {
localizedCategory = e.getCategory().getEn();
} else if ("zh".equalsIgnoreCase(languageCode)) {
localizedCategory = e.getCategory().getZh();
} else {
localizedCategory = e.getCategory().getKo();
}
return localizedCategory.equalsIgnoreCase(category);
})
.collect(Collectors.toList());
}


public List<String> getAllCategories(String languageCode) {
if (!"ko".equalsIgnoreCase(languageCode) && !"en".equalsIgnoreCase(languageCode)) {
if (!"ko".equalsIgnoreCase(languageCode)
&& !"en".equalsIgnoreCase(languageCode)
&& !"zh".equalsIgnoreCase(languageCode)) {
throw new OuchException(CommonErrorCode.LANGUAGE_NOT_FOUND);
}

return allGuides.stream()
.map(e -> "en".equalsIgnoreCase(languageCode)
? e.getCategory().getEn()
: e.getCategory().getKo())
.map(e -> {
if ("en".equalsIgnoreCase(languageCode)) {
return e.getCategory().getEn();
} else if ("zh".equalsIgnoreCase(languageCode)) {
return e.getCategory().getZh();
} else {
return e.getCategory().getKo();
}
})
.distinct()
.sorted()
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public List<DiagnosisAlgorithmMapping> getAll() {

public Optional<DiagnosisAlgorithmMapping> findMatch(String lang, String system, String symptom, String condition) {
return mappings.stream()
.filter(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), lang).equals(system))
.filter(e -> getByLang(e.getSymptom().getKo(), e.getSymptom().getEn(), lang).equals(symptom))
.filter(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), e.getSystem().getZh(), lang).equals(system))
.filter(e -> getByLang(e.getSymptom().getKo(), e.getSymptom().getEn(), e.getSymptom().getZh(), lang).equals(symptom))
.filter(e -> {
if ("three-step".equals(e.getType()) && e.getCondition() != null) {
return getByLang(e.getCondition().getKo(), e.getCondition().getEn(), lang).equals(condition);
return getByLang(e.getCondition().getKo(), e.getCondition().getEn(), e.getCondition().getZh(), lang).equals(condition);
}
return true;
})
Expand All @@ -45,25 +45,25 @@ public Optional<DiagnosisAlgorithmMapping> findMatch(String lang, String system,

public List<String> getUniqueSystems(String lang) {
return mappings.stream()
.map(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), lang))
.map(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), e.getSystem().getZh(), lang))
.distinct()
.sorted()
.toList();
}

public List<String> getSymptomsBySystem(String system, String lang) {
return mappings.stream()
.filter(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), lang).equals(system))
.map(e -> getByLang(e.getSymptom().getKo(), e.getSymptom().getEn(), lang))
.filter(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), e.getSystem().getZh(), lang).equals(system))
.map(e -> getByLang(e.getSymptom().getKo(), e.getSymptom().getEn(), e.getSymptom().getZh(), lang))
.distinct()
.sorted()
.toList();
}

public List<String> getConditionsBySystemAndSymptom(String system, String symptom, String lang) {
List<DiagnosisAlgorithmMapping> matched = mappings.stream()
.filter(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), lang).equals(system))
.filter(e -> getByLang(e.getSymptom().getKo(), e.getSymptom().getEn(), lang).equals(symptom))
.filter(e -> getByLang(e.getSystem().getKo(), e.getSystem().getEn(), e.getSystem().getZh(), lang).equals(system))
.filter(e -> getByLang(e.getSymptom().getKo(), e.getSymptom().getEn(), e.getSymptom().getZh(), lang).equals(symptom))
.toList();

if (matched.isEmpty()) {
Expand All @@ -72,7 +72,7 @@ public List<String> getConditionsBySystemAndSymptom(String system, String sympto

List<String> conditions = matched.stream()
.filter(e -> "three-step".equals(e.getType()))
.map(e -> getByLang(e.getCondition().getKo(), e.getCondition().getEn(), lang))
.map(e -> getByLang(e.getCondition().getKo(), e.getCondition().getEn(), e.getCondition().getZh(), lang))
.distinct()
.sorted()
.toList();
Expand All @@ -84,10 +84,11 @@ public List<String> getConditionsBySystemAndSymptom(String system, String sympto
return conditions;
}

private String getByLang(String ko, String en, String languageCode) {
private String getByLang(String ko, String en, String zn, String languageCode) {
return switch (languageCode.toLowerCase()) {
case "ko" -> ko;
case "en" -> en;
case "zh" -> zn;
default -> throw new OuchException(CommonErrorCode.LANGUAGE_NOT_FOUND);
};
}
Expand Down
38 changes: 19 additions & 19 deletions src/main/resources/data/department.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
[
{ "code": 1, "nameKr": "내과", "nameEn": "Internal Medicine" },
{ "code": 23, "nameKr": "가정의학과", "nameEn": "Family Medicine" },
{ "code": 4, "nameKr": "외과", "nameEn": "General Surgery" },
{ "code": 5, "nameKr": "정형외과", "nameEn": "Orthopedic Surgery" },
{ "code": 6, "nameKr": "신경외과", "nameEn": "Neurosurgery" },
{ "code": 2, "nameKr": "신경과", "nameEn": "Neurology" },
{ "code": 3, "nameKr": "정신건강의학과", "nameEn": "Psychiatry" },
{ "code": 7, "nameKr": "흉부외과", "nameEn": "Thoracic Surgery" },
{ "code": 8, "nameKr": "성형외과", "nameEn": "Plastic Surgery" },
{ "code": 9, "nameKr": "마취통증의학과", "nameEn": "Anesthesiology & Pain Medicine" },
{ "code": 10, "nameKr": "산부인과", "nameEn": "Obstetrics & Gynecology" },
{ "code": 11, "nameKr": "소아청소년과", "nameEn": "Pediatrics" },
{ "code": 12, "nameKr": "안과", "nameEn": "Ophthalmology" },
{ "code": 13, "nameKr": "이비인후과", "nameEn": "Otorhinolaryngology (ENT)" },
{ "code": 14, "nameKr": "피부과", "nameEn": "Dermatology" },
{ "code": 15, "nameKr": "비뇨의학과", "nameEn": "Urology" },
{ "code": 21, "nameKr": "재활의학과", "nameEn": "Rehabilitation Medicine" },
{ "code": 49, "nameKr": "치과", "nameEn": "Dentistry" },
{ "code": 24, "nameKr": "응급의학과", "nameEn": "Emergency Medicine" }
{ "code": 1, "nameKr": "내과", "nameEn": "Internal Medicine", "nameZh": "内科" },
{ "code": 23, "nameKr": "가정의학과", "nameEn": "Family Medicine", "nameZh": "家庭医学科" },
{ "code": 4, "nameKr": "외과", "nameEn": "General Surgery", "nameZh": "外科" },
{ "code": 5, "nameKr": "정형외과", "nameEn": "Orthopedic Surgery", "nameZh": "骨科" },
{ "code": 6, "nameKr": "신경외과", "nameEn": "Neurosurgery", "nameZh": "神经外科" },
{ "code": 2, "nameKr": "신경과", "nameEn": "Neurology", "nameZh": "神经内科" },
{ "code": 3, "nameKr": "정신건강의학과", "nameEn": "Psychiatry", "nameZh": "精神健康医学科" },
{ "code": 7, "nameKr": "흉부외과", "nameEn": "Thoracic Surgery", "nameZh": "胸外科" },
{ "code": 8, "nameKr": "성형외과", "nameEn": "Plastic Surgery", "nameZh": "整形外科" },
{ "code": 9, "nameKr": "마취통증의학과", "nameEn": "Anesthesiology & Pain Medicine", "nameZh": "麻醉疼痛医学科" },
{ "code": 10, "nameKr": "산부인과", "nameEn": "Obstetrics & Gynecology", "nameZh": "妇产科" },
{ "code": 11, "nameKr": "소아청소년과", "nameEn": "Pediatrics", "nameZh": "儿科" },
{ "code": 12, "nameKr": "안과", "nameEn": "Ophthalmology", "nameZh": "眼科" },
{ "code": 13, "nameKr": "이비인후과", "nameEn": "Otorhinolaryngology (ENT)", "nameZh": "耳鼻咽喉科" },
{ "code": 14, "nameKr": "피부과", "nameEn": "Dermatology", "nameZh": "皮肤科" },
{ "code": 15, "nameKr": "비뇨의학과", "nameEn": "Urology", "nameZh": "泌尿外科" },
{ "code": 21, "nameKr": "재활의학과", "nameEn": "Rehabilitation Medicine", "nameZh": "康复医学科" },
{ "code": 49, "nameKr": "치과", "nameEn": "Dentistry", "nameZh": "口腔科" },
{ "code": 24, "nameKr": "응급의학과", "nameEn": "Emergency Medicine", "nameZh": "急诊医学科" }
]
Loading