AI integration package for Runt runtime agents, providing OpenAI and Ollama clients with streaming responses, tool calling, and agentic conversation support.
- 🤖 Multiple AI Providers: OpenAI and Ollama support with unified interface
- 🔄 Streaming Responses: Real-time token-by-token streaming with markdown rendering
- 🛠️ Tool Calling: Full support for notebook tools (create_cell, modify_cell, execute_cell)
- 🤖 Agentic Conversations: Multi-iteration conversations with tool execution
- 📦 Dynamic Model Discovery: Runtime-based model discovery with capability detection
- 🌐 Connection Management: Robust connection handling with error recovery
- ⚡ Performance: Efficient streaming and async processing
- 🔧 Configuration: Flexible configuration options
- 🧪 Testing: Comprehensive test suite with 95%+ coverage
# Install as part of Runt
npm install @runt/ai
# Or use with Deno
import { OpenAIClient, RuntOllamaClient } from "jsr:@runt/ai";import { OpenAIClient } from "@runt/ai";
const client = new OpenAIClient({
apiKey: process.env.OPENAI_API_KEY,
});
// Simple conversation
const messages = [
{ role: "user", content: "Explain Python list comprehensions" },
];
await client.generateAgenticResponse(messages, context, {
model: "gpt-4",
temperature: 0.7,
enableTools: false,
});import { RuntOllamaClient } from "@runt/ai";
const client = new RuntOllamaClient({
host: "http://localhost:11434",
});
// Check if ready
const isReady = await client.isReady();
console.log("Client ready:", isReady);
// Simple conversation
await client.generateAgenticResponse(messages, context, {
model: "llama3.1",
temperature: 0.7,
enableTools: false,
});The package supports runtime model discovery for both providers:
import {
discoverAvailableAiModels,
filterModelsByCapabilities,
} from "@runt/ai";
// Discover all available models
const allModels = await discoverAvailableAiModels();
console.log(`Found ${allModels.length} models`);
// Filter by capabilities
const toolCapableModels = filterModelsByCapabilities(allModels, ["tools"]);
const visionModels = filterModelsByCapabilities(allModels, ["vision"]);
// Group by provider
const modelsByProvider = new Map();
for (const model of allModels) {
if (!modelsByProvider.has(model.provider)) {
modelsByProvider.set(model.provider, []);
}
modelsByProvider.get(model.provider).push(model);
}Models are automatically classified with these capabilities:
- completion: Basic text completion
- tools: Function/tool calling support
- vision: Image understanding
- thinking: Chain of thought reasoning
- code: Code generation/understanding
- multimodal: Multiple input types
Both clients implement the same core interface:
interface AiClient {
generateAgenticResponse(
messages: Message[],
context: ExecutionContext,
options: AgenticOptions,
): Promise<void>;
isReady(): Promise<boolean>;
discoverAiModels(): Promise<AiModel[]>;
}interface AgenticOptions {
model?: string;
temperature?: number;
enableTools?: boolean;
maxIterations?: number;
onToolCall?: (toolCall: ToolCall) => Promise<string>;
onIteration?: (iteration: number, messages: Message[]) => Promise<boolean>;
interruptSignal?: AbortSignal;
}Both clients support the same tool calling interface:
await client.generateAgenticResponse(messages, context, {
model: "gpt-4", // or "llama3.1"
enableTools: true,
onToolCall: async (toolCall) => {
console.log(`Tool called: ${toolCall.name}`);
console.log("Arguments:", toolCall.arguments);
// Execute the tool
const result = await executeNotebookTool(toolCall);
return result;
},
});- create_cell: Create new notebook cells
- modify_cell: Edit existing cell content
- execute_cell: Run code cells
The package includes a built-in tool registry:
import { handleToolCallWithResult } from "@runt/ai";
const result = await handleToolCallWithResult(
store,
sessionId,
currentCell,
toolCall,
);const openaiClient = new OpenAIClient({
apiKey: process.env.OPENAI_API_KEY,
baseURL: "https://api.openai.com/v1", // Custom base URL
organization: "org-123", // Organization ID
project: "proj-456", // Project ID
});const ollamaClient = new RuntOllamaClient({
host: "http://localhost:11434", // Default Ollama host
model: "llama3.1", // Default model
headers: {
"Custom-Header": "value",
},
});# OpenAI
export OPENAI_API_KEY=sk-...
export OPENAI_BASE_URL=https://api.openai.com/v1
# Ollama
export OLLAMA_HOST=http://localhost:11434Both clients support real-time streaming:
// Streaming is enabled by default
await client.generateAgenticResponse(messages, context, {
model: "gpt-4",
// Content appears progressively in the execution context
});Comprehensive error handling with contextual messages:
try {
await client.generateAgenticResponse(messages, context, {
model: "nonexistent-model",
});
} catch (error) {
// Error is automatically displayed in context
console.log("Conversation failed:", error);
}Conversations can be interrupted gracefully:
const abortController = new AbortController();
// Start conversation
const promise = client.generateAgenticResponse(messages, context, {
interruptSignal: abortController.signal,
});
// Interrupt after 5 seconds
setTimeout(() => abortController.abort(), 5000);
await promise; // Will stop cleanlyFor local AI with Ollama:
# Install Ollama
curl -fsSL https://ollama.ai/install.sh | sh
# Start Ollama server
ollama serve
# Pull some models
ollama pull llama3.1
ollama pull mistral
ollama pull codellama// Check if model exists
const exists = await client.ensureModelExists("llama3.1");
// Get available models
const models = await client.getAvailableModels();
models.forEach((model) => {
console.log(`${model.name} - ${model.details.parameter_size}`);
});import { OpenAIClient, RuntOllamaClient } from "@runt/ai";
// In your runtime agent
const aiClient = new OpenAIClient(); // or RuntOllamaClient
export async function executeAI(context: ExecutionContext) {
const messages = buildConversationMessages(context);
await aiClient.generateAgenticResponse(messages, context, {
model: "gpt-4",
enableTools: true,
onToolCall: handleToolCall,
});
}Include AI model discovery in your runtime capabilities:
import { discoverAvailableAiModels } from "@runt/ai";
const capabilities = {
runtimeType: "pyodide",
availableAiModels: await discoverAvailableAiModels(),
// ... other capabilities
};| Feature | OpenAI Client | Ollama Client |
|---|---|---|
| Streaming | ✅ | ✅ |
| Tool Calling | ✅ | ✅ |
| Model Management | ❌ | ✅ |
| Local Models | ❌ | ✅ |
| Cost | 💰 | 🆓 |
| Privacy | ☁️ | 🏠 |
| Speed | Fast | Variable |
| Setup | API Key | Local Install |
discoverAvailableAiModels(): Promise<AiModel[]>- Discover all available AI modelsfilterModelsByCapabilities(models: AiModel[], capabilities: string[]): AiModel[]- Filter models by capabilitieshandleToolCallWithResult(store, sessionId, currentCell, toolCall): Promise<string>- Execute tool calls
OpenAIClient- OpenAI API client with streaming and tool supportRuntOllamaClient- Ollama client with local model management
AiModel- Model information with capabilities and metadataModelCapability- Capability types (completion, tools, vision, etc.)ToolCall- Tool call structure with name and argumentsAgenticOptions- Configuration options for agentic responses
-
"API key not found"
export OPENAI_API_KEY=sk-your-key-here -
Rate limiting
- Reduce temperature for faster responses
- Implement exponential backoff
- Use different models for different tasks
-
"Ollama server not available"
ollama serve curl http://localhost:11434/api/tags
-
"Model not found"
ollama pull llama3.1 ollama list
-
Connection refused
export OLLAMA_HOST=http://localhost:11434
# Run all AI tests
deno task test packages/ai/
# Run specific test files
deno task test packages/ai/test/ollama-client.test.ts
deno task test packages/ai/test/streaming-markdown.test.ts
# Run with coverage
deno task test:coverage- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Run the test suite:
deno task ci - Submit a pull request
BSD-3-Clause License - see the main Runt project for details.