This commit is contained in:
lunaticbum 2025-03-19 18:27:39 +09:00
parent 931ec332d7
commit 2e3667d0a1
13 changed files with 3676 additions and 3345 deletions

View File

@ -55,6 +55,7 @@ dependencies {
implementation ("org.seleniumhq.selenium:selenium-java:4.10.0")
implementation ("org.commonmark:commonmark:0.18.0")
implementation ("com.drewnoakes:metadata-extractor:2.19.0")
implementation("org.springframework.boot:spring-boot-starter-security")

View File

@ -87,8 +87,8 @@ class BumsInterceptor : HandlerInterceptor {
request.cookies?.forEach {
if (it.name.equals("CLEAR", true)) {
request.getSession(true)?.let { session ->
session.invalidate()
request.getSession(false)?.let { session ->
// session.invalidate()
session.setAttribute(WRITE_PERMISSION_KEY, false)
}
}

View File

@ -46,13 +46,16 @@ class SecurityConfig {
logService.log(it.toString())
it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers(HttpMethod.POST,"/user/**").permitAll()
// it.requestMatchers("/", "/user/**").permitAll()
// .requestMatchers(".ajax").permitAll()
// it.requestMatchers("/", "/user/joinUser.api").permitAll()
// it.requestMatchers("user/joinUser.api").permitAll()
it.requestMatchers("/blog/viewer/**").permitAll()
it.anyRequest().permitAll()
// .requestMatchers("/", "/login/**").permitAll()
// .requestMatchers("/posts/**", "/api/v1/posts/**").hasRole(Role.USER.name)
// .requestMatchers("/admins/**", "/api/v1/admins/**").hasRole(Role.ADMIN.name)
// .anyRequest().authenticated()
}

View File

@ -5,6 +5,11 @@ import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.model.PostManageg
import kr.lunaticbum.back.lun.model.ResultMV
import kr.lunaticbum.back.lun.utils.LogService
import org.commonmark.node.Node
import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Pageable
import org.springframework.web.bind.annotation.GetMapping
@ -25,13 +30,24 @@ class Home {
@GetMapping("/","/home")
fun home() : ResultMV {
val vm = ResultMV("content/home")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
logService.log(Gson().toJson(it))
}
})
try {
vm.modelMap.put("Posts", postManageg.find4().apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
val parser: Parser = Parser.builder().build()
val document: Node = parser.parse(it.content)
val renderer = HtmlRenderer.builder().build()
Jsoup.parse(renderer.render(document))?.let { doc ->
val firstImg: Element? = doc.select("img")?.first()
val imgSrc: String = firstImg?.attr("src") ?: ""
it.image = imgSrc
it.html = doc.text()
}
it.title = if ((it.title?.length ?: 0) >= 1) it.title else ""
}
}.chunked(2))
}catch (ex: Exception){ex.printStackTrace()}
vm.modelMap.put("path","/blog/viewer/")
return vm
}

View File

@ -312,8 +312,8 @@ class Telegram {
.bodyToMono(String::class.java).block()
}
CoroutineScope(Dispatchers.IO).launch {
var originalQuery = msg.text
lama.generateResponse(originalQuery?.replace("오늘","오늘(${SimpleDateFormat("yyyy-MM-dd").format(Date())})"),msg.from?.id.toString())
var originalQuery = msg.text ?: ""
lama.generateResponse(originalQuery.replace("오늘","오늘(${SimpleDateFormat("yyyy-MM-dd").format(Date())})"),msg.from?.id.toString())
}
}
@ -371,7 +371,7 @@ class Telegram {
// }
// }
CoroutineScope(Dispatchers.IO).async {
lama.generateResponse(originalQuery?.replace("오늘","오늘(${SimpleDateFormat("yyyy-MM-dd").format(Date())})"))
lama.generateResponse(originalQuery.replace("오늘","오늘(${SimpleDateFormat("yyyy-MM-dd").format(Date())})"))
}
return "TEST"
}

View File

@ -34,6 +34,9 @@ class Post {
var category : String? = null
var tags : String? = null
var html : String? = null
var image : String? = null
var writer : String? = null
var writeTime : Long = 0
var posting : Boolean = false
@ -69,6 +72,19 @@ class PostManageg {
return postRepository.findAllByPostingTrue(pageable).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf()
}
fun find4() : List<Post> {
val originalList = postRepository.findAllByModifyTime(0)
.takeLast(4)
.buffer(4)
.blockLast(Duration.ofSeconds(30)) ?: listOf()
return originalList + List((4 - originalList.size).coerceAtLeast(0)) {
Post() // 기본값 생성 (필드 초기화 필요)
}
// return postRepository.findAllByModifyTime(0).takeLast(4).buffer(4).blockLast(Duration.ofSeconds(30)) ?: listOf()
}
fun find20() : List<Post> {
return postRepository.findAllByModifyTime(0).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf()
}

View File

@ -2,22 +2,21 @@ package kr.lunaticbum.back.lun.service
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import io.micrometer.observation.ObservationRegistry
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
import kotlinx.coroutines.reactive.awaitSingle
import kr.lunaticbum.back.lun.configs.GlobalEnvironment
import kr.lunaticbum.back.lun.controllers.TelegramSendMsg
import kr.lunaticbum.back.lun.model.*
import kr.lunaticbum.back.lun.utils.RssFeedsParser
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.select.Elements
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.remote.RemoteWebDriver
import org.springframework.ai.embedding.EmbeddingRequest
@ -37,6 +36,8 @@ import java.net.URLEncoder
import java.text.SimpleDateFormat
import java.time.Duration
import java.util.*
import java.util.regex.Matcher
import java.util.regex.Pattern
@Service
@ -84,54 +85,108 @@ class Lama {
val joinString = "\n#"
var lastElements : Elements = Elements()
var body = Jsoup.connect(url).timeout(30000).get().body()
// var elements : Elements? = null
// if (url.contains("nate.com", true)) {
// if (url.contains("view", true)) {
// elements = body.select("[class*=articleView]")
// }else {
// elements = body.select("[class*=postRankSubjectList]")
// }
// } else if (url.contains("newsis.com/view", true)) {
// elements = body.select("[class*=articleView]")
// } else if (url.contains("blog.naver.com", true)) {
// elements = body.select("[class*=se-viewer]")
// } else if (url.contains("bbc.com/korean/articles", true)) {
// elements = body.select("main[role$=main]")
// } else if (url.contains("chosun.com/client", true)) {
// elements = body.select("[class*=articleBody]")
// } else if (url.contains("nocutnews.co.kr/news", true)) {
// elements = body.select("[class*=container]")
// } else if (url.contains("hani.co.kr/arti/", true)) {
// elements = body.select("[class*=ArticleDetail]")
// } else if (url.contains("yna.co.kr/view", true)) {
// elements = body.select("[class*=container]")
// } else if (url.contains("newspim.com/news", true)) {
// elements = body.select("[class*=container]")
// } else {
//
// }
// if (elements?.size ?: 0 > 0) {
// elements?.forEach {
// lastElements.add(it)
// }
// }
//
// if (lastElements.size < 1) {
// arrayOf("container","article","main","viewer","content").forEach {
// var result = Elements()
// result.addAll(body.select("[class*=$it]"))
// result.addAll(body.select("[id*=$it]"))
// result.addAll(body.select(it))
// result.forEach { if (it.text().length > 100 && it.children().size < 5) { lastElements.add(it) } }
// }
// }
return if (lastElements.size > 0) {
lastElements.eachText().joinToString(joinString)
var elements : Elements? = null
if (url.contains("nate.com", true)) {
if (url.contains("view", true)) {
elements = body.select("[class*=articleView]")
}else {
elements = body.select("[class*=postRankSubjectList]")
}
} else if (url.contains("newsis.com/view", true)) {
elements = body.select("[class*=articleView]")
} else if (url.contains("blog.naver.com", true)) {
elements = body.select("[class*=se-viewer]")
} else if (url.contains("bbc.com/korean/articles", true)) {
elements = body.select("main[role$=main]")
} else if (url.contains("chosun.com/client", true)) {
elements = body.select("[class*=articleBody]")
} else if (url.contains("nocutnews.co.kr/news", true)) {
elements = body.select("[class*=container]")
} else if (url.contains("hani.co.kr/arti/", true)) {
elements = body.select("[class*=ArticleDetail]")
} else if (url.contains("yna.co.kr/view", true)) {
elements = body.select("[class*=container]")
} else if (url.contains("newspim.com/news", true)) {
elements = body.select("[class*=container]")
} else {
body.children().eachText().joinToString(joinString)
}
if (elements?.size ?: 0 > 0) {
elements?.forEach {
lastElements.add(it)
}
}
if (lastElements.size < 1) {
arrayOf("container","article","main","viewer","content").forEach {
var result = Elements()
result.addAll(body.select("[class*=$it]"))
result.addAll(body.select("[id*=$it]"))
result.addAll(body.select(it))
result.forEach { if (it.text().length > 100 && it.children().size < 5) { lastElements.add(it) } }
}
}
return if (lastElements.size > 0) {
lastElements.text()
} else {
body.text()
}
}
fun jsopFilter(doc : Document) : String {
var url = doc.baseUri()
val joinString = "\n#"
var lastElements : Elements = Elements()
var body = doc
var elements : Elements? = null
if (url.contains("nate.com", true)) {
if (url.contains("view", true)) {
elements = body.select("[class*=articleView]")
}else {
elements = body.select("[class*=postRankSubjectList]")
}
} else if (url.contains("newsis.com/view", true)) {
elements = body.select("[class*=articleView]")
} else if (url.contains("blog.naver.com", true)) {
elements = body.select("[class*=se-viewer]")
} else if (url.contains("bbc.com/korean/articles", true)) {
elements = body.select("main[role$=main]")
} else if (url.contains("chosun.com/client", true)) {
elements = body.select("[class*=articleBody]")
} else if (url.contains("nocutnews.co.kr/news", true)) {
elements = body.select("[class*=container]")
} else if (url.contains("hani.co.kr/arti/", true)) {
elements = body.select("[class*=ArticleDetail]")
} else if (url.contains("yna.co.kr/view", true)) {
elements = body.select("[class*=container]")
} else if (url.contains("newspim.com/news", true)) {
elements = body.select("[class*=container]")
} else {
}
if (elements?.size ?: 0 > 0) {
elements?.forEach {
lastElements.add(it)
}
}
if (lastElements.size < 1) {
arrayOf("container","article","main","viewer","content").forEach {
var result = Elements()
result.addAll(body.select("[class*=$it]"))
result.addAll(body.select("[id*=$it]"))
result.addAll(body.select(it))
result.forEach { if (it.text().length > 100 && it.children().size < 5) { lastElements.add(it) } }
}
}
return if (lastElements.size > 0) {
lastElements.text()
} else {
body.text()
}
}
// class WebScrap {
// @SerializedName("query", alternate = ["question"])
// var query: String? = null
@ -153,17 +208,65 @@ class Lama {
val llmPhi4Mini = "phi4-mini"
val llmDolphin3 = "dolphin3"
var llm_gemma3_4b = "gemma3:4b"
var llm_phi4_mini = "phi4-mini:latest"
var llm_dolphin3 = "dolphin3:latest"
var llm_gemma3_12b = "gemma3:12b"
var llm_phi4_14b = "phi4:14b"
var llm_mistral_7b = "mistral:7b"
val currentLLM = llmDolphin3
val currentLLM = llm_dolphin3
fun getGoogleSearch(query:String){
Jsoup.connect("https://www.google.com/search?q=".plus(query)).timeout(30000).get().select("a[href]").forEach { }
}
val waitTime = 1000L
val waitTime = 1500L
val topCount = 2
fun webDriver() : RemoteWebDriver {
val options : ChromeOptions = ChromeOptions();
options.addArguments("--headless");
options.addArguments("--disable-popup-blocking");
options.addArguments("--disable-default-apps");
options.addArguments("--disable-notifications");
options.addArguments("--disable-blink-features=AutomationControlled")
return RemoteWebDriver(URL("https://video.lunaticbum.kr"), options)
}
fun isValidUrl(url: String): Boolean {
val urlRegex = "^(https?|ftp)://[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)$".toRegex()
return url.matches(urlRegex)
}
@Async
suspend fun getterUrl(urlString: String) {
try {
webDriver()?.let { driver ->
var findCount = 0
try {
driver.get("urlString");
Thread.sleep(waitTime)
println(driver.currentUrl)
driver.findElement(By.ByTagName("Body"))?.let { webElement ->
Jsoup.parse(driver.pageSource).select("[href*=https]").forEach {
var href = it.attr("href")
println(href)
}
}
}catch (e:Exception){
e.printStackTrace()
}
driver.close()
driver.quit()
}
}catch (e:Exception){}
}
@Async
suspend fun addDocuments(query : String , refinedQuery: RefinedQuery?) {
var querys : ArrayList<String> = ArrayList()
@ -171,48 +274,65 @@ class Lama {
refinedQuery?.ko_query?.let { querys.add(it) }
refinedQuery?.en_query?.let { querys.add(it) }
refinedQuery?.keywords?.let { querys.add(it.joinToString { " " })}
refinedQuery?.ko_keywords?.let { querys.add(it.joinToString( " "))}
refinedQuery?.en_keywords?.let { querys.add(it.joinToString( " "))}
val readedUrls = ArrayList<String>()
try {
var options : ChromeOptions = ChromeOptions();
options.addArguments("--disable-popup-blocking");
options.addArguments("--disable-default-apps");
options.addArguments("--disable-notifications");
options.addArguments("--disable-blink-features=AutomationControlled");
val targetUrls = hashSetOf<String>()
RemoteWebDriver(URL("https://video.lunaticbum.kr"), options).let { driver ->
querys.forEach { refinedQuery->
var findCount = 0
try {
driver.get("https://www.google.com/search?q=$refinedQuery");
Thread.sleep(waitTime)
println(driver.currentUrl)
driver.findElement(By.ByTagName("Body"))?.let { webElement ->
Jsoup.parse(driver.pageSource).select("[href*=https]").forEach {
var href = it.attr("href")
if (href?.length ?: 0 > 5 && href.startsWith("https://") && findCount < topCount && href.contains("google") == false && href.contains("youtube") == false) {
targetUrls.add(href)
println("add targetUrls $href")
findCount += 1
querys.forEach { refinedQuery->
try {
webDriver()?.let { driver ->
var findCount = 0
try {
driver.get("https://www.google.com/search?q=$refinedQuery");
Thread.sleep(waitTime)
println(driver.currentUrl)
driver.findElement(By.ByTagName("Body"))?.let { webElement ->
Jsoup.parse(driver.pageSource).select("[href*=https]").forEach {
var href = it.attr("href")
if (href?.length ?: 0 > 5 && href.startsWith("https://") && findCount < topCount && href.contains("google") == false && href.contains("youtube") == false) {
targetUrls.add(href)
println("add targetUrls $href")
findCount += 1
}
}
}
}
}catch (e:Exception){
e.printStackTrace()
}catch (e:Exception){
e.printStackTrace()
}
driver.close()
driver.quit()
}
}
driver.close()
driver.quit()
}catch (e:Exception){}
}
options = ChromeOptions();
options.addArguments("--disable-popup-blocking");
options.addArguments("--disable-default-apps");
options.addArguments("--disable-notifications");
options.addArguments("--disable-blink-features=AutomationControlled");
RemoteWebDriver(URL("https://video.lunaticbum.kr"), options).let { driver ->
targetUrls.forEach { url ->
querys.forEach { refinedQuery ->
try {
webDriver()?.let { driver ->
var findCount = 0
RssFeedsParser().readFeed("https://news.google.com/rss/search?q=${URLEncoder.encode(refinedQuery)}=ko&gl=KR&ceid=KR%3Ako/")?.messages?.forEach {
var url: String? = it.link
if (url?.length ?: 0 > 5 && url?.startsWith("https://") == true && readedUrls.contains(url) == false && findCount < topCount) {
println("url >>>> $url")
targetUrls.add(url!!)
findCount += 1
}
}
driver.close()
driver.quit()
}
}catch (e:Exception){
}
}
targetUrls.forEach { url ->
webDriver()?.let { driver ->
var result = SearXngResult()
if (url?.length ?: 0 > 5 && url?.startsWith("https://") == true && readedUrls.contains(url) == false) {
readedUrls.add(url!!)
@ -222,85 +342,47 @@ class Lama {
driver.get(url);
Thread.sleep(waitTime)
driver.findElement(By.ByTagName("Body"))?.let { webElement ->
if(webElement.text.length > 120) {
println(driver.currentUrl)
println(webElement.text)
result.title = driver.title
result.originHtml = webElement.text
webPageSummarize(result, webElement.text)
jsopFilter(Jsoup.parse(driver.pageSource)).let { text ->
result.originHtml = text
webPageSummarize(result)
}
}
} catch (e: Exception) {
e.printStackTrace()
// e.printStackTrace()
}
}
driver.close();
driver.quit()
}
driver.close();
driver.quit()
}
options = ChromeOptions();
options.addArguments("--disable-popup-blocking");
options.addArguments("--disable-default-apps");
options.addArguments("--disable-notifications");
options.addArguments("--disable-blink-features=AutomationControlled");
RemoteWebDriver(URL("https://video.lunaticbum.kr"), options).let { driver ->
querys.forEach { refinedQuery ->
var googleSCount = 0
RssFeedsParser().readFeed("https://news.google.com/rss/search?q=${URLEncoder.encode(query)}=ko&gl=KR&ceid=KR%3Ako/")?.messages?.forEach {
var url: String? = it.link
var result = SearXngResult()
println("url >>>> $url")
if (url?.length ?: 0 > 5 && url?.startsWith("https://") == true && readedUrls.contains(url) == false && googleSCount < topCount) {
readedUrls.add(url!!)
result.url = url!!
result.originQuery = query
result.refinedQuery = refinedQuery
result.title = it.title
println(result.title)
try {
driver.get(url);
Thread.sleep(waitTime)
println(driver.currentUrl)
driver.findElement(By.ByTagName("Body"))?.let { webElement ->
println(driver.currentUrl)
println(webElement.text)
result.title = driver.title
result.originHtml = webElement.text
webPageSummarize(result, webElement.text)
googleSCount += 1
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
driver.close()
driver.quit()
}
} catch (e:Exception){e.printStackTrace()}
querys.forEach { refinedQuery ->
val gSearch = "https://psn.lunaticbum.kr/search?q=${refinedQuery?.replace("오늘", SimpleDateFormat("yyyMMdd").format(Date()))}&language=ko&time_range=month&safesearch=0&categories=general&format=json"
println("gSearch >>> ${gSearch}")
// println("gSearch >>> ${gSearch}")
WebClient.create().get()
.uri(gSearch)
.retrieve()
.bodyToMono(SearXng::class.java).timeout(Duration.ofMinutes(20L)).block()?.let { gsResult ->
gsResult.results?.filter { it.url?.startsWith("https://") == true && it.score > 0.4 }?.forEach {
println("in filter ${it.url}")
gsResult.results?.filter { it.url?.startsWith("https://") == true && it.score > 5.0 }?.forEach {
if (readedUrls.contains(it.url) == false) {
readedUrls.add(it.url!!)
it.originQuery = query
it.refinedQuery = refinedQuery
println(it.title)
try {
jsopFilter(it.url!!).let { text ->
it.originHtml = text
webPageSummarize(it, text)
webDriver()?.let { driver ->
driver.get(it.url!!)
Thread.sleep(waitTime)
driver.findElement(By.ByTagName("Body"))?.let { webElement ->
jsopFilter(it.url!!).let { text ->
it.originHtml = text
webPageSummarize(it)
}
}
driver.close()
driver.quit()
}
} catch (e: Exception) {
e.printStackTrace()
@ -308,37 +390,107 @@ class Lama {
}
}
}
println("end of search")
// println("end of search")
}
}
var format = "context:'%s'\ncontext는 웹 페이지 문자를 가져온 것 '%s'이 질문에 대해 연관 결과로 받은 내용임. 해당 context 정리 해서 본문 내용을 최대한 자세히 알려줘\n'{query:질문 내용, contents_ko:자세한 내용 한국어 , summary_ko:요약된 내용 한국어, keywords:[키워드], related_links:[{link,description}}], relatedness_score:0.0~10.0}'\n이 형식의 결과로 만들어 줘"
var format = "\"context:'%s'\n" +
"The context is extracted text from a web page. '%s' is the content received as a relevant result for this question. Please analyze and summarize the given context in detail, and provide the following information in JSON format.\n" +
"\n" +
"Please provide the result in this format, ensuring that all information is in Korean language."
var webSummaryResultFormat : String = """{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Original question"
},
"contents_ko": {
"type": "string",
"description": "Detailed content in Korean"
},
"summary_ko": {
"type": "string",
"description": "Concise summary in Korean"
},
"keywords": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"related_links": {
"type": "array",
"items": {
"type": "object",
"properties": {
"link": { "type": "string" },
"description": { "type": "string" }
},
"required": ["link", "description"],
"additionalProperties": false
}
},
"relatedness_score": {
"type": "number",
"minimum": 0.0,
"maximum": 10.0
}
},
"required": ["query", "contents_ko", "summary_ko", "keywords", "related_links", "relatedness_score"],
"additionalProperties": false
}""";
internal fun makeSummarizeRequestMsg(it : SearXngResult) : String= format.format(it.originHtml,it.originQuery)
internal fun makeCahtReq(reqMsg:String) = OllamaApi.ChatRequest.Builder(currentLLM).stream(false).format("json").messages(reqMsg.chunked(100).map { println(it); OllamaApi.Message.Builder(OllamaApi.Message.Role.USER).content(it).build()}.toList()).build()
internal fun makeCahtReq(reqMsg:String, ollamaOptions: OllamaOptions?, format: Any) = OllamaApi.ChatRequest.Builder(currentLLM).options(ollamaOptions).stream(false).format(format).messages(reqMsg.chunked(100).map { OllamaApi.Message.Builder(OllamaApi.Message.Role.USER).content(it).build()}.toList()).build()
var options = OllamaOptions.builder().build()//.temperature(0.8).topK(3).seed(30)
@Async
fun webPageSummarize(it : SearXngResult , text : String) {
fun webPageSummarize(it : SearXngResult) {
try {
infomationDic.get(it.originQuery)!!.put(it.url!!, text)
val chatClient = OllamaApi("https://lama.lunaticbum.kr")
val embeddingModel = OllamaEmbeddingModel(chatClient, OllamaOptions.builder().build(), ObservationRegistry.create(), ModelManagementOptions.defaults())
val embeddingResponse = embeddingModel.call(EmbeddingRequest(text.chunked(400).toList(), OllamaOptions.builder().model(currentEmbedimg).truncate(false).build()))
it.originHtml = text
val sdss = QPut(arrayListOf())
sdss.points.add(QData(id = System.currentTimeMillis(), embeddingResponse.result.output, it))
if (sdss.points.size > 0) {
val qUrl = "https://ollama.lunaticbum.kr/collections/blama_vectors".plus("/points")
val client = WebClient.create()
client.put()
.uri(qUrl)
.header("api-key", "blama-admin-key-gb")
.body(BodyInserters.fromValue(Gson().toJson(sdss)))
.retrieve()
.bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).subscribe(
{ resultString -> }, { error -> error.printStackTrace() }
)
// println("send to blama >> ${it.url}")
infomationDic.get(it.originQuery)?.put(it.url!!,Gson().toJson(it))
try {
CoroutineScope(Dispatchers.IO).launch {
val chatClient = OllamaApi("https://lama.lunaticbum.kr")
chatClient.chat(makeCahtReq(makeSummarizeRequestMsg(it), options, ObjectMapper().readValue(webSummaryResultFormat,Map::class.java))).toMono().subscribe({aiResponce ->
it.pageData = aiResponce.message.content
var needSave = true
try {
var jsonObj = JsonParser.parseString(aiResponce.message.content)
needSave = jsonObj.isJsonObject && (jsonObj as JsonObject)?.get("relatedness_score")?.asDouble ?: 0.0 > 0.5 } catch (e: Exception) {
e.printStackTrace()
}
if (needSave) {
sendTlg("유효한 정보가 수집됨.".plus(aiResponce.message.content))
val embeddingModel = OllamaEmbeddingModel(chatClient, OllamaOptions.builder().build(), ObservationRegistry.create(), ModelManagementOptions.defaults())
val embeddingResponse = embeddingModel.call(EmbeddingRequest(Gson().toJson(it).chunked(400).toList(), OllamaOptions.builder().model(currentEmbedimg).truncate(false).build()))
val sdss = QPut(arrayListOf())
sdss.points.add(QData(id = System.currentTimeMillis(), embeddingResponse.result.output, it))
if (sdss.points.size > 0) {
val qUrl = "https://ollama.lunaticbum.kr/collections/blama_vectors".plus("/points")
val client = WebClient.create()
client.put()
.uri(qUrl)
.header("api-key", "blama-admin-key-gb")
.body(BodyInserters.fromValue(Gson().toJson(sdss)))
.retrieve()
.bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L)).subscribe(
{ resultString -> }, { error -> error.printStackTrace() }
)
}}
},{err->
err.printStackTrace()
})
}
}
catch (e:Exception){e.printStackTrace()}
}catch (e : Exception) {
e.printStackTrace()
}
@ -347,16 +499,46 @@ class Lama {
class RefinedQuery {
var ko_query : String? = null
var en_query : String? = null
var keywords : Array<String>? = null
var ko_keywords : Array<String>? = null
var en_keywords : Array<String>? = null
}
var queryFormat = "질문:\n'%s'\n앞은 질문의 내용을 정리해서 '{ko_query:한국어 질문,en_query:영어 번역된 질문,ko_keywords:[한국어 키워드],en_keyword:[영문키워드]}'이 형식의 결과를 부탁할께"
var jsonSchema: String = """{
"type": "object",
"properties": {
"ko_query": {
"type": "string",
"description": "korean query"
},
"en_query": {
"type": "string",
"description": "english query"
},
"ko_keywords": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"description": "korean keywords"
},
"en_keywords": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"description": "query keyword"
}
},
"required": ["ko_query", "en_query", "ko_keywords", "en_keywords"],
"additionalProperties": false
}""".trimIndent()
var queryFormat = "Question:\n'%s'\nBased on the above question, please provide a JSON result formatted\nPlease ensure:\n1. Faithful translation maintaining original intent\n2. Keyword extraction focusing on core concepts\n3. Bilingual keyword matching\n4. Proper JSON formatting"
internal fun makeQuerySummarizeRequestMsg(query : String) : String= queryFormat.format(query)
fun querySummarize(query: String) : RefinedQuery? {
var refinedQuery : RefinedQuery? = null
try {
val chatClient = OllamaApi("https://lama.lunaticbum.kr")
var dispoable = chatClient.chat(makeCahtReq(makeQuerySummarizeRequestMsg(query))).toMono().subscribe({aiResponce ->
var dispoable = chatClient.chat(makeCahtReq(makeQuerySummarizeRequestMsg(query),options, ObjectMapper().readValue(jsonSchema,Map::class.java))).toMono().subscribe({aiResponce ->
println("summary result >>>>> ${aiResponce.message.content}")
refinedQuery = Gson().fromJson(aiResponce.message.content, RefinedQuery::class.java)
},{err->
@ -380,7 +562,7 @@ class Lama {
.body(BodyInserters.fromValue(Gson().toJson(QSearchData(embedFlots,3))))
.retrieve()
.bodyToMono(QSearch::class.java).timeout(Duration.ofMinutes(20L)).block()
println(Gson().toJson(lists))
return if (lists?.result?.size ?: 0 > 0) {
val qContents = QContentsList()
lists?.result?.filter { it.score > 8.0 }?.forEach { qContents.ids.add(it.id) }
@ -400,64 +582,195 @@ class Lama {
@Autowired
lateinit var globalEvv : GlobalEnvironment
val resultJsonScheme = """{
"type": "object",
"properties": {
"querys": {
"type": "array",
"items": {
"type": "string"
},
"description": "사용자의 질문 목록"
},
"answers": {
"type": "array",
"items": {
"type": "string"
},
"description": "질문에 대한 상세한 답변 목록"
},
"keywords": {
"type": "array",
"items": {
"type": "string"
},
"description": "답변과 관련된 주요 키워드 목록"
},
"links": {
"type": "array",
"items": {
"type": "string"
},
"description": "참고할 만한 관련 링크 목록"
}
},
"required": ["querys", "answers", "keywords", "links"],
"additionalProperties": false
}""".trimIndent()
var infomationDic = hashMapOf<String,HashMap<String,String>>()
suspend fun generateResponse(query: String?, targetId: String? = globalEvv.telegramMyId) {
suspend fun generateResponse(query: String, targetId: String? = globalEvv.telegramMyId) {
val chatClient = OllamaApi("https://lama.lunaticbum.kr")
val embeddingModel = OllamaEmbeddingModel(
chatClient, OllamaOptions.builder().build(), ObservationRegistry.create(), ModelManagementOptions.defaults())
println("On generateResponse :: find something ${query}")
if (isValidUrl(query)) {
getterUrl(query)
}else {
query?.let { originalQuery ->
infomationDic.put(query!!, hashMapOf())
var embeddingResponse = embeddingModel.call(EmbeddingRequest(listOf(originalQuery), OllamaOptions.builder().model(currentEmbedimg).truncate(false).build()))
addDocuments(originalQuery, querySummarize(originalQuery))
println("points size ${embeddingResponse.result.output.size}")
var context : StringBuffer = StringBuffer()
try {
embedQuery(embeddingResponse.result.output)?.result?.forEach { result ->
if (infomationDic.get(query!!)!!.contains(result.payload?.url ?: "NONE") == false) {
context.append("\n# :".plus(if (result.payload?.pageData?.length ?: 0 > 10) {
result.payload?.pageData
} else {
result.payload?.content
}))
}
}
}catch (e:Exception){
e.printStackTrace()
}
infomationDic.get(query!!)!!.iterator().forEach { context.append("\n#${it.key}:${it.value}") }
val prompt : StringBuffer = StringBuffer().append("참조:\n").append(context).append("\n참조 내용을 고려 해서\n'$query'").append(query).append("\n에 {querys:[],answers:[],keywords:[],links:[]}형식으로 최대한 자세히 대답 해줘 모든 내용은 한국어로 해줘")
val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
val chatClient = OllamaApi("https://lama.lunaticbum.kr")
val embeddingModel = OllamaEmbeddingModel(
chatClient,
OllamaOptions.builder().build(),
ObservationRegistry.create(),
ModelManagementOptions.defaults()
)
println("On generateResponse :: find something ${query}")
val response: OllamaApi.ChatResponse = chatClient.chat(OllamaApi.ChatRequest.Builder(currentLLM).stream(false).format("json").messages(
prompt.chunked(300).map { println(it); OllamaApi.Message.Builder(OllamaApi.Message.Role.USER).content(it).build()}.toList()).build())
// println(response.message.content)
CoroutineScope(Dispatchers.IO).launch {
var toalmsg = "${query}의 대답이 도착했어요.\n${response.message.content}"
toalmsg.chunked(512).forEach { chunkedMsg ->
println("chunkedMsg >>> ${chunkedMsg}")
(targetId ?: globalEvv.telegramMyId)?.let {
var tlgSend = TelegramSendMsg(it, chunkedMsg)
WebClient
.create(fullUrl)
.post()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(Gson().toJson(tlgSend)))
.retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L))
.block()?.let { result ->
println("result >>> ${result}")
}
query.let { originalQuery ->
infomationDic.put(query!!, hashMapOf())
try {
var embeddingResponse = embeddingModel.call(
EmbeddingRequest(
listOf(originalQuery),
OllamaOptions.builder().model(currentEmbedimg).truncate(false).build()
)
)
addDocuments(originalQuery, querySummarize(originalQuery))
println("points size ${embeddingResponse.result.output.size}")
var context: StringBuffer = StringBuffer()
embedQuery(embeddingResponse.result.output)?.result?.forEach { result ->
if (infomationDic.get(query!!)!!.contains(result.payload?.url ?: "NONE") == false) {
context.append(
"\nReference:#".plus(
if (result.payload?.pageData?.length ?: 0 > 10) {
result.payload?.pageData
} else {
result.payload?.content
}
)
)
}
}
infomationDic.get(query!!)!!.iterator()
.forEach { context.append("\nReference:#${it.key}:${it.value}") }
val prompt: StringBuffer = StringBuffer()
prompt.append(context)
prompt.append("\nConsidering the above reference, please answer the following question:\n'$query'\n\nProvide a detailed response in the following JSON format\nPlease ensure all content is in Korean language and as detailed as possible.")
val answers = StringBuffer()
chatClient.streamingChat(OllamaApi.ChatRequest.Builder(currentLLM).stream(true)
.format(ObjectMapper().readValue(resultJsonScheme, Map::class.java)).messages(
prompt.chunked(1024)
.map { OllamaApi.Message.Builder(OllamaApi.Message.Role.USER).content(it).build() }.toList()
).build()
).timeout(Duration.ofMinutes(20)).subscribe({ responce ->
answers.append(responce.message.content)
println("responce.message.content >>> ${answers.length}")
try {
if (answers.length % 100 == 0) {
var tlgSend = TelegramSendMsg(
targetId ?: globalEvv.telegramMyId!!,
"결과를 수집 중 ${responce.message.content.length}의 문자열이 추가됨. 수집된 통 데이터는 ${answers.length}"
)
WebClient
.create(fullUrl)
.post()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(Gson().toJson(tlgSend)))
.retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L))
.block()?.let { result ->
println("result >>> ${result}")
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}, { error ->
}, {
var toalmsg: StringBuffer = StringBuffer("${query}의 대답이 도착했어요.\n").append(answers.toString())
sendTlg(toalmsg.toString())
infomationDic.remove(query)
println("On generateResponse :: END OF Answer")
})
} catch (e: Exception) {
e.printStackTrace()
}
infomationDic.remove(query!!)
}
println("On generateResponse :: END OF Answer")
}
}
fun sendTlg(toalmsg : String) {
telegramScope.launch {
val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
toalmsg.chunked(512).forEach { chunkedMsg ->
launch {
println("chunkedMsg >>> ${chunkedMsg}")
globalEvv.telegramMyId?.let {
try {
var tlgSend = TelegramSendMsg(it, chunkedMsg)
WebClient
.create(fullUrl)
.post()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(Gson().toJson(tlgSend)))
.retrieve().bodyToMono(String::class.java).timeout(Duration.ofMinutes(20L))
.awaitSingle()?.let { result ->
println("result >>> ${result}")
}
}catch (e:Exception){
e.printStackTrace()
}
}
}
}
}
}
private val telegramScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
// fun sendTlg2(toalmsg: String) {
// telegramScope.launch {
// val fullUrl = "https://api.telegram.org/${globalEvv.telegramBotKey}/sendMessage"
// val chunkedMessages = toalmsg.chunked(512)
// chunkedMessages.forEach { chunkedMsg ->
// launch {
// try {
// globalEvv.telegramMyId?.let { chatId ->
// val tlgSend = TelegramSendMsg(chatId, chunkedMsg)
// val result = WebClient.create(fullUrl)
// .post()
// .contentType(MediaType.APPLICATION_JSON)
// .bodyValue(Gson().toJson(tlgSend))
// .retrieve()
// .awaitBody<String>()
//
// println("Telegram send success: $result")
// }
// } catch (e: Exception) {
// println("Telegram send failed: ${e.localizedMessage}")
// // 추가 에러 처리 로직
// }
// }
// }
// }
// }
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,8 @@ function goToEditor(path,id,sk) {
location.href = path + id+"?token="+sk;
}
function onclickWrite(type, keyword, html) {
let title_field = document.getElementById('title_field')
var hasValues = true

View File

@ -34,16 +34,25 @@
</script>
</th:block>
<th:block layout:fragment="content" id="content">
<div id="main_layer">
<label id="title_layer" class="write_option" ></label>
<div id="editor" ></div>
<div class="write_controllbox">
<div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div>
<div style="width: 15px" ></div>
<div class="write_option btn-example" to="#popLayer2" onclick="openPopup(this)" id="hashtag_field"></div>
<div style="width: 15px" ></div>
<label class="write_option" readonly id="location_field"></label>
<section class="wrapper style1">
<div class="container">
<div id="content">
<!-- Content -->
<article>
<label id="title_layer" class="write_option" ></label>
<div id="editor" ></div>
<div class="write_controllbox">
<div class="write_option btn-example" to="#popLayer1" onclick="openPopup(this)" ></div>
<div style="width: 15px" ></div>
<div class="write_option btn-example" to="#popLayer2" onclick="openPopup(this)" id="hashtag_field"></div>
<div style="width: 15px" ></div>
<label class="write_option" readonly id="location_field"></label>
</div>
</article>
</div>
</div>
</div>
</section>
</th:block>
</html>

View File

@ -10,25 +10,10 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
let editor
document.addEventListener("DOMContentLoaded", onLoaded);
function onLoaded() {
var els = document.getElementsByClassName('content')
for (i=0;i<els.length;i++) {
var item = $(els[i])
console.log(item[0])
console.log(item.attr("data"))
new toastui.Editor({
el: item[0],
width : (item[0].getBoundingClientRect().width * 0.8) + 'px',
height : (item[0].getBoundingClientRect().width * 0.8) + 'px',
viewer: true,
usageStatistics : false,
initialValue: item.attr("data"),
theme:"dark",
});
}
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script>
</th:block>
@ -36,7 +21,7 @@
<section id="banner">
<header>
<h2>Bum's: <em>짧은 헛소리 혹은 기사?! 링크 있으면 링크까지</a></em></h2>
<a href="#" class="button">더 읽으쉴?!<br>[Read More Gibberish]</a>
<a href="#" class="button">더 읽으쉴?!<br>[Read More Gibberish]</a>
</header>
</section>
@ -52,7 +37,7 @@
</header>
</div>
</section>
<section id="cta__3" class="wrapper style3">
<section id="cta2" class="wrapper style3">
<div class="container">
<header>
<h2>Are you ready to continue your quest?</h2>
@ -62,42 +47,14 @@
<!-- Posts -->
<section class="wrapper style2">
<div class="container">
<div class="row">
<section class="col-6 col-12-narrower">
<div class="box post">
<a href="#" class="image left"><img src="images/pic01.jpg" alt="" /></a>
<div class="row" th:each="row : ${Posts}">
<section class="col-6 col-12-narrower" th:each="post : ${row}">
<!-- <span th:text="${cell}"></span>-->
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}">
<a href="#" class="image left"><img th:src="${#strings.length(post.image) > 0} ? ${post.image} : 'images/pic01.jpg'" alt="" /></a>
<div class="inner">
<h3>The First Thing</h3>
<p>Duis neque nisi, dapibus sed mattis et quis, nibh. Sed et dapibus nisl amet mattis, sed a rutrum accumsan sed. Suspendisse eu.</p>
</div>
</div>
</section>
<section class="col-6 col-12-narrower">
<div class="box post">
<a href="#" class="image left"><img src="images/pic02.jpg" alt="" /></a>
<div class="inner">
<h3>The Second Thing</h3>
<p>Duis neque nisi, dapibus sed mattis et quis, nibh. Sed et dapibus nisl amet mattis, sed a rutrum accumsan sed. Suspendisse eu.</p>
</div>
</div>
</section>
</div>
<div class="row">
<section class="col-6 col-12-narrower">
<div class="box post">
<a href="#" class="image left"><img src="images/pic03.jpg" alt="" /></a>
<div class="inner">
<h3>The Third Thing</h3>
<p>Duis neque nisi, dapibus sed mattis et quis, nibh. Sed et dapibus nisl amet mattis, sed a rutrum accumsan sed. Suspendisse eu.</p>
</div>
</div>
</section>
<section class="col-6 col-12-narrower">
<div class="box post">
<a href="#" class="image left"><img src="images/pic04.jpg" alt="" /></a>
<div class="inner">
<h3>The Fourth Thing</h3>
<p>Duis neque nisi, dapibus sed mattis et quis, nibh. Sed et dapibus nisl amet mattis, sed a rutrum accumsan sed. Suspendisse eu.</p>
<h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3>
<p th:text="${#strings.abbreviate(post.html, 80)}" class="ellipsis"></p>
</div>
</div>
</section>
@ -133,11 +90,10 @@
</div>
</section>
<!-- CTA -->
<section id="cta" class="wrapper style3">
<section id="cta2" class="wrapper style3">
<div class="container">
<header>
<h2>Are you ready to continue your quest?</h2>
<a href="#" class="button">Insert Coin</a>
</header>
</div>
</section>

View File

@ -64,7 +64,7 @@
<!-- Copyright -->
<div class="copyright">
<ul class="menu">
<li>&copy; Untitled. All rights reserved</li><li>Design: <a href="http://html5up.net">HTML5 UP</a></li>
<li>&copy;lunaticbum. All rights reserved</li><li>Design: <a href="http://html5up.net">HTML5 UP</a></li>
</ul>
</div>
</div>