
Graph-based typed workflows and AI agents modeled as suspendable step functions, enabling tool calls, streaming outputs, checkpoints, human approvals, parallel branches, and compile-time graph validation.
AI agents and workflows for Kotlin, as small typed graphs. You write each step as a suspend
function on a data class of your own, and connect the steps with arrows. Telar runs the graph on
every Kotlin platform, from a server to Android, iOS and the browser, and takes care of loops,
parallel steps, live streaming, save points, and pausing until a person approves.
Try it in your browser: Pixel Pizza is a small game in which every stage runs a Telar graph, from two nodes in a row to a full agent workflow. No account and no API key needed.
Documentation · Quick start · Tutorial · Guides · API reference
A model that calls your functions until it can answer. toolAgent is that loop, ready-made:
import dev.deeptelar.telar.agent.*
import dev.deeptelar.telar.anthropic.AnthropicChatModel
import io.ktor.client.HttpClient
import kotlinx.serialization.Serializable
@Serializable
data class City(@Description("A city, for example \"Lisbon\"") val name: String)
// A tool: a function the model may call. Its input class becomes the schema the model reads.
val weather = Tool<City>("weather", "Returns today's weather in a city.") { city ->
"Light rain and 17 °C in ${city.name}." // call your real service here
}
suspend fun main() {
val model = AnthropicChatModel(HttpClient(), apiKey = System.getenv("ANTHROPIC_API_KEY"), model = "claude-opus-5-5")
val agent = toolAgent(model, tools = listOf(weather), system = "You are a travel assistant.")
// The agent is a graph like any other: stream it, save it, pause it before a tool runs.
agent.stream(AgentState("Do I need an umbrella in Lisbon today?")).collect { event ->
event.textDelta?.let { print(it) } // the answer, piece by piece
}
}// build.gradle.kts. `@Serializable` needs the plugin kotlin("plugin.serialization").
implementation("dev.deeptelar:telar-agent:0.1.0-alpha10")
implementation("dev.deeptelar:telar-anthropic:0.1.0-alpha10") // or telar-openai: OpenAI, Gemini, Ollama, Groq, ...
implementation("io.ktor:ktor-client-cio:3.6.0") // any Ktor engineThe same agent with OpenAI, Gemini or a local Ollama model, a person who approves each tool call, and a chat that continues across turns: Agents with tools.
When an agent alone is not enough, draw the workflow yourself. Nodes are steps, then is an arrow,
and a conditional edge picks the next step by looking at the state:
import dev.deeptelar.telar.*
data class Refund(val orderId: String, val amount: Int, val approved: Boolean = false, val status: String = "new")
val refunds = StateGraph<Refund> {
val check = node("check") { it.copy(status = if (it.amount > 1000) "too large" else "checked") }
val pay = node("pay") { it.copy(status = if (it.approved) "paid" else "rejected") }
val decline = node("decline") { it.copy(status = "declined") }
START then check
conditionalEdge(check, targets = setOf(pay, decline)) { if (it.status == "checked") pay else decline }
pay then END
decline then END
}.compile() // a node nothing leads to, or a target that is not declared, fails here
suspend fun main() {
// Stop before money moves. The run is saved after every step.
val config = GraphConfig(threadId = "order-1001", checkpointer = MemoryCheckpointer<Refund>(), interruptBefore = setOf("pay"))
refunds.invoke(Refund("1001", amount = 250), config) // runs "check", then waits before "pay"
val result = refunds.resume(config) { it.copy(approved = true) } // a person said yes
println(result.state.status) // paid
}This one needs only telar-core. With FileCheckpointer in place of MemoryCheckpointer, the run
waits in a file and continues after a restart:
Human-in-the-loop. The
quick start explains a graph line by line.
data class. Each node returns a .copy() of it, so a run is easy to
test, print and save, and parallel branches cannot overwrite each other by accident.compile(). An unknown node, a node nothing leads to, or two
parallel nodes with no rule to merge their results are reported before anything runs.interrupt from inside one when
it finds out it needs a person. The run is saved, and resume continues it hours later, in
another process. history and fork go back to any earlier step.localStorage.suspend functions. Cancellation works
as you expect, and parallel nodes use structured concurrency.ChatModel is one function, so a model of your
own, or a fake one in a test, takes a few lines. Tools are suspend functions of your own or the
tools of any MCP server, and ask returns a @Serializable class in place of text.telar-test tests a graph with an agent without an API key.Choose Telar when the workflow itself is the hard part: several steps, branches that join, people who approve, runs that must survive a restart, and state you want to read as ordinary Kotlin. If you need a stable API today, many ready-made integrations, or runs saved in a database server, Koog, LangGraph4j or LangChain4j are the safer choice. Why Telar compares Telar with Koog, LangGraph4j and LangChain4j.
Telar is an independent project inspired by LangGraph. It is not affiliated with or endorsed by LangChain, Inc.
dependencies {
implementation("dev.deeptelar:telar-core:0.1.0-alpha10") // the graph builder and the engine
implementation("dev.deeptelar:telar-agent:0.1.0-alpha10") // chat models, tools and the tool-calling agent
implementation("dev.deeptelar:telar-anthropic:0.1.0-alpha10") // Claude
implementation("dev.deeptelar:telar-openai:0.1.0-alpha10") // OpenAI, Gemini, Ollama and compatible servers
}The artifacts are on Maven Central and need Kotlin 2.x. There are more modules, for saving runs to files or in a browser, for LangChain4j models and for decision models: Installation lists every module and its targets.
Telar is in alpha. 0.1.0-alpha10 is the latest release. The API may still change before 1.0; the
changelog lists what changes in each version, and the roadmap lists
what is planned.
| I want to | Go to |
|---|---|
| See a complete example with every line explained | Quick start |
| Learn graphs and agents from zero, in eight levels like a game | Tutorial |
| Look up one feature: streaming, approvals, parallel steps, subgraphs, models, tools | Guides |
| See graphs run before I write any code | Pixel Pizza, a game in the browser |
| Run complete programs |
Samples, such as ./gradlew :samples:runQuickStart
|
| Read what a function does | API reference |
| See a complete app | telar-demo, a Compose Multiplatform app for the browser, the desktop and Android |
The pages of the site are the Markdown files in docs/.
Contributions are welcome. The issues labeled good first issue are small and say where to start. CONTRIBUTING.md has the development setup and the three design rules (immutable state, coroutines only, type-safe DSL), and the roadmap lists what is planned.
AI agents and workflows for Kotlin, as small typed graphs. You write each step as a suspend
function on a data class of your own, and connect the steps with arrows. Telar runs the graph on
every Kotlin platform, from a server to Android, iOS and the browser, and takes care of loops,
parallel steps, live streaming, save points, and pausing until a person approves.
Try it in your browser: Pixel Pizza is a small game in which every stage runs a Telar graph, from two nodes in a row to a full agent workflow. No account and no API key needed.
Documentation · Quick start · Tutorial · Guides · API reference
A model that calls your functions until it can answer. toolAgent is that loop, ready-made:
import dev.deeptelar.telar.agent.*
import dev.deeptelar.telar.anthropic.AnthropicChatModel
import io.ktor.client.HttpClient
import kotlinx.serialization.Serializable
@Serializable
data class City(@Description("A city, for example \"Lisbon\"") val name: String)
// A tool: a function the model may call. Its input class becomes the schema the model reads.
val weather = Tool<City>("weather", "Returns today's weather in a city.") { city ->
"Light rain and 17 °C in ${city.name}." // call your real service here
}
suspend fun main() {
val model = AnthropicChatModel(HttpClient(), apiKey = System.getenv("ANTHROPIC_API_KEY"), model = "claude-opus-5-5")
val agent = toolAgent(model, tools = listOf(weather), system = "You are a travel assistant.")
// The agent is a graph like any other: stream it, save it, pause it before a tool runs.
agent.stream(AgentState("Do I need an umbrella in Lisbon today?")).collect { event ->
event.textDelta?.let { print(it) } // the answer, piece by piece
}
}// build.gradle.kts. `@Serializable` needs the plugin kotlin("plugin.serialization").
implementation("dev.deeptelar:telar-agent:0.1.0-alpha10")
implementation("dev.deeptelar:telar-anthropic:0.1.0-alpha10") // or telar-openai: OpenAI, Gemini, Ollama, Groq, ...
implementation("io.ktor:ktor-client-cio:3.6.0") // any Ktor engineThe same agent with OpenAI, Gemini or a local Ollama model, a person who approves each tool call, and a chat that continues across turns: Agents with tools.
When an agent alone is not enough, draw the workflow yourself. Nodes are steps, then is an arrow,
and a conditional edge picks the next step by looking at the state:
import dev.deeptelar.telar.*
data class Refund(val orderId: String, val amount: Int, val approved: Boolean = false, val status: String = "new")
val refunds = StateGraph<Refund> {
val check = node("check") { it.copy(status = if (it.amount > 1000) "too large" else "checked") }
val pay = node("pay") { it.copy(status = if (it.approved) "paid" else "rejected") }
val decline = node("decline") { it.copy(status = "declined") }
START then check
conditionalEdge(check, targets = setOf(pay, decline)) { if (it.status == "checked") pay else decline }
pay then END
decline then END
}.compile() // a node nothing leads to, or a target that is not declared, fails here
suspend fun main() {
// Stop before money moves. The run is saved after every step.
val config = GraphConfig(threadId = "order-1001", checkpointer = MemoryCheckpointer<Refund>(), interruptBefore = setOf("pay"))
refunds.invoke(Refund("1001", amount = 250), config) // runs "check", then waits before "pay"
val result = refunds.resume(config) { it.copy(approved = true) } // a person said yes
println(result.state.status) // paid
}This one needs only telar-core. With FileCheckpointer in place of MemoryCheckpointer, the run
waits in a file and continues after a restart:
Human-in-the-loop. The
quick start explains a graph line by line.
data class. Each node returns a .copy() of it, so a run is easy to
test, print and save, and parallel branches cannot overwrite each other by accident.compile(). An unknown node, a node nothing leads to, or two
parallel nodes with no rule to merge their results are reported before anything runs.interrupt from inside one when
it finds out it needs a person. The run is saved, and resume continues it hours later, in
another process. history and fork go back to any earlier step.localStorage.suspend functions. Cancellation works
as you expect, and parallel nodes use structured concurrency.ChatModel is one function, so a model of your
own, or a fake one in a test, takes a few lines. Tools are suspend functions of your own or the
tools of any MCP server, and ask returns a @Serializable class in place of text.telar-test tests a graph with an agent without an API key.Choose Telar when the workflow itself is the hard part: several steps, branches that join, people who approve, runs that must survive a restart, and state you want to read as ordinary Kotlin. If you need a stable API today, many ready-made integrations, or runs saved in a database server, Koog, LangGraph4j or LangChain4j are the safer choice. Why Telar compares Telar with Koog, LangGraph4j and LangChain4j.
Telar is an independent project inspired by LangGraph. It is not affiliated with or endorsed by LangChain, Inc.
dependencies {
implementation("dev.deeptelar:telar-core:0.1.0-alpha10") // the graph builder and the engine
implementation("dev.deeptelar:telar-agent:0.1.0-alpha10") // chat models, tools and the tool-calling agent
implementation("dev.deeptelar:telar-anthropic:0.1.0-alpha10") // Claude
implementation("dev.deeptelar:telar-openai:0.1.0-alpha10") // OpenAI, Gemini, Ollama and compatible servers
}The artifacts are on Maven Central and need Kotlin 2.x. There are more modules, for saving runs to files or in a browser, for LangChain4j models and for decision models: Installation lists every module and its targets.
Telar is in alpha. 0.1.0-alpha10 is the latest release. The API may still change before 1.0; the
changelog lists what changes in each version, and the roadmap lists
what is planned.
| I want to | Go to |
|---|---|
| See a complete example with every line explained | Quick start |
| Learn graphs and agents from zero, in eight levels like a game | Tutorial |
| Look up one feature: streaming, approvals, parallel steps, subgraphs, models, tools | Guides |
| See graphs run before I write any code | Pixel Pizza, a game in the browser |
| Run complete programs |
Samples, such as ./gradlew :samples:runQuickStart
|
| Read what a function does | API reference |
| See a complete app | telar-demo, a Compose Multiplatform app for the browser, the desktop and Android |
The pages of the site are the Markdown files in docs/.
Contributions are welcome. The issues labeled good first issue are small and say where to start. CONTRIBUTING.md has the development setup and the three design rules (immutable state, coroutines only, type-safe DSL), and the roadmap lists what is planned.