From c85996e4537a5e727b6a7a08407a79b509685180 Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Wed, 5 Mar 2025 18:08:59 +0900 Subject: [PATCH] ..... --- build.gradle.kts | 10 ++ .../lunaticbum/back/lun/configs/AppConfig.kt | 13 +++ .../back/lun/controllers/Telegram.kt | 91 ++++++++++++++----- .../kr/lunaticbum/back/lun/model/SearXng.kt | 26 ++++++ .../kr/lunaticbum/back/lun/service/Lama.kt | 61 +++++++++++++ src/main/resources/application.properties | 9 ++ 6 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/kr/lunaticbum/back/lun/model/SearXng.kt create mode 100644 src/main/kotlin/kr/lunaticbum/back/lun/service/Lama.kt diff --git a/build.gradle.kts b/build.gradle.kts index f70b70c..9ac771a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,6 +27,9 @@ configurations { repositories { mavenCentral() + + maven { url = uri("https://repo.spring.io/milestone") } + } dependencies { @@ -56,6 +59,13 @@ dependencies { // implementation(platform("com.google.cloud:libraries-bom: 26.55.0")) // implementation("com.google.cloud:google-cloud-apikeys") implementation ("com.google.maps:google-maps-services:2.2.0") +// implementation ("org.springframework.ai:spring-ai-openai-spring-boot-starter:1.0.0-SNAPSHOT") +// implementation ("org.springframework.ai:spring-ai-vertex-ai-gemini-spring-boot-starter:1.0.0-SNAPSHOT") +// implementation("org.springframework.ai:spring-ai-ollama-spring-boot-starter:1.0.0-SNAPSHOT") + implementation(platform("org.springframework.ai:spring-ai-bom:1.0.0-M6")) + implementation("org.springframework.ai:spring-ai-ollama-spring-boot-starter:1.0.0-M6") + implementation("org.springframework.ai:spring-ai-redis-store") + implementation ("org.slf4j:slf4j-simple:1.7.25") implementation("io.jsonwebtoken:jjwt-api:0.11.5") diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt b/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt index 6bc1783..0e9b03e 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/configs/AppConfig.kt @@ -1,5 +1,7 @@ package kr.lunaticbum.back.lun.configs +import org.springframework.ai.ollama.api.OllamaApi +import org.springframework.ai.ollama.api.OllamaOptions import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -27,6 +29,17 @@ class AppConfig : WebMvcConfigurer { registry.addInterceptor(authInterceptor()) super.addInterceptors(registry) } + + @Bean + fun chatClient(): OllamaApi { + return OllamaApi("https://lama.lunaticbum.kr") +// .withDefaultOptions( +// OllamaOptions.create() +// .withModel("phi4:14b") +// .withNumThread(5) +// .withSeed(5) +// .withTemperature(0.9f)) + } // @Bean // fun getProperty() : Map{ // println("telegramBotKey >>>> $telegramBotKey") diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt index 6228b2e..69059e0 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/Telegram.kt @@ -15,9 +15,14 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kr.lunaticbum.back.lun.configs.GlobalEnvironment import kr.lunaticbum.back.lun.model.* +import kr.lunaticbum.back.lun.service.Lama import kr.lunaticbum.back.lun.utils.LogService -import org.jsoup.Jsoup +import org.springframework.ai.chat.messages.UserMessage +import org.springframework.ai.chat.prompt.Prompt +import org.springframework.ai.ollama.api.OllamaApi +import org.springframework.ai.ollama.api.OllamaOptions import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier import org.springframework.context.annotation.Bean import org.springframework.http.MediaType import org.springframework.scheduling.annotation.Scheduled @@ -25,6 +30,7 @@ import org.springframework.ui.ModelMap import org.springframework.web.bind.annotation.* import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient +import reactor.core.publisher.Mono import java.math.BigDecimal import java.math.RoundingMode import java.text.SimpleDateFormat @@ -37,6 +43,7 @@ import java.util.prefs.Preferences @RequestMapping("/tlg") class Telegram { + @Autowired lateinit var globalEvv : GlobalEnvironment @@ -89,6 +96,7 @@ class Telegram { comp = (word).plus(comp) return comp } + fun trimWithDecompString(comp : String) : String { var doubleIpmt = false var compressed : String? = comp @@ -322,32 +330,32 @@ class Telegram { } else if (req.reqMsg?.contains("검색") == true) { var originalQuery = req.reqMsg - val gSearch = "https://www.googleapis.com/customsearch/v1?key=AIzaSyARLXyvmr_554tOy3UCh3naFlZQS3-qQQM&cx=207f328d3ad7242f2&q=${originalQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date()))}&num=5&lr=kr" + val gSearch = "https://www.google.com/search?q=${originalQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date()))}&tbs=qdr%3Am" println("gSearch >>> ${gSearch}") WebClient.create().get() .uri(gSearch) .retrieve() - .bodyToMono(GoogleSearchResult::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { gsResult -> + .bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { gsResult -> println("gsearch result ==> ${Gson().toJson(gsResult)}") var additionalInfo = StringBuffer() - gsResult.items?.forEach { - additionalInfo.append("- 정보 출처 :").append(it.link).append("\n") - try { - Jsoup.connect(it.link).get().body().text().let { - if (it.length > 1000) { - additionalInfo.append("- 정보 내용 :").append(it.chunked(500).first()).append("\n") - } else { - additionalInfo.append("- 정보 내용 :").append(it).append("\n") - } - } - } catch (e : Exception) { - additionalInfo.append("- 정보 타이틀 : ").append(it.title).append("\n") - additionalInfo.append("- 정보 요약 :").append(it.snippet).append("\n") - println(it.link) - e.printStackTrace() - } - additionalInfo.append("\n") - } +// gsResult.items?.forEach { +// additionalInfo.append("- 정보 출처 :").append(it.link).append("\n") +// try { +// Jsoup.connect(it.link).get().body().text().let { +// if (it.length > 1000) { +// additionalInfo.append("- 정보 내용 :").append(it.chunked(500).first()).append("\n") +// } else { +// additionalInfo.append("- 정보 내용 :").append(it).append("\n") +// } +// } +// } catch (e : Exception) { +// additionalInfo.append("- 정보 타이틀 : ").append(it.title).append("\n") +// additionalInfo.append("- 정보 요약 :").append(it.snippet).append("\n") +// println(it.link) +// e.printStackTrace() +// } +// additionalInfo.append("\n") +// } println("additionalInfo >>> ${additionalInfo.toString()}") req.reqMsg = "질문 : " + originalQuery + "\n\n추가정보:" + "\n${additionalInfo.toString()} 위의 질문과 추가 정보를 고려하여 답변해주세요." @@ -415,6 +423,47 @@ class Telegram { } + +// fun chatClient(): ChatClient { +// return OllamaChatClient(OllamaApi("https://lama.lunaticbum.kr")) +// .withDefaultOptions( +// OllamaOptions.create() +// .withModel("phi4:14b") +// .withNumThread(5) +// .withSeed(5) +// .withTemperature(0.9f) +// ) +// } + @Autowired + lateinit var lama : Lama + + @ResponseBody + @GetMapping("query/{path}") + fun googleQueryTest(@PathVariable path: String): String { + var originalQuery = path + val gSearch = "https://psn.lunaticbum.kr/search?q=${originalQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date()))}&language=auto&time_range=month&safesearch=0&categories=general&format=json" + println("gSearch >>> ${gSearch}") + var additionalInfo = StringBuffer() + additionalInfo.append("참고자료") + var idx = 0 + WebClient.create().get() + .uri(gSearch) + .retrieve() + .bodyToMono(SearXng::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { gsResult -> + gsResult.results?.filter { it.score > 0.5}?.forEach { + additionalInfo.append(idx).append(":").append(Gson().toJson(it)) + idx += 1 + } + } + + lama.generateResponse(query = originalQuery) + return "TEST" + } + + + + + enum class LamaQueryType(val keywords : ArrayList) { None(arrayListOf()), Search(arrayListOf("검색")), diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/model/SearXng.kt b/src/main/kotlin/kr/lunaticbum/back/lun/model/SearXng.kt new file mode 100644 index 0000000..d5edf82 --- /dev/null +++ b/src/main/kotlin/kr/lunaticbum/back/lun/model/SearXng.kt @@ -0,0 +1,26 @@ +package kr.lunaticbum.back.lun.model + + +class SearXng { + var query: String? = null + var number_of_results: Int = 0 + var results: ArrayList? = null + var answers: ArrayList? = null + var corrections: ArrayList? = null + var infoboxes: ArrayList? = null + var suggestions: ArrayList? = null + var unresponsive_engines: ArrayList>? = null +} +class SearXngResult { + var url: String? = null + var title: String? = null + var content: String? = null + var thumbnail: Any? = null + var engine: String? = null + var template: String? = null + var parsed_url: ArrayList? = null + var engines: ArrayList? = null + var positions: ArrayList? = null + var score: Double = 0.0 + var category: String? = null +} diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/service/Lama.kt b/src/main/kotlin/kr/lunaticbum/back/lun/service/Lama.kt new file mode 100644 index 0000000..8dc8ec2 --- /dev/null +++ b/src/main/kotlin/kr/lunaticbum/back/lun/service/Lama.kt @@ -0,0 +1,61 @@ +package kr.lunaticbum.back.lun.service + + +import org.springframework.ai.chat.messages.UserMessage +import org.springframework.ai.chat.prompt.Prompt +import org.springframework.ai.document.Document +import org.springframework.ai.ollama.OllamaChatModel +import org.springframework.ai.ollama.api.OllamaApi +import org.springframework.ai.vectorstore.SearchRequest +import org.springframework.ai.vectorstore.VectorStore +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Service +import java.util.stream.Collectors + + +@Service +class Lama { + @Qualifier("chatClient") + @Autowired + private lateinit var chatClient: OllamaApi + + @Autowired + private lateinit var vectorStore: VectorStore + + fun generateResponse(query: String?): String { + println("On generateResponse :: find something") + // 1. 유사 문서 검색 + + val relevantDocs = vectorStore.similaritySearch( + SearchRequest.builder().query(query!!).topK(3).build() + ) + + // 2. 프롬프트 구성 + val context = relevantDocs?.map { it.text }?.joinToString(separator = "\n") + val prompt = """ + Context information is below. + ---- + $context + ---- + Given the context information and not prior knowledge, answer the query: $query + \n한국어로 대답해줘 + """.trimIndent() + + // 3. Ollama를 사용하여 응답 생성 + val response: OllamaApi.ChatResponse = chatClient.chat(OllamaApi.ChatRequest.Builder("phi4:14b").stream(false).format("json").messages( + listOf(OllamaApi.Message.Builder(OllamaApi.Message.Role.USER).content(prompt).build()) + ).build()) + println(response.message.content) + println("On generateResponse :: END OF Answer") + return response.message.content + } + + // 문서 추가 메소드 + fun addDocuments(documents: List?) { + documents?.let { + vectorStore.add(it) + } +// vectorStore!!.add(documents) + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8d67a21..9f89ff2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -62,5 +62,14 @@ spring.data.mongodb.option.min-heartbeat-frequency=500 spring.data.mongodb.option.heartbeat-frequency=10000 spring.data.mongodb.option.local-threshold=15 +spring.ai.ollama.base-url=https://lama.lunaticbum.kr +spring.ai.ollama.chat.options.model=phi4:14b + +spring.data.redis.host=ollama.lunaticbum.kr +spring.ai.vectorstore.redis.initialize-schema=true +spring.ai.vectorstore.redis.index=spring-ai-redis-index +spring.ai.vectorstore.redis.prefix=spring-ai-redis-embedding + + #>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59