Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.simiacryptus.skyenet.apps.coding.ShellToolAgent.kt Maven / Gradle / Ivy
package com.simiacryptus.skyenet.apps.coding
import com.simiacryptus.jopenai.API
import com.simiacryptus.jopenai.ApiModel
import com.simiacryptus.jopenai.OpenAIClient
import com.simiacryptus.jopenai.describe.AbbrevWhitelistYamlDescriber
import com.simiacryptus.jopenai.describe.TypeDescriber
import com.simiacryptus.jopenai.models.ChatModels
import com.simiacryptus.jopenai.util.JsonUtil
import com.simiacryptus.skyenet.core.actors.CodingActor
import com.simiacryptus.skyenet.core.actors.CodingActor.CodeResult
import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.indent
import com.simiacryptus.skyenet.core.actors.CodingActor.Companion.sortCode
import com.simiacryptus.skyenet.core.actors.ParsedActor
import com.simiacryptus.skyenet.core.actors.SimpleActor
import com.simiacryptus.skyenet.core.platform.*
import com.simiacryptus.skyenet.interpreter.Interpreter
import com.simiacryptus.skyenet.kotlin.KotlinInterpreter
import com.simiacryptus.skyenet.webui.application.ApplicationInterface
import com.simiacryptus.skyenet.webui.session.SessionTask
import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown
import com.simiacryptus.skyenet.webui.util.OpenAPI
import jakarta.servlet.http.HttpServlet
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.Response
import org.eclipse.jetty.webapp.WebAppClassLoader
import org.openapitools.codegen.OpenAPIGenerator
import org.openapitools.codegen.SpecValidationException
import org.slf4j.LoggerFactory
import java.io.File
import kotlin.reflect.KClass
private val String.escapeQuotedString: String
get() = replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("$", "\\$")
abstract class ShellToolAgent(
api: API,
dataStorage: StorageInterface,
session: Session,
user: User?,
ui: ApplicationInterface,
interpreter: KClass,
symbols: Map,
temperature: Double = 0.1,
details: String? = null,
model: ChatModels,
actorMap: Map = mapOf(
ActorTypes.CodingActor to CodingActor(
interpreter,
symbols = symbols,
temperature = temperature,
details = details,
model = model
)
),
mainTask: SessionTask = ui.newTask(),
) : CodingAgent(
api,
dataStorage,
session,
user,
ui,
interpreter,
symbols,
temperature,
details,
model,
mainTask,
actorMap
) {
override fun displayFeedback(task: SessionTask, request: CodingActor.CodeRequest, response: CodeResult) {
val formText = StringBuilder()
var formHandle: StringBuilder? = null
formHandle = task.add(
"""
|
|${if (!canPlay) "" else playButton(task, request, response, formText) { formHandle!! }}
|${super.regenButton(task, request, formText) { formHandle!! }}
|${createToolButton(task, request, response, formText) { formHandle!! }}
|
|${super.reviseMsg(task, request, response, formText) { formHandle!! }}
""".trimMargin(), className = "reply-message"
)
formText.append(formHandle.toString())
formHandle.toString()
task.complete()
}
private var lastResult: String? = null
private fun createToolButton(
task: SessionTask,
request: CodingActor.CodeRequest,
response: CodeResult,
formText: StringBuilder,
formHandle: () -> StringBuilder
) = ui.hrefLink("\uD83D\uDCE4", "href-link regen-button") {
val task = ui.newTask()
responseAction(task, "Exporting...", formHandle(), formText) {
displayCodeFeedback(
task, schemaActor(), request.copy(
messages = listOf(
response.code to ApiModel.Role.assistant,
"From the given code prototype, identify input out output data structures and generate Kotlin data classes to define this schema" to ApiModel.Role.user
)
)
) { schemaCode ->
val command = actor.symbols.get("command")?.let { command ->
when (command) {
is String -> command.split(" ")
is List<*> -> command.map { it.toString() }
else -> throw IllegalArgumentException("Invalid command: $command")
}
} ?: listOf("bash")
val cwd = actor.symbols.get("workingDir")?.toString()?.let { File(it) } ?: File(".")
val env = actor.symbols.get("env")?.let { env -> (env as Map) } ?: mapOf()
val codePrefix = """
fun execute() : Pair {
val command = "${command.joinToString(" ").escapeQuotedString}".split(" ")
val cwd = java.io.File("${cwd.absolutePath.escapeQuotedString}")
val env = mapOf(${env.entries.joinToString(",") { "\"${it.key.escapeQuotedString}\" to \"${it.value.escapeQuotedString}\"" }})
val processBuilder = ProcessBuilder(*command.toTypedArray()).directory(cwd)
processBuilder.environment().putAll(env)
val process = processBuilder.start()
process.outputStream.write("${response.code.escapeQuotedString}".toByteArray())
process.outputStream.close()
val output = process.inputStream.bufferedReader().readText()
val error = process.errorStream.bufferedReader().readText()
val waitFor = process.waitFor(5, java.util.concurrent.TimeUnit.MINUTES)
if (!waitFor) {
process.destroy()
throw RuntimeException("Timeout; output: " + output + "; error: " + error)
} else {
return Pair(output, error)
}
}
""".trimIndent()
val messages = listOf(
"Shell Code: \n```${actor.language}\n${(response.code)/*.indent(" ")*/}\n```" to ApiModel.Role.assistant,
) + (lastResult?.let {
listOf(
"Example Output:\n\n```text\n${it/*.indent(" ")*/}\n```" to ApiModel.Role.assistant
)
} ?: listOf()) + listOf(
"Schema: \n```kotlin\n${schemaCode/*.indent(" ")*/}\n```" to ApiModel.Role.assistant,
"Implement a parsing method to convert the shell output to the requested data structure" to ApiModel.Role.user
)
displayCodeFeedback(
task, parsedActor(), request.copy(
messages = messages,
codePrefix = codePrefix
)
) { parsedCode ->
displayCodeFeedback(
task, servletActor(), request.copy(
messages = listOf(
(codePrefix + "\n\n" + parsedCode) to ApiModel.Role.assistant,
"Reprocess this code prototype into a servlet. " +
"The last line should instantiate the new servlet class and return it via the returnBuffer collection." to ApiModel.Role.user
),
codePrefix = schemaCode
)
) { servletHandler ->
val servletImpl = (schemaCode + "\n\n" + servletHandler).sortCode()
val toolsPrefix = "/tools"
var openAPI = openAPIParsedActor().getParser(api).apply(servletImpl).let { openApi ->
openApi.copy(paths = openApi.paths?.mapKeys { toolsPrefix + it.key.removePrefix(toolsPrefix) })
}
task.add(renderMarkdown("```json\n${JsonUtil.toJson(openAPI)/*.indent(" ")*/}\n```", ui = ui))
for (i in 0..5) {
try {
OpenAPIGenerator.main(
arrayOf(
"generate",
"-i",
File.createTempFile("openapi", ".json").apply {
writeText(JsonUtil.toJson(openAPI))
deleteOnExit()
}.absolutePath,
"-g",
"html2",
"-o",
File(
dataStorage.getSessionDir(user, session),
"openapi/html2"
).apply { mkdirs() }.absolutePath,
)
)
task.add("Validated OpenAPI Descriptor - Documentation Saved ")
break
} catch (e: SpecValidationException) {
val error = """
|${e.message}
|${e.errors.joinToString("\n") { "ERROR:" + it.toString() }}
|${e.warnings.joinToString("\n") { "WARN:" + it.toString() }}
""".trimIndent()
task.hideable(
ui,
renderMarkdown(
"```\n${error.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```",
ui = ui
)
)
openAPI = openAPIParsedActor().answer(
listOf(
servletImpl,
JsonUtil.toJson(openAPI),
error
), api
).obj.let { openApi ->
val paths = HashMap(openApi.paths)
openApi.copy(paths = paths.mapKeys { toolsPrefix + it.key.removePrefix(toolsPrefix) })
}
task.hideable(
ui,
renderMarkdown(
"```json\n${JsonUtil.toJson(openAPI)/*.indent(" ")*/}\n```",
ui = ui
)
)
}
}
if (ApplicationServices.authorizationManager.isAuthorized(
ShellToolAgent.javaClass,
user,
AuthorizationInterface.OperationType.Admin
)
) {
/*
ToolServlet.addTool(
ToolServlet.Tool(
path = openAPI.paths?.entries?.first()?.key?.removePrefix(toolsPrefix) ?: "unknown",
openApiDescription = openAPI,
interpreterString = getInterpreterString(),
servletCode = servletImpl
)
)
*/
}
buildTestPage(openAPI, servletImpl, task)
}
}
}
}
}
private fun openAPIParsedActor() = object : ParsedActor(
// parserClass = OpenApiParser::class.java,
resultClass = OpenAPI::class.java,
model = model,
prompt = "You are a code documentation assistant. You will create the OpenAPI definition for a servlet handler written in kotlin",
parsingModel = model,
) {
override val describer: TypeDescriber
get() = object : AbbrevWhitelistYamlDescriber(
//"com.simiacryptus", "com.github.simiacryptus"
) {
override val includeMethods: Boolean get() = false
}
}
private fun servletActor() = object : CodingActor(
interpreterClass = KotlinInterpreter::class,
symbols = actor.symbols + mapOf(
"returnBuffer" to ServletBuffer(),
"json" to JsonUtil,
"req" to Request(null, null),
"resp" to Response(null, null),
),
describer = object : AbbrevWhitelistYamlDescriber(
"com.simiacryptus",
"com.github.simiacryptus"
) {
override fun describe(
rawType: Class,
stackMax: Int,
describedTypes: MutableSet
): String = when (rawType) {
Request::class.java -> describe(HttpServletRequest::class.java)
Response::class.java -> describe(HttpServletResponse::class.java)
else -> super.describe(rawType, stackMax, describedTypes)
}
},
details = actor.details,
model = actor.model,
fallbackModel = actor.fallbackModel,
temperature = actor.temperature,
runtimeSymbols = actor.runtimeSymbols
) {
override val prompt: String
get() = super.prompt
}
private fun schemaActor() = object : CodingActor(
interpreterClass = KotlinInterpreter::class,
symbols = mapOf(),
details = actor.details,
model = actor.model,
fallbackModel = actor.fallbackModel,
temperature = actor.temperature,
runtimeSymbols = actor.runtimeSymbols
) {
override val prompt: String
get() = super.prompt
}
private fun parsedActor() = object : CodingActor(
interpreterClass = KotlinInterpreter::class,
symbols = mapOf(),
details = actor.details,
model = actor.model,
fallbackModel = actor.fallbackModel,
temperature = actor.temperature,
runtimeSymbols = actor.runtimeSymbols
) {
override val prompt: String
get() = super.prompt
}
/**
*
* TODO: This method seems redundant.
*
* */
private fun displayCodeFeedback(
task: SessionTask,
actor: CodingActor,
request: CodingActor.CodeRequest,
response: CodeResult = execWrap { actor.answer(request, api = api) },
onComplete: (String) -> Unit
) {
task.hideable(
ui,
renderMarkdown("```kotlin\n${response.code.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui = ui)
)
val formText = StringBuilder()
var formHandle: StringBuilder? = null
formHandle = task.add(
"""
|
|${
super.ui.hrefLink("\uD83D\uDC4D", "href-link play-button") {
super.responseAction(task, "Accepted...", formHandle!!, formText) {
onComplete(response.code)
}
}
}
|${
if (!super.canPlay) "" else
ui.hrefLink("▶", "href-link play-button") {
execute(ui.newTask(), response)
}
}
|${
super.ui.hrefLink("♻", "href-link regen-button") {
super.responseAction(task, "Regenerating...", formHandle!!, formText) {
//val task = super.ui.newTask()
val codeRequest =
request.copy(messages = request.messages.dropLastWhile { it.second == ApiModel.Role.assistant })
try {
val lastUserMessage =
codeRequest.messages.last { it.second == ApiModel.Role.user }.first.trim()
val codeResponse: CodeResult = if (lastUserMessage.startsWith("```")) {
actor.CodeResultImpl(
messages = actor.chatMessages(codeRequest),
input = codeRequest,
api = super.api as OpenAIClient,
givenCode = lastUserMessage.removePrefix("```").removeSuffix("```")
)
} else {
actor.answer(codeRequest, api = super.api)
}
super.displayCode(task, codeResponse)
displayCodeFeedback(
task,
actor,
super.append(codeRequest, codeResponse),
codeResponse,
onComplete
)
} catch (e: Throwable) {
log.warn("Error", e)
val error = task.error(super.ui, e)
var regenButton: StringBuilder? = null
regenButton = task.complete(super.ui.hrefLink("♻", "href-link regen-button") {
regenButton?.clear()
val header = task.header("Regenerating...")
super.displayCode(task, codeRequest)
header?.clear()
error?.clear()
task.complete()
})
}
}
}
}
|
|${
super.ui.textInput { feedback ->
super.responseAction(task, "Revising...", formHandle!!, formText) {
//val task = super.ui.newTask()
try {
task.echo(renderMarkdown(feedback, ui = ui))
val codeRequest = CodingActor.CodeRequest(
messages = request.messages +
listOf(
response.code to ApiModel.Role.assistant,
feedback to ApiModel.Role.user,
).filter { it.first.isNotBlank() }.map { it.first to it.second }
)
try {
val lastUserMessage =
codeRequest.messages.last { it.second == ApiModel.Role.user }.first.trim()
val codeResponse: CodeResult = if (lastUserMessage.startsWith("```")) {
actor.CodeResultImpl(
messages = actor.chatMessages(codeRequest),
input = codeRequest,
api = super.api as OpenAIClient,
givenCode = lastUserMessage.removePrefix("```").removeSuffix("```")
)
} else {
actor.answer(codeRequest, api = super.api)
}
displayCodeFeedback(
task,
actor,
super.append(codeRequest, codeResponse),
codeResponse,
onComplete
)
} catch (e: Throwable) {
log.warn("Error", e)
val error = task.error(super.ui, e)
var regenButton: StringBuilder? = null
regenButton = task.complete(super.ui.hrefLink("♻", "href-link regen-button") {
regenButton?.clear()
val header = task.header("Regenerating...")
super.displayCode(task, codeRequest)
header?.clear()
error?.clear()
task.complete()
})
}
} catch (e: Throwable) {
log.warn("Error", e)
task.error(ui, e)
}
}
}
}
""".trimMargin(), className = "reply-message"
)
formText.append(formHandle.toString())
formHandle.toString()
task.complete()
}
class ServletBuffer : ArrayList()
private fun buildTestPage(
openAPI: OpenAPI,
servletImpl: String,
task: SessionTask
) {
var testPage = SimpleActor(
prompt = "Given the definition for a servlet handler, create a test page that can be used to test the servlet",
model = model,
).answer(
listOf(
JsonUtil.toJson(openAPI),
servletImpl
), api
)
// if ```html unwrap
if (testPage.contains("```html")) testPage = testPage.substringAfter("```html").substringBefore("```")
task.add(renderMarkdown("```html\n${testPage.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}\n```", ui = ui))
task.complete(
"Test Page for ${openAPI.paths?.entries?.first()?.key ?: "unknown"} Saved"
)
}
abstract fun getInterpreterString(): String
private fun answer(
actor: CodingActor,
request: CodingActor.CodeRequest,
task: SessionTask = ui.newTask(),
feedback: Boolean = true,
): CodeResult {
val response = actor.answer(request, api = api)
if (feedback) displayCodeAndFeedback(task, request, response)
else displayCode(task, response)
return response
}
companion object {
val log = LoggerFactory.getLogger(ShellToolAgent::class.java)
fun execWrap(fn: () -> T): T {
val classLoader = Thread.currentThread().contextClassLoader
val prevCL = KotlinInterpreter.classLoader
KotlinInterpreter.classLoader = classLoader //req.javaClass.classLoader
return try {
WebAppClassLoader.runWithServerClassAccess {
require(null != classLoader.loadClass("org.eclipse.jetty.server.Response"))
require(null != classLoader.loadClass("org.eclipse.jetty.server.Request"))
// com.simiacryptus.jopenai.OpenAIClient
require(null != classLoader.loadClass("com.simiacryptus.jopenai.OpenAIClient"))
require(null != classLoader.loadClass("com.simiacryptus.jopenai.API"))
fn()
}
} finally {
KotlinInterpreter.classLoader = prevCL
}
}
}
}