Skip to content

Commit f22c0f0

Browse files
authored
Merge pull request #682 from ESTiOSAI/refactor/dashboard-llm-jh
커뮤니티 분위기 디코딩 에러 llm 프롬프트 수정 + 시장 분위기 로직 삭제
2 parents 2ad4200 + 741c0e1 commit f22c0f0

7 files changed

Lines changed: 4 additions & 126 deletions

File tree

AIProject/iCo/Core/Local/UserDefaults/AppStorageKey.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ enum AppStorageKey {
2222
static let cacheCoinRecomURL = "cacheCoinRecomURL"
2323
/// AI 추천 코인 URL 캐시 시각 저장 키
2424
static let cacheCoinRecomTimestamp = "cacheCoinRecomTimestamp"
25-
/// 오늘의 브리핑 캐시 시각 저장 키
26-
static let cacheBriefTodayTimestamp = "cacheBriefTodayTimestamp"
2725
/// 커뮤니티 브리핑 캐시 시각 저장 키
2826
static let cacheBriefCommunityTimestamp = "cacheBriefCommunityTimestamp"
2927
/// 위젯에 북마크된 데이터 저장 키

AIProject/iCo/Data/API/Gemini/LLMAPIService.swift

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -179,65 +179,6 @@ extension LLMAPIService {
179179
return try await fetchDTO(prompt: prompt, action: .coinReportGeneration)
180180
}
181181

182-
/// 2시간 단위 전체 시장 요약 데이터를 가져옵니다.
183-
/// 캐시가 유효하면 캐시를 우선 사용하고, 없거나 만료되면 새로 요청 후 캐싱합니다.
184-
///
185-
/// - Parameter ignoreCache: 캐시 여부
186-
/// - Returns: 디코딩된 DTO
187-
func fetchTodayInsight(ignoreCache: Bool = false) async throws -> Insight {
188-
let now = Date.now
189-
let interval: TimeInterval = 60 * 60
190-
191-
if !ignoreCache {
192-
if let lastTimestamp = UserDefaults.standard.value(forKey: AppStorageKey.cacheBriefTodayTimestamp) as? String, let savedDate = Date.dateAndTimeFormatter.date(from: lastTimestamp) {
193-
let cacheURL = URL(string: "https://cache.local/dashboard/today/\(lastTimestamp)")!
194-
let request = URLRequest(url: cacheURL, cachePolicy: .returnCacheDataElseLoad)
195-
196-
if let cachedResponse = URLCache.shared.cachedResponse(for: request),
197-
now.timeIntervalSince(savedDate) < interval {
198-
do {
199-
let dto: InsightDTO = try JSONDecoder().decode(InsightDTO.self, from: cachedResponse.data)
200-
return dto.toDomain()
201-
} catch let decodingError as DecodingError {
202-
throw NetworkError.decodingError(decodingError)
203-
}
204-
205-
}
206-
}
207-
}
208-
209-
let cacheURL = URL(string: "https://cache.local/dashboard/today/\(now.dateAndTime)")!
210-
let request = URLRequest(url: cacheURL, cachePolicy: .returnCacheDataElseLoad)
211-
212-
let prompt = Prompt.generateTodayInsight()
213-
let dto: InsightDTO = try await fetchDTO(prompt: prompt, action: .dashboardBriefingGeneration)
214-
215-
do {
216-
let jsonData = try JSONEncoder().encode(dto)
217-
218-
let response = URLResponse(
219-
url: cacheURL,
220-
mimeType: "application/json",
221-
expectedContentLength: jsonData.count,
222-
textEncodingName: "utf-8"
223-
)
224-
let cacheEntry = CachedURLResponse(response: response, data: jsonData)
225-
URLCache.shared.storeCachedResponse(cacheEntry, for: request)
226-
} catch {
227-
throw NetworkError.encodingError
228-
}
229-
230-
if let lastTimestamp = UserDefaults.standard.value(forKey: AppStorageKey.cacheBriefTodayTimestamp) as? String {
231-
let oldCacheURL = URL(string: "https://cache.local/dashboard/today/\(lastTimestamp)")!
232-
let oldRequest = URLRequest(url: oldCacheURL, cachePolicy: .returnCacheDataElseLoad)
233-
URLCache.shared.removeCachedResponse(for: oldRequest)
234-
}
235-
236-
UserDefaults.standard.set(now.dateAndTime, forKey: AppStorageKey.cacheBriefTodayTimestamp)
237-
238-
return dto.toDomain()
239-
}
240-
241182
/// 커뮤니티(예: Reddit) 게시글 요약을 기반으로 감정(`Sentiment`)과 요약을 생성합니다.
242183
/// 캐시가 유효하면 캐시를 우선 사용하고, 없거나 만료되면 새로 요청 후 캐싱합니다.
243184
///

AIProject/iCo/Domain/Interface/LLMProvider.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ protocol LLMRecommendCoinFetching {
2020
protocol LLMProvider: LLMReportFetching, LLMRecommendCoinFetching {
2121
func postAnswer(content: String, action: LLMAction) async throws -> LLMResponseDTO
2222
func fetchRecommendCoins(preference: String, bookmarkCoins: String, ignoreCache: Bool) async throws -> [RecommendCoinDTO]
23-
func fetchTodayInsight(ignoreCache: Bool) async throws -> Insight
2423
func fetchCommunityInsight(from post: String, now: Date, ignoreCache: Bool) async throws -> Insight
2524
func fetchBookmarkBriefing(for coins: [BookmarkEntity], character: RiskTolerance) async throws -> PortfolioBriefingDTO
2625
}

AIProject/iCo/Domain/Model/Common/Prompt.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ enum Prompt {
1414
case generateTodayNews(coinKName: String, today: String = Date().dateAndTime)
1515
case generateWeeklyTrends(coinKName: String, today: String = Date().dateAndTime)
1616
case extractCoinID(text: String)
17-
case generateTodayInsight(today: String = Date().dateAndTime)
1817
case generateCommunityInsight(redditPost: String)
1918
case generateBookmarkBriefing(importance: String, bookmarks: String)
2019

@@ -76,17 +75,6 @@ enum Prompt {
7675
아래의 문자열에서 가상화폐를 찾아. 빈 배열에 모든 화폐의 심볼을 담고 “,” 로 구분해서 반환해. 응답에 다른 설명은 배제해.
7776
\(text)
7877
"""
79-
case.generateTodayInsight(let today):
80-
"""
81-
struct InsightDTO: Codable {
82-
let todaysSentiment: String
83-
let summary: String
84-
}
85-
86-
\(today) 기준 최근 2시간동안 한국 암호화폐 뉴스 분석 후 분위기(호재, 악재, 중립)와 그렇게 판단한 이유 200자로 요약
87-
이유는 호재라면 긍정 요인, 악재라면 부정 요인만 요약, 중립이라면 긍정, 부정 요인을 자연스럽게 연결해 요약
88-
위 JSON 형식으로 작성 (답변은 한글, 마크다운 금지, 출처 제외)
89-
"""
9078
case.generateCommunityInsight(let redditPost):
9179
"""
9280
\(redditPost)
@@ -97,7 +85,7 @@ enum Prompt {
9785
let summary: String
9886
}
9987
100-
커뮤니티 분위기(호재, 악재, 중립)와 그렇게 평가한 이유를 한글로 200자 이상으로 요약해 위 JSON으로 제공 (답변은 한글, 마크다운 금지, 출처 제외)
88+
커뮤니티 분위기(호재, 악재, 중립)와 그렇게 평가한 이유를 한글로 200자 이상으로 요약해 위 형식으로 작성해서 JSON으로 제공 (답변은 한글, 마크다운 금지, 출처 제외)
10189
"""
10290
case .generateBookmarkBriefing(let importance, let bookmarks):
10391
"""

AIProject/iCo/Features/Dashboard/View/AIBriefingView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import SwiftUI
99

1010
/// 대시보드에서 AI 브리핑 섹션을 보여주는 뷰입니다.
1111
///
12-
/// 오늘의 인사이트, 커뮤니티 반응, 공포 탐욕 지수으로 구성합니다.
12+
/// 오늘의 커뮤니티 반응, 공포 탐욕 지수, 거래대금/상승률 TOP5로 구성합니다.
1313
struct AIBriefingView: View {
1414
@Environment(\.horizontalSizeClass) var hSizeClass
1515
@Environment(\.verticalSizeClass) var vSizeClass

AIProject/iCo/Features/Dashboard/View/TopCoinListView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ struct TopCoinListSection: View {
103103
CoinView(symbol: coin.coinSymbol, size: 40)
104104
.padding(.trailing, 8)
105105

106-
VStack(alignment: .leading, spacing: 8) {
106+
VStack(alignment: .leading, spacing: 4) {
107107
Text(viewModel.koreanName(for: coin.id))
108108
.font(.ico16Sb)
109109
.lineLimit(1)

AIProject/iCo/Features/Dashboard/ViewModel/InsightViewModel.swift

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,21 @@
77

88
import SwiftUI
99

10-
/// 오늘의 코인 시장/커뮤니티 분위기를 제공하는 뷰 모델입니다.
10+
/// 오늘의 코인 커뮤니티 분위기를 제공하는 뷰 모델입니다.
1111
///
1212
/// AI 또는 커뮤니티 기반의 분위기를 비동기적으로 불러오고,
1313
/// 감정(`Sentiment`)과 요약(`summary`)을 제공합니다.
1414
///
1515
/// - Properties:
16-
/// - overall: 오늘의 전체 시장 분위기(`FetchState<Insight>`)
1716
/// - community: 커뮤니티 기반 분위기(`FetchState<Insight>`)
1817
final class InsightViewModel: ObservableObject {
19-
@AppStorage(AppStorageKey.cacheBriefTodayTimestamp) private var cacheBriefTodayTimestamp: String = ""
2018
@AppStorage(AppStorageKey.cacheBriefCommunityTimestamp) private var cacheBriefCommunityTimestamp: String = ""
2119

22-
@Published var overall: FetchState<Insight> = .loading
2320
@Published var community: FetchState<Insight> = .loading
2421

2522
private let llmService = LLMAPIService()
2623
private let redditAPIService = RedditAPIService()
2724

28-
private var overallTask: Task<Insight, Error>?
2925
private var communityTask: Task<Insight, Error>?
3026

3127
init() {
@@ -36,14 +32,9 @@ final class InsightViewModel: ObservableObject {
3632
cancelAll()
3733

3834
Task { @MainActor in
39-
overall = .loading
4035
community = .loading
4136
}
4237

43-
overallTask = Task {
44-
try await llmService.fetchTodayInsight()
45-
}
46-
4738
communityTask = Task { [weak self] in
4839
try await withTaskCancellationHandler(
4940
operation: {
@@ -58,8 +49,6 @@ final class InsightViewModel: ObservableObject {
5849
}
5950

6051
Task {
61-
await updateOverallUI()
62-
try? await Task.sleep(for: .milliseconds(350)) // UI가 순차적으로 적용되는 효과를 주기 위한 딜레이
6352
await updateCommunityUI()
6453
}
6554
}
@@ -71,20 +60,6 @@ final class InsightViewModel: ObservableObject {
7160
return try await llmService.fetchCommunityInsight(from: communityData.communitySummary, ignoreCache: ignoreCache)
7261
}
7362

74-
// overall만 다시 시도
75-
func retryOverall() {
76-
if overall.isLoading { return }
77-
overallTask?.cancel()
78-
overallTask = nil
79-
80-
Task {
81-
await MainActor.run { self.overall = .loading }
82-
try? await Task.sleep(for: .milliseconds(350)) // 새로고침 효과를 주기 위한 딜레이
83-
overallTask = Task { try await llmService.fetchTodayInsight(ignoreCache: true) }
84-
await updateOverallUI()
85-
}
86-
}
87-
8863
// community만 다시 시도
8964
func retryCommunity() {
9065
if community.isLoading { return }
@@ -100,16 +75,11 @@ final class InsightViewModel: ObservableObject {
10075
}
10176
}
10277

103-
func cancelOverall() {
104-
overallTask?.cancel()
105-
}
106-
10778
func cancelCommunity() {
10879
communityTask?.cancel()
10980
}
11081

11182
func cancelAll() {
112-
overallTask?.cancel()
11383
communityTask?.cancel()
11484
}
11585

@@ -119,15 +89,6 @@ final class InsightViewModel: ObservableObject {
11989
}
12090

12191
extension InsightViewModel {
122-
private func updateOverallUI() async {
123-
await TaskResultHandler.apply(
124-
of: overallTask,
125-
update: { [weak self] state in
126-
self?.overall = state
127-
}
128-
)
129-
}
130-
13192
private func updateCommunityUI() async {
13293
await TaskResultHandler.apply(
13394
of: communityTask,
@@ -141,15 +102,6 @@ extension InsightViewModel {
141102
extension InsightViewModel {
142103
var sectionDataSource: [ReportSectionData<Insight>] {
143104
[
144-
// ReportSectionData(
145-
// id: "overall",
146-
// icon: "bitcoinsign.bank.building",
147-
// title: "전반적인 시장의 분위기",
148-
// state: overall,
149-
// timestamp: Date.dateAndTimeFormatter.date(from: cacheBriefTodayTimestamp),
150-
// onCancel: { [weak self] in self?.cancelOverall() },
151-
// onRetry: { [weak self] in self?.retryOverall() }
152-
// ),
153105
ReportSectionData(
154106
id: "community",
155107
icon: "shareplay",

0 commit comments

Comments
 (0)