UnderOcean 1.0.3
dotnet add package UnderOcean --version 1.0.3
NuGet\Install-Package UnderOcean -Version 1.0.3
<PackageReference Include="UnderOcean" Version="1.0.3" />
<PackageVersion Include="UnderOcean" Version="1.0.3" />
<PackageReference Include="UnderOcean" />
paket add UnderOcean --version 1.0.3
#r "nuget: UnderOcean, 1.0.3"
#:package UnderOcean@1.0.3
#addin nuget:?package=UnderOcean&version=1.0.3
#tool nuget:?package=UnderOcean&version=1.0.3
UnderOcean š
A powerful .NET library for AI conversation management with Ollama integration, featuring intelligent Mixture of Experts (MoE) routing, embedding services, and seamless RAG capabilities.
š Features
- š§ Mixture of Experts (MoE) Routing: Intelligent query analysis and automatic routing to optimal processing modes
- š Retrieval-Augmented Generation (RAG): Advanced semantic search with hybrid scoring and smart context injection
- š ļø Function Calling: Extensible tool system with automatic parameter extraction and validation
- š¬ Conversation Management: Stateful conversation handling with context awareness
- ā” Performance Optimization: Built-in monitoring, caching, and fallback strategies
- š Hybrid Processing: Seamlessly combine RAG and tools for complex queries
š¦ Installation
dotnet add package UnderOcean
šļø Architecture
UnderOcean uses a sophisticated MoE architecture that automatically routes conversations based on context analysis. See ARCHITECTURE.md for detailed system design.
User Query ā MoE Router ā Route Selection ā Processing Engine ā Response
ā
[Native|RAG|Tools|Hybrid]
šÆ Quick Start
1. Basic Setup
using Microsoft.Extensions.DependencyInjection;
using UnderOcean.Abstractions;
using UnderOcean.Services;
var services = new ServiceCollection();
services.AddHttpClient();
services.AddSingleton<IOllamaClient>(sp => new OllamaClient(sp.GetRequiredService<HttpClient>()));
services.AddSingleton<IEmbeddingService, EmbeddingService>();
services.AddSingleton<IMoERouter>(sp => new MoERouter(sp.GetRequiredService<IOllamaClient>()));
services.AddSingleton<IChatService, ChatService>();
services.AddSingleton<IConversationManager, ConversationManager>();
var provider = services.BuildServiceProvider();
2. Simple Conversation
var conversationManager = provider.GetRequiredService<IConversationManager>();
var request = new ConversationRequest(
UserMessage: "Hello, how are you?",
SystemPrompt: "You are a helpful assistant.",
ConversationHistory: new List<ChatMessage>(),
KnowledgeBase: new List<EmbeddingContext>(),
AvailableTools: new List<ToolSchema>(),
Mode: ConversationMode.Auto // Let MoE router decide
);
var response = await conversationManager.ProcessAsync(request, CancellationToken.None);
Console.WriteLine($"Response: {response.Response.Message.Content}");
š End-to-End RAG Integration
Step 1: Prepare Knowledge Base from Documents
var embeddingService = provider.GetRequiredService<IEmbeddingService>();
var conversationManager = provider.GetRequiredService<IConversationManager>();
// Load and process documents
var knowledgeBase = new List<EmbeddingContext>();
// Method 1: Load from text files
var filePaths = new[] { "docs/manual.txt", "docs/faq.txt" };
var documentsKB = await conversationManager.LoadKnowledgeBaseAsync(filePaths, CancellationToken.None);
knowledgeBase.AddRange(documentsKB);
// Method 2: Load from structured data (JSON/CSV)
var structuredData = await LoadStructuredDataAsync("data/products.json");
foreach (var item in structuredData)
{
var text = $"Product: {item.Name}. Description: {item.Description}. Price: {item.Price}";
var vector = await embeddingService.EmbedAsync(text, CancellationToken.None);
knowledgeBase.Add(new EmbeddingContext { Text = text, Vector = vector });
}
// Method 3: Load from existing embeddings
var embeddingLines = await File.ReadAllLinesAsync("embeddings.jsonl");
foreach (var line in embeddingLines.Where(l => !string.IsNullOrWhiteSpace(l)))
{
try
{
var jsonObj = JsonSerializer.Deserialize<JsonElement>(line);
if (jsonObj.TryGetProperty("text", out var textElement) &&
jsonObj.TryGetProperty("embedding", out var embeddingElement))
{
var text = textElement.GetString() ?? string.Empty;
var embedding = embeddingElement.EnumerateArray()
.Select(e => (float)e.GetDouble())
.ToArray();
knowledgeBase.Add(new EmbeddingContext { Text = text, Vector = embedding });
}
}
catch (JsonException ex)
{
Console.WriteLine($"Skipping invalid JSON line: {ex.Message}");
}
}
Console.WriteLine($"š Knowledge base loaded with {knowledgeBase.Count} entries");
Step 2: RAG-Enabled Conversation
var conversationHistory = new List<ChatMessage>();
// System prompt optimized for RAG
var systemPrompt = @"You are an intelligent assistant with access to a comprehensive knowledge base.
When answering questions:
1. ALWAYS prioritize information from the knowledge base when available
2. Provide specific, detailed answers based on the retrieved context
3. If information is not in the knowledge base, clearly state this limitation
4. Cite relevant information without exposing technical details about retrieval";
// Interactive RAG session
while (true)
{
Console.Write("\nYou: ");
var userInput = Console.ReadLine();
if (string.IsNullOrEmpty(userInput) || userInput.ToLower() == "quit") break;
var request = new ConversationRequest(
UserMessage: userInput,
SystemPrompt: systemPrompt,
ConversationHistory: conversationHistory.ToList(),
KnowledgeBase: knowledgeBase, // š Your knowledge base
AvailableTools: new List<ToolSchema>(),
Mode: ConversationMode.Auto // MoE will decide if RAG is needed
);
var response = await conversationManager.ProcessAsync(request, CancellationToken.None);
Console.WriteLine($"\nš¤: {response.Response.Message.Content}");
// Update conversation history
conversationHistory.Add(new ChatMessage { Role = "user", Content = userInput });
conversationHistory.Add(new ChatMessage { Role = "assistant", Content = response.Response.Message.Content });
}
Step 3: Advanced RAG with Forced Mode
// Force RAG mode for knowledge-intensive queries
var ragRequest = new ConversationRequest(
UserMessage: "What are the technical specifications of our latest product?",
SystemPrompt: systemPrompt,
ConversationHistory: conversationHistory,
KnowledgeBase: knowledgeBase,
AvailableTools: new List<ToolSchema>(),
Mode: ConversationMode.ForceRAG // š§ Force RAG processing
);
var ragResponse = await conversationManager.ProcessAsync(ragRequest, CancellationToken.None);
š ļø Function Calling Integration
Define Tools
var tools = new List<ToolSchema>
{
new()
{
Function = new ToolFunction
{
Name = "GetWeather",
Description = "Get current weather information for a specific city",
Parameters = new
{
type = "object",
properties = new
{
city = new
{
type = "string",
description = "The city name (required)"
},
country = new
{
type = "string",
description = "Country code (optional)"
}
},
required = new[] { "city" }
}
}
},
new()
{
Function = new ToolFunction
{
Name = "SearchProducts",
Description = "Search for products in the inventory",
Parameters = new
{
type = "object",
properties = new
{
query = new { type = "string", description = "Search query" },
category = new { type = "string", description = "Product category filter" },
maxResults = new { type = "integer", description = "Maximum number of results" }
},
required = new[] { "query" }
}
}
}
};
Tool-Enabled Conversation
var toolRequest = new ConversationRequest(
UserMessage: "What's the weather like in Istanbul and show me winter jackets",
SystemPrompt: "You are a helpful assistant with access to weather data and product search.",
ConversationHistory: conversationHistory,
KnowledgeBase: knowledgeBase,
AvailableTools: tools, // š ļø Available function calls
Mode: ConversationMode.Auto
);
var response = await conversationManager.ProcessAsync(toolRequest, CancellationToken.None);
// Check if tools were called
if (response.Response.Message.ToolCalls?.Any() == true)
{
Console.WriteLine($"š§ Tools called: {string.Join(", ", response.Response.Message.ToolCalls.Select(t => t.Function?.Name))}");
}
š Hybrid Mode: RAG + Tools
var hybridRequest = new ConversationRequest(
UserMessage: "Based on our product documentation, what's the weather-appropriate clothing for today in London?",
SystemPrompt: "You have access to both product knowledge and weather data. Use both to provide comprehensive recommendations.",
ConversationHistory: conversationHistory,
KnowledgeBase: knowledgeBase, // š Product documentation
AvailableTools: tools, // š ļø Weather API
Mode: ConversationMode.ForceHybrid // š Use both RAG and tools
);
var hybridResponse = await conversationManager.ProcessAsync(hybridRequest, CancellationToken.None);
āļø Advanced Configuration
Custom Model Selection
// The MoE router automatically selects optimal models, but you can influence selection:
// Native: gpt-oss:20b (fast conversational)
// RAG: llama3.1:8b (balanced speed/quality)
// Tools: qwen2.5:32b (strong function calling)
// Hybrid: llama3.1:8b (versatile)
Performance Monitoring
// Enable detailed performance logging
var request = new ConversationRequest(
UserMessage: userInput,
SystemPrompt: systemPrompt,
ConversationHistory: conversationHistory,
KnowledgeBase: knowledgeBase,
AvailableTools: tools,
Mode: ConversationMode.Auto
);
var stopwatch = Stopwatch.StartNew();
var response = await conversationManager.ProcessAsync(request, CancellationToken.None);
stopwatch.Stop();
Console.WriteLine($"ā±ļø Total processing time: {stopwatch.ElapsedMilliseconds}ms");
// Detailed timing information is logged to console automatically
Conversation Modes
public enum ConversationMode
{
Auto, // š¤ Let MoE router decide (recommended)
ForceNative, // š¬ Force native conversation only
ForceRAG, // š Force knowledge base retrieval
ForceTools, // š ļø Force function calling
ForceHybrid // š Force combined RAG + tools
}
š Core Components
Component | Purpose | Key Features |
---|---|---|
ConversationManager | High-level orchestration | Request processing, mode handling, knowledge base loading |
MoERouter | Intelligent routing | SLM-based analysis, route decision, model selection |
ChatService | Core conversation logic | Route execution, RAG processing, tool handling |
EmbeddingService | Vector operations | Text embedding, batch processing |
OllamaClient | API communication | Model interaction, request/response handling |
š ļø Helper Methods
Loading Knowledge Base from Various Sources
// CSV files
public static async Task<List<EmbeddingContext>> LoadFromCsvAsync(string csvPath, IEmbeddingService embeddingService)
{
var contexts = new List<EmbeddingContext>();
var lines = await File.ReadAllLinesAsync(csvPath);
foreach (var line in lines.Skip(1)) // Skip header
{
var columns = line.Split(',');
if (columns.Length >= 2)
{
var text = $"{columns[0]}: {columns[1]}"; // Combine relevant columns
var vector = await embeddingService.EmbedAsync(text, CancellationToken.None);
contexts.Add(new EmbeddingContext { Text = text, Vector = vector });
}
}
return contexts;
}
// Database integration
public static async Task<List<EmbeddingContext>> LoadFromDatabaseAsync(string connectionString, IEmbeddingService embeddingService)
{
var contexts = new List<EmbeddingContext>();
// Your database loading logic here
// Convert database records to text and generate embeddings
return contexts;
}
š Requirements
- .NET 8.0 or later
- Ollama running locally or accessible via network
- Required Ollama Models:
nomic-embed-text:v1.5
(embeddings)qwen2.5:7b
(routing decisions)llama3.1:8b
(RAG processing)qwen2.5:32b
(function calling)gpt-oss:20b
(native conversation)
Install Required Models
ollama pull nomic-embed-text:v1.5
ollama pull qwen2.5:7b
ollama pull llama3.1:8b
ollama pull qwen2.5:32b
ollama pull gpt-oss:20b
š Performance Tips
- Pre-compute Embeddings: Generate embeddings offline for large knowledge bases
- Batch Processing: Use batch embedding for multiple texts
- Conversation History Management: Limit history to recent messages for performance
- Model Warm-up: Keep models loaded in Ollama for faster response times
- Caching: Implement embedding caching for frequently accessed content
š§ Troubleshooting
Common Issues
Q: RAG is not retrieving relevant information
// Ensure your knowledge base texts are properly chunked and relevant
// Lower the similarity threshold if needed
// Check embedding quality with test queries
Q: Tools are not being called
// Verify tool schemas are properly formatted
// Check if the query clearly indicates need for external data
// Use ConversationMode.ForceTools to test tool functionality
Q: Poor routing decisions
// The MoE router learns from conversation context
// Provide clear, descriptive system prompts
// Use conversation history for better context
š License
MIT License - see LICENSE file for details.
š Documentation
- Architecture Guide - Detailed system design and MoE routing
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
- System.Net.Http.Json (>= 8.0.0)
- System.Text.Json (>= 8.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.