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 @@ -2,7 +2,7 @@ package com.back.koreaTravelGuide.domain.ai.aiChat.controller

// TODO: 채팅 컨트롤러 - AI 채팅 API 및 SSE 스트리밍 엔드포인트 제공
import com.back.koreaTravelGuide.domain.ai.aiChat.tool.WeatherTool
import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.WeatherResponse
import com.back.koreaTravelGuide.domain.ai.weather.dto.MidForecastDto
import org.springframework.ai.chat.client.ChatClient
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
Expand Down Expand Up @@ -117,40 +117,36 @@ class ChatController(
return emitter
}

// 날씨 API 직접 테스트용 엔드포인트
// 날씨 API 직접 테스트용 엔드포인트
@GetMapping("/weather/test")
fun testWeather(
@RequestParam(required = false) location: String?,
@RequestParam(required = false) regionCode: String?,
@RequestParam(required = false) baseTime: String?,
): WeatherResponse {
return weatherTool.getWeatherForecast(
location = location,
regionCode = regionCode,
): List<MidForecastDto>? {
return weatherTool.queryMidTermNarrative(
baseTime = baseTime,
)
}

// 지역별 날씨 간단 조회
@GetMapping("/weather/simple")
fun simpleWeather(
@RequestParam(defaultValue = "서울") location: String,
): String {
val response =
weatherTool.getWeatherForecast(
location = location,
regionCode = null,
baseTime = null,
)

return """
|지역: ${response.region}
|지역코드: ${response.regionCode}
|발표시각: ${response.baseTime}
|
|${response.forecast}
""".trimMargin()
}
// @GetMapping("/weather/simple")
// fun simpleWeather(
// @RequestParam(defaultValue = "서울") location: String,
// ): String {
// val response =
// weatherTool.getWeatherForecast(
// location = location,
// regionCode = null,
// baseTime = null,
// )
//
// return """
// |지역: ${response.region}
// |지역코드: ${response.regionCode}
// |발표시각: ${response.baseTime}
// |
// |${response.forecast}
// """.trimMargin()
// }

// 현재 서버 시간 확인용 엔드포인트
@GetMapping("/time/current")
Expand All @@ -164,37 +160,37 @@ class ChatController(
}

// 원시 XML 응답 확인용 엔드포인트
@GetMapping("/weather/debug")
fun debugWeatherApi(
@RequestParam(defaultValue = "서울") location: String,
@RequestParam(required = false) regionCode: String?,
@RequestParam(required = false) baseTime: String?,
): Map<String, Any?> {
return try {
println("🚀 디버그 API 호출 시작 - location: $location")
val response =
weatherTool.getWeatherForecast(
location = location,
regionCode = regionCode,
baseTime = baseTime,
)

mapOf(
"success" to true,
"location" to location,
"regionCode" to (regionCode ?: "자동변환"),
"baseTime" to (baseTime ?: "자동계산"),
"response" to response,
"hasData" to (response.details.day4 != null || response.details.day5 != null),
"message" to "디버그 정보가 콘솔에 출력되었습니다.",
)
} catch (e: Exception) {
mapOf(
"success" to false,
"error" to (e.message ?: "알 수 없는 오류"),
"location" to location,
"message" to "오류 발생: ${e.message ?: "알 수 없는 오류"}",
)
}
}
// @GetMapping("/weather/debug")
// fun debugWeatherApi(
// @RequestParam(defaultValue = "서울") location: String,
// @RequestParam(required = false) regionCode: String?,
// @RequestParam(required = false) baseTime: String?,
// ): Map<String, Any?> {
// return try {
// println("🚀 디버그 API 호출 시작 - location: $location")
// val response =
// weatherTool.getWeatherForecast(
// location = location,
// regionCode = regionCode,
// baseTime = baseTime,
// )
//
// mapOf(
// "success" to true,
// "location" to location,
// "regionCode" to (regionCode ?: "자동변환"),
// "baseTime" to (baseTime ?: "자동계산"),
// "response" to response,
// "hasData" to (response.details.day4 != null || response.details.day5 != null),
// "message" to "디버그 정보가 콘솔에 출력되었습니다.",
// )
// } catch (e: Exception) {
// mapOf(
// "success" to false,
// "error" to (e.message ?: "알 수 없는 오류"),
// "location" to location,
// "message" to "오류 발생: ${e.message ?: "알 수 없는 오류"}",
// )
// }
// }
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.back.koreaTravelGuide.domain.ai.aiChat.tool

// TODO: AI 날씨 도구 - Spring AI @Tool 어노테이션으로 AI가 호출할 수 있는 날씨 기능
import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.MidTermMetricsResult
import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.MidTermNarrativeResult
import com.back.koreaTravelGuide.domain.ai.weather.dto.remove.WeatherResponse
import com.back.koreaTravelGuide.domain.ai.weather.service.WeatherService
import com.back.koreaTravelGuide.domain.ai.weather.dto.MidForecastDto
import com.back.koreaTravelGuide.domain.ai.weather.dto.TemperatureAndLandForecastDto
import com.back.koreaTravelGuide.domain.ai.weather.service.WeatherServiceCore
import org.springframework.ai.tool.annotation.Tool
import org.springframework.ai.tool.annotation.ToolParam
import org.springframework.stereotype.Service
Expand All @@ -14,53 +13,51 @@ import java.time.format.DateTimeFormatter

@Service
class WeatherTool(
private val weatherService: WeatherService,
private val weatherServiceCore: WeatherServiceCore,
) {
@Tool(description = "현재 한국 시간을 조회합니다.")
fun getCurrentTime(): String {
val now = ZonedDateTime.now(ZoneId.of("Asia/Seoul"))
return "현재 한국 표준시(KST): ${now.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분"))}"
}

@Tool(description = "중기전망 텍스트를 조회해 여행하기 좋은 지역 후보를 파악합니다. 먼저 호출하여 비교할 지역 코드를 추려 주세요.")
@Tool(description = "전국 중기전망 텍스트를 조회해 여행하기 좋은 지역 후보를 파악합니다. 먼저 호출하여 비교할 지역 코드를 추려 주세요.")
fun queryMidTermNarrative(
@ToolParam(description = "권역 또는 대표 지역 이름 (예: 전국, 서울, 부산). 비워 두면 서울 기준.", required = false) regionGroup: String?,
@ToolParam(description = "중기예보 regId (예: 11B10101). regionGroup 대신 직접 지정 가능.", required = false) regionCode: String?,
@ToolParam(description = "발표 시각 (YYYYMMDDHHMM). 미지정 시 최근 발표시각 사용.", required = false) baseTime: String?,
): MidTermNarrativeResult {
return weatherService.getMidTermNarrative(
regionGroup = regionGroup,
regionCode = regionCode,
): List<MidForecastDto>? {
return weatherServiceCore.getWeatherForecast(
baseTime = baseTime,
)
}

@Tool(description = "중기 기온과 강수 확률 지표를 지역별로 조회합니다. 첫 번째 툴에서 제안한 지역 코드로 비교 분석에 사용하세요.")
fun queryMidTermMetrics(
@ToolParam(description = "중기예보 regId 배열 (예: [\"11B10101\", \"11H20301\"]).", required = true) regionCodes: List<String>,
fun queryMidTermAndLandForecastMetrics(
@ToolParam(description = "지역 이름 (예: 서울, 인천)", required = true) location: String?,
@ToolParam(description = "중기예보 regId (예: [\"11B10101\", \"11H20301\"]).", required = true) regionCode: String?,
// @ToolParam(description = "중기예보 regId 배열 (예: [\"11B10101\", \"11H20301\"]).", required = true) regionCodes: List<String>,
@ToolParam(description = "발표 시각 (YYYYMMDDHHMM). 미지정 시 최근 발표시각 사용.", required = false) baseTime: String?,
@ToolParam(description = "확인할 일 수 offset 목록 (4~10). 비워 두면 4~10일 모두 반환.", required = false) days: List<Int>?,
): MidTermMetricsResult {
return weatherService.getMidTermMetrics(
regionCodes = regionCodes,
baseTime = baseTime,
days = days,
)
}

@Deprecated(
message = "AI 툴 분리 이후에는 queryMidTermNarrative/queryMidTermMetrics를 사용하세요.",
level = DeprecationLevel.WARNING,
)
fun getWeatherForecast(
location: String?,
regionCode: String?,
baseTime: String?,
): WeatherResponse {
return weatherService.getWeatherForecast(
// @ToolParam(description = "확인할 일 수 offset 목록 (4~10). 비워 두면 4~10일 모두 반환.", required = false) days: List<Int>?,
): TemperatureAndLandForecastDto? {
return weatherServiceCore.getTemperatureAndLandForecast(
location = location,
regionCode = regionCode,
baseTime = baseTime,
)
}

// @Deprecated(
// message = "AI 툴 분리 이후에는 queryMidTermNarrative/queryMidTermMetrics를 사용하세요.",
// level = DeprecationLevel.WARNING,
// )
// fun getWeatherForecast(
// location: String?,
// regionCode: String?,
// baseTime: String?,
// ): WeatherResponse {
// return weatherService.getWeatherForecast(
// location = location,
// regionCode = regionCode,
// baseTime = baseTime,
// )
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.back.koreaTravelGuide.domain.ai.weather.client
// TODO: 기상청 API 클라이언트 - HTTP 요청으로 날씨 데이터 조회 및 JSON 파싱
import com.back.koreaTravelGuide.domain.ai.weather.client.parser.DataParser
import com.back.koreaTravelGuide.domain.ai.weather.client.tools.Tools
import com.back.koreaTravelGuide.domain.ai.weather.dto.LandForecastData
import com.back.koreaTravelGuide.domain.ai.weather.dto.TemperatureData
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.back.koreaTravelGuide.domain.ai.weather.client.tools

import org.springframework.stereotype.Component

@Component
@Component("clientTools")
class Tools {
fun getStnIdFromRegionCode(regionCode: String): String {
return when {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package com.back.koreaTravelGuide.domain.ai.weather.dto

data class TemperatureAndLandForecastDto()
data class TemperatureAndLandForecastDto(
val a: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class DtoParser {
temperatureData: TemperatureData,
landForecastData: LandForecastData,
): TemperatureAndLandForecastDto {
return TemperatureAndLandForecastDto()
return TemperatureAndLandForecastDto(
"a",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class WeatherService(
private val weatherApiClient: WeatherApiClient,
private val parser: DtoParser,
) {
@Cacheable("weatherMidFore", key = "#actualRegionCode + '_' + #actualBaseTime")
@Cacheable("weatherMidFore", key = "'전국_' + #actualBaseTime")
fun fetchMidForecast(actualBaseTime: String): List<MidForecastDto>? {
val prefixes = listOf("11B", "11D1", "11D2", "11C2", "11C1", "11F2", "11F1", "11H1", "11H2", "11G")
val midForecastList = mutableListOf<MidForecastDto>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

@Component
@Component("serviceTools")
open class Tools {
fun getRegionCodeFromLocation(location: String): String {
return REGION_MAP[location] ?: "11B10101"
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ springdoc:
# Weather API 설정
weather:
api:
key: ${WEATHER_API_KEY:your-weather-api-key-here}
key: ${WEATHER_API_KEY:6f5c5689b976ae29611f2b2e1a024430f0fd31383ecb502104a250954a2b916e}
base-url: http://apis.data.go.kr/1360000/MidFcstInfoService


Expand Down