CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-api

Fluent DSL and Kotlin DSL for building autonomous agents with planning capabilities on the JVM, featuring annotation-based and programmatic configuration for agentic flows with Spring Boot integration

Overview
Eval results
Files

chat.mddocs/

Chat and Conversation

The chat and conversation interfaces enable building interactive chatbot applications with message history management, multi-turn conversations, and session handling.

Capabilities

Chatbot Interface

Main interface for creating chat sessions.

interface Chatbot {
    /** Create new chat session for user */
    fun createSession(user: User): ChatSession

    /** Get existing session by ID */
    fun getSession(sessionId: String): ChatSession?

    /** List all sessions for user */
    fun listSessions(user: User): List<ChatSession>
}

Basic Chatbot Example:

@Service
class CustomerSupportChatbot(
    private val agentPlatform: AgentPlatform,
    private val supportAgent: Agent
) {

    fun startChat(user: User): ChatSession {
        val chatbot = AgentProcessChatbot.builder()
            .agentPlatform(agentPlatform)
            .agent(supportAgent)
            .user(user)
            .build()

        return chatbot.createSession(user)
    }
}

ChatSession Interface

Represents an ongoing conversation session.

interface ChatSession {
    /** Session identifier */
    val sessionId: String

    /** User participating in session */
    val user: User

    /** Conversation history */
    val conversation: Conversation

    /** Session status */
    val status: SessionStatus

    /** Send message and get response */
    fun sendMessage(message: Message): Message

    /** Send user text and get response */
    fun sendUserMessage(text: String): Message

    /** Close the session */
    fun close()

    /** Get session metadata */
    fun metadata(): Map<String, Any>
}

enum class SessionStatus {
    ACTIVE,
    PAUSED,
    CLOSED,
    ERROR
}

ChatSession Usage:

fun chatWithAgent(session: ChatSession, userInput: String): String {
    // Send user message
    val response = session.sendUserMessage(userInput)

    // Return assistant response
    return response.content
}

fun multiTurnConversation(session: ChatSession) {
    // Turn 1
    session.sendUserMessage("I need help with my order")
    // Agent responds and maintains context

    // Turn 2
    session.sendUserMessage("The order number is #12345")
    // Agent has access to previous messages

    // Turn 3
    val finalResponse = session.sendUserMessage("Can you expedite it?")
    // Agent can reference entire conversation history

    session.close()
}

Conversation Interface

Manages message history.

interface Conversation {
    /** All messages in conversation */
    val messages: List<Message>

    /** Conversation ID */
    val conversationId: String

    /** Add message to conversation */
    fun addMessage(message: Message)

    /** Add multiple messages */
    fun addMessages(messages: List<Message>)

    /** Get messages since timestamp */
    fun getMessagesSince(timestamp: Instant): List<Message>

    /** Get last N messages */
    fun getLastMessages(count: Int): List<Message>

    /** Clear all messages */
    fun clear()
}

Conversation Management:

@Service
class ConversationService {

    fun getConversationHistory(conversation: Conversation): ConversationHistory {
        return ConversationHistory(
            id = conversation.conversationId,
            messageCount = conversation.messages.size,
            messages = conversation.messages.map { MessageSummary(it) },
            startTime = conversation.messages.firstOrNull()?.timestamp,
            lastTime = conversation.messages.lastOrNull()?.timestamp
        )
    }

    fun addContextToConversation(
        conversation: Conversation,
        context: String
    ) {
        conversation.addMessage(
            Message.system("Context: $context")
        )
    }

    fun exportConversation(conversation: Conversation): String {
        return conversation.messages.joinToString("\n\n") { msg ->
            "${msg.role.name}: ${msg.content}"
        }
    }
}

Message Interface

Individual message in conversation.

interface Message {
    /** Message role (user, assistant, system, tool) */
    val role: MessageRole

    /** Message content */
    val content: String

    /** Message timestamp */
    val timestamp: Instant

    /** Message ID */
    val id: String

    companion object {
        fun user(content: String): Message
        fun assistant(content: String): Message
        fun system(content: String): Message
        fun tool(content: String, toolCallId: String): Message
    }
}

enum class MessageRole {
    USER,
    ASSISTANT,
    SYSTEM,
    TOOL
}

interface AssistantMessage : Message {
    /** Tool calls made by assistant */
    val toolCalls: List<ToolCall>?

    /** Stop reason */
    val stopReason: String?
}

Message Creation:

fun createMessages(): List<Message> {
    return listOf(
        Message.system("You are a helpful customer service agent"),
        Message.user("I have a question about my order"),
        Message.assistant("I'd be happy to help with your order. What's your order number?"),
        Message.user("It's #12345"),
        Message.assistant("Let me look that up for you...")
    )
}

ContentPart Interface

Part of multimodal message content.

interface ContentPart {
    val type: ContentPartType
}

enum class ContentPartType {
    TEXT,
    IMAGE,
    DOCUMENT
}

data class TextContentPart(
    override val type: ContentPartType = ContentPartType.TEXT,
    val text: String
) : ContentPart

data class ImageContentPart(
    override val type: ContentPartType = ContentPartType.IMAGE,
    val image: Image
) : ContentPart

MessageBuilders

Utility for building complex messages.

object MessageBuilders {
    fun user(text: String): Message
    fun assistant(text: String): Message
    fun system(text: String): Message

    fun userWithImage(text: String, image: Image): Message
    fun multimodal(role: MessageRole, parts: List<ContentPart>): Message
}

MessageBuilder Example:

fun createMultimodalMessage(): Message {
    val image = Image.fromUrl("https://example.com/product.jpg")

    return MessageBuilders.userWithImage(
        text = "What is this product?",
        image = image
    )
}

fun createComplexMessage(): Message {
    return MessageBuilders.multimodal(
        role = MessageRole.USER,
        parts = listOf(
            TextContentPart(text = "Analyze these images:"),
            ImageContentPart(image = Image.fromUrl("url1")),
            ImageContentPart(image = Image.fromUrl("url2")),
            TextContentPart(text = "What are the differences?")
        )
    )
}

AgentProcessChatbot

Chatbot backed by agent process.

class AgentProcessChatbot private constructor(
    private val agentPlatform: AgentPlatform,
    private val agent: Agent,
    private val defaultUser: User?
) : Chatbot {

    companion object {
        fun builder(): Builder
    }

    interface Builder {
        fun agentPlatform(platform: AgentPlatform): Builder
        fun agent(agent: Agent): Builder
        fun user(user: User): Builder
        fun outputChannel(channel: OutputChannel): Builder
        fun build(): AgentProcessChatbot
    }
}

AgentProcessChatbot Example:

@Configuration
class ChatConfiguration {

    @Bean
    fun customerSupportChatbot(
        agentPlatform: AgentPlatform,
        supportAgent: Agent
    ): Chatbot {
        return AgentProcessChatbot.builder()
            .agentPlatform(agentPlatform)
            .agent(supportAgent)
            .outputChannel(loggingOutputChannel)
            .build()
    }
}

@Service
class ChatService(
    private val chatbot: Chatbot
) {

    fun startConversation(user: User): String {
        val session = chatbot.createSession(user)
        val response = session.sendUserMessage("Hello!")
        return response.content
    }
}

DefaultChatAgentBuilder

Builder for chat agents with default configuration.

class DefaultChatAgentBuilder {
    fun withName(name: String): DefaultChatAgentBuilder
    fun withSystemPrompt(prompt: String): DefaultChatAgentBuilder
    fun withTools(tools: List<Tool>): DefaultChatAgentBuilder
    fun withModel(model: String): DefaultChatAgentBuilder
    fun build(): Agent
}

Chat Agent Builder Example:

fun createChatAgent(): Agent {
    return DefaultChatAgentBuilder()
        .withName("support-chat-agent")
        .withSystemPrompt("""
            You are a helpful customer support agent.
            Be friendly, professional, and concise.
            Always ask for order numbers when discussing orders.
        """)
        .withTools(listOf(
            OrderLookupTool(),
            ShippingTrackingTool(),
            RefundProcessingTool()
        ))
        .withModel(AnthropicModels.CLAUDE_3_5_SONNET)
        .build()
}

ConversationStatus

Track conversation status and metrics.

interface ConversationStatus {
    val totalMessages: Int
    val userMessages: Int
    val assistantMessages: Int
    val toolCalls: Int
    val averageResponseTime: Duration
    val conversationStartTime: Instant
}

Asset Management

Handle attachments and assets in conversations.

interface Asset {
    val id: String
    val type: AssetType
    val name: String
    val url: String?
    val data: ByteArray?
}

enum class AssetType {
    IMAGE,
    DOCUMENT,
    AUDIO,
    VIDEO
}

interface AssetView {
    fun render(): String
}

interface AssetTracker {
    fun trackAsset(asset: Asset): String
    fun getAsset(id: String): Asset?
    fun listAssets(): List<Asset>
}

Asset Example:

@Service
class ConversationAssetService(
    private val assetTracker: AssetTracker
) {

    fun addImageToConversation(
        session: ChatSession,
        imageUrl: String
    ): Message {
        // Track asset
        val asset = Asset.fromUrl(imageUrl, AssetType.IMAGE)
        val assetId = assetTracker.trackAsset(asset)

        // Create message with image
        val image = Image.fromUrl(imageUrl)
        return session.sendMessage(
            MessageBuilders.userWithImage(
                text = "Please analyze this image",
                image = image
            )
        )
    }
}

ConversationFormatter

Format conversations for display or logging.

interface ConversationFormatter {
    fun format(conversation: Conversation): String
    fun formatMessage(message: Message): String
}

class WindowingConversationFormatter(
    private val windowSize: Int
) : ConversationFormatter {
    override fun format(conversation: Conversation): String {
        val recent = conversation.getLastMessages(windowSize)
        return recent.joinToString("\n") { formatMessage(it) }
    }

    override fun formatMessage(message: Message): String {
        val timestamp = message.timestamp.toString()
        return "[$timestamp] ${message.role.name}: ${message.content}"
    }
}

Formatter Example:

@Service
class ConversationLogger(
    private val formatter: ConversationFormatter
) {

    fun logConversation(conversation: Conversation) {
        val formatted = formatter.format(conversation)
        logger.info("Conversation:\n$formatted")
    }

    fun exportConversationMarkdown(conversation: Conversation): String {
        return conversation.messages.joinToString("\n\n") { msg ->
            when (msg.role) {
                MessageRole.USER -> "**User**: ${msg.content}"
                MessageRole.ASSISTANT -> "**Assistant**: ${msg.content}"
                MessageRole.SYSTEM -> "*System: ${msg.content}*"
                MessageRole.TOOL -> "`Tool: ${msg.content}`"
            }
        }
    }
}

In-Memory Conversation Storage

Simple in-memory conversation implementation.

class InMemoryConversation : Conversation {
    private val messageList = mutableListOf<Message>()

    override val conversationId: String = UUID.randomUUID().toString()
    override val messages: List<Message> get() = messageList.toList()

    override fun addMessage(message: Message) {
        messageList.add(message)
    }

    override fun addMessages(messages: List<Message>) {
        messageList.addAll(messages)
    }

    override fun getMessagesSince(timestamp: Instant): List<Message> {
        return messageList.filter { it.timestamp.isAfter(timestamp) }
    }

    override fun getLastMessages(count: Int): List<Message> {
        return messageList.takeLast(count)
    }

    override fun clear() {
        messageList.clear()
    }
}

Tool Messages

Handle tool-related messages in conversations.

object ToolMessages {
    fun toolCall(
        toolName: String,
        arguments: String,
        callId: String
    ): Message

    fun toolResult(
        result: Tool.Result,
        callId: String
    ): Message
}

Personas

Define agent personas for different chat scenarios.

object Personas {
    val HELPFUL_ASSISTANT: String = """
        You are a helpful, friendly assistant.
        Provide clear and concise responses.
    """

    val TECHNICAL_SUPPORT: String = """
        You are a technical support specialist.
        Provide step-by-step troubleshooting guidance.
    """

    val SALES_AGENT: String = """
        You are a knowledgeable sales consultant.
        Help customers find the right products.
    """
}

Types

interface User {
    val id: String
    val displayName: String
    val username: String
    val email: String
}

data class SimpleUser(
    override val id: String,
    override val displayName: String,
    override val username: String,
    override val email: String
) : User

interface ToolCall {
    val id: String
    val name: String
    val arguments: String
}

Install with Tessl CLI

npx tessl i tessl/maven-com-embabel-agent--embabel-agent-api@0.3.0

docs

actions-goals.md

agent-definition.md

builtin-tools.md

chat.md

conditions.md

events.md

human-in-the-loop.md

index.md

invocation.md

io-binding.md

llm-interaction.md

models.md

planning-workflows.md

platform-management.md

runtime-context.md

state-management.md

streaming.md

subagents.md

tools.md

type-system.md

typed-operations.md

validation.md

tile.json