Skip to main content

Python client for ekoDB - a high-performance document database

Project description

ekoDB Python Client

High-performance Python client for ekoDB, built with Rust for speed and safety.

This package wraps the ekodb_client Rust library using PyO3 to provide a native Python interface.

Features

  • Fast: Built with Rust, leveraging the same client library as the Rust SDK
  • Type-safe: Strong typing with Python type hints
  • Async/await: Full async support using Python's asyncio
  • Easy to use: Pythonic API that feels natural
  • Complete: All ekoDB features supported
  • Query Builder - Fluent API for complex queries with operators, sorting, and pagination
  • Search - Full-text search, fuzzy search, and field-specific search with scoring
  • Schema Management - Define and enforce data schemas with validation
  • Join Operations - Single and multi-collection joins with queries
  • Rate limiting with automatic retry (429, 503, network errors)
  • Rate limit tracking (X-RateLimit-* headers)
  • Configurable retry behavior
  • Retry-After header support

Installation

pip install ekodb

Or install from source:

cd ekodb-py
pip install maturin
maturin develop

Quick Start

import asyncio
from ekodb_client import Client, RateLimitError

async def main():
    # Create client with configuration
    client = Client.new(
        "http://localhost:8080",
        "your-api-key",
        should_retry=True,  # Enable automatic retries (default: True)
        max_retries=3,      # Maximum retry attempts (default: 3)
        timeout_secs=30     # Request timeout in seconds (default: 30)
    )

    try:
        # Insert a document
        record = await client.insert("users", {
            "name": "John Doe",
            "age": 30,
            "email": "john@example.com",
            "active": True
        })
        print(f"Inserted: {record['id']}")

        # Find by ID
        user = await client.find_by_id("users", record["id"])
        print(f"Found: {user}")

        # Find with query
        results = await client.find("users", limit=10)
        print(f"Found {len(results)} users")

        # Update
        updated = await client.update("users", record["id"], {
            "age": 31
        })
        print(f"Updated: {updated}")

        # Delete
        await client.delete("users", record["id"])
        print("Deleted")

    except RateLimitError as e:
        print(f"Rate limited! Retry after {e.retry_after_secs} seconds")

asyncio.run(main())

Usage Examples

Query Builder

from ekodb_client import Client, QueryBuilder

async def main():
    client = Client.new("http://localhost:8080", "your-api-key")

    # Simple query with operators
    query = QueryBuilder() \
        .eq("status", "active") \
        .gte("age", 18) \
        .lt("age", 65) \
        .limit(10) \
        .build()

    results = await client.find("users", query)

    # Complex query with sorting and pagination
    query = QueryBuilder() \
        .in_array("status", ["active", "pending"]) \
        .contains("email", "@example.com") \
        .sort_desc("created_at") \
        .skip(20) \
        .limit(10) \
        .build()

    results = await client.find("users", query)

Search Operations

# Basic text search
search_query = {
    "query": "programming",
    "min_score": 0.1,
    "limit": 10
}

results = await client.search("articles", search_query)
for result in results["results"]:
    print(f"Score: {result['score']:.4f} - {result['record']['title']}")

# Search with field weights
search_query = {
    "query": "rust database",
    "fields": ["title", "description"],
    "weights": {"title": 2.0},
    "limit": 5
}

results = await client.search("articles", search_query)

Schema Management

# Create a collection with schema
schema = {
    "fields": {
        "name": {
            "field_type": "String",
            "required": True,
            "regex": "^[a-zA-Z ]+$"
        },
        "email": {
            "field_type": "String",
            "required": True,
            "unique": True
        },
        "age": {
            "field_type": "Integer",
            "min": 0,
            "max": 150
        }
    }
}

await client.create_collection("users", schema)

# Get collection schema
schema = await client.get_schema("users")

Join Operations

# Single collection join
query = {
    "join": {
        "collections": ["departments"],
        "local_field": "department_id",
        "foreign_field": "id",
        "as_field": "department"
    },
    "limit": 10
}

results = await client.find("users", query)

# Multi-collection join
query = {
    "join": [
        {
            "collections": ["departments"],
            "local_field": "department_id",
            "foreign_field": "id",
            "as_field": "department"
        },
        {
            "collections": ["profiles"],
            "local_field": "id",
            "foreign_field": "id",
            "as_field": "profile"
        }
    ],
    "limit": 10
}

results = await client.find("users", query)

API Reference

Client

Client.new(base_url: str, api_key: str, should_retry: bool = True, max_retries: int = 3, timeout_secs: int = 30) -> Client

Create a new ekoDB client.

Parameters:

  • base_url: The base URL of the ekoDB server
  • api_key: Your API key
  • should_retry: Enable automatic retries (default: True)
  • max_retries: Maximum number of retry attempts (default: 3)
  • timeout_secs: Request timeout in seconds (default: 30)

Returns:

  • A new Client instance

RateLimitInfo

Rate limit information is automatically tracked and logged by the client. The client will automatically retry on rate limit errors using the server's Retry-After header.

Properties

  • limit: int - Maximum requests allowed per window
  • remaining: int - Requests remaining in current window
  • reset: int - Unix timestamp when the rate limit resets

Methods

  • is_near_limit() -> bool - Check if approaching rate limit (<10% remaining)
  • is_exceeded() -> bool - Check if the rate limit has been exceeded
  • remaining_percentage() -> float - Get the percentage of requests remaining

RateLimitError

Exception raised when rate limit is exceeded (if retries are disabled or exhausted).

Properties

  • retry_after_secs: int - Number of seconds to wait before retrying

await client.insert(collection: str, record: dict) -> dict

Insert a document into a collection.

Parameters:

  • collection: The collection name
  • record: A dictionary representing the document

Returns:

  • The inserted document with ID

await client.find_by_id(collection: str, id: str) -> dict

Find a document by ID.

Parameters:

  • collection: The collection name
  • id: The document ID

Returns:

  • The found document

await client.find(collection: str, limit: Optional[int] = None) -> List[dict]

Find documents in a collection.

Parameters:

  • collection: The collection name
  • limit: Optional limit on number of results

Returns:

  • List of matching documents

await client.update(collection: str, id: str, updates: dict) -> dict

Update a document.

Parameters:

  • collection: The collection name
  • id: The document ID
  • updates: Dictionary of fields to update

Returns:

  • The updated document

await client.delete(collection: str, id: str) -> None

Delete a document.

Parameters:

  • collection: The collection name
  • id: The document ID

await client.list_collections() -> List[str]

List all collections.

Returns:

  • List of collection names

await client.delete_collection(collection: str) -> None

Delete a collection.

Parameters:

  • collection: The collection name to delete

await client.search(collection: str, query: dict) -> dict

Perform full-text search on a collection.

Parameters:

  • collection: The collection name
  • query: Search query dictionary with fields like query, fields, weights, min_score, limit

Returns:

  • Search results with scores and matched records

await client.create_collection(collection: str, schema: dict) -> None

Create a collection with a schema.

Parameters:

  • collection: The collection name
  • schema: Schema definition dictionary

await client.get_schema(collection: str) -> dict

Get the schema for a collection.

Parameters:

  • collection: The collection name

Returns:

  • Schema definition dictionary

await client.get_collection(collection: str) -> dict

Get collection metadata including schema.

Parameters:

  • collection: The collection name

Returns:

  • Collection metadata dictionary

await client.collection_exists(collection: str) -> bool

Check if a collection exists.

Parameters:

  • collection: The collection name

Returns:

  • True if the collection exists, False otherwise

await client.count_documents(collection: str) -> int

Count documents in a collection.

Parameters:

  • collection: The collection name

Returns:

  • Number of documents in the collection

Chat Models

await client.get_chat_models() -> dict

Get all available chat models organized by provider.

Returns:

  • Dictionary mapping provider names to lists of model names

await client.get_chat_model(provider: str) -> list

Get models for a specific provider.

Parameters:

  • provider: The provider name (e.g., "openai", "anthropic")

Returns:

  • List of model names for the provider

User Functions

await client.save_user_function(user_function: dict) -> str

Create a new user function.

Parameters:

  • user_function: Dictionary containing label, name, parameters, functions, etc.

Returns:

  • The ID of the created user function

await client.get_user_function(label: str) -> dict

Get a user function by its label.

Parameters:

  • label: The user function label

Returns:

  • User function definition dictionary

await client.list_user_functions(tags: Optional[list] = None) -> list

List all user functions, optionally filtered by tags.

Parameters:

  • tags: Optional list of tags to filter by

Returns:

  • List of user function dictionaries

await client.update_user_function(label: str, user_function: dict) -> None

Update an existing user function.

Parameters:

  • label: The user function label
  • user_function: Updated user function definition

await client.delete_user_function(label: str) -> None

Delete a user function by its label.

Parameters:

  • label: The user function label

Examples

See the examples directory for complete working examples:

  • client_simple_crud.py - Basic CRUD operations
  • client_query_builder.py - Complex queries with QueryBuilder
  • client_search.py - Full-text search operations
  • client_schema.py - Schema management
  • client_joins.py - Join operations
  • client_batch_operations.py - Batch operations
  • client_kv_operations.py - Key-value operations
  • client_chat_models.py - Chat models API
  • client_user_functions.py - User functions API
  • And more...

Development

Building

# Install maturin
pip install maturin

# Build and install in development mode
maturin develop

# Build release wheel
maturin build --release

Testing

# Run Python tests
pytest

# Run with coverage
pytest --cov=ekodb

Goals, Tasks, and Agents

import asyncio
from ekodb_client import Client

async def main():
    client = Client.new("http://localhost:8080", "your-api-key")

    # Goals
    goal = await client.goal_create({"title": "Migrate data", "status": "active"})
    goals = await client.goal_list()
    await client.goal_complete("goal-id", {"summary": "Done"})

    # Tasks
    task = await client.task_create({"title": "Backup", "schedule": "0 0 * * *"})
    await client.task_start("task-id")

    # Agents
    agent = await client.agent_create({"name": "processor", "model": "gpt-4.1"})

asyncio.run(main())

Schedules

# Create a schedule
sched = await client.create_schedule({"name": "nightly", "cron": "0 2 * * *"})

# Pause a schedule
await client.pause_schedule("sched-id")

WebSocket Operations

ws = await client.websocket("ws://localhost:8080")

# Full CRUD over WebSocket (14 methods)
result = await ws.ws_insert("users", {"name": "Alice", "email": "a@b.com"})
results = await ws.ws_query("users", filter={"field": "status", "operator": "Eq", "value": "active"})
user = await ws.ws_find_by_id("users", "record-id")
await ws.ws_update("users", "record-id", {"name": "Updated"})
await ws.ws_delete("users", "record-id")

# Batch operations
await ws.ws_batch_insert("logs", [{"msg": "a"}, {"msg": "b"}])
await ws.ws_batch_delete("logs", ["id1", "id2"])

# Search + collection management
hits = await ws.ws_text_search("docs", "python async", limit=10)
collections = await ws.ws_list_collections()
await ws.ws_create_collection("new_coll")

# Atomic field actions
await ws.ws_update_with_action("counters", "views", "increment", "count", 1)

WebSocket Chat Streaming

stream = await ws.chat_send(chat_id, "What is the capital of France?")
async for event in stream:
    if event.type == "chunk":
        print(event.content, end="")
    elif event.type == "end":
        print(f"\nDone (context: {event.context_window} tokens)")
    elif event.type == "tool_call":
        print(f"[Tool] {event.tool_name}")
        await ws.send_tool_result(
            chat_id, event.call_id, True, {"result": "done"}
        )
    elif event.type == "error":
        print(f"Error: {event.error}")

License

MIT OR Apache-2.0

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ekodb_client-0.18.0.tar.gz (197.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ekodb_client-0.18.0-cp38-abi3-manylinux_2_28_aarch64.whl (6.1 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.28+ ARM64

ekodb_client-0.18.0-cp38-abi3-macosx_11_0_arm64.whl (5.5 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file ekodb_client-0.18.0.tar.gz.

File metadata

  • Download URL: ekodb_client-0.18.0.tar.gz
  • Upload date:
  • Size: 197.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.3

File hashes

Hashes for ekodb_client-0.18.0.tar.gz
Algorithm Hash digest
SHA256 3349d8139717b12ea5a172839ddb25715630579321364df10d838f2af29f6429
MD5 b19680c8db85bfb28c68cb5e2df873b5
BLAKE2b-256 75d9a00f75112785b34edb1048039892a3fc0a8c8b40d51678702ea873be4f53

See more details on using hashes here.

File details

Details for the file ekodb_client-0.18.0-cp38-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for ekodb_client-0.18.0-cp38-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 277c029b23cc901355c225ec3e7f65a9d03fe8916adbbbb6eaaaee704ab316fd
MD5 cf89173bb4c9e804032ebdcfb88f7713
BLAKE2b-256 24f0ef50d7927b3f87e07b180a02776a0858a5bc975ce47e36969d249d514ad6

See more details on using hashes here.

File details

Details for the file ekodb_client-0.18.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ekodb_client-0.18.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5159c6d89f97ee7cef5abac448403eee40f043e32d1d77b9939ecf1b85e5ab67
MD5 a40e242897b0a1fa467017cfc72dc3ca
BLAKE2b-256 3624462cd81db899218b44ebc942e7fdbf7739fd94793af19b43017232b5748e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page