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

typed-operations.mddocs/

Type-Safe Transformations

The TypedOps API provides a high-level, type-safe interface for transforming data between types using agentic systems. It enables declarative transformations where you specify input and output types, and the agent platform determines how to accomplish the transformation.

Capabilities

TypedOps Interface

Core interface for type-safe transformations between arbitrary types.

interface TypedOps {

    /**
     * Transform between these two types if possible.
     */
    fun <I : Any, O> transform(
        input: I,
        outputClass: Class<O>,
        processOptions: ProcessOptions = ProcessOptions(),
    ): O

    /**
     * Return a reusable function that performs this transformation.
     * Validates whether it's possible and include metadata.
     */
    fun <I : Any, O> asFunction(
        outputClass: Class<O>,
    ): AgentFunction<I, O>

    /**
     * Return a reusable function for a specific named agent.
     */
    @Throws(NoSuchAgentException::class)
    fun <I : Any, O> asFunction(
        outputClass: Class<O>,
        agentName: String,
    ): AgentFunction<I, O>

    /**
     * Transform user input into the target type
     */
    fun <O> handleUserInput(
        intent: String,
        outputClass: Class<O>,
        processOptions: ProcessOptions = ProcessOptions(),
    ): O
}

Basic Transformation Example:

import com.embabel.agent.api.common.TypedOps
import com.embabel.agent.api.common.AgentPlatformTypedOps
import com.embabel.agent.core.ProcessOptions
import com.embabel.agent.domain.io.UserInput

val typedOps: TypedOps = AgentPlatformTypedOps(agentPlatform)

// Transform user input to specific type
val result: CustomerOrder = typedOps.transform(
    input = UserInput("I want to order 3 pizzas for delivery"),
    outputClass = CustomerOrder::class.java,
    processOptions = ProcessOptions()
)

data class CustomerOrder(
    val item: String,
    val quantity: Int,
    val deliveryMethod: String
)

Extension Functions

Type-safe extension functions with reified generics for cleaner Kotlin syntax.

/**
 * Transform with reified output type
 */
inline fun <I : Any, reified O : Any> TypedOps.transform(
    input: I,
    processOptions: ProcessOptions = ProcessOptions(),
): O

/**
 * Transform with named parameter order
 */
fun <I : Any, O : Any> TypedOps.transform(
    input: I,
    processOptions: ProcessOptions = ProcessOptions(),
    outputClass: Class<O>,
): O

/**
 * Create function with reified output type
 */
inline fun <I : Any, reified O> TypedOps.asFunction(): AgentFunction<I, O>

Reified Type Transform Example:

import com.embabel.agent.api.common.transform
import com.embabel.agent.domain.io.UserInput

// Use reified generics - no need to pass class explicitly
val summary: DocumentSummary = typedOps.transform<UserInput, DocumentSummary>(
    input = UserInput("Summarize the Q4 financial report"),
    processOptions = ProcessOptions()
)

data class DocumentSummary(
    val title: String,
    val keyPoints: List<String>,
    val recommendations: List<String>
)

AgentFunction Interface

Reusable transformation function that can be invoked multiple times with different inputs and process options.

interface AgentFunction<I, O> : BiFunction<I, ProcessOptions, O> {
    /** The agent scope where this function executes */
    val agentScope: AgentScope

    /** The output type this function produces */
    val outputClass: Class<O>
}

Reusable Function Example:

import com.embabel.agent.api.common.asFunction
import com.embabel.agent.domain.io.UserInput
import com.embabel.agent.domain.library.HasContent
import com.embabel.agent.core.ProcessOptions

// Create reusable function
val contentGenerator: AgentFunction<UserInput, HasContent> =
    typedOps.asFunction()

// Use multiple times with different inputs
val blog1 = contentGenerator.apply(
    UserInput("Write about machine learning"),
    ProcessOptions()
)

val blog2 = contentGenerator.apply(
    UserInput("Write about cloud computing"),
    ProcessOptions()
)

// Can also use as Java BiFunction
val results = listOf(
    UserInput("Topic 1"),
    UserInput("Topic 2"),
    UserInput("Topic 3")
).map { input ->
    contentGenerator.apply(input, ProcessOptions())
}

Named Agent Functions

Create agent functions bound to specific deployed agents by name.

@Throws(NoSuchAgentException::class)
fun <I : Any, O> TypedOps.asFunction(
    outputClass: Class<O>,
    agentName: String,
): AgentFunction<I, O>

class NoSuchAgentException(
    val agentName: String,
    val knownAgents: String,
) : IllegalArgumentException

Named Agent Example:

import com.embabel.agent.domain.io.UserInput
import com.embabel.agent.domain.library.HasContent

// Bind to specific deployed agent
val starFinderFunction = typedOps.asFunction<UserInput, HasContent>(
    outputClass = HasContent::class.java,
    agentName = "TestStarNewsFinder"
)

val horoscope = starFinderFunction.apply(
    UserInput("Lynda is a Scorpio, find some news for her"),
    ProcessOptions()
)

// Error handling for unknown agents
try {
    val unknownFunction = typedOps.asFunction<UserInput, Report>(
        outputClass = Report::class.java,
        agentName = "NonExistentAgent"
    )
} catch (e: NoSuchAgentException) {
    println("Agent '${e.agentName}' not found")
    println("Available agents: ${e.knownAgents}")
}

User Input Handling

Simplified API for transforming natural language user input into structured types.

fun <O> TypedOps.handleUserInput(
    intent: String,
    outputClass: Class<O>,
    processOptions: ProcessOptions = ProcessOptions(),
): O

User Input Example:

// Transform natural language to structured data
val searchQuery = typedOps.handleUserInput(
    intent = "Find all customers who purchased in the last 30 days",
    outputClass = DatabaseQuery::class.java,
    processOptions = ProcessOptions()
)

data class DatabaseQuery(
    val table: String,
    val filters: List<Filter>,
    val timeRange: TimeRange
)

// Another example with booking
val reservation = typedOps.handleUserInput(
    intent = "Book a table for 4 people tomorrow at 7pm",
    outputClass = RestaurantBooking::class.java
)

data class RestaurantBooking(
    val partySize: Int,
    val dateTime: LocalDateTime,
    val specialRequests: String?
)

AgentPlatformTypedOps

Platform-level implementation of TypedOps that uses the agent platform to perform transformations.

class AgentPlatformTypedOps(
    private val agentPlatform: AgentPlatform,
) : TypedOps {

    override fun <I : Any, O> asFunction(
        outputClass: Class<O>,
    ): AgentFunction<I, O>

    override fun <I : Any, O> asFunction(
        outputClass: Class<O>,
        agentName: String,
    ): AgentFunction<I, O>
}

Platform Integration Example:

import com.embabel.agent.api.common.AgentPlatformTypedOps
import com.embabel.agent.core.AgentPlatform

@Service
class DataTransformationService(
    private val agentPlatform: AgentPlatform
) {
    private val typedOps = AgentPlatformTypedOps(agentPlatform)

    fun processUserRequest(request: String): ProcessedResult {
        // Use TypedOps to transform user input
        return typedOps.handleUserInput(
            intent = request,
            outputClass = ProcessedResult::class.java
        )
    }

    fun convertFormat(data: RawData): CleanData {
        // Direct transformation
        return typedOps.transform(
            input = data,
            outputClass = CleanData::class.java
        )
    }
}

Comprehensive Examples

Transforming Collections

Process multiple items through agent transformations.

import com.embabel.agent.api.common.transform
import com.embabel.agent.core.ProcessOptions

data class EmailThread(val messages: List<String>)
data class ThreadSummary(val summary: String, val actionItems: List<String>)

val threads: List<EmailThread> = loadEmailThreads()

// Create reusable function
val summarizer = typedOps.asFunction<EmailThread, ThreadSummary>()

// Transform all threads
val summaries = threads.map { thread ->
    summarizer.apply(thread, ProcessOptions())
}

// With custom process options
val detailedSummaries = threads.map { thread ->
    summarizer.apply(
        thread,
        ProcessOptions(
            maxSteps = 20,
            timeout = 60000
        )
    )
}

Type-Safe Workflow Pipeline

Chain transformations for complex workflows.

import com.embabel.agent.domain.io.UserInput

data class CustomerQuery(val question: String, val customerId: String)
data class EnrichedQuery(val query: CustomerQuery, val customerData: CustomerProfile)
data class Response(val answer: String, val suggestedActions: List<String>)

// Step 1: Parse user input to structured query
val parseFunction = typedOps.asFunction<UserInput, CustomerQuery>()

// Step 2: Enrich with customer data
val enrichFunction = typedOps.asFunction<CustomerQuery, EnrichedQuery>()

// Step 3: Generate response
val responseFunction = typedOps.asFunction<EnrichedQuery, Response>()

// Execute pipeline
fun handleCustomerSupport(userMessage: String): Response {
    val userInput = UserInput(userMessage)

    val query = parseFunction.apply(userInput, ProcessOptions())
    val enriched = enrichFunction.apply(query, ProcessOptions())
    val response = responseFunction.apply(enriched, ProcessOptions())

    return response
}

// Usage
val response = handleCustomerSupport(
    "Hi, I'm customer #12345 and I have a question about my recent order"
)

Dynamic Agent Selection

Choose agents dynamically based on input characteristics.

import com.embabel.agent.domain.io.UserInput
import com.embabel.agent.domain.library.HasContent

class ContentProcessingService(
    private val typedOps: TypedOps
) {
    private val agentMap = mapOf(
        "news" to "NewsAnalysisAgent",
        "technical" to "TechnicalDocAgent",
        "creative" to "CreativeWritingAgent"
    )

    fun processContent(input: String, contentType: String): HasContent {
        val agentName = agentMap[contentType]
            ?: throw IllegalArgumentException("Unknown content type: $contentType")

        val function = typedOps.asFunction<UserInput, HasContent>(
            outputClass = HasContent::class.java,
            agentName = agentName
        )

        return function.apply(
            UserInput(input),
            ProcessOptions()
        )
    }
}

// Usage
val service = ContentProcessingService(typedOps)

val newsArticle = service.processContent(
    "Market analysis for Q1",
    "news"
)

val techDoc = service.processContent(
    "API integration guide",
    "technical"
)

Error Handling and Validation

Handle transformation errors gracefully.

import com.embabel.agent.api.common.NoSuchAgentException
import com.embabel.agent.core.AgentProcessStatusCode

fun safeTransform<I : Any, O>(
    typedOps: TypedOps,
    input: I,
    outputClass: Class<O>,
    agentName: String? = null
): Result<O> {
    return try {
        val function = if (agentName != null) {
            typedOps.asFunction(outputClass, agentName)
        } else {
            typedOps.asFunction(outputClass)
        }

        val result = function.apply(input, ProcessOptions())
        Result.success(result)

    } catch (e: NoSuchAgentException) {
        Result.failure(Exception("Agent not found: ${e.agentName}"))

    } catch (e: Exception) {
        Result.failure(Exception("Transformation failed: ${e.message}", e))
    }
}

// Usage with error handling
val result = safeTransform(
    typedOps = typedOps,
    input = UserInput("Analyze customer sentiment"),
    outputClass = SentimentReport::class.java,
    agentName = "SentimentAnalyzer"
)

result.fold(
    onSuccess = { report ->
        println("Analysis complete: ${report.overallSentiment}")
    },
    onFailure = { error ->
        println("Analysis failed: ${error.message}")
    }
)

Custom Process Options

Fine-tune transformation behavior with process options.

import com.embabel.agent.core.ProcessOptions
import com.embabel.agent.api.common.PlannerType

data class ComplexAnalysis(val findings: List<String>, val confidence: Double)

// Create function with custom options for expensive operations
val analyzerFunction = typedOps.asFunction<DataSet, ComplexAnalysis>()

// Quick analysis with time limit
val quickResult = analyzerFunction.apply(
    dataSet,
    ProcessOptions(
        maxSteps = 5,
        timeout = 30000,  // 30 seconds
        plannerType = PlannerType.UTILITY
    )
)

// Deep analysis with more resources
val deepResult = analyzerFunction.apply(
    dataSet,
    ProcessOptions(
        maxSteps = 50,
        timeout = 300000,  // 5 minutes
        plannerType = PlannerType.GOAL_BASED
    )
)

Integration with Spring Framework

Use TypedOps in Spring-managed services.

import org.springframework.stereotype.Service
import com.embabel.agent.api.common.AgentPlatformTypedOps
import com.embabel.agent.core.AgentPlatform

@Service
class AgentTransformationService(
    agentPlatform: AgentPlatform
) {
    private val typedOps = AgentPlatformTypedOps(agentPlatform)

    fun <I : Any, O> createTransformer(
        outputClass: Class<O>
    ): AgentFunction<I, O> {
        return typedOps.asFunction(outputClass)
    }

    fun <I : Any, O> transform(
        input: I,
        outputClass: Class<O>,
        options: ProcessOptions = ProcessOptions()
    ): O {
        return typedOps.transform(input, outputClass, options)
    }
}

@RestController
@RequestMapping("/api/transform")
class TransformationController(
    private val transformService: AgentTransformationService
) {

    @PostMapping("/analyze")
    fun analyzeText(@RequestBody request: TextRequest): AnalysisResult {
        return transformService.transform(
            input = UserInput(request.text),
            outputClass = AnalysisResult::class.java
        )
    }
}

data class TextRequest(val text: String)
data class AnalysisResult(
    val sentiment: String,
    val topics: List<String>,
    val summary: String
)

Types

import com.embabel.agent.core.AgentScope
import com.embabel.agent.core.ProcessOptions
import com.embabel.agent.domain.io.UserInput
import java.util.function.BiFunction

/**
 * Reusable AgentFunction.
 * Allows use of AgentPlatform as a function from I to O,
 * with different process options.
 */
interface AgentFunction<I, O> : BiFunction<I, ProcessOptions, O> {
    val agentScope: AgentScope
    val outputClass: Class<O>
}

/**
 * Exception thrown when agent cannot be found by name
 */
class NoSuchAgentException(
    val agentName: String,
    val knownAgents: String,
) : IllegalArgumentException

Import Statements

import com.embabel.agent.api.common.TypedOps
import com.embabel.agent.api.common.AgentPlatformTypedOps
import com.embabel.agent.api.common.AgentFunction
import com.embabel.agent.api.common.NoSuchAgentException
import com.embabel.agent.api.common.transform
import com.embabel.agent.api.common.asFunction
import com.embabel.agent.core.AgentPlatform
import com.embabel.agent.core.ProcessOptions
import com.embabel.agent.domain.io.UserInput

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