Overview
MoFA provides native Java bindings generated through Mozilla UniFFI. These bindings offer full access to MoFA’s Rust core with idiomatic Java APIs, Maven/Gradle integration, and comprehensive error handling.Installation
- Maven
- Gradle
- From Source
Add the following to your
pom.xml:<dependencies>
<dependency>
<groupId>org.mofa</groupId>
<artifactId>mofa-sdk</artifactId>
<version>0.1.0</version>
</dependency>
</dependencies>
Add to your Or for Gradle Kotlin DSL:
build.gradle:dependencies {
implementation 'org.mofa:mofa-sdk:0.1.0'
}
dependencies {
implementation("org.mofa:mofa-sdk:0.1.0")
}
# Install UniFFI bindgen for Java
cargo install uniffi-bindgen-java
# Build FFI library
cd mofa
cargo build --release --features uniffi -p mofa-ffi
# Generate Java bindings
cd crates/mofa-ffi
./generate-bindings.sh java
# Build with Maven
cd bindings/java
mvn clean install
Quick Start
Basic LLM Agent
package com.example;
import com.mofa.*;
import java.util.List;
public class BasicAgent {
public static void main(String[] args) throws MoFaError {
// Set your API key
String apiKey = System.getenv("OPENAI_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("Error: OPENAI_API_KEY not set");
System.exit(1);
}
// Create an agent using the builder pattern
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setId("my-agent");
builder = builder.setName("Java Agent");
builder = builder.setSystemPrompt("You are a helpful assistant.");
builder = builder.setTemperature(0.7f);
builder = builder.setMaxTokens(1000);
builder = builder.setOpenaiProvider(
apiKey,
System.getenv("OPENAI_BASE_URL"),
"gpt-3.5-turbo"
);
LLMAgent agent = builder.build();
// Simple Q&A (no context retention)
String answer = agent.ask("What is Java?");
System.out.println("Answer: " + answer);
// Multi-turn chat (with context)
agent.chat("My favorite language is Java.");
String response = agent.chat("What did I just tell you?");
System.out.println("Response: " + response);
// Get conversation history
List<ChatMessage> history = agent.getHistory();
System.out.println("Total messages: " + history.size());
// Clear history
agent.clearHistory();
}
}
API Reference
UniFFI Namespace
Get the MoFA SDK version string.
Check if Dora-rs distributed runtime is available.
Create a new LLM agent builder.
LLMAgentBuilder
Set the agent ID. If not set, a UUID will be generated.
Set the agent name for display purposes.
Set the system prompt that defines agent behavior.
Set the LLM temperature (0.0 to 1.0). Higher values produce more random outputs.
Set the maximum number of tokens to generate.
Set the initial session ID for conversation tracking.
Set the user ID for multi-tenant scenarios.
Set the tenant ID for multi-tenant isolation.
Set the sliding context window size (in conversation rounds).
Configure the OpenAI provider.
baseUrl and model can be null for defaults.Build the agent. Throws
MoFaError if configuration is invalid.LLMAgent
Get the agent ID.
Get the agent name.
Simple Q&A without context retention. Each call is independent.
Multi-turn chat with context retention. Maintains conversation history.
Clear the conversation history.
Get the full conversation history as a list of messages.
Get structured output from the last execution (tools used, token usage, etc.).
Examples
Example 1: Multi-Provider Support
import com.mofa.*;
public class OpenAIExample {
public static void main(String[] args) throws MoFaError {
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setName("OpenAI Agent");
builder = builder.setOpenaiProvider(
System.getenv("OPENAI_API_KEY"),
null, // Use default base URL
"gpt-4"
);
LLMAgent agent = builder.build();
String response = agent.ask("What is Java?");
System.out.println(response);
}
}
Example 2: Session Management
import com.mofa.*;
import java.util.List;
public class SessionExample {
public static void main(String[] args) throws MoFaError {
// Create in-memory session manager
SessionManager manager = SessionManager.newInMemory();
// Get or create a session
Session session = manager.getOrCreate("user-123");
// Add messages
session.addMessage("user", "Hello!");
session.addMessage("assistant", "Hi there! How can I help?");
session.addMessage("user", "What's the weather like?");
// Retrieve history
List<SessionMessageInfo> history = session.getHistory(10);
System.out.println("Conversation history:");
for (SessionMessageInfo msg : history) {
System.out.println(msg.getRole() + ": " + msg.getContent());
}
// Store metadata
session.setMetadata("user_name", "\"Alice\""); // JSON string
session.setMetadata("preferences", "{\"theme\": \"dark\"}");
// Retrieve metadata
String userName = session.getMetadata("user_name");
System.out.println("User name: " + userName);
// Save session
manager.saveSession(session);
// List all sessions
List<String> allSessions = manager.listSessions();
System.out.println("Total sessions: " + allSessions.size());
// Delete session
boolean deleted = manager.deleteSession("user-123");
System.out.println("Session deleted: " + deleted);
}
}
Example 3: File-Backed Sessions
import com.mofa.*;
import java.io.File;
public class PersistentSessionExample {
public static void main(String[] args) throws MoFaError {
// Create workspace directory
String workspace = System.getProperty("user.home") + "/.mofa/sessions";
new File(workspace).mkdirs();
// Create file-backed session manager
SessionManager manager = SessionManager.newWithStorage(workspace);
// Sessions are automatically persisted to JSONL files
Session session = manager.getOrCreate("conversation-1");
session.addMessage("user", "Remember this: my dog's name is Max.");
// Save explicitly
manager.saveSession(session);
System.out.println("Session saved to: " + workspace);
// Sessions persist across restarts
SessionManager manager2 = SessionManager.newWithStorage(workspace);
Session session2 = manager2.getSession("conversation-1");
if (session2 != null) {
List<SessionMessageInfo> history = session2.getHistory(100);
System.out.println("Loaded " + history.size() + " messages from disk");
}
}
}
Example 4: Custom Tool Registration
import com.mofa.*;
import org.json.JSONObject;
public class ToolExample {
// Define a custom tool
static class CalculatorTool implements FfiToolCallback {
@Override
public String name() {
return "calculator";
}
@Override
public String description() {
return "Perform basic arithmetic operations";
}
@Override
public String parametersSchemaJson() {
return new JSONObject()
.put("type", "object")
.put("properties", new JSONObject()
.put("operation", new JSONObject()
.put("type", "string")
.put("enum", new String[]{"add", "subtract", "multiply", "divide"})
)
.put("a", new JSONObject().put("type", "number"))
.put("b", new JSONObject().put("type", "number"))
)
.put("required", new String[]{"operation", "a", "b"})
.toString();
}
@Override
public FfiToolResult execute(String argumentsJson) {
try {
JSONObject args = new JSONObject(argumentsJson);
String op = args.getString("operation");
double a = args.getDouble("a");
double b = args.getDouble("b");
double result;
switch (op) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b == 0) {
return new FfiToolResult(
false,
"null",
"Division by zero"
);
}
result = a / b;
break;
default:
return new FfiToolResult(
false,
"null",
"Unknown operation: " + op
);
}
return new FfiToolResult(
true,
new JSONObject().put("result", result).toString(),
null
);
} catch (Exception e) {
return new FfiToolResult(
false,
"null",
e.getMessage()
);
}
}
}
public static void main(String[] args) throws MoFaError {
// Create registry and register tool
ToolRegistry registry = new ToolRegistry();
registry.registerTool(new CalculatorTool());
// List registered tools
System.out.println("Registered tools:");
for (ToolInfo tool : registry.listTools()) {
System.out.println(" - " + tool.getName() + ": " + tool.getDescription());
}
// Execute the tool
FfiToolResult result = registry.executeTool(
"calculator",
new JSONObject()
.put("operation", "add")
.put("a", 3)
.put("b", 7)
.toString()
);
System.out.println("Success: " + result.getSuccess());
System.out.println("Output: " + result.getOutputJson());
}
}
Example 5: Conversation History
import com.mofa.*;
import java.util.List;
public class HistoryExample {
public static void main(String[] args) throws MoFaError {
String apiKey = System.getenv("OPENAI_API_KEY");
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setName("History Agent");
builder = builder.setOpenaiProvider(apiKey, null, "gpt-3.5-turbo");
LLMAgent agent = builder.build();
// Have a conversation
agent.chat("My name is Alice.");
agent.chat("I'm learning about AI agents.");
agent.chat("What's my name again?");
// Get full history
List<ChatMessage> history = agent.getHistory();
System.out.println("Total messages: " + history.size());
for (int i = 0; i < history.size(); i++) {
ChatMessage msg = history.get(i);
String preview = msg.getContent().length() > 50
? msg.getContent().substring(0, 50) + "..."
: msg.getContent();
System.out.println("[" + (i + 1) + "] " + msg.getRole().name() + ": " + preview);
}
// Clear history
System.out.println("\nClearing history...");
agent.clearHistory();
history = agent.getHistory();
System.out.println("Messages after clear: " + history.size());
}
}
Error Handling
Exception Types
import com.mofa.MoFaError;
public class ErrorHandlingExample {
public static void main(String[] args) {
try {
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setOpenaiProvider(
System.getenv("OPENAI_API_KEY"),
null,
"gpt-3.5-turbo"
);
LLMAgent agent = builder.build();
String response = agent.ask("Hello!");
System.out.println(response);
} catch (MoFaError e) {
String message = e.getMessage();
if (message.contains("ConfigError")) {
System.err.println("Configuration error: " + e.getMessage());
} else if (message.contains("RuntimeError")) {
System.err.println("Runtime error: " + e.getMessage());
} else if (message.contains("LLMError")) {
System.err.println("LLM provider error: " + e.getMessage());
} else if (message.contains("IoError")) {
System.err.println("I/O error: " + e.getMessage());
} else if (message.contains("InvalidArgument")) {
System.err.println("Invalid argument: " + e.getMessage());
} else if (message.contains("ToolError")) {
System.err.println("Tool execution error: " + e.getMessage());
} else if (message.contains("SessionError")) {
System.err.println("Session management error: " + e.getMessage());
} else {
System.err.println("Unknown error: " + e.getMessage());
}
e.printStackTrace();
System.exit(1);
}
}
}
Graceful Degradation
import com.mofa.*;
public class FallbackExample {
public static LLMAgent createAgentWithFallback() throws RuntimeException {
// Try OpenAI first
String openaiKey = System.getenv("OPENAI_API_KEY");
if (openaiKey != null && !openaiKey.isEmpty()) {
try {
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setOpenaiProvider(openaiKey, null, "gpt-3.5-turbo");
return builder.build();
} catch (MoFaError e) {
System.err.println("OpenAI unavailable: " + e.getMessage());
}
}
// Fallback to Ollama
try {
LLMConfig config = new LLMConfig(
LLMProviderType.OLLAMA,
"llama2",
null,
"http://localhost:11434",
null,
null,
null,
null
);
return LLMAgent.fromConfig(config, "fallback", "Fallback Agent");
} catch (MoFaError e) {
System.err.println("Ollama unavailable: " + e.getMessage());
throw new RuntimeException("No LLM provider available");
}
}
public static void main(String[] args) {
LLMAgent agent = createAgentWithFallback();
// Use agent...
}
}
Best Practices
1. Environment Variables
public class ConfigExample {
public static void main(String[] args) {
// Load API key from environment
String apiKey = System.getenv("OPENAI_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("Error: OPENAI_API_KEY not set");
System.err.println("Set it with: export OPENAI_API_KEY=your-key");
System.exit(1);
}
// Use the key
// ...
}
}
2. Resource Management
public class ResourceExample {
public static void main(String[] args) throws MoFaError {
LLMAgent agent = null;
try {
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setOpenaiProvider(
System.getenv("OPENAI_API_KEY"),
null,
"gpt-3.5-turbo"
);
agent = builder.build();
String response = agent.ask("Hello!");
System.out.println(response);
} finally {
// UniFFI handles cleanup automatically via GC
// No manual resource management needed
}
}
}
3. Context Window Management
public class ContextExample {
public static void main(String[] args) throws MoFaError {
LLMAgentBuilder builder = UniFFI.INSTANCE.newLlmAgentBuilder();
builder = builder.setContextWindowSize(10); // Keep last 10 rounds
builder = builder.setOpenaiProvider(
System.getenv("OPENAI_API_KEY"),
null,
"gpt-3.5-turbo"
);
LLMAgent agent = builder.build();
// Agent will automatically maintain sliding window
}
}
Maven Configuration
Complete pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mofa-java-example</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- MoFA SDK -->
<dependency>
<groupId>org.mofa</groupId>
<artifactId>mofa-sdk</artifactId>
<version>0.1.0</version>
</dependency>
<!-- JSON processing (for tools) -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230227</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.example.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Troubleshooting
Library Not Found
# If you get: UnsatisfiedLinkError: no mofa_ffi in java.library.path
# Make sure the shared library is built and accessible
cd mofa
cargo build --release --features uniffi -p mofa-ffi
# Set library path when running
java -Djava.library.path=/path/to/mofa/target/release -jar app.jar
API Key Issues
// Verify API key is set
String apiKey = System.getenv("OPENAI_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.err.println("Error: OPENAI_API_KEY not set");
System.err.println("Set it with: export OPENAI_API_KEY=your-key");
System.exit(1);
}
Next Steps
Kotlin Bindings
Use MoFA with Kotlin and coroutines
Python Bindings
Python integration guide
Examples
Browse full Java examples
API Reference
Complete API documentation