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)
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-embabel-agent--embabel-agent-common@0.3.0