Skip to content

Commit

Permalink
1.0.18
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski committed Mar 21, 2023
1 parent 93d846f commit 66a99e5
Show file tree
Hide file tree
Showing 26 changed files with 1,312 additions and 1,153 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
### Added
-

## [1.0.18]

### Improved
- API stability and performance
- Various bug fixes
- Max tokens handling

## [1.0.17]

### Added
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pluginGroup = com.github.simiacryptus
pluginName = intellij-aicoder
pluginRepositoryUrl = https://github.com/SimiaCryptus/intellij-aicoder
# SemVer format -> https://semver.org
pluginVersion = 1.0.17
pluginVersion = 1.0.18

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 203
Expand Down
360 changes: 360 additions & 0 deletions src/main/kotlin/com/github/simiacryptus/aicoder/SoftwareProjectAI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
package com.github.simiacryptus.aicoder

import com.github.simiacryptus.openai.proxy.Description
import com.github.simiacryptus.openai.proxy.ValidatedObject
import java.io.File
import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicInteger
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

interface SoftwareProjectAI {

fun newProject(description: String): Project

data class Project(
val name: String? = "",
val description: String? = "",
val language: String? = "",
val features: List<String>? = listOf(),
val libraries: List<String>? = listOf(),
val buildTools: List<String>? = listOf(),
) : ValidatedObject

fun getProjectStatements(description: String, project: Project): ProjectStatements

data class ProjectStatements(
val assumptions: List<String>? = listOf(),
val designPatterns: List<String>? = listOf(),
val requirements: List<String>? = listOf(),
val risks: List<String>? = listOf(),
) : ValidatedObject

fun buildProjectDesign(project: Project, requirements: ProjectStatements): ProjectDesign

data class ProjectDesign(
@Description("Major components e.g. 'core', 'ui', 'api', etc.")
val components: List<ComponentDetails>? = listOf(),
@Description("Documentation files e.g. README.md, LICENSE, etc.")
val documents: List<DocumentationDetails>? = listOf(),
@Description("Individual test cases")
val tests: List<TestDetails>? = listOf(),
) : ValidatedObject

data class ComponentDetails(
val name: String? = "",
val description: String? = "",
val features: List<String>? = listOf(),
) : ValidatedObject

data class TestDetails(
val name: String? = "",
val steps: List<String>? = listOf(),
val expectations: List<String>? = listOf(),
) : ValidatedObject

data class DocumentationDetails(
val name: String? = "",
val description: String? = "",
val sections: List<String>? = listOf(),
) : ValidatedObject

fun buildProjectFileSpecifications(
project: Project,
requirements: ProjectStatements,
design: ProjectDesign,
recursive: Boolean = true
): List<CodeSpecification>

fun buildComponentFileSpecifications(
project: Project,
requirements: ProjectStatements,
design: ComponentDetails,
recursive: Boolean = true
): List<CodeSpecification>

fun buildTestFileSpecifications(
project: Project,
requirements: ProjectStatements,
design: TestDetails,
recursive: Boolean = true
): List<TestSpecification>

fun buildDocumentationFileSpecifications(
project: Project,
requirements: ProjectStatements,
design: DocumentationDetails,
recursive: Boolean = true
): List<DocumentSpecification>

data class CodeSpecification(
val description: String? = "",
val requires: List<FilePath>? = listOf(),
val publicProperties: List<String>? = listOf(),
val publicMethodSignatures: List<String>? = listOf(),
val language: String? = "",
val location: FilePath? = FilePath(),
) : ValidatedObject

data class DocumentSpecification(
val description: String? = "",
val requires: List<FilePath>? = listOf(),
val sections: List<String>? = listOf(),
val language: String? = "",
val location: FilePath? = FilePath(),
) : ValidatedObject

data class TestSpecification(
val description: String? = "",
val requires: List<FilePath>? = listOf(),
val steps: List<String>? = listOf(),
val expectations: List<String>? = listOf(),
val language: String? = "",
val location: FilePath? = FilePath(),
) : ValidatedObject

data class FilePath(
@Description("File name relative to project root, e.g. src/main/java/Foo.java")
val file: String? = "",
) : ValidatedObject {
override fun toString(): String {
return file ?: ""
}

override fun validate(): Boolean {
if (file?.isBlank() != false) return false
return super.validate()
}
}

fun implementComponentSpecification(
project: Project,
component: ComponentDetails,
imports: List<Any>,
specification: CodeSpecification,
): SourceCode


fun implementTestSpecification(
project: Project,
specification: TestSpecification,
test: TestDetails,
imports: List<Any>,
specificationAgain: TestSpecification,
): SourceCode


fun implementDocumentationSpecification(
project: Project,
specification: DocumentSpecification,
documentation: DocumentationDetails,
imports: List<Any>,
specificationAgain: DocumentSpecification,
): SourceCode

data class SourceCode(
@Description("language of the code, e.g. \"java\" or \"kotlin\"")
val language: String? = "",
@Description("Fully implemented source code")
val code: String? = "",
) : ValidatedObject

companion object {
val log = org.slf4j.LoggerFactory.getLogger(SoftwareProjectAI::class.java)
fun parallelImplement(
api: SoftwareProjectAI,
project: Project,
components: Map<ComponentDetails, List<CodeSpecification>>?,
documents: Map<DocumentationDetails, List<DocumentSpecification>>?,
tests: Map<TestDetails, List<TestSpecification>>?,
drafts: Int,
threads: Int
): Map<FilePath, SourceCode?> = parallelImplementWithAlternates(
api,
project,
components ?: mapOf(),
documents ?: mapOf(),
tests ?: mapOf(),
drafts,
threads
).mapValues { it.value.maxByOrNull { it.code?.length ?: 0 } }

fun parallelImplementWithAlternates(
api: SoftwareProjectAI,
project: Project,
components: Map<ComponentDetails, List<CodeSpecification>>,
documents: Map<DocumentationDetails, List<DocumentSpecification>>,
tests: Map<TestDetails, List<TestSpecification>>,
drafts: Int,
threads: Int,
progress: (Double) -> Unit = {}
): Map<FilePath, List<SourceCode>> {
val threadPool = Executors.newFixedThreadPool(threads)
try {
val totalDrafts = (components + tests + documents).values.sumOf { it.size } * drafts
val currentDraft = AtomicInteger(0)
val fileImplCache = ConcurrentHashMap<String, List<Future<Pair<FilePath, SourceCode>>>>()
val normalizeFileName: (String?) -> String = {
it?.trimStart('/', '.') ?: ""
}

// Build Components
fun buildCodeSpec(
component: ComponentDetails,
files: List<CodeSpecification>,
file: CodeSpecification
): List<Future<Pair<FilePath, SourceCode>>> {
if (file.location == null) {
return emptyList()
}
return fileImplCache.getOrPut(normalizeFileName(file.location.file)) {
(0 until drafts).map { _ ->
threadPool.submit(Callable {
val implement = api.implementComponentSpecification(
project,
component,
files.filter { file.requires?.contains(it.location) ?: false }.toList(),
file.copy(requires = listOf())
)
(currentDraft.incrementAndGet().toDouble() / totalDrafts)
.also { progress(it) }
.also { log.info("Progress: $it") }
file.location to implement
})
}
}
}

fun buildComponentDetails(
component: ComponentDetails,
files: List<CodeSpecification>
): List<Future<Pair<FilePath, SourceCode>>> {
return files.flatMap(fun(file: CodeSpecification): List<Future<Pair<FilePath, SourceCode>>> {
return buildCodeSpec(component, files, file)
}).toTypedArray().toList()
}

val componentFutures = components.flatMap { (component, files) ->
buildComponentDetails(component, files)
}.toTypedArray()

// Build Documents
fun buildDocumentSpec(
documentation: DocumentationDetails,
files: List<DocumentSpecification>,
file: DocumentSpecification
): List<Future<Pair<FilePath, SourceCode>>> {
if (file.location == null) {
return emptyList()
}
return fileImplCache.getOrPut(normalizeFileName(file.location.file)) {
(0 until drafts).map { _ ->
threadPool.submit(Callable {
val implement = api.implementDocumentationSpecification(
project,
file.copy(requires = listOf()),
documentation,
files.filter { file.requires?.contains(it.location) ?: false }.toList(),
file.copy(requires = listOf())
)
(currentDraft.incrementAndGet().toDouble() / totalDrafts)
.also { progress(it) }
.also { log.info("Progress: $it") }
file.location to implement
})
}
}
}

fun buildDocumentDetails(
documentation: DocumentationDetails,
files: List<DocumentSpecification>
): List<Future<Pair<FilePath, SourceCode>>> {
return files.flatMap(fun(file: DocumentSpecification): List<Future<Pair<FilePath, SourceCode>>> {
return buildDocumentSpec(documentation, files, file)
}).toTypedArray().toList()
}

val documentFutures = documents.flatMap { (documentation, files) ->
buildDocumentDetails(documentation, files)
}.toTypedArray()

// Build Tests
fun buildTestSpec(
test: TestDetails,
files: List<TestSpecification>,
file: TestSpecification
): List<Future<Pair<FilePath, SourceCode>>> {
if (file.location == null) {
return emptyList()
}
return fileImplCache.getOrPut(normalizeFileName(file.location.file)) {
(0 until drafts).map { _ ->
threadPool.submit(Callable {
val implement = api.implementTestSpecification(
project,
file.copy(requires = listOf()),
test,
files.filter { file.requires?.contains(it.location) ?: false }.toList(),
file.copy(requires = listOf())
)
(currentDraft.incrementAndGet().toDouble() / totalDrafts)
.also { progress(it) }
.also { log.info("Progress: $it") }
file.location to implement
})
}
}
}

fun buildTestDetails(
test: TestDetails,
files: List<TestSpecification>
): List<Future<Pair<FilePath, SourceCode>>> {
return files.flatMap(fun(file: TestSpecification): List<Future<Pair<FilePath, SourceCode>>> {
return buildTestSpec(test, files, file)
}).toTypedArray().toList()
}

val testFutures = tests.flatMap { (test, files) ->
buildTestDetails(test, files)
}.toTypedArray()

return (getAll(componentFutures) + getAll(documentFutures) + getAll(testFutures)).mapValues {
it.value.map { it.second }.sortedBy { it.code?.length ?: 0 }
}
} finally {
threadPool.shutdown()
}
}

private fun <K, V> getAll(testFutures: Array<Future<Pair<K, V>>>) =
testFutures.map {
try {
Optional.ofNullable(it.get())
} catch (e: Throwable) {
Optional.empty()
}
}.filter { !it.isEmpty }.map { it.get() }.groupBy { it.first }

fun write(
zipArchiveFile: File,
implementations: Map<FilePath, SourceCode?>
) {
ZipOutputStream(zipArchiveFile.outputStream()).use { zip ->
implementations.forEach { (file, sourceCodes) ->
zip.putNextEntry(ZipEntry(file.toString()))
zip.write(sourceCodes!!.code?.toByteArray() ?: byteArrayOf())
zip.closeEntry()
}
}
}
}


}

Loading

0 comments on commit 66a99e5

Please sign in to comment.