Skip to content

Commit

Permalink
1.0.19
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski committed Mar 22, 2023
1 parent b3397c8 commit 9c21356
Show file tree
Hide file tree
Showing 12 changed files with 635 additions and 154 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
### Added
-

## [1.0.19]

### Improved
- Quality for generated projects

## [1.0.18]

### Improved
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.18
pluginVersion = 1.0.19

# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 203
Expand Down
133 changes: 81 additions & 52 deletions src/main/kotlin/com/github/simiacryptus/aicoder/SoftwareProjectAI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface SoftwareProjectAI {
data class Project(
val name: String? = "",
val description: String? = "",
val language: String? = "",
val languages: List<String>? = listOf(),
val features: List<String>? = listOf(),
val libraries: List<String>? = listOf(),
val buildTools: List<String>? = listOf(),
Expand All @@ -42,16 +42,17 @@ interface SoftwareProjectAI {
@Description("Documentation files e.g. README.md, LICENSE, etc.")
val documents: List<DocumentationDetails>? = listOf(),
@Description("Individual test cases")
val tests: List<TestDetails>? = listOf(),
val testCases: List<TestCase>? = listOf(),
) : ValidatedObject

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

data class TestDetails(
data class TestCase(
val name: String? = "",
val steps: List<String>? = listOf(),
val expectations: List<String>? = listOf(),
Expand All @@ -63,33 +64,36 @@ interface SoftwareProjectAI {
val sections: List<String>? = listOf(),
) : ValidatedObject

fun buildProjectFileSpecifications(
project: Project,
requirements: ProjectStatements,
design: ProjectDesign,
recursive: Boolean = true
): List<CodeSpecification>
data class CodeSpecifications(
val specifications: List<CodeSpecification>? = listOf(),
) : ValidatedObject

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

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

data class TestSpecifications(
val specifications: List<TestSpecification>? = listOf(),
) : ValidatedObject

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

data class DocumentSpecifications(
@Description("Specifications for each human-language document. Does not include code files.")
val documents: List<DocumentSpecification>? = listOf(),
) : ValidatedObject

data class CodeSpecification(
val description: String? = "",
Expand All @@ -98,15 +102,25 @@ interface SoftwareProjectAI {
val publicMethodSignatures: List<String>? = listOf(),
val language: String? = "",
val location: FilePath? = FilePath(),
) : ValidatedObject
) : ValidatedObject {
override fun validate(): Boolean {
if(description?.isBlank() != false) return false
return super.validate()
}
}

data class DocumentSpecification(
val description: String? = "",
val requires: List<FilePath>? = listOf(),
val sections: List<String>? = listOf(),
val language: String? = "",
val location: FilePath? = FilePath(),
) : ValidatedObject
) : ValidatedObject {
override fun validate(): Boolean {
if(description?.isBlank() != false) return false
return super.validate()
}
}

data class TestSpecification(
val description: String? = "",
Expand All @@ -115,7 +129,12 @@ interface SoftwareProjectAI {
val expectations: List<String>? = listOf(),
val language: String? = "",
val location: FilePath? = FilePath(),
) : ValidatedObject
) : ValidatedObject {
override fun validate(): Boolean {
if(description?.isBlank() != false) return false
return super.validate()
}
}

data class FilePath(
@Description("File name relative to project root, e.g. src/main/java/Foo.java")
Expand All @@ -131,35 +150,40 @@ interface SoftwareProjectAI {
}
}

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


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


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

data class Document(
@Description("e.g. \"markdown\" or \"text\"")
val language: String? = "",
@Description("Complete Document Text")
val text: String? = "",
) : ValidatedObject

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

Expand All @@ -168,9 +192,9 @@ interface SoftwareProjectAI {
fun parallelImplement(
api: SoftwareProjectAI,
project: Project,
components: Map<ComponentDetails, List<CodeSpecification>>?,
documents: Map<DocumentationDetails, List<DocumentSpecification>>?,
tests: Map<TestDetails, List<TestSpecification>>?,
components: Map<ComponentDetails, CodeSpecifications>?,
documents: Map<DocumentationDetails, DocumentSpecifications>?,
tests: Map<TestCase, TestSpecifications>?,
drafts: Int,
threads: Int
): Map<FilePath, SourceCode?> = parallelImplementWithAlternates(
Expand All @@ -186,16 +210,20 @@ interface SoftwareProjectAI {
fun parallelImplementWithAlternates(
api: SoftwareProjectAI,
project: Project,
components: Map<ComponentDetails, List<CodeSpecification>>,
documents: Map<DocumentationDetails, List<DocumentSpecification>>,
tests: Map<TestDetails, List<TestSpecification>>,
components: Map<ComponentDetails, CodeSpecifications>,
documents: Map<DocumentationDetails, DocumentSpecifications>,
tests: Map<TestCase, TestSpecifications>,
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 totalDrafts = (
components.values.sumOf { it.specifications?.size ?: 0 } +
tests.values.sumOf { it.specifications?.size ?: 0 } +
documents.values.sumOf { it.documents?.size ?: 0 }
) * drafts
val currentDraft = AtomicInteger(0)
val fileImplCache = ConcurrentHashMap<String, List<Future<Pair<FilePath, SourceCode>>>>()
val normalizeFileName: (String?) -> String = {
Expand All @@ -214,7 +242,7 @@ interface SoftwareProjectAI {
return fileImplCache.getOrPut(normalizeFileName(file.location.file)) {
(0 until drafts).map { _ ->
threadPool.submit(Callable {
val implement = api.implementComponentSpecification(
val implement = api.implementCode(
project,
component,
files.filter { file.requires?.contains(it.location) ?: false }.toList(),
Expand All @@ -239,7 +267,7 @@ interface SoftwareProjectAI {
}

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

// Build Documents
Expand All @@ -254,17 +282,19 @@ interface SoftwareProjectAI {
return fileImplCache.getOrPut(normalizeFileName(file.location.file)) {
(0 until drafts).map { _ ->
threadPool.submit(Callable {
val implement = api.implementDocumentationSpecification(
val implement = api.writeDocument(
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
file.location to SourceCode(
language = implement.language,
code = implement.text
)
})
}
}
Expand All @@ -280,12 +310,12 @@ interface SoftwareProjectAI {
}

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

// Build Tests
fun buildTestSpec(
test: TestDetails,
test: TestCase,
files: List<TestSpecification>,
file: TestSpecification
): List<Future<Pair<FilePath, SourceCode>>> {
Expand All @@ -295,9 +325,8 @@ interface SoftwareProjectAI {
return fileImplCache.getOrPut(normalizeFileName(file.location.file)) {
(0 until drafts).map { _ ->
threadPool.submit(Callable {
val implement = api.implementTestSpecification(
val implement = api.implementTest(
project,
file.copy(requires = listOf()),
test,
files.filter { file.requires?.contains(it.location) ?: false }.toList(),
file.copy(requires = listOf())
Expand All @@ -312,7 +341,7 @@ interface SoftwareProjectAI {
}

fun buildTestDetails(
test: TestDetails,
test: TestCase,
files: List<TestSpecification>
): List<Future<Pair<FilePath, SourceCode>>> {
return files.flatMap(fun(file: TestSpecification): List<Future<Pair<FilePath, SourceCode>>> {
Expand All @@ -321,7 +350,7 @@ interface SoftwareProjectAI {
}

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

return (getAll(componentFutures) + getAll(documentFutures) + getAll(testFutures)).mapValues {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,12 @@ class GenerateProjectAction : AnAction() {
if (it.isCanceled) throw InterruptedException()
buildProjectDesign
}
val files = UITools.run(
e.project, "Specifying Files", true
) {
val buildProjectFileSpecifications =
api.buildProjectFileSpecifications(project, requirements, projectDesign)
if (it.isCanceled) throw InterruptedException()
buildProjectFileSpecifications
}

val components =
UITools.run(
e.project, "Specifying Components", true
) {
projectDesign.components?.map { it to api.buildComponentFileSpecifications(project, requirements, it) }
projectDesign.components?.map { it to api.getComponentFiles(project, requirements, it) }
?.toMap()
}

Expand All @@ -108,19 +100,19 @@ class GenerateProjectAction : AnAction() {

val tests = UITools.run(
e.project, "Specifying Tests", true
) { projectDesign.tests?.map { it to api.buildTestFileSpecifications(project, requirements, it) }?.toMap() }
) { projectDesign.testCases?.map { it to api.getTestFiles(project, requirements, it) }?.toMap() }

val sourceCodeMap = UITools.run(
e.project, "Implementing Files", true
) {
SoftwareProjectAI.parallelImplementWithAlternates(
api,
project,
components ?: emptyMap(),
documents ?: emptyMap(),
tests ?: emptyMap(),
config.drafts,
AppSettingsState.instance.apiThreads
api = api,
project = project,
components = components ?: emptyMap(),
documents = documents ?: emptyMap(),
tests = tests ?: emptyMap(),
drafts = config.drafts,
threads = AppSettingsState.instance.apiThreads
) { progress ->
if (it.isCanceled) throw InterruptedException()
it.fraction = progress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ class ChatProxy(
ChatMessage(
ChatMessage.Role.system, """
|You are a JSON-RPC Service
|Responses are expected to be a single JSON object without explaining text.
|Responses are in JSON format
|Do not include explaining text outside the JSON
|All input arguments are optional
|You will respond to the following method:
|Outputs are based on inputs, with any missing information filled randomly
|You will respond to the following method
|
|${prompt.apiYaml}
|""".trimMargin().trim()
Expand Down
Loading

0 comments on commit 9c21356

Please sign in to comment.