Skip to content

Commit 12bd206

Browse files
authored
1.5.7 (#169)
1 parent 1685750 commit 12bd206

File tree

6 files changed

+199
-9
lines changed

6 files changed

+199
-9
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ dependencies {
4242
exclude(group = "org.jetbrains.kotlin", module = "")
4343
}
4444

45-
implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.0.57")
45+
implementation(group = "com.simiacryptus", name = "jo-penai", version = "1.0.59")
4646
{
4747
exclude(group = "org.jetbrains.kotlin", module = "")
4848
}

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.6
3+
pluginVersion=1.5.7
44

55
jvmArgs=-Xmx8g
66
org.gradle.jvmargs=-Xmx8g

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package com.github.simiacryptus.aicoder.actions.generic
22

3-
import com.github.simiacryptus.aicoder.actions.BaseAction
43
import com.github.simiacryptus.aicoder.AppServer
5-
import com.github.simiacryptus.aicoder.actions.BaseAction.Companion
4+
import com.github.simiacryptus.aicoder.actions.BaseAction
65
import com.github.simiacryptus.aicoder.config.AppSettingsState
76
import com.github.simiacryptus.aicoder.config.AppSettingsState.Companion.chatModel
87
import com.github.simiacryptus.aicoder.util.CodeChatSocketManager
@@ -20,13 +19,11 @@ import java.awt.Desktop
2019
class CodeChatAction : BaseAction() {
2120
override fun getActionUpdateThread() = ActionUpdateThread.BGT
2221

23-
val path = "/codeChat"
24-
2522
override fun handle(e: AnActionEvent) {
2623
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
2724

2825
val session = StorageInterface.newGlobalID()
29-
val language = ComputerLanguage.getComputerLanguage(e)?.name ?: return
26+
val language = ComputerLanguage.getComputerLanguage(e)?.name ?: ""
3027
val filename = FileDocumentManager.getInstance().getFile(editor.document)?.name ?: return
3128
SessionProxyServer.agents[session] = CodeChatSocketManager(
3229
session = session,
@@ -50,7 +47,6 @@ class CodeChatAction : BaseAction() {
5047
Thread {
5148
Thread.sleep(500)
5249
try {
53-
5450
val uri = server.server.uri.resolve("/#$session")
5551
BaseAction.log.info("Opening browser to $uri")
5652
Desktop.getDesktop().browse(uri)
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package com.github.simiacryptus.aicoder.actions.generic
2+
3+
import com.github.simiacryptus.aicoder.AppServer
4+
import com.github.simiacryptus.aicoder.actions.BaseAction
5+
import com.github.simiacryptus.aicoder.actions.BaseAction.Companion
6+
import com.github.simiacryptus.aicoder.actions.generic.MultiStepPatchAction.AutoDevApp.Settings
7+
import com.github.simiacryptus.aicoder.config.AppSettingsState
8+
import com.github.simiacryptus.aicoder.util.UITools
9+
import com.intellij.openapi.actionSystem.ActionUpdateThread
10+
import com.intellij.openapi.actionSystem.AnActionEvent
11+
import com.intellij.openapi.actionSystem.PlatformDataKeys
12+
import com.intellij.openapi.vfs.VirtualFile
13+
import com.simiacryptus.diff.addApplyFileDiffLinks
14+
import com.simiacryptus.diff.addSaveLinks
15+
import com.simiacryptus.jopenai.API
16+
import com.simiacryptus.jopenai.ApiModel
17+
import com.simiacryptus.jopenai.ApiModel.Role
18+
import com.simiacryptus.jopenai.GPT4Tokenizer
19+
import com.simiacryptus.jopenai.util.ClientUtil.toContentList
20+
import com.simiacryptus.skyenet.Discussable
21+
import com.simiacryptus.skyenet.core.actors.SimpleActor
22+
import com.simiacryptus.skyenet.core.platform.ClientManager
23+
import com.simiacryptus.skyenet.core.platform.Session
24+
import com.simiacryptus.skyenet.core.platform.StorageInterface
25+
import com.simiacryptus.skyenet.core.platform.User
26+
import com.simiacryptus.skyenet.webui.application.ApplicationInterface
27+
import com.simiacryptus.skyenet.webui.application.ApplicationServer
28+
import com.simiacryptus.skyenet.webui.util.MarkdownUtil.renderMarkdown
29+
import org.slf4j.LoggerFactory
30+
import java.awt.Desktop
31+
import java.io.File
32+
import java.nio.file.Path
33+
import java.util.concurrent.Semaphore
34+
import java.util.concurrent.atomic.AtomicReference
35+
36+
class MultiCodeChatAction : BaseAction() {
37+
override fun getActionUpdateThread() = ActionUpdateThread.BGT
38+
39+
override fun handle(event: AnActionEvent) {
40+
var root: Path? = null
41+
val codeFiles: MutableSet<Path> = mutableSetOf()
42+
fun codeSummary() = codeFiles.filter {
43+
root!!.resolve(it).toFile().exists()
44+
}.associateWith { root!!.resolve(it).toFile().readText(Charsets.UTF_8) }
45+
.entries.joinToString("\n\n") { (path, code) ->
46+
val extension = path.toString().split('.').lastOrNull()?.let { /*escapeHtml4*/(it)/*.indent(" ")*/ }
47+
"""
48+
|# $path
49+
|```$extension
50+
|${code}
51+
|```
52+
""".trimMargin()
53+
}
54+
55+
val dataContext = event.dataContext
56+
val virtualFiles = PlatformDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext)
57+
val folder = UITools.getSelectedFolder(event)
58+
root = if (null != folder) {
59+
folder.toFile.toPath()
60+
} else {
61+
getModuleRootForFile(UITools.getSelectedFile(event)?.parent?.toFile ?: throw RuntimeException("")).toPath()
62+
}
63+
val files = getFiles(virtualFiles, root!!)
64+
codeFiles.addAll(files)
65+
66+
val session = StorageInterface.newGlobalID()
67+
SessionProxyServer.chats[session] = PatchApp(root!!.toFile(), { codeSummary() }, codeFiles)
68+
val server = AppServer.getServer(event.project)
69+
70+
Thread {
71+
Thread.sleep(500)
72+
try {
73+
74+
val uri = server.server.uri.resolve("/#$session")
75+
BaseAction.log.info("Opening browser to $uri")
76+
Desktop.getDesktop().browse(uri)
77+
} catch (e: Throwable) {
78+
log.warn("Error opening browser", e)
79+
}
80+
}.start()
81+
}
82+
83+
inner class PatchApp(
84+
override val root: File,
85+
val codeSummary: () -> String,
86+
val codeFiles: Set<Path> = setOf(),
87+
) : ApplicationServer(
88+
applicationName = "Multi-file Patch Chat",
89+
path = "/patchChat",
90+
showMenubar = false,
91+
) {
92+
override val singleInput = false
93+
override val stickyInput = true
94+
private val mainActor: SimpleActor
95+
get() = SimpleActor(
96+
prompt = """
97+
|You are a helpful AI that helps people with coding.
98+
|
99+
|You will be answering questions about the following code:
100+
|
101+
|${codeSummary()}
102+
|
103+
""".trimMargin(),
104+
model = AppSettingsState.instance.defaultSmartModel()
105+
)
106+
107+
override fun userMessage(
108+
session: Session,
109+
user: User?,
110+
userMessage: String,
111+
ui: ApplicationInterface,
112+
api: API
113+
) {
114+
val settings = getSettings(session, user) ?: Settings()
115+
if (api is ClientManager.MonitoredClient) api.budget = settings.budget ?: 2.00
116+
117+
val task = ui.newTask()
118+
val codex = GPT4Tokenizer()
119+
task.header(renderMarkdown(codeFiles.joinToString("\n") { path ->
120+
"* $path - ${codex.estimateTokenCount(root.resolve(path.toFile()).readText())} tokens"
121+
}))
122+
val toInput = { it: String -> listOf(codeSummary(), it) }
123+
Discussable(
124+
task = task,
125+
userMessage = { userMessage },
126+
heading = renderMarkdown(userMessage),
127+
initialResponse = { it: String -> mainActor.answer(toInput(it), api = api) },
128+
outputFn = { design: String ->
129+
var markdown = ui.socketManager?.addApplyFileDiffLinks(
130+
root = root.toPath(),
131+
code = { codeFiles.associateWith { root.resolve(it.toFile()).readText(Charsets.UTF_8) } },
132+
response = design,
133+
handle = { newCodeMap ->
134+
newCodeMap.forEach { (path, newCode) ->
135+
task.complete("<a href='${"fileIndex/$session/$path"}'>$path</a> Updated")
136+
}
137+
},
138+
ui = ui,
139+
)
140+
markdown = ui.socketManager?.addSaveLinks(
141+
response = markdown!!,
142+
task = task,
143+
ui = ui,
144+
handle = { path, newCode ->
145+
root.resolve(path.toFile()).writeText(newCode, Charsets.UTF_8)
146+
},
147+
)
148+
"""<div>${renderMarkdown(markdown!!)}</div>"""
149+
},
150+
ui = ui,
151+
reviseResponse = { userMessages: List<Pair<String, Role>> ->
152+
mainActor.respond(
153+
messages = (userMessages.map { ApiModel.ChatMessage(it.second, it.first.toContentList()) }
154+
.toTypedArray<ApiModel.ChatMessage>()),
155+
input = toInput(userMessage),
156+
api = api
157+
)
158+
},
159+
atomicRef = AtomicReference(),
160+
semaphore = Semaphore(0),
161+
).call()
162+
}
163+
}
164+
165+
166+
private fun getFiles(
167+
virtualFiles: Array<out VirtualFile>?,
168+
root: Path
169+
): MutableSet<Path> {
170+
val codeFiles = mutableSetOf<Path>()
171+
virtualFiles?.forEach { file ->
172+
if (file.isDirectory) {
173+
getFiles(file.children, root)
174+
} else {
175+
codeFiles.add(root.relativize(file.toNioPath()))
176+
}
177+
}
178+
return codeFiles
179+
}
180+
181+
override fun isEnabled(event: AnActionEvent) = true
182+
183+
companion object {
184+
private val log = LoggerFactory.getLogger(MultiDiffChatAction::class.java)
185+
186+
}
187+
}

src/main/kotlin/com/github/simiacryptus/aicoder/util/PluginStartupActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.intellij.openapi.vfs.VirtualFileManager
1010
import com.simiacryptus.skyenet.core.OutputInterceptor
1111
import com.simiacryptus.skyenet.core.platform.*
1212
import com.simiacryptus.skyenet.core.platform.ApplicationServicesConfig.isLocked
13+
import com.simiacryptus.skyenet.core.platform.file.DataStorage
1314
import com.simiacryptus.skyenet.core.platform.file.UsageManager
1415
import java.io.File
1516
import java.util.concurrent.atomic.AtomicBoolean
@@ -84,6 +85,7 @@ class PluginStartupActivity : ProjectActivity {
8485

8586
private fun init() {
8687
if (isInitialized.getAndSet(true)) return
88+
ApplicationServicesConfig.dataStorageRoot = AppSettingsState.instance.pluginHome.resolve(".skyenet")
8789
OutputInterceptor.setupInterceptor()
8890
ApplicationServices.clientManager = object : ClientManager() {
8991
override fun createClient(session: Session, user: User?) =
@@ -103,7 +105,6 @@ class PluginStartupActivity : ProjectActivity {
103105
override fun logout(accessToken: String, user: User) {}
104106
}
105107
isLocked = true
106-
val resolve = AppSettingsState.instance.pluginHome.resolve(".skyenet")
107108
}
108109

109110

src/main/resources/META-INF/plugin.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@
8989

9090
<add-to-group group-id="com.github.simiacryptus.aicoder.ui.ProjectMenu" anchor="last"/>
9191
</action>
92+
<action class="com.github.simiacryptus.aicoder.actions.generic.MultiCodeChatAction"
93+
text="Code Chat"
94+
description="Open a chat session with multiple files">
95+
96+
<add-to-group group-id="com.github.simiacryptus.aicoder.ui.ProjectMenu" anchor="last"/>
97+
</action>
9298
<action class="com.github.simiacryptus.aicoder.actions.generic.CreateImageAction"
9399
text="Create Image"
94100
description="Create an image file based on the selected code">

0 commit comments

Comments
 (0)