Add admin panel for OpenAI management
authorLanius Trolling <lanius@laniustrolling.dev>
Mon, 13 May 2024 23:07:47 +0000 (19:07 -0400)
committerLanius Trolling <lanius@laniustrolling.dev>
Mon, 13 May 2024 23:07:47 +0000 (19:07 -0400)
src/jvmMain/kotlin/info/mechyrdia/Factbooks.kt
src/jvmMain/kotlin/info/mechyrdia/robot/RobotService.kt
src/jvmMain/kotlin/info/mechyrdia/robot/ViewsRobot.kt
src/jvmMain/kotlin/info/mechyrdia/route/ResourceBodies.kt
src/jvmMain/kotlin/info/mechyrdia/route/ResourceTypes.kt

index 63f7f44ff02d8f489a8b97b09fd5689e93b5e349..8d95c019c4d4992dcf49c5befe78aecabe521ac0 100644 (file)
@@ -229,6 +229,9 @@ fun Application.factbooks() {
                get<Root.User.ById>()
                post<Root.Admin.Ban, _>()
                post<Root.Admin.Unban, _>()
+               get<Root.Admin.NukeManagement>()
+               post<Root.Admin.NukeManagement.Update, _>()
+               post<Root.Admin.NukeManagement.Reset, _>()
                get<Root.Admin.Vfs.Inline>()
                get<Root.Admin.Vfs.Download>()
                get<Root.Admin.Vfs.View>()
index 80f640c3fb1a9c6744e11e7856c10a3c9cfef876..2afa2aeb0fcb6cb9d525b0bf0c345de19b3877e5 100644 (file)
@@ -45,6 +45,7 @@ data class RobotGlobals(
                
                suspend fun get() = Table.get(RobotGlobalsId)
                suspend fun set(instance: RobotGlobals) = Table.put(instance)
+               suspend fun delete() = Table.del(RobotGlobalsId)
                
                override suspend fun initialize() = Unit
        }
@@ -228,6 +229,39 @@ class RobotService(
                logger.info("Vector store update is complete")
        }
        
+       suspend fun reset() {
+               RobotGlobals.get()?.gcOldThreads()?.copy(
+                       lastFileUpload = null,
+                       fileIdMap = emptyMap(),
+                       vectorStoreId = null,
+                       assistantId = null,
+               )?.save()
+               
+               while (true) {
+                       val assistants = robotClient.listAssistants().data
+                       if (assistants.isEmpty()) break
+                       
+                       assistants.map { it.id }.forEach {
+                               robotClient.deleteAssistant(it)
+                       }
+               }
+               
+               while (true) {
+                       val vectorStores = robotClient.listVectorStores().data
+                       if (vectorStores.isEmpty()) break
+                       
+                       vectorStores.map { it.id }.forEach {
+                               robotClient.deleteVectorStore(it)
+                       }
+               }
+               
+               robotClient.listFiles().data.map { it.id }.forEach {
+                       robotClient.deleteFile(it)
+               }
+               
+               initialize()
+       }
+       
        inner class Conversation(private val nationId: Id<NationData>) {
                private var assistantId: RobotAssistantId? = null
                private var threadId: RobotThreadId? = null
index fb063b08f8fb22eee57322f4a08885e9efe89222..f68b2b533ffe72a85ecfa0ec00ed74a791dabebe 100644 (file)
@@ -2,6 +2,7 @@ package info.mechyrdia.robot
 
 import info.mechyrdia.auth.createCsrfToken
 import info.mechyrdia.data.currentNation
+import info.mechyrdia.lore.adminPage
 import info.mechyrdia.lore.page
 import info.mechyrdia.lore.redirectHref
 import info.mechyrdia.lore.standardNavBar
@@ -84,3 +85,32 @@ suspend fun DefaultWebSocketServerSession.robotConversation(csrfToken: String? =
        
        conversation.close()
 }
+
+fun ApplicationCall.robotManagementPage(): HTML.() -> Unit {
+       val robotServiceStatus = RobotService.status
+       
+       return adminPage("NUKE Management") {
+               h1 { +"NUKE Management" }
+               when (robotServiceStatus) {
+                       RobotServiceStatus.NOT_CONFIGURED -> p { +"Unfortunately, the NUKE is not configured on this website." }
+                       RobotServiceStatus.LOADING -> p { +"The NUKE is still in the process of initializing." }
+                       RobotServiceStatus.FAILED -> p { +"Tragically, the NUKE has failed to initialize due to an internal error." }
+                       RobotServiceStatus.READY -> ul {
+                               li {
+                                       form(action = href(Root.Admin.NukeManagement.Update()), method = FormMethod.post) {
+                                               submitInput {
+                                                       value = "Manually Trigger File Update"
+                                               }
+                                       }
+                               }
+                               li {
+                                       form(action = href(Root.Admin.NukeManagement.Reset()), method = FormMethod.post) {
+                                               submitInput(classes = "evil") {
+                                                       value = "Reset All Data And Start Over"
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
index dea60c48205250be7e3628db39c37e6120a21020..44cf57a8d2a3a14a577cdab66c9ad511ac59013a 100644 (file)
@@ -43,6 +43,12 @@ class AdminBanUserPayload(override val csrfToken: String? = null) : CsrfProtecte
 @Serializable
 class AdminUnbanUserPayload(override val csrfToken: String? = null) : CsrfProtectedResourcePayload
 
+@Serializable
+class AdminNukeUpdatePayload(override val csrfToken: String? = null) : CsrfProtectedResourcePayload
+
+@Serializable
+class AdminNukeResetPayload(override val csrfToken: String? = null) : CsrfProtectedResourcePayload
+
 @Serializable
 class AdminVfsCopyFilePayload(val from: String, override val csrfToken: String? = null) : CsrfProtectedResourcePayload
 
index b1e33b57064be26cec4300314282fff3e1c2e8e3..c1c7381cec732261009a9709d521b06957abb5f8 100644 (file)
@@ -3,7 +3,9 @@ package info.mechyrdia.route
 import info.mechyrdia.auth.*
 import info.mechyrdia.data.*
 import info.mechyrdia.lore.*
+import info.mechyrdia.robot.RobotService
 import info.mechyrdia.robot.robotConversation
+import info.mechyrdia.robot.robotManagementPage
 import info.mechyrdia.robot.robotPage
 import io.ktor.http.*
 import io.ktor.http.content.*
@@ -287,6 +289,42 @@ class Root(val error: String? = null) : ResourceHandler, ResourceFilter {
                        }
                }
                
+               @Resource("nuke")
+               class NukeManagement(val admin: Admin = Admin()) : ResourceFilter, ResourceHandler {
+                       override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {
+                               with(admin) { filterCall() }
+                       }
+                       
+                       override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall() {
+                               filterCall()
+                               call.respondHtml(HttpStatusCode.OK, call.robotManagementPage())
+                       }
+                       
+                       @Resource("update")
+                       class Update(val nukeManagement: NukeManagement = NukeManagement()) : ResourceReceiver<AdminNukeUpdatePayload> {
+                               override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminNukeUpdatePayload) {
+                                       with(nukeManagement) { filterCall() }
+                                       with(payload) { call.verifyCsrfToken() }
+                                       
+                                       RobotService.getInstance()?.performMaintenance()
+                                       
+                                       call.redirectHref(NukeManagement())
+                               }
+                       }
+                       
+                       @Resource("reset")
+                       class Reset(val nukeManagement: NukeManagement = NukeManagement()) : ResourceReceiver<AdminNukeResetPayload> {
+                               override suspend fun PipelineContext<Unit, ApplicationCall>.handleCall(payload: AdminNukeResetPayload) {
+                                       with(nukeManagement) { filterCall() }
+                                       with(payload) { call.verifyCsrfToken() }
+                                       
+                                       RobotService.getInstance()?.reset()
+                                       
+                                       call.redirectHref(NukeManagement())
+                               }
+                       }
+               }
+               
                @Resource("vfs")
                class Vfs(val admin: Admin = Admin()) : ResourceFilter {
                        override suspend fun PipelineContext<Unit, ApplicationCall>.filterCall() {