Installation
pip install kodexa-document
npm install @kodexa-ai/document-wasm-ts
Initialization
from kodexa_document import Document
# Python SDK is ready to use immediately after import
# Use context managers for automatic cleanup
with Document() as doc:
# Your code here
pass
import { Kodexa } from '@kodexa-ai/document-wasm-ts';
async function main() {
// Initialize once at application start
await Kodexa.init();
// Your code here...
}
main();
Creating Documents
Empty Document
Create a new document and build its structure:from kodexa_document import Document
with Document() as doc:
# Create the root node
root = doc.create_node("document", "My Document")
doc.content_node = root
# Add child nodes
section = doc.create_node("section", "Introduction", parent=root)
para1 = doc.create_node("paragraph", "First paragraph.", parent=section)
para2 = doc.create_node("paragraph", "Second paragraph.", parent=section)
print(f"Created document with {len(root.get_children())} sections")
import { Kodexa } from '@kodexa-ai/document-wasm-ts';
async function createDocument() {
await Kodexa.init();
const doc = await Kodexa.createDocument();
try {
// Create root node
const root = await doc.createNode('document');
await root.setContent('My Document');
// Add child nodes
const section = await doc.createNode('section');
await section.setContent('Introduction');
await root.addChild(section);
const para1 = await doc.createNode('paragraph');
await para1.setContent('First paragraph.');
await section.addChild(para1);
const para2 = await doc.createNode('paragraph');
await para2.setContent('Second paragraph.');
await section.addChild(para2);
const children = await root.getChildren();
console.log(`Created document with ${children.length} sections`);
} finally {
doc.dispose();
}
}
From Text
Automatically parse text into paragraphs:text = """First paragraph of content.
Second paragraph with more details.
Third paragraph to conclude."""
with Document.from_text(text, separator="\n") as doc:
paragraphs = doc.select("//paragraph")
print(f"Created {len(paragraphs)} paragraphs from text")
async function fromText() {
await Kodexa.init();
const text = 'First paragraph.\nSecond paragraph.\nThird paragraph.';
const doc = await Kodexa.fromText(text);
try {
const paragraphs = await doc.select('//paragraph');
console.log(`Created ${paragraphs.length} paragraphs from text`);
} finally {
doc.dispose();
}
}
With Metadata
Initialize documents with metadata:with Document(metadata={
"title": "Invoice Analysis",
"author": "Processing System",
"created": "2024-01-15"
}) as doc:
title = doc.get_metadata("title")
print(f"Document title: {title}")
async function withMetadata() {
await Kodexa.init();
const doc = await Kodexa.createDocument();
try {
await doc.setMetadataValue('title', 'Invoice Analysis');
await doc.setMetadataValue('author', 'Processing System');
await doc.setMetadataValue('created', '2024-01-15');
const title = await doc.getMetadataValue('title');
console.log(`Document title: ${title}`);
} finally {
doc.dispose();
}
}
Loading Documents
From KDDB File / Blob
# Load into memory for fast processing (creates a copy)
with Document.from_kddb("document.kddb", detached=True) as doc:
print(f"Loaded document: {doc.uuid}")
nodes = doc.select("//*")
print(f"Total nodes: {len(nodes)}")
# Load from bytes (e.g., API response)
import requests
response = requests.get("https://api.example.com/documents/123")
with Document.from_kddb(response.content) as doc:
print(f"Loaded from API: {doc.uuid}")
// From file input (browser)
async function loadFromFile() {
await Kodexa.init();
const fileInput = document.getElementById('file') as HTMLInputElement;
const file = fileInput.files?.[0];
if (file) {
const doc = await Kodexa.fromBlob(file);
try {
const nodes = await doc.select('//*');
console.log(`Total nodes: ${nodes.length}`);
} finally {
doc.dispose();
}
}
}
// From fetch response
async function loadFromApi() {
await Kodexa.init();
const response = await fetch('https://api.example.com/documents/123');
const blob = await response.blob();
const doc = await Kodexa.fromBlob(blob);
try {
console.log('Loaded from API');
} finally {
doc.dispose();
}
}
From JSON
json_data = '{"uuid": "...", "metadata": {"title": "Test"}}'
with Document.from_json(json_data) as doc:
print(f"Loaded from JSON: {doc.uuid}")
async function fromJson() {
await Kodexa.init();
const jsonData = JSON.stringify({
uuid: 'example-uuid',
metadata: { title: 'Test Document' }
});
const doc = await Kodexa.fromJson(jsonData);
try {
console.log('Loaded from JSON');
} finally {
doc.dispose();
}
}
Working with Content Nodes
Navigation
Traverse the document tree:with Document.from_kddb("document.kddb") as doc:
root = doc.content_node
for child in root.get_children():
parent = child.get_parent() # Back to root
siblings = child.get_siblings() # Other children
next_node = child.next_node() # Next sibling
depth = child.get_depth() # Depth in tree
print(f"Node type: {child.type}, depth: {depth}")
async function navigateTree() {
await Kodexa.init();
const doc = await Kodexa.fromText('Para 1\nPara 2\nPara 3');
try {
const root = await doc.getRoot();
if (root) {
const children = await root.getChildren();
for (const child of children) {
console.log(`Type: ${child.type}`);
console.log(`Content: ${child.content}`);
const parent = await child.getParent();
const nextNode = await child.nextNode();
const depth = await child.getDepth();
}
}
} finally {
doc.dispose();
}
}
Content Access
Read and modify node content:with Document() as doc:
root = doc.create_node("document")
doc.content_node = root
para = doc.create_node("paragraph", "Initial content", parent=root)
# Read content
print(f"Content: {para.content}")
# Update content
para.content = "Updated content"
# Multi-part content
para.set_content_parts(["Part 1", "Part 2", "Part 3"])
parts = para.get_content_parts()
# Get all content from node and descendants
all_text = root.get_all_content(separator=" ")
async function workWithContent() {
await Kodexa.init();
const doc = await Kodexa.createDocument();
try {
const node = await doc.createNode('paragraph');
// Set content
await node.setContent('Initial content');
// Read content
const content = await node.getContent();
console.log(`Content: ${content}`);
// Synchronous access (after first async call)
console.log(`Direct access: ${node.content}`);
} finally {
doc.dispose();
}
}
Querying with Selectors
Use XPath-like selectors to find nodes:with Document.from_text("Para 1\nPara 2\nPara 3", separator="\n") as doc:
# Select all nodes of a type
all_paragraphs = doc.select("//paragraph")
# Select first match only
first_para = doc.select_first("//paragraph")
# Filter by content
matching = doc.select("//paragraph[contains(@content, 'Para 2')]")
# Select tagged nodes
tagged = doc.select("//*[@tag='important']")
# Select with variables
variables = {"search_term": "Para 1"}
results = doc.select("//paragraph[contains(@content, $search_term)]", variables)
print(f"Found {len(all_paragraphs)} paragraphs")
async function queryDocument() {
await Kodexa.init();
const doc = await Kodexa.fromText('Important note\nRegular text\nAnother note');
try {
// Select all nodes of a type
const allParagraphs = await doc.select('//paragraph');
console.log(`Found ${allParagraphs.length} paragraphs`);
// Select first match
const firstPara = await doc.selectFirst('//paragraph');
if (firstPara) {
console.log(`First paragraph: ${firstPara.content}`);
}
// Filter by content
const noteParagraphs = await doc.select("//paragraph[contains(@content, 'note')]");
// Select by tag
const tagged = await doc.select("//*[@tag='important']");
} finally {
doc.dispose();
}
}
Common Selector Patterns
| Selector | Description |
|---|---|
//* | All nodes |
//paragraph | All paragraphs |
//section/paragraph | Direct child paragraphs of sections |
//paragraph[1] | First paragraph |
//*[@tag='important'] | Nodes with ‘important’ tag |
//paragraph[contains(@content, 'text')] | Paragraphs containing ‘text’ |
Adding Features
Attach metadata to nodes:with Document() as doc:
root = doc.create_node("document")
doc.content_node = root
para = doc.create_node("paragraph", "Styled text", parent=root)
# Add features (type, name, value)
para.add_feature("style", "font-family", "Arial")
para.add_feature("style", "font-size", "12pt")
para.add_feature("analysis", "word-count", 2)
para.add_feature("position", "bbox", {"x": 100, "y": 200, "w": 300, "h": 50})
# Retrieve features
font = para.get_feature("style", "font-family")
if font:
print(f"Font: {font.get_value()}")
# Get all features of a type
style_features = para.get_features_of_type("style")
async function addFeatures() {
await Kodexa.init();
const doc = await Kodexa.createDocument();
try {
const node = await doc.createNode('paragraph');
await node.setContent('Styled text');
// Add features (type, name, value)
await node.setFeature('style', 'font-family', 'Arial');
await node.setFeature('style', 'font-size', '12pt');
await node.setFeature('analysis', 'word-count', 2);
await node.setFeature('position', 'bbox', { x: 100, y: 200, w: 300, h: 50 });
// Retrieve features
const fontValue = await node.getFeatureValue('style', 'font-family');
console.log(`Font: ${fontValue}`);
// Get features by type
const styleFeatures = await node.getFeaturesOfType('style');
} finally {
doc.dispose();
}
}
Adding Tags
Annotate nodes with tags:with Document() as doc:
root = doc.create_node("document")
doc.content_node = root
para = doc.create_node("paragraph", "Invoice total: $1,234.56", parent=root)
# Simple tag
para.tag("important")
# Tag with confidence and value
para.tag("invoice-total", confidence=0.95, value="$1,234.56")
# Check for tags
if para.has_tag("important"):
print("This paragraph is marked as important")
# Get tag details
tag = para.get_tag("invoice-total")
if tag:
confidence = tag.get("Confidence")
value = tag.get("Value")
print(f"Invoice total: {value} (confidence: {confidence})")
# List all tags
all_tags = para.get_tags()
async function tagNodes() {
await Kodexa.init();
const doc = await Kodexa.fromText('Invoice total: $500.00');
try {
const firstPara = await doc.selectFirst('//paragraph');
if (firstPara) {
// Simple tag
await firstPara.tag('important');
// Tag with options
await firstPara.tagWithOptions('invoice-total', {
confidence: 0.95,
value: '500.00'
});
// Check for tags
const hasTag = await firstPara.hasTag('important');
console.log(`Has 'important' tag: ${hasTag}`);
// Get all tags
const tags = await firstPara.getTags();
console.log(`Tags: ${tags.join(', ')}`);
// Remove a tag
await firstPara.removeTag('important');
}
} finally {
doc.dispose();
}
}
Saving Documents
To KDDB File / Blob
with Document() as doc:
root = doc.create_node("document", "Content to save")
doc.content_node = root
# Save to file
doc.save("output.kddb")
# Get as bytes (for API responses)
kddb_bytes = doc.to_kddb()
async function saveDocument() {
await Kodexa.init();
const doc = await Kodexa.fromText('Content for download');
try {
// Export as KDDB blob
const blob = await doc.toBlob();
// Download in browser
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.kddb';
a.click();
URL.revokeObjectURL(url);
} finally {
doc.dispose();
}
}
To JSON
with Document() as doc:
root = doc.create_node("document", "Debug output")
doc.content_node = root
# Pretty-printed JSON
json_str = doc.to_json(indent=2)
print(json_str)
# As dictionary
doc_dict = doc.to_dict()
async function saveToJson() {
await Kodexa.init();
const doc = await Kodexa.fromText('Content to save');
try {
const json = await doc.toJson();
console.log(json);
// Send to server
await fetch('/api/documents', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: json
});
} finally {
doc.dispose();
}
}
Memory Management
Both SDKs use native code (Go via CFFI for Python, WebAssembly for TypeScript). Proper cleanup prevents memory leaks.
# Python: Use context managers (recommended)
with Document() as doc:
# Document automatically closed when exiting the block
root = doc.create_node("document", "Safe content")
# Manual cleanup if needed
doc = Document()
try:
# Work with document
pass
finally:
doc.close()
// TypeScript: Use try-finally pattern
async function safeDocumentHandling() {
await Kodexa.init();
const doc = await Kodexa.createDocument();
try {
// All document operations here
const root = await doc.createNode('document');
await root.setContent('Safe content');
} finally {
// Always dispose, even if an error occurred
doc.dispose();
}
}
// Application cleanup
window.addEventListener('beforeunload', () => {
Kodexa.cleanup();
});
Complete Example
A full invoice processing workflow:from kodexa_document import Document
def process_document():
with Document() as doc:
# Set document metadata
doc.set_metadata("title", "Invoice Processing Result")
doc.add_label("invoice")
# Build document structure
root = doc.create_node("document", "Invoice #12345")
doc.content_node = root
# Add header section
header = doc.create_node("section", "Header", parent=root)
doc.create_node("paragraph", "Vendor: Acme Corp", parent=header)
doc.create_node("paragraph", "Date: 2024-01-15", parent=header)
# Add line items
items = doc.create_node("section", "Line Items", parent=root)
for i, (desc, amount) in enumerate([
("Widget A", 100.00),
("Widget B", 250.00),
("Service Fee", 50.00)
]):
item = doc.create_node("paragraph", f"{desc}: ${amount:.2f}", parent=items)
item.add_feature("line-item", "amount", amount)
item.tag("line-item", value=str(amount))
# Add total
total = doc.create_node("paragraph", "Total: $400.00", parent=root)
total.tag("invoice-total", confidence=1.0, value="400.00")
# Query the document
line_items = doc.select("//*[@tag='line-item']")
print(f"Found {len(line_items)} line items")
total_node = doc.select_first("//*[@tag='invoice-total']")
if total_node:
print(f"Invoice total: {total_node.content}")
# Save the result
doc.save("processed_invoice.kddb")
if __name__ == "__main__":
process_document()
import { Kodexa } from '@kodexa-ai/document-wasm-ts';
async function processInvoice() {
await Kodexa.init();
const doc = await Kodexa.createDocument();
try {
// Set document metadata
await doc.setMetadataValue('title', 'Invoice Processing Result');
// Build document structure
const root = await doc.createNode('document');
await root.setContent('Invoice #12345');
// Add header section
const header = await doc.createNode('section');
await header.setContent('Header');
await root.addChild(header);
const vendor = await doc.createNode('paragraph');
await vendor.setContent('Vendor: Acme Corp');
await header.addChild(vendor);
// Add line items
const items = await doc.createNode('section');
await items.setContent('Line Items');
await root.addChild(items);
const lineItems = [
{ desc: 'Widget A', amount: 100.00 },
{ desc: 'Widget B', amount: 250.00 },
{ desc: 'Service Fee', amount: 50.00 }
];
for (const { desc, amount } of lineItems) {
const item = await doc.createNode('paragraph');
await item.setContent(`${desc}: $${amount.toFixed(2)}`);
await items.addChild(item);
await item.setFeature('line-item', 'amount', amount);
await item.tagWithOptions('line-item', { value: String(amount) });
}
// Add total
const total = await doc.createNode('paragraph');
await total.setContent('Total: $400.00');
await root.addChild(total);
await total.tagWithOptions('invoice-total', { confidence: 1.0, value: '400.00' });
// Query the document
const taggedItems = await doc.select("//*[@tag='line-item']");
console.log(`Found ${taggedItems.length} line items`);
const totalNode = await doc.selectFirst("//*[@tag='invoice-total']");
if (totalNode) {
console.log(`Invoice total: ${totalNode.content}`);
}
// Export
const json = await doc.toJson();
console.log('Document processed successfully');
} finally {
doc.dispose();
}
}
processInvoice().catch(console.error);
Next Steps
As you become more familiar with the SDK, explore:- Advanced XPath selectors for complex queries
- Processing steps for workflow tracking
- Integration with Kodexa Platform for cloud processing
