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
@@ -0,0 +1,26 @@
package simplerag.ragback.domain.prompt.controller

import jakarta.validation.Valid
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
import simplerag.ragback.domain.prompt.dto.PromptCreateRequest
import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse
import simplerag.ragback.domain.prompt.service.PromptService
import simplerag.ragback.global.response.ApiResponse

@RestController
@RequestMapping("/api/v1/prompts")
class PromptController(
private val promptService: PromptService
) {
Comment on lines +11 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Return HTTP 201 for creation and enable bean validation on the request

Consistent with your Index/DataFile controllers, mark the POST as 201 Created and enable validation.

 @RestController
 @RequestMapping("/api/v1/prompts")
-class PromptController(
+@Validated
+class PromptController(
     private val promptService: PromptService
 ) {

     @PostMapping
+    @ResponseStatus(HttpStatus.CREATED)
     fun createPrompt(
-        @RequestBody promptCreateRequest: PromptCreateRequest
+        @RequestBody @Valid promptCreateRequest: PromptCreateRequest
     ): ApiResponse<PromptPreviewResponse> {
         val savedPrompt = promptService.createPrompt(promptCreateRequest)
         return ApiResponse.ok(savedPrompt)
     }

To confirm consistency across controllers, you can run:

Also applies to: 18-23


🏁 Script executed:

#!/bin/bash
# Show POST endpoints and whether they declare 201
rg -nP --type=kt -C2 '@PostMapping' src/main/kotlin/simplerag/ragback/domain/.+?/controller/.+?Controller\.kt \
  | sed -n '1,200p'

Length of output: 153


To ensure we correctly mirror the behavior of your existing controllers, let’s re-scan all controller classes for @PostMapping and check whether they already declare @ResponseStatus(HttpStatus.CREATED) and @Valid. Please run:


🏁 Script executed:

#!/bin/bash
# Find all @PostMapping in controller classes and show surrounding lines
rg -n '@PostMapping' -C2 src/main/kotlin/simplerag/ragback/domain/*/controller/*Controller.kt

Length of output: 1791


To confirm the exact annotations on your existing POST endpoints, let’s extract the relevant snippets from both controllers:


🏁 Script executed:

#!/bin/bash
echo "=== IndexController.createIndex ==="
rg -nP -C3 'fun createIndex' src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt || true
rg -nP '@RequestBody' -C3 src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt || true
rg -nP '@Valid' -C3 src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt || true
rg -nP '@ResponseStatus' -C3 src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt || true

echo
echo "=== DataFileController POST ==="
rg -nP -C5 '@PostMapping' src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt || true
rg -nP '@ResponseStatus' -C3 src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt || true
rg -nP '@Validated' -C3 src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt || true

Length of output: 3599


PromptController: mark POST create as 201 Created and enable bean validation

To stay consistent with your other controllers, please update PromptController as follows:

• File: src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt
• Add @Validated at the class level
• Annotate the createPrompt method with @ResponseStatus(HttpStatus.CREATED)
• Add @Valid to the @RequestBody parameter

 @RestController
 @RequestMapping("/api/v1/prompts")
-@Validated
-class PromptController(
+@Validated
+class PromptController(
     private val promptService: PromptService
 ) {

     @PostMapping
+    @ResponseStatus(HttpStatus.CREATED)
     fun createPrompt(
-        @RequestBody promptCreateRequest: PromptCreateRequest
+        @RequestBody @Valid promptCreateRequest: PromptCreateRequest
     ): ApiResponse<PromptPreviewResponse> {
         val savedPrompt = promptService.createPrompt(promptCreateRequest)
         return ApiResponse.ok(savedPrompt)
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@RestController
@RequestMapping("/api/v1/prompts")
class PromptController(
private val promptService: PromptService
) {
@RestController
@RequestMapping("/api/v1/prompts")
@Validated
class PromptController(
private val promptService: PromptService
) {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun createPrompt(
@RequestBody @Valid promptCreateRequest: PromptCreateRequest
): ApiResponse<PromptPreviewResponse> {
val savedPrompt = promptService.createPrompt(promptCreateRequest)
return ApiResponse.ok(savedPrompt)
}
}
🤖 Prompt for AI Agents
In
src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt
around lines 12–16, the controller should enable bean validation and return the
correct status for POST: add the @Validated annotation at the class level,
annotate the createPrompt handler with @ResponseStatus(HttpStatus.CREATED), and
add @Valid to the @RequestBody parameter; also add the necessary imports
(org.springframework.validation.annotation.Validated,
org.springframework.web.bind.annotation.ResponseStatus,
org.springframework.http.HttpStatus and javax/ jakarta.validation.Valid
depending on your setup).


@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun createPrompt(
@RequestBody @Valid promptCreateRequest: PromptCreateRequest
): ApiResponse<PromptPreviewResponse> {
val savedPrompt = promptService.createPrompt(promptCreateRequest)
return ApiResponse.ok(savedPrompt)
}
Comment on lines +17 to +24
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Optional: set Location header for the created resource

If PromptPreviewResponse exposes the new prompt’s ID, consider returning ResponseEntity with a Location header for better REST semantics.

Example (illustrative):

-import org.springframework.http.HttpStatus
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.http.HttpHeaders
+import org.springframework.web.util.UriComponentsBuilder
@@
-    @ResponseStatus(HttpStatus.CREATED)
-    fun createPrompt(
+    fun createPrompt(
         @RequestBody @Valid promptCreateRequest: PromptCreateRequest
-    ): ApiResponse<PromptPreviewResponse> {
-        val savedPrompt = promptService.createPrompt(promptCreateRequest)
-        return ApiResponse.ok(savedPrompt)
+    ): ResponseEntity<ApiResponse<PromptPreviewResponse>> {
+        val savedPrompt = promptService.createPrompt(promptCreateRequest)
+        val location = UriComponentsBuilder.fromPath("/api/v1/prompts/{id}")
+            .buildAndExpand(savedPrompt.id)
+            .toUri()
+        return ResponseEntity
+            .status(HttpStatus.CREATED)
+            .header(HttpHeaders.LOCATION, location.toString())
+            .body(ApiResponse.ok(savedPrompt))
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun createPrompt(
@RequestBody @Valid promptCreateRequest: PromptCreateRequest
): ApiResponse<PromptPreviewResponse> {
val savedPrompt = promptService.createPrompt(promptCreateRequest)
return ApiResponse.ok(savedPrompt)
}
// Add these imports (in addition to the existing HttpStatus import)
import org.springframework.http.ResponseEntity
import org.springframework.http.HttpHeaders
import org.springframework.web.util.UriComponentsBuilder
@PostMapping
fun createPrompt(
@RequestBody @Valid promptCreateRequest: PromptCreateRequest
): ResponseEntity<ApiResponse<PromptPreviewResponse>> {
val savedPrompt = promptService.createPrompt(promptCreateRequest)
val location = UriComponentsBuilder
.fromPath("/api/v1/prompts/{id}")
.buildAndExpand(savedPrompt.id)
.toUri()
return ResponseEntity
.status(HttpStatus.CREATED)
.header(HttpHeaders.LOCATION, location.toString())
.body(ApiResponse.ok(savedPrompt))
}
🤖 Prompt for AI Agents
In
src/main/kotlin/simplerag/ragback/domain/prompt/controller/PromptController.kt
around lines 17 to 24, update the createPrompt method to return a ResponseEntity
and set a Location header pointing to the newly created prompt resource: after
saving, extract the new prompt ID from PromptPreviewResponse (if it does not
contain an ID, add it to the DTO or return an object that includes the ID),
build the resource URI (e.g. using
ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri()),
and return ResponseEntity.created(location).body(savedPrompt) so the response
status is 201 and the Location header is set.


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package simplerag.ragback.domain.prompt.dto

import jakarta.validation.constraints.Size
import simplerag.ragback.domain.prompt.entity.enums.PreSet


data class PromptCreateRequest(
@field:Size(max = 100)
val name: String,
Comment on lines +8 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Align DTO with DB non-null constraint for name; add @notblank

The DB declares name NOT NULL with length 100. Add @notblank in addition to @SiZe to block all-whitespace names.

Apply the @NotBlank addition from the main diff above.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptRequestDTO.kt
around lines 8 to 9, the DTO field 'name' only has @Size(max = 100) but the DB
column is NOT NULL and should reject blank/whitespace-only values; add the
@NotBlank annotation alongside @Size to enforce non-blank input and update
imports to include javax.validation.constraints.NotBlank (or the project's
validation package) so the validator will reject empty/whitespace names to match
the DB constraint.

val preSet: PreSet,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider renaming preSet → preset for consistent, idiomatic naming

The mixed-case “PreSet” leaks into the API as preSet. Kotlin/Java community typically uses “preset”. Renaming early avoids long-term churn across DB column names, JSON, and code.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptRequestDTO.kt
around line 8, the property name `preSet: PreSet` leaks non-idiomatic
mixed-casing into the API; rename the property to `preset: PreSet` and update
all references accordingly (constructor usages, JSON serialization names, DTO
mapping, database/ORM column mappings, tests and any callers) ensuring
serialization annotations (e.g., @JsonProperty) or column mappings are adjusted
if you must preserve an external contract during rollout; run and update
unit/integration tests and compile-time references to finish the rename.

@field:Size(max = 20000)
val systemPrompt: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package simplerag.ragback.domain.prompt.dto

import simplerag.ragback.domain.prompt.entity.Prompt
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Import PreSet if exposing it in the response

If you include preset in the preview, add the missing import.

Apply this diff if you adopt the response changes below:

-import simplerag.ragback.domain.prompt.entity.Prompt
+import simplerag.ragback.domain.prompt.entity.Prompt
+import simplerag.ragback.domain.prompt.entity.enums.PreSet
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import simplerag.ragback.domain.prompt.entity.Prompt
// src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt
import simplerag.ragback.domain.prompt.entity.Prompt
import simplerag.ragback.domain.prompt.entity.enums.PreSet
// …rest of file…
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt
around line 3, the DTO now exposes the PreSet type but the corresponding import
is missing; add an import for simplerag.ragback.domain.prompt.entity.PreSet
(i.e. import simplerag.ragback.domain.prompt.entity.PreSet) alongside the
existing Prompt import so the file compiles and the response can reference
PreSet.


data class PromptPreviewResponse(
val id: Long,
) {
companion object {
fun from(
prompt: Prompt
): PromptPreviewResponse {
return PromptPreviewResponse(prompt.id)
}
}
Comment on lines +5 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Return minimally useful metadata in the preview (id, name, preset)

Returning only id creates an immediate follow-up GET to show what was created. Mirror the pattern from other “PreviewResponse” types and include name and preSet.

Apply this diff:

-data class PromptPreviewResponse(
-    val id: Long,
-) {
-    companion object {
-        fun from(
-            prompt: Prompt
-        ): PromptPreviewResponse {
-            return PromptPreviewResponse(prompt.id)
-        }
-    }
-}
+data class PromptPreviewResponse(
+    val id: Long,
+    val name: String,
+    val preSet: PreSet,
+) {
+    companion object {
+        fun from(prompt: Prompt): PromptPreviewResponse =
+            PromptPreviewResponse(
+                id = prompt.id,
+                name = prompt.name,
+                preSet = prompt.preSet,
+            )
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class PromptPreviewResponse(
val id: Long,
) {
companion object {
fun from(
prompt: Prompt
): PromptPreviewResponse {
return PromptPreviewResponse(prompt.id)
}
}
data class PromptPreviewResponse(
val id: Long,
val name: String,
val preSet: PreSet,
) {
companion object {
fun from(prompt: Prompt): PromptPreviewResponse =
PromptPreviewResponse(
id = prompt.id,
name = prompt.name,
preSet = prompt.preSet,
)
}
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/dto/PromptResponseDTO.kt
around lines 5 to 14, the PromptPreviewResponse currently only returns id;
update the data class to include val name: String and val preSet: Boolean (or
the actual type used on Prompt) and modify the companion from(prompt: Prompt) to
populate those fields from prompt.name and prompt.preSet so the preview contains
id, name and preSet to avoid an extra GET.

}
28 changes: 26 additions & 2 deletions src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package simplerag.ragback.domain.prompt.entity

import jakarta.persistence.*
import simplerag.ragback.domain.prompt.entity.enums.PreSet
import jakarta.persistence.*
import simplerag.ragback.domain.prompt.dto.PromptCreateRequest
import simplerag.ragback.global.entity.BaseEntity

@Entity
Expand All @@ -19,8 +20,31 @@ class Prompt(
@Lob
val systemPrompt: String,

) : BaseEntity() {
) : BaseEntity() {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "prompts_id")
val id: Long = 0
Comment on lines 24 to 26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify your Kotlin JPA configuration if keeping val id: Long = 0

Hibernate must set this generated id on a val field via reflection/bytecode enhancement. Ensure the Kotlin JPA/all-open plugins are applied. Alternatively, prefer val id: Long? = null to reduce surprises (will require adapting DTO mapping).

Run this quick check:


🏁 Script executed:

#!/bin/bash
# Verify Kotlin JPA/all-open is enabled
rg -nP 'kotlin\("plugin\.jpa"\)|allOpen|kotlin-jpa' -g 'build.gradle*' -C2 || true

Length of output: 267


🏁 Script executed:

#!/bin/bash
# Display the first 50 lines of build.gradle* to inspect plugin declarations
head -n 50 build.gradle build.gradle.kts || true

Length of output: 1683


Verify Kotlin JPA compiler plugin setup – confirms ID handling

Your Gradle configuration already applies the necessary Kotlin compiler plugins to support JPA entities:

  • The org.jetbrains.kotlin.plugin.jpa compiler plugin is declared in the plugins block, which wraps both the no-arg and all-open plugins for JPA support, automatically generating a no-argument constructor and opening classes annotated with @Entity, @Embeddable, and @MappedSuperclass (kotlinlang.org).
  • An explicit allOpen block lists jakarta.persistence.Entity and jakarta.persistence.MappedSuperclass, ensuring these classes aren’t final so Hibernate can proxy them (kotlinlang.org).

Given this setup, Hibernate will correctly set the generated id on your val id: Long = 0 field via reflection/bytecode enhancement.
As an optional refactoring for clearer “unsaved” semantics, you may choose to change the declaration to:

val id: Long? = null

and update any DTO mappings accordingly to use null as the sentinel for new entities.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt around lines
24-26, confirm the Kotlin JPA compiler plugins are enabled in Gradle
(org.jetbrains.kotlin.plugin.jpa / no-arg and all-open configured for
jakarta.persistence annotations) so Hibernate can set the @Id @GeneratedValue
val id: Long = 0 via reflection; optionally refactor the entity id to a nullable
sentinel by changing it to val id: Long? = null and update any DTO/mapping code
that assumes 0 to use null for unsaved entities.


companion object {
fun from(
promptCreateRequest: PromptCreateRequest
): Prompt {

val prompt = if (promptCreateRequest.preSet == PreSet.CUSTOM) {
Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.systemPrompt
)
} else {
Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.preSet.defaultSystemPrompt
)
}

return prompt
}
}
Comment on lines +28 to +49
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Normalize inputs, enforce invariants, and fail fast if presets are not configured

  • Trim and validate name against blank input.
  • Make systemPrompt resolution explicit with a when and validate non-blank.
  • Guard against accidentally persisting TODO defaults.
  • Reduce duplication in the factory.

Apply this diff within the factory:

     companion object {
         fun from(
             promptCreateRequest: PromptCreateRequest
         ): Prompt {
-
-            val prompt = if (promptCreateRequest.preSet == PreSet.CUSTOM) {
-                Prompt(
-                    promptCreateRequest.name,
-                    promptCreateRequest.preSet,
-                    promptCreateRequest.systemPrompt
-                )
-            } else {
-                Prompt(
-                    promptCreateRequest.name,
-                    promptCreateRequest.preSet,
-                    promptCreateRequest.preSet.defaultSystemPrompt
-                )
-            }
-
-            return prompt
+            val name = promptCreateRequest.name.trim()
+            require(name.isNotBlank()) { "Prompt name must not be blank." }
+
+            val resolvedSystemPrompt = when (promptCreateRequest.preSet) {
+                PreSet.CUSTOM -> (promptCreateRequest.systemPrompt ?: "").trim()
+                else -> promptCreateRequest.preSet.defaultSystemPrompt.trim()
+            }
+
+            require(resolvedSystemPrompt.isNotBlank()) {
+                "systemPrompt must not be blank. For non-CUSTOM presets, defaultSystemPrompt must be configured."
+            }
+            if (promptCreateRequest.preSet != PreSet.CUSTOM &&
+                resolvedSystemPrompt.startsWith("TODO", ignoreCase = true)
+            ) {
+                throw IllegalStateException("Preset ${promptCreateRequest.preSet} defaultSystemPrompt is not configured.")
+            }
+
+            return Prompt(
+                name = name,
+                preSet = promptCreateRequest.preSet,
+                systemPrompt = resolvedSystemPrompt,
+            )
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
companion object {
fun from(
promptCreateRequest: PromptCreateRequest
): Prompt {
val prompt = if (promptCreateRequest.preSet == PreSet.CUSTOM) {
Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.systemPrompt
)
} else {
Prompt(
promptCreateRequest.name,
promptCreateRequest.preSet,
promptCreateRequest.preSet.defaultSystemPrompt
)
}
return prompt
}
}
companion object {
fun from(
promptCreateRequest: PromptCreateRequest
): Prompt {
val name = promptCreateRequest.name.trim()
require(name.isNotBlank()) { "Prompt name must not be blank." }
val resolvedSystemPrompt = when (promptCreateRequest.preSet) {
PreSet.CUSTOM -> (promptCreateRequest.systemPrompt ?: "").trim()
else -> promptCreateRequest.preSet.defaultSystemPrompt.trim()
}
require(resolvedSystemPrompt.isNotBlank()) {
"systemPrompt must not be blank. For non-CUSTOM presets, defaultSystemPrompt must be configured."
}
if (promptCreateRequest.preSet != PreSet.CUSTOM &&
resolvedSystemPrompt.startsWith("TODO", ignoreCase = true)
) {
throw IllegalStateException("Preset ${promptCreateRequest.preSet} defaultSystemPrompt is not configured.")
}
return Prompt(
name = name,
preSet = promptCreateRequest.preSet,
systemPrompt = resolvedSystemPrompt,
)
}
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt around lines
28 to 49, the factory does not normalize or validate inputs and duplicates
construction logic; change it to trim and validate the incoming name (reject
blank after trim), resolve the systemPrompt with an explicit when on preSet (use
promptCreateRequest.systemPrompt for PreSet.CUSTOM, otherwise use
preSet.defaultSystemPrompt), validate the resolved systemPrompt is non-blank and
fail fast if it is blank or matches any TODO/default sentinel, and then
construct and return a single Prompt instance (reduce duplication by building
the resolvedName and resolvedSystemPrompt first and reuse them for
construction).

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
package simplerag.ragback.domain.prompt.entity.enums

enum class PreSet {
NONE
enum class PreSet(
val description: String,
val defaultSystemPrompt: String
) {
Comment on lines +3 to +6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Future-proof the enum API by decoupling data from code

Hardcoding large prompt bodies in an enum couples deployments to copy updates. If you keep the current shape, at least document that defaultSystemPrompt must be non-blank for non-CUSTOM presets.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/entity/enums/PreSet.kt around
lines 3 to 6, the enum currently embeds large prompt text which couples
deployments to code; refactor by removing large prompt bodies from the enum and
instead store only identifiers/keys in the enum (e.g., name or resource key) and
load the actual prompt text from external resources (files, config, or a prompt
service) at runtime; if you keep a defaultSystemPrompt field, make it nullable
and add a runtime validation that non-CUSTOM presets must resolve to a non-blank
prompt (throw IllegalStateException or return Result) and add a KDoc comment on
the enum and the defaultSystemPrompt field stating that non-CUSTOM presets
require a non-blank prompt loaded externally.

CUSTOM(
"User-defined custom prompt",
"TODO: custom system prompt here"
),

// Retrieval / Q&A
RAG_QA(
"Question Answering with retrieved context",
"TODO: system prompt for RAG_QA"
),
RAG_SUMMARIZER(
"Summarize documents retrieved via RAG",
"TODO: system prompt for RAG_SUMMARIZER"
),

// Code related
CODE_REVIEW_BACKEND(
"Backend code review (Java/Kotlin/Spring)",
"TODO: system prompt for CODE_REVIEW_BACKEND"
),
CODE_REVIEW_GENERAL(
"General code review and best practices",
"TODO: system prompt for CODE_REVIEW_GENERAL"
),

// Language tasks
TRANSLATION_EN_KO(
"English ↔ Korean translation",
"TODO: system prompt for TRANSLATION_EN_KO"
),
TRANSLATION_MULTI(
"Multi-language translation",
"TODO: system prompt for TRANSLATION_MULTI"
),
PROOFREAD_KR(
"Korean proofreading / grammar correction",
"TODO: system prompt for PROOFREAD_KR"
),

// Content tasks
TEXT_SUMMARIZER(
"General text summarization",
"TODO: system prompt for TEXT_SUMMARIZER"
),
DATA_CLEANER(
"Text cleaning and preprocessing",
"TODO: system prompt for DATA_CLEANER"
),
EMAIL_WRITER(
"Email / message generation",
"TODO: system prompt for EMAIL_WRITER"
),

// Safety / Guardrails
SAFETY_FILTERED(
"Safe response (avoid harmful or unsafe outputs)",
"TODO: system prompt for SAFETY_FILTERED"
)
Comment on lines +7 to +64
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

⚠️ Potential issue

Do not ship with “TODO” default prompts — this violates requirements and will persist placeholders

Per PR objectives (“Add OpenSource Prompt Set”), every non-CUSTOM preset currently writes “TODO …” into the database via defaultSystemPrompt. That’s a correctness and product issue.

Pick one of the following immediately:

  • Minimal fix now: replace TODOs with sane, short defaults.
  • Safer guard: block non-CUSTOM creation until defaults exist.
  • Better architecture: store template keys here and load full prompt text from external resources (files/DB), enabling iteration without redeploys.

Minimal, immediate fix (replace only the string literals below):

@@
     CUSTOM(
         "User-defined custom prompt",
-        "TODO: custom system prompt here"
+        ""  // Not used — CUSTOM comes from request
     ),
@@
     RAG_QA(
         "Question Answering with retrieved context",
-        "TODO: system prompt for RAG_QA"
+        "You are a grounded QA assistant. Use ONLY the provided CONTEXT to answer. If the answer is not in CONTEXT, say \"I don't know\". Cite sources by title or id."
     ),
     RAG_SUMMARIZER(
         "Summarize documents retrieved via RAG",
-        "TODO: system prompt for RAG_SUMMARIZER"
+        "Summarize the provided CONTEXT for a busy reader. Use bullet points, keep facts verifiable, avoid speculation, and include key entities, dates, and numbers."
     ),
@@
     CODE_REVIEW_BACKEND(
         "Backend code review (Java/Kotlin/Spring)",
-        "TODO: system prompt for CODE_REVIEW_BACKEND"
+        "Review backend code (Kotlin/Java/Spring). Focus on correctness, concurrency, security, input validation, performance, error handling, transactions, and testability. Output concise action items with code snippets."
     ),
     CODE_REVIEW_GENERAL(
         "General code review and best practices",
-        "TODO: system prompt for CODE_REVIEW_GENERAL"
+        "Perform a pragmatic code review. Identify bugs, edge cases, readability issues, dead code, and missing tests. Propose minimal diffs to fix critical issues."
     ),
@@
     TRANSLATION_EN_KO(
         "English ↔ Korean translation",
-        "TODO: system prompt for TRANSLATION_EN_KO"
+        "Translate between English and Korean. Preserve meaning, tone, and formatting. Provide only the translation without additional commentary."
     ),
     TRANSLATION_MULTI(
         "Multi-language translation",
-        "TODO: system prompt for TRANSLATION_MULTI"
+        "Translate between the user’s source and target languages. Maintain tone and formatting. Do not add explanations unless asked."
     ),
     PROOFREAD_KR(
         "Korean proofreading / grammar correction",
-        "TODO: system prompt for PROOFREAD_KR"
+        "Proofread Korean text. Fix grammar, spacing, and style while preserving meaning. Output only the corrected text."
     ),
@@
     TEXT_SUMMARIZER(
         "General text summarization",
-        "TODO: system prompt for TEXT_SUMMARIZER"
+        "Summarize the text in 5–7 bullet points. Be faithful, concise, and include critical facts (who/what/when/where/why)."
     ),
     DATA_CLEANER(
         "Text cleaning and preprocessing",
-        "TODO: system prompt for DATA_CLEANER"
+        "Normalize whitespace, remove boilerplate, fix common encoding issues, and standardize quotes and dashes. Preserve semantic content."
     ),
     EMAIL_WRITER(
         "Email / message generation",
-        "TODO: system prompt for EMAIL_WRITER"
+        "Draft a clear, polite email given purpose, audience, and key points. Include subject and body. Keep it concise and actionable."
     ),
@@
     SAFETY_FILTERED(
         "Safe response (avoid harmful or unsafe outputs)",
-        "TODO: system prompt for SAFETY_FILTERED"
+        "Follow safety best practices. Refuse requests involving illegal, harmful, or sensitive content. Offer safe alternatives when declining."
     )

Longer-term architecture (recommended):

  • Replace defaultSystemPrompt: String with templateKey: String and look up prompt text from a registry (JSON/YAML in resources or DB), enabling A/B tests and localization without code changes.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package simplerag.ragback.domain.prompt.repository

import org.springframework.data.jpa.repository.JpaRepository
import simplerag.ragback.domain.prompt.entity.Prompt

interface PromptRepository: JpaRepository<Prompt, Long>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Style nit + add helper to prevent duplicate names

  • Kotlin style: add a space before the colon.
  • Expose existsByName to preempt duplicates at the service layer (pair with a DB unique constraint).

Apply this diff:

-interface PromptRepository: JpaRepository<Prompt, Long>
+interface PromptRepository : JpaRepository<Prompt, Long> {
+    fun existsByName(name: String): Boolean
+}

And add a unique constraint on prompts.name (outside this hunk):

@Table(
    name = "prompts",
    uniqueConstraints = [UniqueConstraint(name = "uk_prompts_name", columnNames = ["name"])]
)
🤖 Prompt for AI Agents
In
src/main/kotlin/simplerag/ragback/domain/prompt/repository/PromptRepository.kt
around line 6, the interface declaration needs Kotlin style spacing and an
existence helper: change "interface PromptRepository: JpaRepository<Prompt,
Long>" to use a space before the colon and add a method signature fun
existsByName(name: String): Boolean so services can check duplicates; also add a
unique constraint on the prompts table in the Prompt entity with @Table(name =
"prompts", uniqueConstraints = [UniqueConstraint(name = "uk_prompts_name",
columnNames = ["name"])]) to enforce uniqueness at the DB level.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package simplerag.ragback.domain.prompt.service

import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import simplerag.ragback.domain.prompt.dto.PromptCreateRequest
import simplerag.ragback.domain.prompt.dto.PromptPreviewResponse
import simplerag.ragback.domain.prompt.entity.Prompt
import simplerag.ragback.domain.prompt.repository.PromptRepository

@Service
@Transactional(readOnly = true)
class PromptService(
private val promptRepository: PromptRepository
) {

@Transactional
fun createPrompt(
promptCreateRequest: PromptCreateRequest
): PromptPreviewResponse {
val prompt = Prompt.from(promptCreateRequest)
val savedPrompt = promptRepository.save(prompt)
return PromptPreviewResponse.from(savedPrompt)
}
Comment on lines +16 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider guarding against duplicate names and clarifying default system prompt behavior

  • If prompt names must be unique, add a repository existence check (or handle unique index violations) to return a domain-friendly error rather than surfacing a DB exception.
  • Ensure Prompt.from applies a sensible default: when preSet != CUSTOM and systemPrompt is null/blank, use the preset’s defaultSystemPrompt. If that logic isn’t in the factory yet, move it there to centralize invariants.

Example (outside this file):

// PromptRepository.kt
interface PromptRepository : JpaRepository<Prompt, Long> {
    fun existsByName(name: String): Boolean
}
// PromptService.kt (inside createPrompt)
if (promptRepository.existsByName(promptCreateRequest.name)) {
    // throw your domain exception, e.g., DuplicateResourceException("Prompt name already exists")
}

If a DB-level guarantee is desired, also add a unique constraint on the name column and translate DataIntegrityViolationException.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/prompt/service/PromptService.kt
around lines 16-23, guard against duplicate prompt names and centralize default
system-prompt logic: before saving, call
promptRepository.existsByName(promptCreateRequest.name) and throw a
domain-friendly DuplicateResourceException (or similar) if true; also
catch/translate DataIntegrityViolationException from the repository save to the
same domain exception if you prefer DB-level uniqueness. Move or augment
Prompt.from to ensure that when promptCreateRequest.preSet != PreSet.CUSTOM and
systemPrompt is null/blank it sets systemPrompt to the preset’s
defaultSystemPrompt so the factory enforces that invariant rather than relying
on service code.

}