Skip to content

Commit

Permalink
refactor/#16 : 내용 입력 화면 구성
Browse files Browse the repository at this point in the history
- gemini LLM api 추가
  • Loading branch information
TaewoongR committed Feb 18, 2025
1 parent fb090e7 commit 9183636
Show file tree
Hide file tree
Showing 9 changed files with 408 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.boostcamp.mapisode.designsystem.theme.MapisodeTheme
fun MapisodePlaceHolder(value: String) {
MapisodeText(
text = value,
modifier = Modifier.fillMaxSize(),
style = MapisodeTheme.typography.labelLarge,
color = MapisodeTheme.colorScheme.textFieldContent,
)
Expand Down Expand Up @@ -156,8 +157,8 @@ fun MapisodeTextField(
minLines = minLines,
decorationBox = { innerTextField ->
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.CenterStart,
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.TopStart,
) {
if (prefix != null) {
Box(modifier = Modifier.padding(end = 8.dp)) {
Expand Down
51 changes: 51 additions & 0 deletions core/designsystem/src/main/res/drawable/ic_generative_ai_star.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1080" android:viewportWidth="1080" android:width="24dp">

<path android:pathData="M515.1,725.8L472,824.5C455.4,862.4 403,862.4 386.4,824.5L343.3,725.8C305,638 236,568.1 149.9,529.9L31.3,477.3C-6.4,460.5 -6.4,405.7 31.3,388.9L146.2,337.9C234.5,298.7 304.7,226.2 342.4,135.4L386,30.3C402.2,-8.8 456.2,-8.8 472.4,30.3L516,135.4C553.7,226.2 623.9,298.7 712.2,337.9L827.1,388.9C864.8,405.7 864.8,460.5 827.1,477.3L708.5,529.9C622.4,568.1 553.4,638 515.1,725.8Z">

<aapt:attr name="android:fillColor">

<gradient android:centerX="670.4" android:centerY="474" android:gradientRadius="665.5" android:type="radial">

<item android:color="#FF1BA1E3" android:offset="0"/>

<item android:color="#FF1BA1E3" android:offset="0"/>

<item android:color="#FF5489D6" android:offset="0.3"/>

<item android:color="#FF9B72CB" android:offset="0.5"/>

<item android:color="#FFD96570" android:offset="0.8"/>

<item android:color="#FFF49C46" android:offset="1"/>

</gradient>

</aapt:attr>

</path>

<path android:pathData="M915.5,1037L903.4,1064.8C894.5,1085.1 866.3,1085.1 857.5,1064.8L845.4,1037C823.8,987.5 784.9,948 736.3,926.5L699,909.9C678.8,900.9 678.8,871.6 699,862.6L734.2,847C784,824.8 823.6,783.9 844.9,732.8L857.3,702.7C866,681.8 894.9,681.8 903.6,702.7L916,732.8C937.2,783.9 976.8,824.8 1026.6,847L1061.9,862.6C1082.1,871.6 1082.1,900.9 1061.9,909.9L1024.5,926.5C976,948 937.1,987.5 915.5,1037Z">

<aapt:attr name="android:fillColor">

<gradient android:centerX="670.4" android:centerY="474" android:gradientRadius="665.5" android:type="radial">

<item android:color="#FF1BA1E3" android:offset="0"/>

<item android:color="#FF1BA1E3" android:offset="0"/>

<item android:color="#FF5489D6" android:offset="0.3"/>

<item android:color="#FF9B72CB" android:offset="0.5"/>

<item android:color="#FFD96570" android:offset="0.8"/>

<item android:color="#FFF49C46" android:offset="1"/>

</gradient>

</aapt:attr>

</path>

</vector>
18 changes: 18 additions & 0 deletions feature/episode/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
alias(libs.plugins.mapisode.feature)
}

android {
namespace = "com.boostcamp.mapisode.episode"

defaultConfig {
val googleGenerativeAi =
gradleLocalProperties(rootDir, providers).getProperty("GOOGLE_GENERATIVE_AI") ?: ""

if (googleGenerativeAi.isEmpty()) {
throw GradleException("GOOGLE_GENERATIVE_AI Key is not set.")
}
buildConfigField("String", "GOOGLE_GENERATIVE_AI", "\"$googleGenerativeAi\"")
}

buildFeatures {
buildConfig = true
}
}

dependencies {
implementation(project.libs.bundles.naverMap)
implementation(project.libs.bundles.coil)
implementation(project.libs.google.cloud.generativeai)

implementation(projects.core.network)
implementation(projects.domain.episode)
implementation(projects.domain.mygroup)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.boostcamp.mapisode.episode.state.EpisodeState
import com.boostcamp.mapisode.network.repository.NaverMapsRepository
import com.boostcamp.mapisode.ui.base.RevisedBaseViewModel
import com.boostcamp.mapisode.ui.base.retainFirstIfNavigating
import com.google.ai.client.generativeai.GenerativeModel
import com.naver.maps.geometry.LatLng
import com.naver.maps.map.CameraPosition
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -27,8 +28,14 @@ class EpisodeViewModel @Inject constructor(
private val userPreferenceDataStore: UserPreferenceDataStore,
private val groupRepository: GroupRepository,
private val naverMapsRepository: NaverMapsRepository,
private val imageCaptionRepository: ImageCaptionRepository,
) : RevisedBaseViewModel<EpisodeIntent, EpisodeState, EpisodeEffect>(EpisodeState()) {

private val gemini = GenerativeModel(
modelName = "gemini-1.5-flash",
apiKey = BuildConfig.GOOGLE_GENERATIVE_AI,
)

override suspend fun reducer(intent: SharedFlow<EpisodeIntent>) {
intent.retainFirstIfNavigating()
.collect { event ->
Expand Down Expand Up @@ -64,6 +71,34 @@ class EpisodeViewModel @Inject constructor(
EpisodeIntent.OnCompleteInfoClick -> {
navigateToInfoScreen()
}

is EpisodeIntent.OnUserInputChange -> {
sendState { copy(userInput = event.userInput) }
}

EpisodeIntent.OnGenerateLLMClick -> {
dealWithLLM()
}

is EpisodeIntent.OnSelectEpisodeClick -> {
sendState {
copy(
selectedEpisode = event.generatedEpisode,
selfTypedEpisode = "",
isEpisodeSelected = true,
)
}
}

is EpisodeIntent.OnSelfTypedEpisodeChange -> {
sendState {
copy(
selectedEpisode = "",
selfTypedEpisode = event.selfTypedEpisode,
isEpisodeSelected = currentState.selfTypedEpisode.isNotBlank(),
)
}
}
}
}
}
Expand Down Expand Up @@ -121,12 +156,87 @@ class EpisodeViewModel @Inject constructor(

private fun navigateToInfoScreen() {
if (currentState.episodeAddress.isNotBlank() && currentState.selectedGroups.isNotEmpty()) {
getImageCaption(currentState.imageUrls)
sendEffect { EpisodeEffect.NavigateToContentScreen }
} else {
sendEffect { EpisodeEffect.ShowToast("위치와 그룹을 선택해주세요.") }
}
}

private fun getImageCaption(imageUrls: List<String>) {
viewModelScope.launch {
val imageCaption = imageUrls.map {
imageCaptionRepository.generateImageCaption(it)
}.joinToString("\n") { it.joinToString(",") }
sendState { copy(imageCaption = imageCaption) }
Timber.e("imageCaption: $imageCaption")
}
}

private fun dealWithLLM() {
viewModelScope.launch(Dispatchers.IO) {
sendState {
copy(
selectedEpisode = "",
isLoading = true,
)
}
if (currentState.imageCaption.isNotBlank()) {
generateStories(
currentState.imageCaption,
currentState.userInput,
)
} else {
while (currentState.imageCaption.isBlank()) {
delay(100)
if (currentState.imageCaption.isNotBlank()) {
generateStories(
currentState.imageCaption,
currentState.userInput,
)
}
}
}
}
}

private fun generateStories(imageCaption: String, userInputText: String) {
viewModelScope.launch {
val prompt = """
$imageCaption
위의 글은 내가 촬영한 "사진의 상황"에 대한 객관적인 설명이야.
아래의 글은 내가 촬영한 당일에 느낀 생각을 작성한 글이야. 이 글은 비어있을 수도 있어.
그러나, 이 글이 존재하는 경우 **반드시** 이를 중심으로 일기 요약을 생성해줘.
사진의 상황은 배경 정보로만 활용하고, 핵심은 내가 작성한 글을 반영하는 거야.
"내가 작성한 글"인 아래 글을 기반으로 일기 요약을 작성해줘:
$userInputText
**요구사항**:
- 반드시 **내가 작성한 글을 기반으로** 작성해줘.
- **사진의 상황은 보조 자료일 뿐**, 핵심 내용은 내가 작성한 글에서 가져와.
- 사진 찍은 이야기는 하지마.
- 화자는 나로 설정해줘.
- 문장의 어미를 "~다"로 설정해서 작성해줘.
- 출력 형식: 각 줄의 맨 앞에 `## `를 붙이고, 한 줄에 40자 내외로 작성해줘. 총 세 줄 작성해줘. 각 줄은 독립적인 내용이어야 해.
**추가 처리**:
- 만약 내가 작성한 글이 비어 있다면, 사진 상황을 기반으로 감정이나 생각을 추론해서 작성해줘.
- 단, 추론한 내용은 **사진 설명을 그대로 반복하지 말고**, 감정적인 해석을 포함해야 해.
""".trimIndent()
val episodes =
gemini.generateContent(prompt).text?.split("##")?.drop(1)?.map { it.trim() }
?: emptyList()
sendState {
copy(
generatedEpisodes = episodes.toPersistentList(),
isLoading = false,
)
}
}
}

override fun onCleared() {
super.onCleared()
Timber.e("EpisodeViewModel onCleared")
Expand Down
Loading

0 comments on commit 9183636

Please sign in to comment.