DEV Community

Cover image for Introducing MobileGraph: An Open-Source AI Application Framework for Kotlin Multiplatform
Bharat Hazarika
Bharat Hazarika

Posted on Originally published at Medium AI-assisted

Introducing MobileGraph: An Open-Source AI Application Framework for Kotlin Multiplatform

MobileGraph is an open-source Kotlin Multiplatform SDK built strictly for the realities of Android and iOS edge devices. It allows you to orchestrate non-deterministic AI tasks — from streaming chatbots to autonomous ReAct agents — with absolute deterministic lifecycle control.

This is Part 1 (Introduction) of a series on building AI-powered mobile applications with MobileGraph.

Think of it as the architectural principles of LangGraph, re-engineered for mobile lifecycles.

It’s published on Maven Central, licensed under Apache 2.0, and designed to bind seamlessly to Jetpack Compose and SwiftUI state structures.

A Streaming Chat in 4 Lines

Let’s cut to the code. No callbacks, no listeners — just native Kotlin Flow for structured concurrency and cooperative cancellation.

val session = MobileGraph.instance.createSession()
// Safely collect in your ViewModel / Compose UI
session.stream("Plan a 3-day trip to Tokyo").collect { chunk ->
    print(chunk.text)
}
Enter fullscreen mode Exit fullscreen mode

That’s it. Session management, conversation history, provider communication — all handled for you.

What Can You Build?

MobileGraph isn’t just a chat wrapper. It’s a full AI application framework with below major capability areas:

💬 Multi-Provider Chat

Register multiple AI providers- OpenAI, Google Gemini, Anthropic Claude, DeepSeek, OpenRouter — all through a unified API. Your prompts, tools, and agents work identically across providers. You can swap between models based on prompt compexity or your requirement.

MobileGraph.initialize {
    withModels {
        openai(apiKey = "sk-...", name="gpt-4o-mini") { isDefault = true }
        gemini(apiKey = "...",name="gemini-2.5-flash-lite")
        claude(apiKey = "...",name="claude-3-5-sonnet-20241022")
    }
}
Enter fullscreen mode Exit fullscreen mode

Want to use a local Ollama instance? Just point to a custom endpoint:

val localModel = OpenAIChatModel(
    name = "llama3",
    apiKey = "ollama",
    baseUrl = "http://10.0.2.2:11434/v1"
)
Enter fullscreen mode Exit fullscreen mode

Developers are free to implement the ChatModel interface and can point to their own LLM provider.

📄 On-Device RAG

Load documents, split them into chunks, embed them, and search — all on-device:

// define vector embedding model
val embeddingModel = MediaPipeEmbeddingModel({ context })
// DB driver
val driver = AndroidSqliteDriver(
                schema = VectorDatabase.Schema,
                context = context,
                name = "mobilegraph_vector.db")

// create vector store
val vectorStore = SQLiteVectorStore(driver, embeddingModel)
Enter fullscreen mode Exit fullscreen mode
MobileGraph.knowledge.ingest {
    docId("manual-001")
    docName("Product User Guide")
    loader(PdfDocumentLoader(pdfExtractor))
    splitter(RecursiveTextSplitter(chunkSize = 500, chunkOverlap = 100))
    vectorStore(SQLiteVectorStore(driver, embeddingModel))
}
Enter fullscreen mode Exit fullscreen mode

MobileGraph ships with loaders for PDF, Markdown, HTML, JSON, CSV, and websites. Text splitters include RecursiveTextSplitter, TokenTextSplitter, and CharacterTextSplitter. You can implement custom loaders for any format with a single interface.

Retrieval is the act of finding relevant context from your knowledge base to answer a user’s question. MobileGraph supports several built-in retrievers to balance speed and accuracy. Retrival techniques inlude VectorStoreRetriever BM25Retriever HybridRetriever MultiQueryRetriever

RAG Pipeline: The RagPipeline orchestrates the entire process: Search -> Augment -> Generate.

// Basic setup
val vectorRetriever = VectorStoreRetriever(vectorStore)

MobileGraph.initialize() {
    // ... model and vector store config ...

    withRetrievers {
        register("default", vectorRetriever, isDefault = true)
    }

    withRag {
        retriever(vectorRetriever)
        chatModel(myChatModel)
        // contextBuilder(customBuilder) // Optional: for custom prompts/guardrails
    }
}

// Execute a query
// Standard execution (returns model response)
val result = MobileGraph.rag.pipeline().execute("What is the battery life?")
Enter fullscreen mode Exit fullscreen mode

🤖 Autonomous Tool-Calling Agents

Give your AI “hands” — define Kotlin functions it can autonomously decide to call:

val weatherTool = object : Tool<String, String> {
    override val metadata = ToolMetadata(
        name = "get_weather",
        description = "Gets the current weather for a specific city."
    )
    override suspend fun invoke(input: String, context: ExecutionContext): String {
        return "The weather in $input is 25°C and Sunny."
    }
}
Enter fullscreen mode Exit fullscreen mode
MobileGraph.initialize {
    withTools { register(weatherTool) }
}
Enter fullscreen mode Exit fullscreen mode

The LLM autonomously decides when to call which tool. MobileGraph even includes Semantic Tool Selection — using vector embeddings to pick only the most relevant tools, so you’re not wasting tokens sending 100 tool definitions every call.

🔀 Multi-Agent Workflows

For complex tasks, one agent isn’t enough. MobileGraph’s stateGraph DSL lets you orchestrate teams of agents:

val workflow = stateGraph {
    start("researcher")
    node(AgentNode("researcher", researchAgent, runtime))
    node(AgentNode("writer", writerAgent, runtime))
    node(EndNode("done"))
Enter fullscreen mode Exit fullscreen mode
    edge("researcher", "writer")
    edge("writer", "done")
}
val result = agentRuntime.run(workflow, initialState)
Enter fullscreen mode Exit fullscreen mode

This supports:

- Fan-Out / Fan-In — run agents in parallel, merge at a sync point

- Human-in-the-Loop — pause for user approval, then continue

- Hierarchical Sub-Agents — manager agents that own internal sub-workflows

- Conditional Branching — route based on state variables

💾 Durable Execution (Survives Process Death)

This is MobileGraph’s superpower for mobile. The framework automatically checkpoints state after every graph node execution. If the OS kills your app — or the user navigates away and comes back — you resume exactly where you left off:

// Resume after process death or human-in-the-loop approval
val result = agentRuntime.resume(
    graph = workflow,
    checkpointId = "trace_node_timestamp",
    nodeId = "review",
    input = mapOf("approved" to true)
)
Enter fullscreen mode Exit fullscreen mode

This is powered by immutable state — every transition creates a new state snapshot (State S → Node → State S'), making the entire workflow replayable, debuggable, and recoverable.

🔧 Middleware Pipeline

Cross-cutting concerns live in composable middleware — just like HTTP middleware in Ktor:

middleware {
    +LoggingMiddleware()            // Log every prompt & response
    +RetryMiddleware(maxRetries = 3) // Auto-retry on network failures
    +ChatMemoryMiddleware()         // Auto-manage conversation history
}
Enter fullscreen mode Exit fullscreen mode

🌐 Connected Ecosystem: MCP & A2A Protocols

MobileGraph breaks out of single-app boundaries by supporting the industry’s two leading open communication standards.

  • Model Context Protocol (MCP) - Connect your agent to external tools on servers, lambdas, or local hardware over JSON-RPC 2.0 with auto-discovery and auto-registration into ToolRegistry.
  • Agent-to-Agent (A2A) Protocol — Delegate complex graph subtasks from on-device models to specialized cloud agents via A2ANode, featuring Agent Card discovery, SSE artifact streaming, and native human-in-the-loop task resumption.

The Architecture in 30 Seconds

MobileGraph is organized in three layers, with dependencies flowing strictly downward:

📱 Application Layer (Your App)
       │
🔷 MobileGraph SDK (Entry Point & DSL)
       │
  ┌────┴────────────────────────────┐
  │                                 │
🧠 Intelligence Layer          📚 Knowledge Layer
  • Models & Providers           • Documents & Loaders
  • Prompts DSL                  • Embeddings
  • Parsers                      • Vector Stores
  • Tools & Agents               • Retrieval
  • Graph Runtime
  │                                 │
  └────────────┬────────────────────┘
               │
    ⚙️ Foundation Layer
       • Core Runtime
       • State Management
       • Checkpointing
Enter fullscreen mode Exit fullscreen mode

Every module is independent. You only include what you need — a chat-only app doesn’t pull in the RAG or Agent modules.

Design Decisions That Make It Mobile-Ready

Three architectural choices set MobileGraph apart from server-side frameworks:

1. Capability-Based Abstraction, Not instanceof Checks

// ❌ Coupled to a specific provider
if (model is OpenAIChatModel) { /* use vision */ }
Enter fullscreen mode Exit fullscreen mode
// ✅ Query capabilities generically
if (model.supports(Capability.Vision)) { /* use vision */ }
Enter fullscreen mode Exit fullscreen mode

2. Deterministic Runtime, Probabilistic AI

AI outputs are inherently unpredictable. But the framework that runs them must be deterministic. Given a state and a node, MobileGraph always produces predictable execution behavior. This makes workflows testable, replayable, and debuggable.

3. Flow as the Universal Streaming Primitive

No callbacks. No listeners. No RxJava. Every streaming operation uses Kotlin Flow, giving you structured concurrency, cooperative cancellation, and seamless Compose integration for free.

Getting Started

1. Add the Dependency

repositories {
    mavenCentral()
}
Enter fullscreen mode Exit fullscreen mode

2. Initialize

dependencies {
    implementation("io.github.mobilegraph:mobilegraph-sdk:0.5.0")
    implementation("io.ktor:ktor-client-okhttp:3.5.1") // HTTP engine
}
Enter fullscreen mode Exit fullscreen mode

3. Chat

lifecycleScope.launch {
    MobileGraph.initialize {
        withModels {
            openai(apiKey = "sk-...") { isDefault = true }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
val session = MobileGraph.instance.createSession()
session.stream("Hello, MobileGraph!").collect { chunk ->
    print(chunk.text)
}
Enter fullscreen mode Exit fullscreen mode

Requirements: Kotlin 2.0+, Gradle 8.4+, JDK 17+, Android Min SDK 24+.

Try It Yourself

MobileGraph is open source and available today.

  • 🌟 Star it on GitHub: https://github.com/user/MobileGraphSDK

  • 📦 Maven Central: io.github.mobilegraph:mobilegraph-sdk:0.5.0-alpha

  • 📖 Documentation: Full guides for setup, RAG, agents, tools, MCP, A2A and multi-model orchestration

  • 📱 Sample App: Clone the repo and run the :androidApp target — it includes demos for chat, RAG, tool agents, HITL, fan-out, A2A, MCP and hierarchical agents

I’d love to hear your feedback. Your collboration for the project is well appreciated. What AI capabilities do you wish existed in your mobile app?

Next up: a deep-dive into the architecture — how immutable state, checkpointing, and capability-based abstraction make AI workflows survive process death.

Tags: Kotlin, Android, iOS, Artificial Intelligence, Mobile Development, Kotlin Multiplatform, LangChain, RAG, AI Agents, Open Source, MobileGraph

Top comments (0)