Common AI framework utilities for the Embabel Agent system including LLM configuration, output converters, prompt contributors, and embedding service abstractions.
Pluggable system for contributing content to prompts at different locations with role-based identification.
Objects that contribute to prompts with fixed or dynamic content.
interface PromptContributor : PromptElement {
fun promptContribution(): PromptContribution
fun contribution(): String
companion object {
@JvmStatic
@JvmOverloads
fun fixed(
content: String,
role: String? = null,
location: PromptContributionLocation = PromptContributionLocation.BEGINNING
): PromptContributor
@JvmStatic
@JvmOverloads
fun dynamic(
contributionMaker: () -> String,
role: String? = null,
location: PromptContributionLocation = PromptContributionLocation.BEGINNING
): PromptContributor
}
}Basic Usage:
// Fixed content
val fixedContributor = PromptContributor.fixed(
content = "You are a helpful AI assistant.",
role = "system_prompt",
location = PromptContributionLocation.BEGINNING
)
// Dynamic content
val dynamicContributor = PromptContributor.dynamic(
contributionMaker = {
val currentTime = LocalDateTime.now()
"Current time: $currentTime"
},
role = "timestamp",
location = PromptContributionLocation.BEGINNING
)
// Use contributors
val contribution1 = fixedContributor.promptContribution()
println(contribution1.content)Data class representing a single prompt contribution.
data class PromptContribution(
val content: String,
val location: PromptContributionLocation,
val role: String?
) {
companion object {
const val KNOWLEDGE_CUTOFF_ROLE = "knowledge_cutoff"
const val CURRENT_DATE_ROLE = "current_date"
}
}Where contributions are inserted in the prompt.
enum class PromptContributionLocation {
BEGINNING,
END
}Usage:
// Beginning contributions (system instructions)
val systemPrompt = PromptContributor.fixed(
"You are an expert programmer.",
"system",
PromptContributionLocation.BEGINNING
)
// End contributions (formatting instructions)
val formatPrompt = PromptContributor.fixed(
"Respond in JSON format.",
"format",
PromptContributionLocation.END
)Base interface for prompt-related elements.
interface PromptElement {
val role: String?
val promptContributionLocation: PromptContributionLocation
}Interface for objects that consume prompt contributors.
interface PromptContributorConsumer {
val promptContributors: List<PromptContributor>
}Usage:
class MyPromptBuilder : PromptContributorConsumer {
override val promptContributors = listOf(
CurrentDate(),
PromptContributor.fixed(
"Be helpful and concise.",
"instruction",
PromptContributionLocation.BEGINNING
)
)
fun buildPrompt(userMessage: String): String {
val beginningContribs = promptContributors
.filter { it.promptContributionLocation == PromptContributionLocation.BEGINNING }
.joinToString("\n") { it.contribution() }
val endContribs = promptContributors
.filter { it.promptContributionLocation == PromptContributionLocation.END }
.joinToString("\n") { it.contribution() }
return """
$beginningContribs
$userMessage
$endContribs
""".trimIndent()
}
}Contributes current date to prompts.
class CurrentDate(
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
) : PromptContributor {
override val role: String = PromptContribution.CURRENT_DATE_ROLE
override val promptContributionLocation: PromptContributionLocation =
PromptContributionLocation.BEGINNING
}Usage:
import java.time.format.DateTimeFormatter
// Default format
val currentDate = CurrentDate()
val contribution = currentDate.promptContribution()
println(contribution.content) // "Current date: 2026-02-06\n"
// Custom format
val customDate = CurrentDate(
DateTimeFormatter.ofPattern("MMMM dd, yyyy")
)
val customContribution = customDate.promptContribution()
println(customContribution.content) // "Current date: February 06, 2026\n"Contributes knowledge cutoff date information.
class KnowledgeCutoffDate(
val date: LocalDate,
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM")
) : PromptContributor {
override val role: String = PromptContribution.KNOWLEDGE_CUTOFF_ROLE
override val promptContributionLocation: PromptContributionLocation =
PromptContributionLocation.BEGINNING
}Usage:
import java.time.LocalDate
import java.time.format.DateTimeFormatter
// Default format
val cutoff = KnowledgeCutoffDate(
date = LocalDate.of(2023, 4, 1)
)
val contribution = cutoff.promptContribution()
println(contribution.content) // "Knowledge cutoff: 2023-04\n"
// Custom format
val customCutoff = KnowledgeCutoffDate(
date = LocalDate.of(2023, 4, 1),
formatter = DateTimeFormatter.ofPattern("MMMM yyyy")
)Create custom contributors by implementing the interface.
class TemperatureContributor(
private val location: String
) : PromptContributor {
override val role = "weather"
override val promptContributionLocation = PromptContributionLocation.BEGINNING
override fun contribution(): String {
val temp = fetchCurrentTemperature(location)
return "Current temperature in $location: ${temp}°C"
}
}
val weatherContributor = TemperatureContributor("New York")Assemble prompts from contributors.
fun assemblePrompt(
contributors: List<PromptContributor>,
userMessage: String
): String {
val beginning = contributors
.filter { it.promptContributionLocation == PromptContributionLocation.BEGINNING }
.map { it.promptContribution() }
.sortedBy { it.role }
.joinToString("\n\n") { it.content }
val end = contributors
.filter { it.promptContributionLocation == PromptContributionLocation.END }
.map { it.promptContribution() }
.sortedBy { it.role }
.joinToString("\n\n") { it.content }
return buildString {
if (beginning.isNotEmpty()) {
appendLine(beginning)
appendLine()
}
appendLine(userMessage)
if (end.isNotEmpty()) {
appendLine()
appendLine(end)
}
}.trim()
}
// Usage
val contributors = listOf(
CurrentDate(),
KnowledgeCutoffDate(LocalDate.of(2023, 4, 1)),
PromptContributor.fixed(
"Respond in a professional tone.",
"tone",
PromptContributionLocation.BEGINNING
),
PromptContributor.fixed(
"Format your response as JSON.",
"format",
PromptContributionLocation.END
)
)
val prompt = assemblePrompt(contributors, "What is the weather today?")import org.springframework.stereotype.Component
@Component
class SystemPromptContributor : PromptContributor {
override val role = "system"
override val promptContributionLocation = PromptContributionLocation.BEGINNING
override fun contribution(): String {
return "You are a helpful AI assistant specialized in programming."
}
}
@Service
class PromptService(
private val contributors: List<PromptContributor>
) {
fun buildPrompt(userInput: String): String {
return assemblePrompt(contributors, userInput)
}
}tessl i tessl/maven-com-embabel-agent--embabel-agent-common@0.3.1