Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import com.simiacryptus.cognotik.util.LoggerFactory
* A processor that handles full file replacement instead of patching.
* This is useful when changes are extensive or when patching would be more complex.
*/
class FullReplacementProcessor : PatchProcessor {
override val label: String = "Full Replacement"
class FullReplacementProcessor : PatchProcessor {
override val label = "FullReplacement"

override val patchFormatPrompt = """
Response should provide the complete updated file content within ```code blocks.
Expand All @@ -24,7 +24,7 @@ class FullReplacementProcessor : PatchProcessor {
const b = 2;

function exampleFunction() {
return b + 2;
return a + b;
}

module.exports = { exampleFunction };
Expand All @@ -42,6 +42,17 @@ class FullReplacementProcessor : PatchProcessor {
});
```
""".trimIndent()
override fun getInitiatorPattern(): Regex {
return "(?s)```\\w*\n".toRegex()
}
override fun extractCodeBlocks(response: String): List<Pair<String, String>> {
val codeblockPattern = """(?s)(?<![^\n])```([^\n]*)\n(.*?)\n```""".toRegex()
return codeblockPattern.findAll(response).map { match ->
val language = match.groupValues[1]
val code = match.groupValues[2].trim()
language to code
}.toList()
}

override fun generatePatch(oldCode: String, newCode: String): String {
log.debug("Generating full replacement patch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ open class FuzzyPatchMatcher(
private val requireAnchorMatch: Boolean = true,
) : PatchProcessor {
/** A descriptive label for this patch processor. */
override val label: String = "Fuzzy Patch Matcher"
override val label: String = "Fuzzy Patch Matcher"

/**
* A detailed instructional prompt intended for a language model (LLM).
Expand Down Expand Up @@ -88,6 +88,26 @@ open class FuzzyPatchMatcher(
Alternately, the patch can be provided as a snippet of updated code with context.
This is useful when the patch is small and can be applied directly, when creating the delete lines is cumbersome, or when creating a new file.
""".trimIndent()
override fun getInitiatorPattern(): Regex {
return "(?s)```\\w*\n".toRegex()
}
override fun extractCodeBlocks(response: String): List<Pair<String, String>> {
val codeblockPattern = """(?s)(?<![^\n])```([^\n]*)\n(.*?)\n```""".toRegex()
val codeblockGreedyPattern = """(?s)(?<![^\n])```([^\n]*)\n(.*)\n```""".toRegex()
val findAll = codeblockPattern.findAll(response).toList()
val findAllGreedy = codeblockGreedyPattern.findAll(response).toList()
// Use greedy pattern if we find markdown blocks, otherwise use non-greedy
val matches = if (findAllGreedy.any { it.groupValues[1] == "markdown" }) {
findAllGreedy
} else {
findAll
}
return matches.map { match ->
val language = match.groupValues[1]
val code = match.groupValues[2].trim()
language to code
}
}

/**
* Generates a diff patch that transforms the `oldCode` into the `newCode`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package com.simiacryptus.cognotik.diff

interface PatchProcessor {
interface PatchProcessor {
val label: String
val patchFormatPrompt: String
fun generatePatch(oldCode: String, newCode: String): String
fun applyPatch(source: String, patch: String): String
/**
* Extracts code blocks from markdown-formatted text
* @param response The markdown text containing code blocks
* @return List of pairs containing (language, code content)
*/
fun extractCodeBlocks(response: String): List<Pair<String, String>>
/**
* Gets the regex pattern that initiates a code block
*/
fun getInitiatorPattern(): Regex
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ enum class PatchProcessors : PatchProcessor {
FullReplacement {
override val label = "FullReplacement"
override val matcher = FullReplacementProcessor()
override fun extractCodeBlocks(response: String) = matcher.extractCodeBlocks(response)
override fun getInitiatorPattern() = matcher.getInitiatorPattern()
},

// Thermodynamic mode - DNA-like binding energy approach
Expand All @@ -15,6 +17,8 @@ enum class PatchProcessors : PatchProcessor {
cooperativityBonus = 2.0,
entropyPenalty = 1.0
)
override fun extractCodeBlocks(response: String) = matcher.extractCodeBlocks(response)
override fun getInitiatorPattern() = matcher.getInitiatorPattern()
},

// Strict mode - exact matching only, no fuzzy logic
Expand All @@ -25,6 +29,8 @@ enum class PatchProcessors : PatchProcessor {
enableSnippetPatching = false,
contextSize = 5
)
override fun extractCodeBlocks(response: String) = matcher.extractCodeBlocks(response)
override fun getInitiatorPattern() = matcher.getInitiatorPattern()
},

// Lenient mode - maximum fuzzy matching
Expand All @@ -39,11 +45,15 @@ enum class PatchProcessors : PatchProcessor {
requireAnchorMatch = false,
contextSize = 2
)
override fun extractCodeBlocks(response: String) = matcher.extractCodeBlocks(response)
override fun getInitiatorPattern() = matcher.getInitiatorPattern()
},

// Default/Fuzzy - balanced configuration
Fuzzy {
override val label = "Fuzzy"
override fun extractCodeBlocks(response: String) = matcher.extractCodeBlocks(response)
override fun getInitiatorPattern() = matcher.getInitiatorPattern()
override val matcher = FuzzyPatchMatcher()
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,13 @@ class ThermodynamicPatchMatcher(
}
}

override fun getInitiatorPattern(): Regex {
return FuzzyPatchMatcher.default.getInitiatorPattern()
}
override fun extractCodeBlocks(response: String): List<Pair<String, String>> {
return FuzzyPatchMatcher.default.extractCodeBlocks(response)
}

/**
* Normalizes a line for comparison.
*/
Expand Down
10 changes: 9 additions & 1 deletion desktop/src/main/resources/welcome/modules/task-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ class TaskConfigManager {
default: 'HttpClient',
tooltip: 'Method used to fetch content from URLs'
},
{
id: 'processing_strategy',
label: 'Processing Method',
type: 'select',
options: ['DefaultSummarizer', 'FactChecking', 'JobMatching', 'SchemaExtraction', 'DataTableAccumulation'],
default: 'DefaultSummarizer',
tooltip: 'Strategy for processing fetched content'
},
{
id: 'max_pages_per_task',
label: 'Max Pages Per Task',
Expand Down Expand Up @@ -209,7 +217,7 @@ class TaskConfigManager {
{
id: 'allowed_domains',
label: 'Allowed Domains',
type: 'textarea',
type: 'text',
placeholder: 'Enter one domain per line (e.g., example.com)',
tooltip: 'List of domains that the crawler is allowed to visit'
}
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pluginName=Cognotik
pluginRepositoryUrl=https://github.com/SimiaCryptus/Cognotik
libraryGroup=com.simiacryptus
libraryVersion=2.0.20
libraryVersion=2.0.21

gradleVersion=8.13
org.gradle.caching=true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package cognotik.actions.task

import cognotik.actions.BaseAction
import cognotik.actions.agent.toFile
import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.simiacryptus.cognotik.CognotikAppServer
import com.simiacryptus.cognotik.apps.general.SingleTaskApp
import com.simiacryptus.cognotik.config.instance
import com.simiacryptus.cognotik.plan.OrchestrationConfig
import com.simiacryptus.cognotik.plan.tools.file.FileModificationTask
import com.simiacryptus.cognotik.plan.tools.file.FileModificationTask.Companion.FileModification
import com.simiacryptus.cognotik.platform.Session
import com.simiacryptus.cognotik.platform.file.DataStorage
import com.simiacryptus.cognotik.platform.file.UserSettingsManager
import com.simiacryptus.cognotik.platform.model.ApiChatModel
import com.simiacryptus.cognotik.util.*
import com.simiacryptus.cognotik.util.BrowseUtil.browse
import com.simiacryptus.cognotik.webui.application.AppInfoData
import com.simiacryptus.cognotik.webui.application.ApplicationServer
import java.io.File
import java.text.SimpleDateFormat

class FileModificationTaskAction : BaseAction() {

override fun getActionUpdateThread() = ActionUpdateThread.BGT

override fun handle(e: AnActionEvent) {
val root = getProjectRoot(e) ?: return
val files = getFiles(e)

val dialog = FileModificationTaskDialog(
e.project,
root,
files
)

if (dialog.showAndGet()) {
try {
val taskConfig = dialog.getTaskConfig()
val orchestrationConfig = dialog.getOrchestrationConfig()

UITools.runAsync(e.project, "Initializing File Modification Task", true) { progress ->
initializeTask(e, progress, orchestrationConfig, taskConfig, root)
}
} catch (ex: Exception) {
log.error("Failed to initialize file modification task", ex)
UITools.showError(e.project, "Failed to initialize task: ${ex.message}")
}
}
}

private fun initializeTask(
e: AnActionEvent,
progress: ProgressIndicator,
orchestrationConfig: OrchestrationConfig,
taskConfig: FileModificationTask.FileModificationTaskExecutionConfigData,
root: File
) {
progress.text = "Setting up session..."
val session = Session.newGlobalID()

DataStorage.sessionPaths[session] = root

progress.text = "Starting server..."
setupTaskSession(session, orchestrationConfig, taskConfig, root)

Thread {
Thread.sleep(500)
try {
val uri = CognotikAppServer.getServer().server.uri.resolve("/#$session")
log.info("Opening browser to $uri")
browse(uri)
} catch (e: Throwable) {
log.warn("Error opening browser", e)
}
}.start()
}

private fun setupTaskSession(
session: Session,
orchestrationConfig: OrchestrationConfig,
taskConfig: FileModificationTask.FileModificationTaskExecutionConfigData,
root: File
) {
val app = object : SingleTaskApp(
applicationName = "File Modification Task",
path = "/fileModificationTask",
showMenubar = false,
taskType = FileModification,
taskConfig = taskConfig,
instanceFn = { model -> model.instance() ?: throw IllegalStateException("Model or Provider not set") }
) {
override fun instance(model: ApiChatModel) = model.instance()
?: throw IllegalStateException("Model or Provider not set")
}

app.getSettingsFile(session, UserSettingsManager.defaultUser).writeText(orchestrationConfig.toJson())
SessionProxyServer.chats[session] = app
ApplicationServer.appInfoMap[session] = AppInfoData(
applicationName = "File Modification Task",
inputCnt = 0,
stickyInput = false,
showMenubar = false
)
SessionProxyServer.metadataStorage.setSessionName(
null,
session,
"File Modification @ ${SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis())}"
)
}

private fun getProjectRoot(e: AnActionEvent): File? {
val folder = e.getSelectedFolder()
return folder?.toFile ?: e.getSelectedFile()?.parent?.toFile?.let { file ->
getModuleRootForFile(file)
}
}
}


fun getFiles(e: AnActionEvent): List<File> {
val selectedFiles = e.getSelectedFiles()
val relatedFiles = if (selectedFiles.isEmpty()) {
e.getSelectedFolder()?.toFile?.let {
FileSelectionUtils.filteredWalk(it) { file ->
when {
FileSelectionUtils.isLLMIgnored(file.toPath()) -> false
it.isDirectory -> true
else -> false
}
}
} ?: emptyList()
} else {
selectedFiles.map { it.toFile }
}
return relatedFiles
}

Loading
Loading