diff --git a/README.md b/README.md
index 1f34edd1..d96f3f8c 100644
--- a/README.md
+++ b/README.md
@@ -76,18 +76,18 @@ Maven:
com.simiacryptusskyenet-webui
- 1.1.4
+ 1.1.5
```
Gradle:
```groovy
-implementation group: 'com.simiacryptus', name: 'skyenet', version: '1.1.4'
+implementation group: 'com.simiacryptus', name: 'skyenet', version: '1.1.5'
```
```kotlin
-implementation("com.simiacryptus:skyenet:1.1.4")
+implementation("com.simiacryptus:skyenet:1.1.5")
```
### 🌟 To Use
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index 96c43774..2058c0ae 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -33,7 +33,7 @@ val hsqldb_version = "2.7.2"
dependencies {
- implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.1.4")
+ implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.1.5")
implementation(group = "org.hsqldb", name = "hsqldb", version = hsqldb_version)
implementation("org.apache.commons:commons-text:1.11.0")
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt
index 7275f62d..2496c789 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ActorSystem.kt
@@ -7,11 +7,19 @@ import com.simiacryptus.skyenet.core.platform.User
open class ActorSystem>(
val actors: Map>,
+ dataStorage: StorageInterface,
+ user: User?,
+ session: Session
+) : PoolSystem(dataStorage, user, session) {
+
+ fun getActor(actor: T) = actors.get(actor.name)!!
+}
+
+open class PoolSystem(
val dataStorage: StorageInterface,
val user: User?,
val session: Session
) {
protected val pool by lazy { ApplicationServices.clientManager.getPool(session, user) }
- fun getActor(actor: T) = actors.get(actor.name)!!
}
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/BaseActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/BaseActor.kt
index 22399203..528edad9 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/BaseActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/BaseActor.kt
@@ -1,7 +1,7 @@
package com.simiacryptus.skyenet.core.actors
import com.simiacryptus.jopenai.API
-import com.simiacryptus.jopenai.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel
import com.simiacryptus.jopenai.ChatClient
import com.simiacryptus.jopenai.OpenAIClient
import com.simiacryptus.jopenai.models.ChatModels
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt
index e9099a2f..4a0c9a6b 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/CodingActor.kt
@@ -1,9 +1,9 @@
package com.simiacryptus.skyenet.core.actors
import com.simiacryptus.jopenai.API
-import com.simiacryptus.jopenai.ApiModel.*
+import com.simiacryptus.jopenai.models.ApiModel.*
import com.simiacryptus.jopenai.ChatClient
-import com.simiacryptus.jopenai.OpenAIClient
+import com.simiacryptus.jopenai.describe.AbbrevWhitelistTSDescriber
import com.simiacryptus.jopenai.describe.AbbrevWhitelistYamlDescriber
import com.simiacryptus.jopenai.describe.TypeDescriber
import com.simiacryptus.jopenai.models.ChatModels
@@ -16,19 +16,24 @@ import java.util.*
import javax.script.ScriptException
import kotlin.reflect.KClass
+private const val TT = "`"+"`"+"`"
+typealias CodeInterceptor = (String) -> String
+
+
open class CodingActor(
val interpreterClass: KClass,
val symbols: Map = mapOf(),
- val describer: TypeDescriber = AbbrevWhitelistYamlDescriber(
+ val describer: TypeDescriber = AbbrevWhitelistTSDescriber(
"com.simiacryptus",
"com.github.simiacryptus"
),
name: String? = interpreterClass.simpleName,
val details: String? = null,
- model: OpenAITextModel,
+ model: OpenAITextModel = OpenAIModels.GPT4o,
val fallbackModel: ChatModels = OpenAIModels.GPT4o,
temperature: Double = 0.1,
- val runtimeSymbols: Map = mapOf()
+ val runtimeSymbols: Map = mapOf(),
+ var codeInterceptor: CodeInterceptor = { it }
) : BaseActor(
prompt = "",
name = name,
@@ -67,43 +72,45 @@ open class CodingActor(
get() {
val formatInstructions =
if (evalFormat) """Code should be structured as appropriately parameterized function(s)
- |with the final line invoking the function with the appropriate request parameters.""" else ""
+ with the final line invoking the function with the appropriate request parameters.""" else ""
return if (symbols.isNotEmpty()) {
"""
- |You are a coding assistant allows users actions to be enacted using $language and the script context.
- |Your role is to translate natural language instructions into code as well as interpret the results and converse with the user.
- |Use ``` code blocks labeled with $language where appropriate. (i.e. ```$language)
- |Each response should have EXACTLY ONE code block. Do not use inline blocks.
- |$formatInstructions
- |
- |Defined symbols include ${symbols.keys.joinToString(", ")} described below:
- |
- |```${this.describer.markupLanguage}
- |${this.apiDescription}
- |```
- |
- |THESE VARIABLES ARE READ-ONLY: ${symbols.keys.joinToString(", ")}
- |They are already defined for you.
- |
- |${details ?: ""}
- |""".trimMargin().trim()
- } else """
- |You are a coding assistant allowing users actions to be enacted using $language and the script context.
- |Your role is to translate natural language instructions into code as well as interpret the results and converse with the user.
- |Use ``` code blocks labeled with $language where appropriate. (i.e. ```$language)
- |Each response should have EXACTLY ONE code block. Do not use inline blocks.
- |$formatInstructions
- |
- |${details ?: ""}
- |""".trimMargin().trim()
+You are a coding assistant allows users actions to be enacted using $language and the script context.
+Your role is to translate natural language instructions into code as well as interpret the results and converse with the user.
+Use $TT code blocks labeled with $language where appropriate. (i.e. ${TT}$language)
+Each response should have EXACTLY ONE code block. Do not use inline blocks.
+$formatInstructions
+
+Defined symbols include ${symbols.keys.joinToString(", ")} described below:
+
+$TT${this.describer.markupLanguage}
+${this.apiDescription}
+${TT}
+
+THESE VARIABLES ARE READ-ONLY: ${symbols.keys.joinToString(", ")}
+They are already defined for you.
+
+${details ?: ""}
+""".trim()
+ } else """
+You are a coding assistant allowing users actions to be enacted using $language and the script context.
+Your role is to translate natural language instructions into code as well as interpret the results and converse with the user.
+Use $TT code blocks labeled with $language where appropriate. (i.e. ${TT}$language)
+Each response should have EXACTLY ONE code block. Do not use inline blocks.
+$formatInstructions
+
+${details ?: ""}
+""".trim()
}
open val apiDescription: String
get() = this.symbols.map { (name, utilityObj) ->
+ val describe = this.describer.describe(utilityObj.javaClass)
+ log.info("Describing $name (${utilityObj.javaClass}) in ${describe.length} characters")
"""
- |$name:
- | ${this.describer.describe(utilityObj.javaClass).indent(" ")}
- |""".trimMargin().trim()
+ $name:
+ ${describe.indent(" ")}
+ """.trimMargin().trim()
}.joinToString("\n")
@@ -123,7 +130,7 @@ open class CodingActor(
}
if (questions.codePrefix.isNotBlank()) {
chatMessages = (chatMessages.dropLast(1) + listOf(
- ChatMessage(Role.assistant, "Code Prefix:\n```\n${questions.codePrefix}\n```".toContentList())
+ ChatMessage(Role.assistant, "Code Prefix:\n$TT\n${questions.codePrefix}\n${TT}".toContentList())
) + chatMessages.last()).toTypedArray()
}
return chatMessages
@@ -156,7 +163,7 @@ open class CodingActor(
val respondWithCode = fixCommand(api, result.code, ex, *messages, model = model)
val blocks = extractTextBlocks(respondWithCode)
val renderedResponse = getRenderedResponse(blocks)
- val codedInstruction = getCode(language, blocks)
+ val codedInstruction = codeInterceptor(getCode(language, blocks))
log.debug("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}".trimMargin())
log.debug("New Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}".trimMargin())
result = CodeResultImpl(
@@ -175,7 +182,7 @@ open class CodingActor(
log.debug("Running $code")
OutputInterceptor.clearGlobalOutput()
val result = try {
- interpreter.run((prefix + "\n" + code).sortCode())
+ interpreter.run((prefix + "\n" + codeInterceptor(code)).sortCode())
} catch (e: Exception) {
when {
e is FailedToImplementException -> throw e
@@ -247,14 +254,14 @@ open class CodingActor(
try {
val codeBlocks = extractTextBlocks(chat(api, request, model))
val renderedResponse = getRenderedResponse(codeBlocks)
- val codedInstruction = getCode(language, codeBlocks)
+ val codedInstruction = codeInterceptor(getCode(language, codeBlocks))
log.debug("Response: \n\t${renderedResponse.replace("\n", "\n\t", false)}".trimMargin())
log.debug("New Code: \n\t${codedInstruction.replace("\n", "\n\t", false)}".trimMargin())
var workingCode = codedInstruction
var workingRenderedResponse = renderedResponse
for (fixAttempt in 0..input.fixIterations) {
try {
- val validate = interpreter.validate((input.codePrefix + "\n" + workingCode).sortCode())
+ val validate = interpreter.validate((input.codePrefix + "\n" + codeInterceptor(workingCode)).sortCode())
if (validate != null) throw validate
log.debug("Validation succeeded")
_status = CodeResult.Status.Success
@@ -264,12 +271,12 @@ open class CodingActor(
throw if (ex is FailedToImplementException) ex else FailedToImplementException(
cause = ex,
message = """
- |**ERROR**
- |
- |```text
- |${ex.stackTraceToString()}
- |```
- |""".trimMargin().trim(),
+**ERROR**
+ |
+${TT}text
+${ex.stackTraceToString()}
+${TT}
+""".trim(),
language = language,
code = workingCode,
prefix = input.codePrefix
@@ -279,7 +286,7 @@ open class CodingActor(
val respondWithCode = fixCommand(api, workingCode, ex, *messages, model = model)
val codeBlocks = extractTextBlocks(respondWithCode)
workingRenderedResponse = getRenderedResponse(codeBlocks)
- workingCode = getCode(language, codeBlocks)
+ workingCode = codeInterceptor(getCode(language, codeBlocks))
log.debug(
"Response: \n\t${
workingRenderedResponse.replace(
@@ -324,22 +331,22 @@ open class CodingActor(
ChatMessage(
Role.assistant,
"""
- |```${language.lowercase()}
- |${previousCode.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}
- |```
- |""".trimMargin().trim().toContentList()
+$TT${language.lowercase()}
+${previousCode.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}
+${TT}
+""".trim().toContentList()
),
ChatMessage(
Role.system,
"""
- |The previous code failed with the following error:
- |
- |```
- |${error.message?.trim() ?: "".let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}
- |```
- |
- |Correct the code and try again.
- |""".trimMargin().trim().toContentList()
+The previous code failed with the following error:
+
+$TT
+${error.message?.trim() ?: "".let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}
+${TT}
+
+Correct the code and try again.
+""".trim().toContentList()
)
)
)
@@ -361,7 +368,8 @@ open class CodingActor(
model = model,
fallbackModel = fallbackModel,
temperature = temperature,
- runtimeSymbols = runtimeSymbols
+ runtimeSymbols = runtimeSymbols,
+ codeInterceptor = codeInterceptor
)
companion object {
@@ -370,7 +378,7 @@ open class CodingActor(
fun String.indent(indent: String = " ") = this.replace("\n", "\n$indent")
fun extractTextBlocks(response: String): List> {
- val codeBlockRegex = Regex("(?s)```(.*?)\\n(.*?)```")
+ val codeBlockRegex = Regex("(?s)$TT(.*?)\\n(.*?)${TT}")
val languageRegex = Regex("([a-zA-Z0-9-_]+)")
val result = mutableListOf>()
@@ -407,9 +415,9 @@ open class CodingActor(
fun getRenderedResponse(respondWithCode: List>, defaultLanguage: String = "") =
respondWithCode.joinToString("\n") {
when (it.first) {
- "code" -> "```$defaultLanguage\n${it.second.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```"
+ "code" -> "$TT$defaultLanguage\n${it.second.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n${TT}"
"text" -> it.second.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }.toString()
- else -> "```${it.first}\n${it.second.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```"
+ else -> "$TT${it.first}\n${it.second.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n${TT}"
}
}
@@ -426,7 +434,7 @@ open class CodingActor(
fun String.sortCode(bodyWrapper: (String) -> String = { it }): String {
val (imports, otherCode) = this.split("\n").partition { it.trim().startsWith("import ") }
- return imports.distinct().sorted().joinToString("\n") + "\n\n" + bodyWrapper(otherCode.joinToString("\n"))
+ return imports.map { it.trim() }.distinct().sorted().joinToString("\n") + "\n\n" + bodyWrapper(otherCode.joinToString("\n"))
}
fun String.camelCase(locale: Locale = Locale.getDefault()): String {
@@ -481,11 +489,11 @@ open class CodingActor(
fun errorMessage(ex: ScriptException, code: String) = try {
"""
- |```text
+ |${TT}text
|${ex.message ?: ""} at line ${ex.lineNumber} column ${ex.columnNumber}
| ${if (ex.lineNumber > 0) code.split("\n")[ex.lineNumber - 1] else ""}
| ${if (ex.columnNumber > 0) " ".repeat(ex.columnNumber - 1) + "^" else ""}
- |```
+ |${TT}
""".trimMargin().trim()
} catch (_: Exception) {
ex.message ?: ""
@@ -499,4 +507,4 @@ open class CodingActor(
val code: String? = null,
val prefix: String? = null,
) : RuntimeException(message, cause)
-}
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageActor.kt
index 16ae1262..7b4f920f 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageActor.kt
@@ -1,10 +1,9 @@
package com.simiacryptus.skyenet.core.actors
import com.simiacryptus.jopenai.API
-import com.simiacryptus.jopenai.ApiModel
-import com.simiacryptus.jopenai.ApiModel.ChatMessage
-import com.simiacryptus.jopenai.ApiModel.ImageGenerationRequest
-import com.simiacryptus.jopenai.GPT4Tokenizer
+import com.simiacryptus.jopenai.models.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel.ChatMessage
+import com.simiacryptus.jopenai.models.ApiModel.ImageGenerationRequest
import com.simiacryptus.jopenai.OpenAIClient
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.ImageModels
@@ -66,7 +65,7 @@ open class ImageActor(
override fun respond(input: List, api: API, vararg messages: ChatMessage): ImageResponse {
var text = response(*messages, api = api).choices.first().message?.content
?: throw RuntimeException("No response")
- while (imageModel.maxPrompt <= text.length) {
+ while (imageModel.maxPrompt <= text.length && null != openAI) {
text = response(
*listOf(
messages.toList(),
@@ -79,7 +78,7 @@ open class ImageActor(
api = api
).choices.first().message?.content ?: throw RuntimeException("No response")
}
- return ImageResponseImpl(text, api = api)
+ return ImageResponseImpl(text, api = this.openAI!!)
}
override fun withModel(model: ChatModels): ImageActor = ImageActor(
@@ -92,9 +91,11 @@ open class ImageActor(
height = height,
)
+ var openAI: OpenAIClient? = null
+ fun setImageAPI(openAI: OpenAIClient): ImageActor {
+ this.openAI = openAI
+ return this
+ }
+
}
-interface ImageResponse {
- val text: String
- val image: BufferedImage
-}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageResponse.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageResponse.kt
new file mode 100644
index 00000000..19703f0c
--- /dev/null
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ImageResponse.kt
@@ -0,0 +1,8 @@
+package com.simiacryptus.skyenet.core.actors
+
+import java.awt.image.BufferedImage
+
+interface ImageResponse {
+ val text: String
+ val image: BufferedImage
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt
index 602319f8..944e69a8 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedActor.kt
@@ -1,16 +1,17 @@
package com.simiacryptus.skyenet.core.actors
import com.simiacryptus.jopenai.API
-import com.simiacryptus.jopenai.ApiModel
import com.simiacryptus.jopenai.ChatClient
-import com.simiacryptus.jopenai.OpenAIClient
import com.simiacryptus.jopenai.describe.AbbrevWhitelistYamlDescriber
import com.simiacryptus.jopenai.describe.TypeDescriber
+import com.simiacryptus.jopenai.models.ApiModel
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.OpenAIModels
import com.simiacryptus.jopenai.models.OpenAITextModel
import com.simiacryptus.jopenai.util.ClientUtil.toContentList
-import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.skyenet.core.util.MultiExeption
+import com.simiacryptus.util.JsonUtil
+import org.slf4j.LoggerFactory
import java.util.function.Function
open class ParsedActor(
@@ -27,6 +28,7 @@ open class ParsedActor(
) {
override val includeMethods: Boolean get() = false
},
+ var parserPrompt: String? = null,
) : BaseActor, ParsedResponse>(
prompt = prompt,
name = name,
@@ -55,7 +57,7 @@ open class ParsedActor(
ParsedResponse(resultClass!!) {
override val text =
response(*messages, api = api).choices.first().message?.content ?: throw RuntimeException("No response")
- private val _obj: T by lazy { getParser(api).apply(text) }
+ private val _obj: T by lazy { getParser(api, parserPrompt).apply(text) }
override val obj get() = _obj
}
@@ -83,7 +85,10 @@ open class ParsedActor(
ApiModel.ChatRequest(
messages = listOf(
ApiModel.ChatMessage(role = ApiModel.Role.system, content = prompt.toContentList()),
- ApiModel.ChatMessage(role = ApiModel.Role.user, content = "The user message to parse:\n\n$input".toContentList()),
+ ApiModel.ChatMessage(
+ role = ApiModel.Role.user,
+ content = "The user message to parse:\n\n$input".toContentList()
+ ),
),
temperature = temperature,
model = parsingModel.modelName,
@@ -106,7 +111,14 @@ open class ParsedActor(
if (endIndex > 7) {
contentUnwrapped = contentUnwrapped.substring(7, endIndex)
} else {
- throw RuntimeException("Failed to parse response: ${contentUnwrapped.replace("\n", "\n ")}")
+ throw RuntimeException(
+ "Failed to parse response: ${
+ contentUnwrapped.replace(
+ "\n",
+ "\n "
+ )
+ }"
+ )
}
}
@@ -143,6 +155,8 @@ open class ParsedActor(
)
companion object {
- private val log = org.slf4j.LoggerFactory.getLogger(ParsedActor::class.java)
+ private val log = LoggerFactory.getLogger(ParsedActor::class.java)
}
+
+
}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt
index 54b4ec29..d943132a 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/ParsedResponse.kt
@@ -4,4 +4,21 @@ abstract class ParsedResponse(val clazz: Class) {
abstract val text: String
abstract val obj: T
override fun toString() = text
+ open fun map(cls: Class, fn: (T) -> V): ParsedResponse = MappedResponse(cls, this.clazz, fn, this)
+}
+
+class MappedResponse(
+ clazz: Class,
+ private val cls: Class,
+ private val fn: (F) -> T,
+ private val inner: ParsedResponse
+) : ParsedResponse(clazz) {
+ override val text: String
+ get() = inner.text
+ override val obj: T
+ get() = fn(inner.obj)
+
+ override fun map(cls: Class, fn: (T) -> V): ParsedResponse {
+ return MappedResponse(cls, this.clazz, fn, this)
+ }
}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SimpleActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SimpleActor.kt
index ed81625d..86899cbb 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SimpleActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SimpleActor.kt
@@ -1,7 +1,7 @@
package com.simiacryptus.skyenet.core.actors
import com.simiacryptus.jopenai.API
-import com.simiacryptus.jopenai.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.OpenAITextModel
import com.simiacryptus.jopenai.util.ClientUtil.toContentList
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SpeechResponse.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SpeechResponse.kt
new file mode 100644
index 00000000..b555568d
--- /dev/null
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/SpeechResponse.kt
@@ -0,0 +1,5 @@
+package com.simiacryptus.skyenet.core.actors
+
+interface SpeechResponse {
+ val mp3data: ByteArray?
+}
\ No newline at end of file
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/TextToSpeechActor.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/TextToSpeechActor.kt
index f8504482..97bce168 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/TextToSpeechActor.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/TextToSpeechActor.kt
@@ -1,9 +1,8 @@
package com.simiacryptus.skyenet.core.actors
import com.simiacryptus.jopenai.API
-import com.simiacryptus.jopenai.ApiModel
-import com.simiacryptus.jopenai.ApiModel.ChatMessage
-import com.simiacryptus.jopenai.GPT4Tokenizer
+import com.simiacryptus.jopenai.models.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel.ChatMessage
import com.simiacryptus.jopenai.OpenAIClient
import com.simiacryptus.jopenai.models.AudioModels
import com.simiacryptus.jopenai.models.ChatModels
@@ -20,6 +19,12 @@ open class TextToSpeechActor(
name = name,
model = models,
) {
+ var openAI: OpenAIClient? = null
+ fun setOpenAI(openAI: OpenAIClient): TextToSpeechActor {
+ this.openAI = openAI
+ return this
+ }
+
override fun chatMessages(questions: List) = questions.map {
ChatMessage(
role = ApiModel.Role.user,
@@ -50,14 +55,11 @@ open class TextToSpeechActor(
override fun respond(input: List, api: API, vararg messages: ChatMessage) =
SpeechResponseImpl(
messages.joinToString("\n") { it.content?.joinToString("\n") { it.text ?: "" } ?: "" },
- api = api
+ api = this.openAI ?: throw RuntimeException("OpenAI client not set")
)
- override fun withModel(model: ChatModels) = this
-}
-
-interface SpeechResponse {
- val mp3data: ByteArray?
+ override fun withModel(model: ChatModels) = TextToSpeechActor(name, audioModel, voice, speed, model)
+ .also { it.openAI = this.openAI }
}
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt
index 85597c16..9f80a256 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ApplicationServices.kt
@@ -3,7 +3,7 @@ package com.simiacryptus.skyenet.core.platform
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonProperty
import com.google.common.util.concurrent.AtomicDouble
-import com.simiacryptus.jopenai.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel
import com.simiacryptus.jopenai.models.APIProvider
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.OpenAIModel
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt
index 606aaa01..af1418d5 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/ClientManager.kt
@@ -4,6 +4,7 @@ import com.google.common.util.concurrent.ListeningScheduledExecutorService
import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.simiacryptus.jopenai.ChatClient
+import com.simiacryptus.jopenai.OpenAIClient
import com.simiacryptus.jopenai.models.APIProvider
import com.simiacryptus.jopenai.util.ClientUtil
import com.simiacryptus.skyenet.core.platform.ApplicationServices.dataStorageFactory
@@ -17,19 +18,28 @@ open class ClientManager {
private data class SessionKey(val session: Session, val user: User?)
- private val clientCache = mutableMapOf()
- private val poolCache = mutableMapOf()
- private val scheduledPoolCache = mutableMapOf()
- fun getClient(
+ private val chatCache = mutableMapOf()
+ fun getChatClient(
session: Session,
user: User?,
): ChatClient {
log.debug("Fetching client for session: {}, user: {}", session, user)
val key = SessionKey(session, user)
- return clientCache.getOrPut(key) { createClient(session, user)!! }
+ return chatCache.getOrPut(key) { createChatClient(session, user)!! }
+ }
+
+ private val openAICache = mutableMapOf()
+ fun getOpenAIClient(
+ session: Session,
+ user: User?,
+ ): OpenAIClient {
+ log.debug("Fetching client for session: {}, user: {}", session, user)
+ val key = SessionKey(session, user)
+ return openAICache.getOrPut(key) { createOpenAIClient(session, user)!! }
}
+ private val poolCache = mutableMapOf()
protected open fun createPool(session: Session, user: User?) =
ThreadPoolExecutor(
0, Integer.MAX_VALUE,
@@ -38,7 +48,7 @@ open class ClientManager {
RecordingThreadFactory(session, user)
)
- /*createScheduledPool*/
+ private val scheduledPoolCache = mutableMapOf()
protected open fun createScheduledPool(session: Session, user: User?, dataStorage: StorageInterface?) =
MoreExecutors.listeningDecorator(ScheduledThreadPoolExecutor(1))
@@ -80,7 +90,7 @@ open class ClientManager {
}
}
- protected open fun createClient(
+ protected open fun createChatClient(
session: Session,
user: User?,
): ChatClient? {
@@ -136,6 +146,62 @@ open class ClientManager {
})!!
}
+ protected open fun createOpenAIClient(
+ session: Session,
+ user: User?,
+ ): OpenAIClient? {
+ log.debug("Creating client for session: {}, user: {}", session, user)
+ val sessionDir = dataStorageFactory(dataStorageRoot).getDataDir(user, session).apply { mkdirs() }
+ if (user != null) {
+ val userSettings = userSettingsManager.getUserSettings(user)
+ val userApi =
+ if (userSettings.apiKeys.isNotEmpty()) {
+ /*
+ MonitoredClient(
+ key = userSettings.apiKeys,
+ apiBase = userSettings.apiBase,
+ logfile = sessionDir.resolve("openai.log"),
+ session = session,
+ user = user,
+ workPool = getPool(session, user),
+ )*/
+ OpenAIClient(
+ key = userSettings.apiKeys,
+ apiBase = userSettings.apiBase,
+ workPool = getPool(session, user),
+ ).apply {
+ this.session = session
+ this.user = user
+ logStreams += sessionDir.resolve("openai.log").outputStream().buffered()
+ }
+ } else null
+ if (userApi != null) return userApi
+ }
+ val canUseGlobalKey = ApplicationServices.authorizationManager.isAuthorized(
+ null, user, OperationType.GlobalKey
+ )
+ if (!canUseGlobalKey) throw RuntimeException("No API key")
+ return (if (ClientUtil.keyMap.isNotEmpty()) {
+ /*MonitoredClient(
+ key = ClientUtil.keyMap.mapKeys { APIProvider.valueOf(it.key) },
+ logfile = sessionDir.resolve("openai.log"),
+ session = session,
+ user = user,
+ workPool = getPool(session, user),
+ )*/
+ OpenAIClient(
+ key = ClientUtil.keyMap.mapKeys { APIProvider.valueOf(it.key) },
+ workPool = getPool(session, user),
+ ).apply {
+ this.session = session
+ this.user = user
+ logStreams += sessionDir.resolve("openai.log").outputStream().buffered()
+ }
+ } else {
+ null
+ })!!
+ }
+
companion object {
private val log = LoggerFactory.getLogger(ClientManager::class.java)
}
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/HSQLUsageManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/HSQLUsageManager.kt
index 043bbaed..3ec0ac13 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/HSQLUsageManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/HSQLUsageManager.kt
@@ -1,7 +1,7 @@
package com.simiacryptus.skyenet.core.platform
import com.google.common.util.concurrent.AtomicDouble
-import com.simiacryptus.jopenai.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.OpenAIModel
import org.slf4j.Logger
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt
index f9f9960f..f97f4a88 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/DataStorage.kt
@@ -1,6 +1,6 @@
package com.simiacryptus.skyenet.core.platform.file
-import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.util.JsonUtil
import com.simiacryptus.skyenet.core.platform.Session
import com.simiacryptus.skyenet.core.platform.StorageInterface
import com.simiacryptus.skyenet.core.platform.StorageInterface.Companion.validateSessionId
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt
index 708ccf01..ebd0f306 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UsageManager.kt
@@ -1,7 +1,7 @@
package com.simiacryptus.skyenet.core.platform.file
import com.simiacryptus.jopenai.models.*
-import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.util.JsonUtil
import com.simiacryptus.skyenet.core.platform.Session
import com.simiacryptus.skyenet.core.platform.UsageInterface
import com.simiacryptus.skyenet.core.platform.UsageInterface.*
@@ -49,21 +49,21 @@ open class UsageManager(val root: File) : UsageInterface {
Session(sessionId),
User(email = user),
modelEnum,
- com.simiacryptus.jopenai.ApiModel.Usage(prompt_tokens = value.toLong())
+ ApiModel.Usage(prompt_tokens = value.toLong())
)
"output" -> incrementUsage(
Session(sessionId),
User(email = user),
modelEnum,
- com.simiacryptus.jopenai.ApiModel.Usage(completion_tokens = value.toLong())
+ ApiModel.Usage(completion_tokens = value.toLong())
)
"cost" -> incrementUsage(
session = Session(sessionId = sessionId),
user = User(email = user),
model = modelEnum,
- tokens = com.simiacryptus.jopenai.ApiModel.Usage(cost = value.toDouble())
+ tokens = ApiModel.Usage(cost = value.toDouble())
)
else -> throw RuntimeException("Unknown direction $direction")
@@ -135,7 +135,7 @@ open class UsageManager(val root: File) : UsageInterface {
session: Session,
apiKey: String?,
model: OpenAIModel,
- tokens: com.simiacryptus.jopenai.ApiModel.Usage
+ tokens: ApiModel.Usage
) {
usagePerSession.computeIfAbsent(session) { UsageCounters() }
.tokensPerModel.computeIfAbsent(UsageKey(session, apiKey, model)) { UsageValues() }
@@ -158,7 +158,7 @@ open class UsageManager(val root: File) : UsageInterface {
}
}
- override fun getUserUsageSummary(apiKey: String): Map {
+ override fun getUserUsageSummary(apiKey: String): Map {
return sessionsByUser[apiKey]?.flatMap { sessionId ->
val usage = usagePerSession[sessionId]
usage?.tokensPerModel?.entries?.map { (model, counter) ->
@@ -166,7 +166,7 @@ open class UsageManager(val root: File) : UsageInterface {
} ?: emptyList()
}?.groupBy { it.first }?.mapValues {
it.value.map { it.second }.reduce { a, b ->
- com.simiacryptus.jopenai.ApiModel.Usage(
+ ApiModel.Usage(
prompt_tokens = a.prompt_tokens + b.prompt_tokens,
completion_tokens = a.completion_tokens + b.completion_tokens,
cost = (a.cost ?: 0.0) + (b.cost ?: 0.0)
@@ -175,12 +175,12 @@ open class UsageManager(val root: File) : UsageInterface {
} ?: emptyMap()
}
- override fun getSessionUsageSummary(session: Session): Map =
+ override fun getSessionUsageSummary(session: Session): Map =
usagePerSession[session]?.tokensPerModel?.entries?.map { (model, counter) ->
model.model to counter.toUsage()
}?.groupBy { it.first }?.mapValues {
it.value.map { it.second }.reduce { a, b ->
- com.simiacryptus.jopenai.ApiModel.Usage(
+ ApiModel.Usage(
prompt_tokens = a.prompt_tokens + b.prompt_tokens,
completion_tokens = a.completion_tokens + b.completion_tokens,
cost = (a.cost ?: 0.0) + (b.cost ?: 0.0)
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt
index 7ef0dc51..de50a35f 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/file/UserSettingsManager.kt
@@ -1,6 +1,6 @@
package com.simiacryptus.skyenet.core.platform.file
-import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.util.JsonUtil
import com.simiacryptus.skyenet.core.platform.ApplicationServices
import com.simiacryptus.skyenet.core.platform.ApplicationServicesConfig.dataStorageRoot
import com.simiacryptus.skyenet.core.platform.User
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/StorageInterfaceTest.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/StorageInterfaceTest.kt
index 30bf2640..383c5c99 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/StorageInterfaceTest.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/StorageInterfaceTest.kt
@@ -1,6 +1,6 @@
package com.simiacryptus.skyenet.core.platform.test
-import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.util.JsonUtil
import com.simiacryptus.skyenet.core.platform.Session
import com.simiacryptus.skyenet.core.platform.StorageInterface
import com.simiacryptus.skyenet.core.platform.User
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/UsageTest.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/UsageTest.kt
index 3b31304a..f10016e5 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/UsageTest.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/platform/test/UsageTest.kt
@@ -1,6 +1,6 @@
package com.simiacryptus.skyenet.core.platform.test
-import com.simiacryptus.jopenai.ApiModel
+import com.simiacryptus.jopenai.models.ApiModel
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.OpenAIModels
import com.simiacryptus.skyenet.core.platform.StorageInterface
diff --git a/core/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CommonRoot.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/CommonRoot.kt
similarity index 94%
rename from core/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CommonRoot.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/util/CommonRoot.kt
index a727309f..c2ef214c 100644
--- a/core/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/CommonRoot.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/CommonRoot.kt
@@ -1,4 +1,4 @@
-package com.github.simiacryptus.aicoder.actions.generic
+package com.simiacryptus.skyenet.core.util
import java.io.File
import java.nio.file.Path
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/FunctionWrapper.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/FunctionWrapper.kt
index b2167484..dcd4c29b 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/FunctionWrapper.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/FunctionWrapper.kt
@@ -6,7 +6,7 @@ import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.models.EmbeddingModels
import com.simiacryptus.jopenai.models.ImageModels
import com.simiacryptus.jopenai.models.OpenAIModel
-import com.simiacryptus.jopenai.util.JsonUtil
+import com.simiacryptus.util.JsonUtil
import java.awt.image.BufferedImage
import java.io.Closeable
import java.io.File
diff --git a/core/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/GetModuleRootForFile.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/GetModuleRootForFile.kt
similarity index 83%
rename from core/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/GetModuleRootForFile.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/util/GetModuleRootForFile.kt
index e239392e..7bf3b458 100644
--- a/core/src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/GetModuleRootForFile.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/GetModuleRootForFile.kt
@@ -1,4 +1,4 @@
-package com.github.simiacryptus.aicoder.actions.generic
+package com.simiacryptus.skyenet.core.util
import java.io.File
diff --git a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/MultiExeption.kt
similarity index 62%
rename from core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt
rename to core/src/main/kotlin/com/simiacryptus/skyenet/core/util/MultiExeption.kt
index 02c61be5..ece5ae42 100644
--- a/core/src/main/kotlin/com/simiacryptus/skyenet/core/actors/MultiExeption.kt
+++ b/core/src/main/kotlin/com/simiacryptus/skyenet/core/util/MultiExeption.kt
@@ -1,6 +1,4 @@
-package com.simiacryptus.skyenet.core.actors
-
-import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent
+package com.simiacryptus.skyenet.core.util
class MultiExeption(exceptions: Collection) : RuntimeException(
exceptions.joinToString("\n\n") { "```text\n${/*escapeHtml4*/(it.stackTraceToString())/*.indent(" ")*/}\n```" }
diff --git a/docs/path_structure.md b/docs/path_structure.md
new file mode 100644
index 00000000..77253e37
--- /dev/null
+++ b/docs/path_structure.md
@@ -0,0 +1,54 @@
+Based on the provided code, here's a document detailing the servlet path layout for the application:
+
+# Servlet Path Layout
+
+The application uses a hierarchical structure for its servlet paths, with different functionalities mapped to specific URLs. Below is a breakdown of the main servlet paths and their purposes:
+
+## Root Level Paths
+
+1. `/` - Welcome page (WelcomeServlet)
+2. `/api` - API endpoint (WelcomeServlet)
+3. `/logout` - Logout functionality (LogoutServlet)
+4. `/proxy` - Proxy HTTP requests (ProxyHttpServlet)
+5. `/userInfo` - User information (UserInfoServlet)
+6. `/userSettings` - User settings (UserSettingsServlet)
+7. `/usage` - Usage information (UsageServlet)
+8. `/apiKeys` - API key management (ApiKeyServlet)
+9. `/stats` - Server statistics (StatisticsServlet)
+
+## Authentication
+
+* `/oauth2callback` - OAuth2 callback URL for Google authentication
+
+## Application-Specific Paths
+
+For each child web application defined in `childWebApps`, a new path is created. These paths are dynamically generated based on the `ApplicationServer` instances.
+
+## Common Paths for Each Application
+
+Within each application-specific path, the following common servlets are typically available:
+
+1. `/` - Default servlet
+2. `/ws` - WebSocket endpoint
+3. `/newSession` - Create a new session
+4. `/appInfo` - Application information
+5. `/userInfo` - User information (may overlap with root level)
+6. `/usage` - Usage information (may overlap with root level)
+7. `/fileIndex/*` - File indexing
+8. `/fileZip` - File compression
+9. `/sessions` - Session listing
+10. `/settings` - Session settings
+11. `/threads` - Session threads
+12. `/share` - Session sharing
+13. `/delete` - Delete session
+14. `/cancel` - Cancel threads
+
+## Notes
+
+* The exact paths may vary depending on the specific implementation of each `ApplicationServer` instance.
+* Some paths (like `/userInfo` and `/usage`) appear both at the root level and within application-specific contexts. The application-specific versions may provide more tailored information.
+* The application uses filters for Cross-Origin Resource Sharing (CORS) and authentication/authorization checks.
+* WebSocket support is configured for real-time communication.
+* The server is set up to handle both HTTP and HTTPS connections, with the latter being used when running in server mode.
+
+This layout provides a comprehensive structure for handling various aspects of the application, from user management and authentication to application-specific functionalities and session handling.
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/planning_agent.md b/docs/planning_agent.md
similarity index 100%
rename from webui/src/main/kotlin/com/simiacryptus/skyenet/apps/plan/planning_agent.md
rename to docs/planning_agent.md
diff --git a/docs/planning_agent_bespoke.html b/docs/planning_agent_bespoke.html
new file mode 100644
index 00000000..2c7ca62f
--- /dev/null
+++ b/docs/planning_agent_bespoke.html
@@ -0,0 +1,223 @@
+
+
+
+
+
+ Plan Feature User and Developer Guide
+
+
+
+
+
+
+
+
Plan Feature User and Developer Guide
+
+
+
+
Introduction
+
The Plan feature is a sophisticated system designed to break down high-level user requests into manageable, actionable tasks. It leverages OpenAI's capabilities to automate task planning, execution, and management, ensuring efficient workflow and seamless integration with existing systems.
+
+
+
+
Features Overview
+
+
Dynamic Task Breakdown
+
Task Execution
+
Dependency Management
+
Asynchronous Processing
+
Customizable Settings
+
Extensible Architecture
+
+
+
+
+
User Guide
+
Accessing the Plan Feature
+
Navigate to the application interface where task planning and management functionalities are exposed.
The Plan feature is a sophisticated system designed to break down high-level user requests into manageable, actionable tasks. It leverages OpenAI's capabilities to automate task planning, execution, and management, ensuring efficient workflow and seamless integration with existing systems.
+
+
+
+
Features Overview
+
+
Dynamic Task Breakdown
+
Task Execution
+
Dependency Management
+
Asynchronous Processing
+
Customizable Settings
+
Extensible Architecture
+
+
+
+
+
User Guide
+
Accessing the Plan Feature
+
Navigate to the application interface where task planning and management functionalities are exposed.
+
+
+
+
Creating and Managing Plans
+
+
Initiate a New Plan
+
Task Breakdown
+
Review Generated Tasks
+
Execute the Plan
+
+
+
+
+
Understanding Task Dependencies
+
+
Visual Representation using Mermaid
+
Interpreting the Graph
+
+
+
+
+
Viewing Execution Logs
+
+
Accessing Logs through UI
+
Log Files stored in .logs directory
+
+
+
+
+
Developer Guide
+
Architecture Overview
+
+
TaskType
+
PlanSettings
+
PlanCoordinator
+
PlanUtil
+
PlanningTask
+
AbstractTask
+
+
+
+
+
Task Types
+
+
CommandAutoFixTask
+
InquiryTask
+
FileModificationTask
+
RunShellCommandTask
+
RefactorTask
+
+
+
+
+
Task Types (continued)
+
+
SecurityAuditTask
+
CodeOptimizationTask
+
CodeReviewTask
+
DocumentationTask
+
TestGenerationTask
+
+
+
+
+
Adding New Task Types
+
+
Define the Task Type
+
Create the Task Class
+
Register the Task Type
+
Update PlanSettings
+
+
+
+
+
Configuring Plan Settings
+
+
Default and Parsing Models
+
Command Environment
+
Temperature and Budget
+
Task-Specific Settings
+
Auto-Fix and Blocking Behavior
+
+
+
+
+
Extending PlanCoordinator
+
+
Initialization
+
Task Execution
+
Logging and Error Handling
+
+
+
+
+
Utilizing PlanUtil
+
+
Diagram Generation
+
Rendering Plans
+
Plan Filtering
+
+
+
+
+
Handling Asynchronous Task Execution
+
+
Concurrency Management
+
Task Dependencies
+
Error Propagation
+
+
+
+
+
Customizing Task Execution
+
+
Overriding the run Method
+
Integrating with External Systems
+
Enhancing the Execution Flow
+
+
+
+
+
Best Practices
+
+
Modular Design
+
Robust Error Handling
+
Efficient Dependency Management
+
Logging and Monitoring
+
Security Considerations
+
+
+
+
+
Troubleshooting
+
+
Circular Dependency Detected
+
Unknown Task Type Error
+
API Errors
+
File Reading Errors
+
Task Execution Failures
+
+
+
+
+
Frequently Asked Questions (FAQs)
+
+
How do I enable or disable specific task types?
+
Can I add custom commands for shell tasks?
+
How are task dependencies managed?
+
Is it possible to log API interactions for auditing purposes?
+
How can I visualize the task execution flow?
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/planning_agent_impress.html b/docs/planning_agent_impress.html
new file mode 100644
index 00000000..540fc96b
--- /dev/null
+++ b/docs/planning_agent_impress.html
@@ -0,0 +1,154 @@
+
+
+
+
+
+ Plan Feature User and Developer Guide
+
+
+
+
+
+
+
Plan Feature User and Developer Guide
+
Welcome to the comprehensive guide for the Plan feature, designed to assist both end-users and developers.
+
+
+
+
Introduction
+
The Plan feature is a sophisticated system designed to break down high-level user requests into manageable, actionable tasks.
+
+
+
+
Features Overview
+
+
Dynamic Task Breakdown
+
Task Execution
+
Dependency Management
+
Asynchronous Processing
+
Customizable Settings
+
Extensible Architecture
+
+
+
+
+
User Guide
+
Accessing the Plan Feature
+
Navigate to the application interface where task planning and management functionalities are exposed.
+
+
+
+
Creating and Managing Plans
+
+
Initiate a New Plan
+
Task Breakdown
+
Review Generated Tasks
+
Execute the Plan
+
+
+
+
+
Understanding Task Dependencies
+
The Plan feature utilizes Mermaid to render task dependency graphs.
+
+
+
+
Viewing Execution Logs
+
Execution logs are automatically generated and can be accessed through the UI.
+
+
+
+
Developer Guide
+
Architecture Overview
+
The Plan feature is built around several core components: TaskType, PlanSettings, PlanCoordinator, PlanUtil, PlanningTask, and AbstractTask.
+
+
+
+
Task Types
+
Various task types are defined, including CommandAutoFixTask, InquiryTask, FileModificationTask, and more.
+
+
+
+
Adding New Task Types
+
To introduce a new task type:
+
+
Define the Task Type
+
Create the Task Class
+
Register the Task Type
+
Update PlanSettings
+
+
+
+
+
Configuring Plan Settings
+
PlanSettings.kt allows for extensive configuration of how tasks are planned and executed.
+
+
+
+
Extending PlanCoordinator
+
PlanCoordinator.kt is pivotal in managing the lifecycle of plans and their tasks.
+
+
+
+
Utilizing PlanUtil
+
PlanUtil.kt provides utility functions essential for task management and visualization.
+
+
+
+
Handling Asynchronous Task Execution
+
The system leverages a ThreadPoolExecutor to manage task execution asynchronously.
+
+
+
+
Customizing Task Execution
+
Developers can customize how individual tasks execute by overriding methods and integrating with external systems.
+
+
+
+
Best Practices
+
+
Modular Design
+
Robust Error Handling
+
Efficient Dependency Management
+
Logging and Monitoring
+
Security Considerations
+
+
+
+
+
Troubleshooting
+
Common issues and their solutions are provided, including circular dependencies, unknown task types, API errors, file reading errors, and task execution failures.
+
+
+
+
Frequently Asked Questions (FAQs)
+
Answers to common questions about enabling/disabling task types, adding custom commands, managing dependencies, logging API interactions, and visualizing task execution flow.
+
+
+
+
+
+
+
diff --git a/docs/planning_agent_reveal.html b/docs/planning_agent_reveal.html
new file mode 100644
index 00000000..7bf55048
--- /dev/null
+++ b/docs/planning_agent_reveal.html
@@ -0,0 +1,153 @@
++
+
+
+
+
+
+ Plan Feature User and Developer Guide
+
+
+
+
+
+
+
+
+
+
+
+
+
Plan Feature User and Developer Guide
+
+
+
+
Introduction
+
The Plan feature is a sophisticated system designed to break down high-level user requests into manageable, actionable tasks. It leverages OpenAI's capabilities to automate task planning, execution, and management, ensuring efficient workflow and seamless integration with existing systems.
+
+
+
+
Features Overview
+
+
Dynamic Task Breakdown
+
Task Execution
+
Dependency Management
+
Asynchronous Processing
+
Customizable Settings
+
Extensible Architecture
+
+
+
+
+
User Guide
+
+
Accessing the Plan Feature
+
Navigate to the application interface where task planning and management functionalities are exposed.
+
+
+
Creating and Managing Plans
+
+
Initiate a New Plan
+
Task Breakdown
+
Review Generated Tasks
+
Execute the Plan
+
+
+
+
Understanding Task Dependencies
+
The Plan feature utilizes Mermaid to render task dependency graphs.
+
+
+
Viewing Execution Logs
+
Execution logs are automatically generated and can be accessed through the UI.
+
+
+
+
+
Developer Guide
+
+
Architecture Overview
+
+
TaskType
+
PlanSettings
+
PlanCoordinator
+
PlanUtil
+
PlanningTask
+
AbstractTask
+
+
+
+
Task Types
+
+
CommandAutoFixTask
+
InquiryTask
+
FileModificationTask
+
RunShellCommandTask
+
RefactorTask
+
SecurityAuditTask
+
CodeOptimizationTask
+
CodeReviewTask
+
DocumentationTask
+
TestGenerationTask
+
+
+
+
Adding New Task Types
+
+
Define the Task Type
+
Create the Task Class
+
Register the Task Type
+
Update PlanSettings
+
+
+
+
+
+
Best Practices
+
+
Modular Design
+
Robust Error Handling
+
Efficient Dependency Management
+
Logging and Monitoring
+
Security Considerations
+
+
+
+
+
Troubleshooting
+
+
Circular Dependency Detected
+
Unknown Task Type Error
+
API Errors
+
File Reading Errors
+
Task Execution Failures
+
+
+
+
+
Frequently Asked Questions (FAQs)
+
Q1: How do I enable or disable specific task types?
+
Q2: Can I add custom commands for shell tasks?
+
Q3: How are task dependencies managed?
+
Q4: Is it possible to log API interactions for auditing purposes?
+
Q5: How can I visualize the task execution flow?
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/project_info.md b/docs/prompts/project_info.md
similarity index 100%
rename from docs/project_info.md
rename to docs/prompts/project_info.md
diff --git a/docs/project_info_template.md b/docs/prompts/project_info_template.md
similarity index 100%
rename from docs/project_info_template.md
rename to docs/prompts/project_info_template.md
diff --git a/docs/project_info_template2.md b/docs/prompts/project_info_template2.md
similarity index 100%
rename from docs/project_info_template2.md
rename to docs/prompts/project_info_template2.md
diff --git a/docs/tabbed_ui.md b/docs/tabbed_ui.md
deleted file mode 100644
index d1ad3f37..00000000
--- a/docs/tabbed_ui.md
+++ /dev/null
@@ -1,190 +0,0 @@
-# Tabbed UI Documentation
-
-## Overview
-
-The tabbed UI components in the codebase are specifically designed to facilitate the creation, management, and seamless
-integration of tabbed interfaces within web applications. Ideal for applications requiring dynamic content switching
-without page reloads, these components support various content types and are driven by user interactions or backend
-processes. They are particularly useful in dashboards, multi-step forms, and settings panels.
-
-## Components
-
-### TabbedDisplay
-
-`TabbedDisplay` is a base class for creating tabbed displays. It manages a list of tabs, each associated with a label
-and content. It provides methods to render tabs, manage tab content, and handle user interactions to switch between
-tabs.
-
-#### Key Methods and Properties
-
-### Retryable
-
-`Retryable` extends `TabbedDisplay` to add functionality for retrying operations. It is particularly useful for
-operations that might fail and need a retry mechanism, encapsulated within a tab.
-
-#### Key Methods
-
-### AgentPatterns
-
-`AgentPatterns` contains utility methods for displaying content in a tabbed format without directly managing the tab
-state.
-
-#### Key Functions
-
-a tabbed interface, optionally splitting content into separate tasks for performance.
-
-### MarkdownUtil
-
-`MarkdownUtil` provides utilities for rendering Markdown content into HTML, including special handling for Mermaid
-diagrams and tabbed displays of Markdown versus rendered HTML.
-
-#### Key Methods
-
-Markdown into HTML, with support for Mermaid diagrams and an optional tabbed interface.
-
-## Usage Examples
-
-### Example 1: Basic Tabbed Display
-
-```kotlin
-val tabbedDisplay = TabbedDisplay(sessionTask)
-tabbedDisplay.set("Tab 1", "Content for Tab 1")
-tabbedDisplay.set("Tab 2", "Content for Tab 2")
-tabbedDisplay.update()
-```
-
-This example demonstrates how to create a simple tabbed interface with two tabs.
-
-Detailed examples of using these components can be added here, including code snippets and explanations of how to
-integrate the tabbed UI into a web application.
-
-## Conclusion
-
-The tabbed UI components are versatile tools for building interactive and dynamic web interfaces. By understanding and
-utilizing these components, developers can enhance the user experience of their applications.
-
-### Tabs
-
-The `TabbedDisplay` class in `TabbedDisplay.kt` implements a tabbed UI component that allows displaying multiple pieces
-of content in a tabbed interface.
-
-#### High Level Design
-
-- The `TabbedDisplay` class maintains a list of tab name and content `StringBuilder` pairs.
-- It provides methods to get/set tab content by name, find tabs by name, and update the rendered HTML.
-- The `render()` method generates the HTML for the tabbed interface, including tab buttons and content divs.
-- An instance keeps a reference to the `container` `StringBuilder` it is rendered into, allowing it to update itself.
-
-#### Usage
-
-To use the `TabbedDisplay` class:
-
-1. Create an instance, passing the `SessionTask` it is associated with
-2. Add tabs using the `set(name: String, content: String)` method. This will create the tab if it doesn't exist or
- update its content if it does.
-3. Retrieve tab content using the `get(i: String)` method
-4. Call `update()` after modifying tabs to re-render the component HTML
-
-The tabbed content will automatically be displayed in the associated `SessionTask`.
-
-### Retry
-
-The `Retryable` class in `Retryable.kt` extends `TabbedDisplay` to add a "retry" button that re-runs a block of code and
-adds the output as a new tab.
-
-#### High Level Design
-
-`Retryable` overrides the `renderTabButtons()` method to add a recycle â™» button after the tab buttons. This button, when
-clicked, triggers the retry mechanism.
-
-- When clicked, the retry callback:
- 1. Adds a new tab with a "Retrying..." placeholder and calls `update()`
- 2. Runs the `process` lambda passed to the constructor, passing a `StringBuilder` for output
- 3. Replaces the placeholder content with the final output
-- This allows easily re-running a block of code and capturing the new output in a new tab
-
-#### Usage
-
-To use `Retryable`:
-
-1. Create an instance, passing the `ApplicationInterface`, `SessionTask` and retry process lambda
-2. The retry button will automatically be shown and will run the `process` lambda when clicked
-3. The `process` lambda should return the `String` content to display in the new tab
-
-### Example 2: Using Retryable
-
- ```kotlin
- val applicationInterface = getApplicationInterface() // Assume this returns an ApplicationInterface instance
-val sessionTask = getSessionTask() // Assume this returns a SessionTask instance
-val retryable = Retryable(applicationInterface, sessionTask) { stringBuilder ->
- try {
- // Code that might fail and needs retrying
- "Operation successful"
- } catch (e: Exception) {
- stringBuilder.append("Error encountered: ${e.message}")
- "Retry failed"
- }
-}
-retryable.update()
- ```
-
-This example demonstrates how to use `Retryable` to add retry functionality. The lambda function provided to `Retryable`
-is executed when the retry button is clicked. If the operation is successful, it returns a success message; otherwise,
-it logs the error and returns a failure message.
-By using `Retryable`, you can add retry functionality to a tabbed display with just a few lines of code.
-
-### Acceptable
-
-The `Acceptable` class is designed to handle user interactions that require acceptance or feedback before proceeding. It
-extends the functionality of `TabbedDisplay` by integrating user input and decision-making processes directly into the
-tabbed interface.
-
-#### High Level Design
-
-`Acceptable` manages a sequence of user interactions within a tabbed display, where each tab can represent a stage in a
-decision-making process. It uses a combination of user prompts, text inputs, and acceptance links to gather and process
-user feedback.
-
-- The class initializes with a user message and a function to process the initial response.
-- It dynamically adds tabs based on user interactions and updates the display accordingly.
-- A feedback mechanism allows users to revise their responses, which the system processes to potentially alter the
- subsequent flow or decisions.
-
-#### Key Methods
-
-- `main()`: Orchestrates the initial display and subsequent updates based on user interactions.
-- `feedbackForm()`: Generates the HTML form for user feedback within the tab.
-- `acceptLink()`: Provides a link for the user to confirm their decision, moving the process to the next stage.
-
-#### Usage
-
-To use `Acceptable`:
-
-1. Instantiate `Acceptable` with necessary parameters like the session task, user message, initial response processing
- function, and UI interface.
-2. Use the `call()` method to start the interaction process and wait for the user's final acceptance.
-3. The class handles user inputs and updates the tabbed display dynamically, reflecting the stages of user interaction
- and decision-making.
-
-### Example 3: Using Acceptable
-
-```kotlin
-val sessionTask = getSessionTask() // Assume this returns a SessionTask instance
-val userMessage = "Please review the information and accept to proceed."
-val acceptable = Acceptable(
- task = sessionTask,
- userMessage = userMessage,
- initialResponse = { msg -> processInitialResponse(msg) },
- outputFn = { response -> response.toString() },
- ui = getApplicationInterface(), // Assume this returns an ApplicationInterface instance
- reviseResponse = { history -> reviseUserResponse(history) },
- heading = "User Acceptance Required"
-)
-acceptable.call()
-```
-
-This example demonstrates how to use `Acceptable` to manage a user acceptance process within a tabbed interface. The
-user is prompted to review information and provide feedback or accept to proceed, with each stage managed as a separate
-tab.
-
-Let me know if you have any other questions!
\ No newline at end of file
diff --git a/docs/discussable.md b/docs/ui/discussable.md
similarity index 100%
rename from docs/discussable.md
rename to docs/ui/discussable.md
diff --git a/docs/task_ui.md b/docs/ui/task_ui.md
similarity index 100%
rename from docs/task_ui.md
rename to docs/ui/task_ui.md
diff --git a/docs/ui_protocol.md b/docs/ui/ui_protocol.md
similarity index 99%
rename from docs/ui_protocol.md
rename to docs/ui/ui_protocol.md
index 582a7792..4bb1bb4a 100644
--- a/docs/ui_protocol.md
+++ b/docs/ui/ui_protocol.md
@@ -122,7 +122,6 @@ val retryable = Retryable(appInterface, task) { content ->
// Process content
"Processed content"
}
-retryable.update()
```
### 6. AgentPatterns
diff --git a/gradle.properties b/gradle.properties
index f0b5132b..a05675d7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
# Gradle Releases -> https://github.com/gradle/gradle/releases
libraryGroup = com.simiacryptus.skyenet
-libraryVersion = 1.2.3
+libraryVersion = 1.2.4
gradleVersion = 7.6.1
kotlin.daemon.jvmargs=-Xmx2g
diff --git a/settings.gradle.kts b/settings.gradle.kts
index b55c8051..409d77e6 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -8,4 +8,4 @@ include("kotlin")
//include("kotlin-hack-test")
include("webui")
-//includeBuild("../jo-penai/")
\ No newline at end of file
+//includeBuild("../jo-penai/")
diff --git a/webui/build.gradle.kts b/webui/build.gradle.kts
index 2503434e..5fafc433 100644
--- a/webui/build.gradle.kts
+++ b/webui/build.gradle.kts
@@ -36,7 +36,7 @@ val jackson_version = "2.17.2"
dependencies {
- implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.1.4") {
+ implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.1.5") {
exclude(group = "org.slf4j")
}
diff --git a/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyDiffLinks.kt b/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyDiffLinks.kt
index 1898b6ae..6899cb05 100644
--- a/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyDiffLinks.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyDiffLinks.kt
@@ -5,7 +5,7 @@ import com.simiacryptus.skyenet.set
import com.simiacryptus.skyenet.webui.application.ApplicationInterface
import com.simiacryptus.skyenet.webui.session.SessionTask
import com.simiacryptus.skyenet.webui.session.SocketManagerBase
-import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown
+import com.simiacryptus.skyenet.util.MarkdownUtil.renderMarkdown
fun SocketManagerBase.addApplyDiffLinks(
code: () -> String,
diff --git a/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyFileDiffLinks.kt b/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyFileDiffLinks.kt
index 217e2937..1b547196 100644
--- a/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyFileDiffLinks.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/diff/AddApplyFileDiffLinks.kt
@@ -9,7 +9,7 @@ import com.simiacryptus.skyenet.core.actors.SimpleActor
import com.simiacryptus.skyenet.set
import com.simiacryptus.skyenet.webui.application.ApplicationInterface
import com.simiacryptus.skyenet.webui.session.SocketManagerBase
-import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown
+import com.simiacryptus.skyenet.util.MarkdownUtil.renderMarkdown
import java.io.File
import java.nio.file.Path
import kotlin.io.path.readText
@@ -33,7 +33,7 @@ fun SocketManagerBase.addApplyFileDiffLinks(
// Single diff block without the closing ``` due to LLM limitations... add it back and recurse
return addApplyFileDiffLinks(
root,
- response + "\n```",
+ response + "\n```\n",
handle,
ui,
api
@@ -96,20 +96,21 @@ private fun SocketManagerBase.renderNewFile(
filepath.toFile().writeText(codeValue, Charsets.UTF_8)
handle(mapOf(File(filename).toPath() to codeValue))
return """
- |```${codeLang}
- |${codeValue}
- |```
- |
- |
+${textInput(design, tabContent, history, task, feedbackSB, feedbackTask = this)}
+"""
)
complete()
}
@@ -215,9 +215,9 @@ ${
return atomicRef.get()
} catch (e: Exception) {
log.warn("""
- |Error in Discussable
- |${e.message}
- """.trimIndent(), e)
+Error in Discussable
+${e.message}
+""", e)
task.error(ui, e)
return null as T
}
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/Retryable.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/Retryable.kt
index ff2d167b..0985d65b 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/Retryable.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/Retryable.kt
@@ -20,13 +20,13 @@ open class Retryable(
}
override fun renderTabButtons(): String = """
-
${
+
${
tabs.withIndex().joinToString("\n") { (index, _) ->
val tabId = "$index"
""""""
}
}
- ${
+${
ui.hrefLink("â™»") {
val idx = tabs.size
val label = label(idx)
@@ -38,7 +38,7 @@ open class Retryable(
set(label, newResult)
}
}
-
- """.trimIndent()
+
+"""
}
diff --git a/webui/src/main/kotlin/com/simiacryptus/skyenet/TabbedDisplay.kt b/webui/src/main/kotlin/com/simiacryptus/skyenet/TabbedDisplay.kt
index 822b6007..c27c7221 100644
--- a/webui/src/main/kotlin/com/simiacryptus/skyenet/TabbedDisplay.kt
+++ b/webui/src/main/kotlin/com/simiacryptus/skyenet/TabbedDisplay.kt
@@ -15,14 +15,14 @@ open class TabbedDisplay(
val size: Int get() = tabs.size
open fun render() = if(tabs.isEmpty()) "" else """
-
+"""
val container: StringBuilder by lazy {
log.debug("Initializing container with rendered content")
@@ -30,7 +30,7 @@ open class TabbedDisplay(
}
open fun renderTabButtons() = """
-
${
+
${
tabs.toTypedArray().withIndex().joinToString("\n") { (idx, pair) ->
if (idx == selectedTab) {
""""""
@@ -39,15 +39,15 @@ open class TabbedDisplay(
}
}
}
- """.trimIndent()
+"""
open fun renderContentTab(t: Pair, idx: Int) = """
-