Skip to content

Commit e4b9411

Browse files
authored
1.5.1 (#161)
* 1.5.1 * 1.5.1 * fix state bug
1 parent 11b86f6 commit e4b9411

File tree

11 files changed

+625
-131
lines changed

11 files changed

+625
-131
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repositories {
2525
val kotlin_version = "2.0.0-Beta5"
2626
val jetty_version = "11.0.18"
2727
val slf4j_version = "2.0.9"
28-
val skyenet_version = "1.0.66"
28+
val skyenet_version = "1.0.67"
2929
val remoterobot_version = "0.11.21"
3030
val jackson_version = "2.17.0"
3131

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pluginName=intellij-aicoder
22
pluginRepositoryUrl=https://github.com/SimiaCryptus/intellij-aicoder
3-
pluginVersion=1.5.0
3+
pluginVersion=1.5.1
44

55
jvmArgs=-Xmx8g
66
org.gradle.jvmargs=-Xmx8g
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
package com.github.simiacryptus.aicoder.actions.generic
2+
3+
import ai.grazie.utils.mpp.UUID
4+
import com.github.simiacryptus.aicoder.AppServer
5+
import com.github.simiacryptus.aicoder.actions.BaseAction
6+
import com.github.simiacryptus.aicoder.actions.generic.MultiStepPatchAction.AutoDevApp.Settings
7+
import com.github.simiacryptus.aicoder.util.UITools
8+
import com.intellij.openapi.actionSystem.ActionUpdateThread
9+
import com.intellij.openapi.actionSystem.AnActionEvent
10+
import com.intellij.openapi.actionSystem.PlatformDataKeys
11+
import com.intellij.openapi.vfs.VirtualFile
12+
import com.simiacryptus.jopenai.API
13+
import com.simiacryptus.jopenai.ApiModel
14+
import com.simiacryptus.jopenai.ApiModel.Role
15+
import com.simiacryptus.jopenai.models.ChatModels
16+
import com.simiacryptus.jopenai.models.ImageModels
17+
import com.simiacryptus.jopenai.util.ClientUtil.toContentList
18+
import com.simiacryptus.skyenet.Discussable
19+
import com.simiacryptus.skyenet.core.actors.*
20+
import com.simiacryptus.skyenet.core.platform.*
21+
import com.simiacryptus.skyenet.core.platform.file.DataStorage
22+
import com.simiacryptus.skyenet.webui.application.ApplicationInterface
23+
import com.simiacryptus.skyenet.webui.application.ApplicationServer
24+
import com.simiacryptus.skyenet.webui.chat.ChatServer
25+
import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown
26+
import org.slf4j.LoggerFactory
27+
import java.awt.Desktop
28+
import java.io.ByteArrayOutputStream
29+
import java.io.File
30+
import java.nio.file.Path
31+
import java.util.concurrent.Semaphore
32+
import java.util.concurrent.atomic.AtomicReference
33+
import javax.imageio.ImageIO
34+
35+
class CreateImageAction : BaseAction() {
36+
override fun getActionUpdateThread() = ActionUpdateThread.BGT
37+
38+
val path = "/imageCreator"
39+
40+
override fun handle(event: AnActionEvent) {
41+
var root: Path? = null
42+
val codeFiles: MutableSet<Path> = mutableSetOf()
43+
fun codeSummary() = codeFiles.filter {
44+
root!!.resolve(it).toFile().exists()
45+
}.associateWith { root!!.resolve(it).toFile().readText(Charsets.UTF_8) }
46+
.entries.joinToString("\n\n") { (path, code) ->
47+
val extension = path.toString().split('.').lastOrNull()?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }
48+
"""
49+
|# $path
50+
|```$extension
51+
|${code.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }}
52+
|```
53+
""".trimMargin()
54+
}
55+
56+
val dataContext = event.dataContext
57+
val virtualFiles = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext)
58+
val folder = UITools.getSelectedFolder(event)
59+
root = if (null != folder) {
60+
folder.toFile.toPath()
61+
} else if (1 == virtualFiles?.size){
62+
UITools.getSelectedFile(event)?.parent?.toNioPath()
63+
} else {
64+
getModuleRootForFile(UITools.getSelectedFile(event)?.parent?.toFile ?: throw RuntimeException("")).toPath()
65+
}
66+
67+
val files = getFiles(virtualFiles, root!!)
68+
codeFiles.addAll(files)
69+
70+
val session = StorageInterface.newGlobalID()
71+
// val storage = ApplicationServices.dataStorageFactory(root?.toFile()!!) as DataStorage?
72+
// val selectedFile = UITools.getSelectedFolder(event)
73+
if (/*null != storage &&*/ null != root) {
74+
DataStorage.sessionPaths[session] = root?.toFile()!!
75+
}
76+
77+
agents[session] = PatchApp(event, root!!.toFile(), ::codeSummary)
78+
79+
val server = AppServer.getServer(event.project)
80+
val app = initApp(server, path)
81+
app.sessions[session] = app.newSession(null, session)
82+
83+
Thread {
84+
Thread.sleep(500)
85+
try {
86+
Desktop.getDesktop().browse(server.server.uri.resolve("$path/#$session"))
87+
} catch (e: Throwable) {
88+
log.warn("Error opening browser", e)
89+
}
90+
}.start()
91+
}
92+
93+
inner class PatchApp(
94+
private val event: AnActionEvent,
95+
override val root: File,
96+
val codeSummary: () -> String = { "" },
97+
) : ApplicationServer(
98+
applicationName = "Multi-file Patch Chat",
99+
path = path,
100+
showMenubar = false,
101+
) {
102+
override val singleInput = false
103+
override val stickyInput = true
104+
105+
override fun userMessage(
106+
session: Session,
107+
user: User?,
108+
userMessage: String,
109+
ui: ApplicationInterface,
110+
api: API
111+
) {
112+
val settings = getSettings(session, user) ?: Settings()
113+
if (api is ClientManager.MonitoredClient) api.budget = settings.budget ?: 2.00
114+
PatchAgent(
115+
api = api,
116+
dataStorage = dataStorage,
117+
session = session,
118+
user = user,
119+
ui = ui,
120+
model = settings.model!!,
121+
codeSummary = { codeSummary() },
122+
event = event,
123+
root = root,
124+
).start(
125+
userMessage = userMessage,
126+
)
127+
}
128+
}
129+
130+
enum class ActorTypes {
131+
MainActor,
132+
}
133+
134+
inner class PatchAgent(
135+
val api: API,
136+
dataStorage: StorageInterface,
137+
session: Session,
138+
user: User?,
139+
val ui: ApplicationInterface,
140+
val model: ChatModels,
141+
val codeSummary: () -> String = { "" },
142+
actorMap: Map<ActorTypes, BaseActor<*, *>> = mapOf(
143+
ActorTypes.MainActor to ImageActor(
144+
prompt = """
145+
|You are a technical drawing assistant.
146+
|
147+
|You will be composing an image about the following code:
148+
|
149+
|${codeSummary()}
150+
|
151+
""".trimMargin(),
152+
textModel = model,
153+
imageModel = ImageModels.DallE3
154+
),
155+
),
156+
val event: AnActionEvent,
157+
val root: File,
158+
) : ActorSystem<ActorTypes>(
159+
actorMap.map { it.key.name to it.value }.toMap(), dataStorage, user, session
160+
) {
161+
162+
private val mainActor by lazy { getActor(ActorTypes.MainActor) as ImageActor }
163+
164+
fun start(
165+
userMessage: String,
166+
) {
167+
val task = ui.newTask()
168+
val toInput = { it: String -> listOf(codeSummary(), it) }
169+
Discussable(
170+
task = task,
171+
userMessage = { userMessage },
172+
heading = userMessage,
173+
initialResponse = { it: String -> mainActor.answer(toInput(it), api = api) },
174+
outputFn = { img: ImageResponse ->
175+
val id = UUID.random().text
176+
renderMarkdown(
177+
"<img src='${
178+
task.saveFile(
179+
"$id.png",
180+
write(img, root.resolve("$id.png").toPath())
181+
)
182+
}' style='max-width: 100%;'/><img src='${
183+
task.saveFile(
184+
"$id.jpg",
185+
write(img, root.resolve("$id.jpg").toPath())
186+
)
187+
}' style='max-width: 100%;'/>", ui = ui
188+
)
189+
},
190+
ui = ui,
191+
reviseResponse = { userMessages: List<Pair<String, Role>> ->
192+
mainActor.respond(
193+
messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }
194+
.toTypedArray<ApiModel.ChatMessage>()),
195+
input = toInput(userMessage),
196+
api = api
197+
)
198+
},
199+
atomicRef = AtomicReference(),
200+
semaphore = Semaphore(0),
201+
).call()
202+
}
203+
}
204+
private fun write(
205+
code: ImageResponse,
206+
path: Path
207+
): ByteArray {
208+
val byteArrayOutputStream = ByteArrayOutputStream()
209+
val data = ImageIO.write(
210+
code.image,
211+
path.toString().split(".").last(),
212+
byteArrayOutputStream
213+
)
214+
val bytes = byteArrayOutputStream.toByteArray()
215+
return bytes
216+
}
217+
218+
private fun getFiles(
219+
virtualFiles: Array<out VirtualFile>?,
220+
root: Path
221+
): MutableSet<Path> {
222+
val codeFiles = mutableSetOf<Path>()
223+
virtualFiles?.forEach { file ->
224+
if (file.isDirectory) {
225+
getFiles(file.children, root)
226+
} else {
227+
val relative = root.relativize(file.toNioPath())
228+
codeFiles.add(relative) //[] = file.contentsToByteArray().toString(Charsets.UTF_8)
229+
}
230+
}
231+
return codeFiles
232+
}
233+
234+
override fun isEnabled(event: AnActionEvent) = true
235+
236+
companion object {
237+
private val log = LoggerFactory.getLogger(CreateImageAction::class.java)
238+
private val agents = mutableMapOf<Session, ApplicationServer>()
239+
private fun initApp(server: AppServer, path: String): ChatServer {
240+
server.appRegistry[path]?.let { return it }
241+
val socketServer = object : ApplicationServer(
242+
applicationName = "Multi-file Patch Chat",
243+
path = path,
244+
showMenubar = false,
245+
) {
246+
override val singleInput = true
247+
override val stickyInput = false
248+
override fun newSession(user: User?, session: Session) = agents[session]!!.newSession(user, session)
249+
}
250+
server.addApp(path, socketServer)
251+
return socketServer
252+
}
253+
254+
}
255+
}

src/main/kotlin/com/github/simiacryptus/aicoder/actions/generic/DiffChatAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.github.simiacryptus.aicoder.config.AppSettingsState
66
import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel
77
import com.github.simiacryptus.aicoder.util.CodeChatSocketManager
88
import com.github.simiacryptus.aicoder.util.ComputerLanguage
9-
import com.github.simiacryptus.diff.addApplyDiffLinks
9+
import com.simiacryptus.diff.addApplyDiffLinks
1010
import com.intellij.openapi.actionSystem.ActionUpdateThread
1111
import com.intellij.openapi.actionSystem.AnActionEvent
1212
import com.intellij.openapi.actionSystem.CommonDataKeys

0 commit comments

Comments
 (0)