Mining
April 24, 2026 · View on GitHub
MemPalace.NET mining infrastructure extracts content from various sources and transforms it into embedded, searchable memories.
Overview
The mining pipeline consists of three main components:
- Miners (
IMiner) — Extract raw items from data sources - MiningPipeline — Orchestrates embedding and storage
- MinedItem — Standardized representation of extracted content
Built-in Miners
FileSystemMiner
Recursively mines a directory for text files.
Features:
- Respects
.gitignorepatterns automatically - Chunks large files (configurable size and overlap)
- Skips binary files by extension and null-byte detection
- Generates stable IDs using SHA-256 prefix for de-duplication
- Extracts metadata: path, extension, size, mtime, chunk info
Options:
var ctx = new MinerContext(
SourcePath: "/path/to/project",
Wing: "code",
Options: new Dictionary<string, string?>
{
["chunk_size"] = "2000", // chars per chunk (default: 2000)
["overlap"] = "200", // overlap between chunks (default: 200)
["include"] = "**/*.cs", // glob include pattern
["exclude"] = "**/bin/**" // glob exclude pattern
});
Chunking Behavior:
- Files ≤
chunk_size: SingleMinedItem - Files >
chunk_size: MultipleMinedItems withchunk_index,chunk_start,chunk_endmetadata - Overlap preserves context across chunk boundaries
ConversationMiner
Parses conversation transcripts from LLM interactions.
Supported Formats:
-
JSON Lines (.jsonl, .ndjson):
{"role": "user", "content": "Hello", "timestamp": "2026-04-24T10:00:00Z"} {"role": "assistant", "content": "Hi there"}Expected fields:
role,content(ormessage), optionaltimestamp -
Markdown:
## User What is the meaning of life? ## Assistant 42.Supports headers:
User,Assistant,Human,AI(case-insensitive)
Metadata:
role: speaker (user/assistant/human/ai)turn_index: 0-based turn numberconversation_id: derived from filenametimestamp: ISO 8601 (JSONL only)
Error Handling:
- Invalid JSON lines are silently skipped
- Empty turns are filtered out
- Robust regex-based markdown parsing
Mining Pipeline
Basic Usage
using MemPalace.Mining;
var backend = ...; // IBackend instance
var embedder = ...; // IEmbedder instance
var miner = new FileSystemMiner();
var ctx = new MinerContext(
SourcePath: "./my-docs",
Wing: "documentation",
Options: new Dictionary<string, string?>());
var pipeline = new MiningPipeline();
var report = await pipeline.RunAsync(
miner: miner,
ctx: ctx,
backend: backend,
embedder: embedder,
collection: "my-collection"
);
Console.WriteLine($"Mined: {report.ItemsMined}, Upserted: {report.Upserted}, Elapsed: {report.Elapsed}");
MiningReport
public sealed record MiningReport(
long ItemsMined, // Total items extracted by miner
int Batches, // Number of embedding batches processed
long Embedded, // Total items embedded
long Upserted, // Total items written to backend
long Skipped, // Duplicates within this run
IReadOnlyList<string> Errors, // Non-fatal errors
TimeSpan Elapsed // Total run time
);
Batching
The pipeline groups items into batches for efficient embedding:
- Default batch size: 32
- Configurable via
batch_sizeoption:ctx.Options["batch_size"] = "64" - Final partial batch is always processed
De-duplication
Within a single run:
- Items with identical IDs are skipped (increments
Skipped) - First occurrence wins
- IDs are stable across runs (based on content hash + metadata)
Across runs:
- Backend handles idempotency via
UpsertAsync - Re-mining same content updates existing records
Error Tolerance
- Batch-level errors are caught and logged to
report.Errors - Mining continues after batch failures
- Check
report.Errors.Countto detect issues
Custom Miners
Implement IMiner:
public class RssFeedMiner : IMiner
{
public string Name => "rss";
public async IAsyncEnumerable<MinedItem> MineAsync(
MinerContext ctx,
[EnumeratorCancellation] CancellationToken ct = default)
{
var feedUrl = ctx.SourcePath;
var feed = await LoadFeedAsync(feedUrl, ct);
foreach (var entry in feed.Entries)
{
ct.ThrowIfCancellationRequested();
yield return new MinedItem(
Id: $"rss:{feed.Id}:{entry.Id}",
Content: $"{entry.Title}\n\n{entry.Summary}",
Metadata: new Dictionary<string, object?>
{
["feed_title"] = feed.Title,
["entry_url"] = entry.Link,
["published"] = entry.PublishDate.ToString("O"),
["author"] = entry.Author
});
}
}
}
Best Practices:
- Generate stable IDs (content-based when possible)
- Include rich metadata for filtering/display
- Respect
CancellationToken - Stream items with
IAsyncEnumerable(don't buffer) - Parse
ctx.Optionsfor configuration
DI Registration
services.AddMemPalaceMining(); // Registers FileSystemMiner, ConversationMiner, MiningPipeline
// Custom miners (keyed services)
services.AddKeyedSingleton<IMiner, RssFeedMiner>("rss");
Access miners:
var filesystemMiner = serviceProvider.GetKeyedService<IMiner>("filesystem");
var conversationMiner = serviceProvider.GetKeyedService<IMiner>("conversation");
var customMiner = serviceProvider.GetKeyedService<IMiner>("rss");
CLI Integration
# Mine a codebase
mempalacenet mine ./my-project --mode files --wing code
# Mine conversation logs
mempalacenet mine ~/.claude/projects --mode convos --wing conversations
# Custom options (via config file)
{
"mining": {
"chunk_size": 3000,
"overlap": 300,
"batch_size": 64
}
}
Performance Considerations
-
FileSystemMiner:
- Large directories: ~1000 files/sec (depends on I/O)
- Chunking overhead: minimal (string slicing)
.gitignoreparsing: once per run
-
ConversationMiner:
- JSONL: streaming parser (low memory)
- Markdown: regex-based (entire file in memory)
-
MiningPipeline:
- Batch size trades latency for throughput
- Larger batches: better GPU utilization (embedder-dependent)
- De-dupe overhead: O(n) hash set
Roadmap
- Additional miners: Git commits, Slack exports, Notion backups
- Incremental mining: Track mtime, skip unchanged files
- Parallel mining: Run multiple miners concurrently
- Schema validation: Enforce metadata schemas per collection