Skip to content

Commit 431e6d7

Browse files
committed
fetching new data if missing in Dictionary
1 parent 264c2d3 commit 431e6d7

7 files changed

Lines changed: 90 additions & 9 deletions

File tree

core/src/main/kotlin/org/evomaster/core/EMConfig.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,6 +3015,12 @@ class EMConfig {
30153015
@Cfg("Enable the use of LLMs.")
30163016
var llm = false
30173017

3018+
@Experimental
3019+
@DependsOnTrueFor("llm")
3020+
@Cfg("The number of threads to use when making calls towards an LLM, in configured." +
3021+
" If connecting to Ollama, this value is ignored, and only 1 thread is used.")
3022+
var llmThreads = 4
3023+
30183024
@Experimental
30193025
@DependsOnTrueFor("llm")
30203026
@Cfg("LLM external service URL. If not specified, default will be based on the LLM provider.")

core/src/main/kotlin/org/evomaster/core/Main.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.evomaster.core.AnsiColor.Companion.inYellow
1313
import org.evomaster.core.DocumentationLinks.EM_DOCKER_LINK
1414
import org.evomaster.core.DocumentationLinks.EM_ISSUES_LINK
1515
import org.evomaster.core.config.ConfigProblemException
16+
import org.evomaster.core.llm.service.LlmService
1617
import org.evomaster.core.logging.LoggingUtil
1718
import org.evomaster.core.output.TestSuiteCode
1819
import org.evomaster.core.output.TestSuiteSplitter
@@ -902,10 +903,17 @@ class Main {
902903
snapshotTimestamp: String ->
903904
writeTestsAsSnapshots(injector, solution, controllerInfo, snapshotTimestamp)
904905
}.also {
906+
/*
907+
TODO should have a better way to specify that some services need to be shutdown
908+
*/
905909
if (config.isEnabledHarvestingActualResponse()) {
906910
val hp = injector.getInstance(HarvestActualHttpWsResponseHandler::class.java)
907911
hp.shutdown()
908912
}
913+
if(config.llm){
914+
val llm = injector.getInstance(LlmService::class.java)
915+
llm.shutdown()
916+
}
909917
}
910918
}
911919

core/src/main/kotlin/org/evomaster/core/llm/DictionaryCreator.kt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ import kotlin.io.path.Path
1717

1818
object DictionaryCreator {
1919

20-
private data class FieldInfo(
21-
val name: String,
22-
val description: String?
23-
)
24-
2520
@JvmStatic
2621
fun main(args: Array<String>) {
2722

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.evomaster.core.llm
2+
3+
data class FieldInfo(
4+
val name: String,
5+
val description: String?
6+
)

core/src/main/kotlin/org/evomaster/core/llm/service/DictionaryService.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import com.fasterxml.jackson.core.type.TypeReference
44
import com.fasterxml.jackson.databind.ObjectMapper
55
import org.evomaster.core.EMConfig
66
import org.evomaster.core.llm.DictionarySearchResult
7+
import org.evomaster.core.llm.FieldInfo
8+
import org.evomaster.core.logging.LoggingUtil
79
import org.evomaster.core.search.service.DataPool
810
import org.slf4j.Logger
911
import org.slf4j.LoggerFactory
10-
import javax.annotation.PostConstruct
1112
import javax.inject.Inject
1213

1314

@@ -128,20 +129,33 @@ class DictionaryService {
128129
private lateinit var dataPool : DataPool
129130

130131

131-
fun updatePoolFromDictionary(names: Collection<String>) {
132+
/**
133+
* Update the data pool with all the info from dictionary based on the input field's names.
134+
* If some names are not in the dictionary, if LLM is enabled then it will be queried,
135+
* including using the names' descriptions (if any is provided)
136+
*/
137+
fun updatePoolFromDictionary(fields: Collection<FieldInfo>) {
132138
if(!isActive()){
133139
throw IllegalStateException("Dictionary service is not active")
134140
}
135141

136142
//TODO check how expensive this is... put on thread if too expensive
137143

138-
val result = searchForNames(names)
144+
val result = searchForNames(fields.map { it.name })
145+
139146
result.data.entries.forEach { entry ->
140147
entry.value.forEach { exm -> dataPool.addValue(entry.key, exm) }
141148
}
142149

143150
if(config.llm && result.missing.isNotEmpty()) {
144-
//TODO defenetively this needs to be on thread
151+
LoggingUtil.getInfoLogger().info("Going to make ${result.missing.size} calls to LLM to obtain new input data" +
152+
" based on field names and descriptions")
153+
154+
val toInfer = fields.filter{ result.missing.contains(it.name)}
155+
156+
llmService.askForNewExamples(toInfer){ name, examples ->
157+
examples.forEach { ex -> dataPool.addValue(name, ex) }
158+
}
145159
}
146160
}
147161

core/src/main/kotlin/org/evomaster/core/llm/service/LlmService.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package org.evomaster.core.llm.service
22

3+
import com.fasterxml.jackson.core.type.TypeReference
4+
import com.fasterxml.jackson.databind.ObjectMapper
35
import dev.langchain4j.model.chat.ChatModel
46
import dev.langchain4j.model.chat.StreamingChatModel
57
import org.evomaster.core.EMConfig
68
import org.evomaster.core.config.ConfigProblemException
9+
import org.evomaster.core.llm.FieldInfo
10+
import org.evomaster.core.llm.LlmProvider
711
import org.evomaster.core.llm.LlmSupport
12+
import org.evomaster.core.llm.Prompts
13+
import java.util.concurrent.ExecutorService
14+
import java.util.concurrent.Executors
815
import java.util.concurrent.Future
916
import javax.annotation.PostConstruct
1017
import javax.inject.Inject
@@ -18,6 +25,8 @@ class LlmService {
1825

1926
private lateinit var syncModel: ChatModel
2027

28+
private lateinit var executor: ExecutorService
29+
2130
@PostConstruct
2231
private fun initService() {
2332

@@ -47,6 +56,13 @@ class LlmService {
4756
}catch (e: Exception){
4857
throw ConfigProblemException("Failed to connect and initialize the chosen LLM: ${e.message}")
4958
}
59+
60+
val n = if(config.llmProvider == LlmProvider.OLLAMA) 1 else config.llmThreads
61+
executor = Executors.newFixedThreadPool(n)
62+
}
63+
64+
fun shutdown() {
65+
executor.shutdown()
5066
}
5167

5268
private fun checkUsingLLM(){
@@ -64,4 +80,36 @@ class LlmService {
6480
fun chat(userMessage: String) : String{
6581
return LlmSupport.chat(syncModel, "", userMessage)
6682
}
83+
84+
fun askForNewExamples(fields: Collection<FieldInfo>, callback: (name: String, examples: Collection<String>) -> Unit){
85+
86+
fields.toList()
87+
.sortedBy { it.name }
88+
.forEach { entry ->
89+
executor.submit { askForExamplesTask(entry.name, entry.description, callback) }
90+
}
91+
//here we do not wait... updates are done asynchronously
92+
}
93+
94+
private fun askForExamplesTask(
95+
name: String,
96+
description: String?,
97+
callback: (name: String, examples: Collection<String>) -> Unit
98+
){
99+
val mapper = ObjectMapper()
100+
val prompt = Prompts.getPromptForNameDescription(name, description)
101+
var result = LlmSupport.chat(syncModel, prompt.first, prompt.second)
102+
val list = try {
103+
mapper.readValue(result, object : TypeReference<List<String>>() {})
104+
} catch (e: Exception) {
105+
try {
106+
val failed = Prompts.getPromptForFailedName(e.toString())
107+
result = LlmSupport.chat(syncModel, failed.first, failed.second)
108+
mapper.readValue(result, object : TypeReference<List<String>>() {})
109+
} catch (e: Exception) {
110+
throw e
111+
}
112+
}
113+
callback(name, list)
114+
}
67115
}

core/src/main/kotlin/org/evomaster/core/problem/enterprise/service/EnterpriseModule.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.evomaster.core.problem.enterprise.service
22

33
import com.google.inject.AbstractModule
4+
import org.evomaster.core.llm.service.DictionaryService
45
import org.evomaster.core.llm.service.LlmService
56
import org.evomaster.core.problem.security.service.SSRFAnalyser
67
import org.evomaster.core.problem.security.service.HttpCallbackVerifier
@@ -16,6 +17,9 @@ abstract class EnterpriseModule : AbstractModule() {
1617
bind(LlmService::class.java)
1718
.asEagerSingleton()
1819

20+
bind(DictionaryService::class.java)
21+
.asEagerSingleton()
22+
1923
bind(SSRFAnalyser::class.java)
2024
.asEagerSingleton()
2125

0 commit comments

Comments
 (0)