All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.simiacryptus.skyenet.Discussable.kt Maven / Gradle / Ivy

There is a newer version: 1.2.21
Show newest version
package com.simiacryptus.skyenet

import com.simiacryptus.jopenai.ApiModel.Role
import com.simiacryptus.skyenet.webui.application.ApplicationInterface
import com.simiacryptus.skyenet.webui.session.SessionTask
import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown
import java.util.concurrent.Callable
import java.util.concurrent.Semaphore
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference

class Discussable(
    private val task: SessionTask,
    private val userMessage: () -> String,
    private val initialResponse: (String) -> T,
    private val outputFn: (T) -> String,
    private val ui: ApplicationInterface,
    private val reviseResponse: (List>) -> T,
    private val atomicRef: AtomicReference = AtomicReference(),
    private val semaphore: Semaphore = Semaphore(0),
    private val heading: String
) : Callable {

    val tabs = object : TabbedDisplay(task) {
        override fun renderTabButtons() = """
${ tabs.withIndex().joinToString("\n") { (index: Int, t: Pair) -> """""" } } ${ ui.hrefLink("♻") { val newTask = ui.newTask(false) val header = newTask.header("Retrying...") val idx: Int = size this.set(label(idx), newTask.placeholder) main(idx, newTask) //this.selectedTab = idx header?.clear() newTask.complete() } }
""".trimMargin() } private val acceptGuard = AtomicBoolean(false) private fun main(tabIndex: Int, task: SessionTask) { log.info("Starting main function for tabIndex: $tabIndex") try { val history = mutableListOf>() val userMessage = userMessage() log.info("User message: $userMessage") history.add(userMessage to Role.user) val design = initialResponse(userMessage) log.info("Initial response generated: $design") val rendered = outputFn(design) log.info("Rendered output: $rendered") history.add(rendered to Role.assistant) val tabContent = task.add(rendered)!! val feedbackForm = feedbackForm(tabIndex, tabContent, design, history, task) tabContent.append("\n" + feedbackForm.placeholder) task.complete() } catch (e: Throwable) { log.error("Error in discussable", e) task.error(ui, e) task.complete(ui.hrefLink("🔄 Retry") { main(tabIndex = tabIndex, task = task) }) } } private fun feedbackForm( tabIndex: Int?, tabContent: StringBuilder, design: T, history: List>, task: SessionTask, ) = ui.newTask(false).apply { log.info("Creating feedback form for tabIndex: $tabIndex") val feedbackSB = add("
")!! feedbackSB.clear() feedbackSB.append( """ |
|${acceptLink(tabIndex, tabContent, design, feedbackSB, feedbackTask = this)} |
|${textInput(design, tabContent, history, task, feedbackSB, feedbackTask = this)} """.trimMargin() ) complete() } private fun acceptLink( tabIndex: Int?, tabContent: StringBuilder, design: T, feedbackSB: StringBuilder, feedbackTask: SessionTask, ) = ui.hrefLink("Accept", classname = "href-link cmd-button") { log.info("Accept link clicked for tabIndex: $tabIndex") feedbackSB.clear() feedbackTask.complete() accept(tabIndex, tabContent, design) } private fun textInput( design: T, tabContent: StringBuilder, history: List>, task: SessionTask, feedbackSB: StringBuilder, feedbackTask: SessionTask, ): String { val feedbackGuard = AtomicBoolean(false) return ui.textInput { userResponse -> log.info("User response received: $userResponse") if (feedbackGuard.getAndSet(true)) return@textInput val prev = feedbackSB.toString() try { feedbackSB.clear() feedbackTask.complete() feedback(tabContent, userResponse, history, design, task) } catch (e: Exception) { log.error("Error processing user feedback", e) task.error(ui, e) feedbackSB.set(prev) feedbackTask.complete() throw e } finally { feedbackGuard.set(false) } } } private fun feedback( tabContent: StringBuilder, userResponse: String, history: List>, design: T, task: SessionTask, ) { log.info("Processing feedback for user response: $userResponse") var history = history history = history + (userResponse to Role.user) val newValue = (tabContent.toString() + "
" + renderMarkdown(userResponse, ui = ui) + "
") tabContent.set(newValue) val stringBuilder = task.add("Processing...") tabs.update() val newDesign = reviseResponse(history) log.info("Revised design: $newDesign") val newTask = ui.newTask(root = false) tabContent.set(newValue + "\n" + newTask.placeholder) tabs.update() stringBuilder?.clear() task.complete() Retryable(ui, newTask) { outputFn(newDesign) + "\n" + feedbackForm( tabIndex = null, tabContent = it, design = newDesign, history = history, task = newTask ).placeholder } } private fun accept(tabIndex: Int?, tabContent: StringBuilder, design: T) { log.info("Accepting design for tabIndex: $tabIndex") if (acceptGuard.getAndSet(true)) { return } try { //if (null != tabIndex) tabs.selectedTab = tabIndex tabContent.apply { val prevTab = toString() set(prevTab) tabs.update() } } catch (e: Exception) { log.error("Error accepting design", e) task.error(ui, e) acceptGuard.set(false) throw e } atomicRef.set(design) semaphore.release() } override fun call(): T { try { //log.info("Calling Discussable with heading: $heading") task.echo(heading) val idx = tabs.size val newTask = ui.newTask(false) val header = newTask.header("Processing...") tabs[tabs.label(idx)] = newTask.placeholder try { main(idx, newTask) //tabs.selectedTab = idx semaphore.acquire() } catch (e: Throwable) { log.error("Error in main function", e) task.error(ui, e) } finally { header?.clear() newTask.complete() } log.info("Returning result from Discussable") return atomicRef.get() } catch (e: Exception) { log.warn(""" |Error in Discussable |${e.message} """.trimIndent(), e) task.error(ui, e) return null as T } } companion object { private val log = org.slf4j.LoggerFactory.getLogger(Discussable::class.java) } } fun java.lang.StringBuilder.set(newValue: String) { clear() append(newValue) }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy