Metadata-Version: 2.4
Name: satori-client
Version: 0.1.9
Summary: Python client for Satori database
Author-email: Sergio Valdes <svcu2310@gmail.com>
Requires-Python: >=3.7
Requires-Dist: uuid
Requires-Dist: websockets
Description-Content-Type: text/markdown

# 📚 Satori Python SDK

A comprehensive Python client for SatoriDB - a powerful in-memory database with graph relationships, AI capabilities, and real-time features.

---

## ✨ Main Features

- **Ultra-fast CRUD operations** ⚡
- **Advanced queries** using `field_array` 🔍
- **Real-time notifications** 📢
- **Graph operations** - vertices, DFS, BFS, shortest path, centrality metrics 🕸️
- **Data encryption and decryption** 🔐
- **AI-powered features** - semantic search, natural language queries, mindspaces 🤖
- **Mindspace operations** - cognitive contexts for AI conversations 🧠

---

## 🚀 Installation

```bash
pip install satori-client
```

Or, if you use the repository directly:

```bash
pip install websockets uuid
```

---

## 🏁 Quick Start

```python
import asyncio
from satori import Satori

async def main():
    client = Satori(
        username='user',
        password='password',
        host='ws://localhost:8000'
    )
    await client.connect()
    
    # Create an object
    await client.set({
        'key': 'user:john',
        'data': {'name': 'John Doe', 'email': 'john@example.com', 'age': 30},
        'type': 'user'
    })
    
    # Get the object
    user = await client.get({'key': 'user:john'})
    print(user)

asyncio.run(main())
```

---

## 📖 API Reference

## Table of Contents

1. [Basic Operations](#basic-operations)
2. [Crypto Operations](#crypto-operations)
3. [Graph Operations](#graph-operations)
4. [AI Operations](#ai-operations)
5. [Analytics Operations](#analytics-operations)
6. [Mindspace Operations](#mindspace-operations)
7. [Real-time Notifications](#real-time-notifications)
8. [Schema Class](#schema-class)
9. [Response Format](#response-format)

---

## Basic Operations

### SET - Create Data

Creates a new object in the database. If no key is provided, a UUID is automatically generated.

```python
await client.set({
    'key': 'user:john',           # Object key (optional, auto-generated if omitted)
    'data': {'name': 'John'},     # Object data content (default: {})
    'type': 'user',               # Object class/type (default: "normal")
    'expires': False,             # Whether object expires (default: false)
    'expiration_time': 1700000000  # Expiration timestamp in milliseconds
})
```

### GET - Read Data

Retrieves one or more objects from the database. Use `"*"` for `key` to return all objects.

```python
# Get single object
user = await client.get({'key': 'user:john'})

# Get all objects
all_objects = await client.get({'key': '*'})

# Query by field
users = await client.get({
    'field_array': [
        {'field': 'email', 'value': 'john@example.com'}
    ]
})

# Get first match only
first_user = await client.get({
    'field_array': [
        {'field': 'age', 'value': 30}
    ],
    'one': True
})

# Limit results
limited = await client.get({
    'field_array': [
        {'field': 'type', 'value': 'user'}
    ],
    'max': 10
})
```

### PUT - Update Data

Updates one or more fields of an existing object.

```python
await client.put({
    'key': 'user:john',
    'replace_field': 'age',
    'replace_value': 31
})

# Batch update by field query
await client.put({
    'field_array': [
        {'field': 'type', 'value': 'user'}
    ],
    'replace_field': 'status',
    'replace_value': 'active'
})
```

### DELETE - Delete Data

Removes one or more objects from the database.

```python
await client.delete({'key': 'user:john'})

# Delete by field query
await client.delete({
    'field_array': [
        {'field': 'status', 'value': 'inactive'}
    ]
})
```

### PUSH - Add to Array

Adds a value to the end of an array field.

```python
await client.push({
    'key': 'user:john',
    'array': 'tags',
    'value': 'premium'
})
```

### POP - Remove Last from Array

Removes the last element from an array field.

```python
await client.pop({
    'key': 'user:john',
    'array': 'notifications'
})
```

### SPLICE - Remove First from Array

Removes the first element from an array field.

```python
await client.splice({
    'key': 'user:john',
    'array': 'notifications'
})
```

### REMOVE - Remove Specific Value

Removes a specific value from an array field.

```python
await client.remove({
    'key': 'user:john',
    'array': 'tags',
    'value': 'premium'
})
```

---

## Crypto Operations

### ENCRYPT

Encrypts an object's data using AES encryption.

```python
await client.encrypt({
    'key': 'user:john',
    'encryption_key': 'secret-key-123'
})
```

### DECRYPT

Decrypts an encrypted object.

```python
await client.decrypt({
    'key': 'user:john',
    'encryption_key': 'secret-key-123'
})
```

---

## Graph Operations

### SET_VERTEX

Adds vertices/edges to an object for graph relationships.

```python
# Simple vertex
await client.set_vertex({
    'key': 'user:john',
    'vertex': 'user:jane'
})

# Vertex with relation
await client.set_vertex({
    'key': 'user:john',
    'vertex': {'vertex': 'user:jane', 'relation': 'friend'}
})

# Vertex with relation and weight
await client.set_vertex({
    'key': 'user:john',
    'vertex': {'vertex': 'post:123', 'relation': 'author', 'weight': 1.0}
})

# Multiple vertices
await client.set_vertex({
    'key': 'user:john',
    'vertex': ['user:jane', 'user:alice', 'user:bob']
})
```

### GET_VERTEX

Retrieves all vertices from an object.

```python
vertices = await client.get_vertex({
    'key': 'user:john'
})
```

### DELETE_VERTEX

Removes a specific vertex from an object.

```python
await client.delete_vertex({
    'key': 'user:john',
    'vertex': 'user:jane'
})
```

### DFS

Performs a distributed depth-first search traversal.

```python
# Traverse from a node
results = await client.dfs({
    'node': 'user:john'
})

# Filter by relation
results = await client.dfs({
    'node': 'user:john',
    'relation': 'friend'
})
```

### GRAPH_BFS

Performs breadth-first search on the graph.

```python
# Get all reachable nodes from a starting node
results = await client.graph_bfs({
    'node': 'user:john'
})
```

### GRAPH_DFS

Performs depth-first search on the graph.

```python
results = await client.graph_dfs({
    'node': 'user:john'
})
```

### GRAPH_SHORTEST_PATH

Finds the shortest path between two nodes using Dijkstra's algorithm.

```python
path = await client.graph_shortest_path({
    'node': 'user:john',
    'target': 'post:123'
})
# Returns: ["user:john", "user:jane", "post:123"]
```

### GRAPH_CONNECTED_COMPONENTS

Finds all connected components in the graph.

```python
components = await client.graph_connected_components({})
# Returns: [["node1", "node2", "node3"], ["node4", "node5"], ["node6"]]
```

### GRAPH_SCC

Finds strongly connected components using Tarjan's algorithm.

```python
scc = await client.graph_scc({})
# Returns: [["node1", "node2", "node3"], ["node4", "node5"]]
```

### GRAPH_DEGREE_CENTRALITY

Calculates degree centrality (number of connections) for each node.

```python
centrality = await client.graph_degree_centrality({})
# Returns: {"node1": 5, "node2": 3, "node3": 8}
```

### GRAPH_CLOSENESS_CENTRALITY

Calculates closeness centrality for each node.

```python
centrality = await client.graph_closeness_centrality({})
# Returns: {"node1": 0.45, "node2": 0.32, "node3": 0.58}
```

### GRAPH_CENTROID

Finds the centroid node (node with highest closeness centrality).

```python
centroid = await client.graph_centroid({})
# Returns: "node-with-highest-centrality"
```

---

## AI Operations

### ASK

Ask questions about your data using AI-powered context gathering.

```python
response = await client.ask({
    'question': 'Which user has the most posts?',
    'session': 'global',        # Mindspace session (default: "global")
    'backend': 'openai'         # LLM backend: "ollama" or "openai"
})
# Returns: {"response": "AI-generated answer...", "context": [...], "session": "..."}
```

### ANN / GET_SIMILAR

Performs approximate nearest neighbor search using vector embeddings.

```python
# Using existing object's embedding
results = await client.ann({
    'key': 'user:john',
    'k': 10,         # Number of neighbors (default: 5)
    'ef': 25,        # Search width (default: 25)
    'use': 'embedding'  # Use "data" or "embedding"
})

# Using a vector directly
results = await client.ann({
    'vector': [0.1, 0.2, 0.3, ...],
    'k': 10
})

# Using get_similar (always available, no license required)
similar = await client.get_similar({
    'key': 'user:john',
    'k': 10
})
```

### QUERY

Make queries in natural language.

```python
result = await client.query({
    'query': 'Insert the value 5 into the grades array of user:123',
    'backend': 'openai:gpt-4o-mini'  # or "ollama:model-name"
})
```

### SET_MIDDLEWARE

Make the LLM analyze incoming queries and decide if it must reject them, accept them or modify them.

```python
await client.set_middleware({
    'operation': 'SET',
    'middleware': 'Only accept requests that have the amount field specified'
})
```

---

## Analytics Operations

### GET_OPERATIONS

Returns history of recent database operations.

```python
operations = await client.get_operations({})
```

### GET_ACCESS_FREQUENCY

Returns the number of times an object has been queried.

```python
frequency = await client.get_access_frequency({
    'key': 'user:john'
})
# Returns: {"type": "SUCCESS", "message": "OK", "id": "...", "data": 42}
```

---

## Mindspace Operations

Mindspaces are cognitive contexts for AI-powered conversations and semantic operations.

### SET_MINDSPACE / CREATE_MINDSPACE

Creates a new mindspace.

```python
# Create with custom ID
mindspace = await client.set_mindspace({
    'mindspace_id': 'conversation-1'
})

# Or use the alias
mindspace = await client.create_mindspace({
    'mindspace_id': 'project-alpha'
})
```

### DELETE_MINDSPACE

Deletes a mindspace and all its associated context.

```python
await client.delete_mindspace({
    'mindspace_id': 'conversation-1'
})
```

### CHAT_MINDSPACE

Chat with a mindspace using AI. The mindspace maintains conversation context.

```python
response = await client.chat_mindspace({
    'mindspace_id': 'conversation-1',
    'message': 'What do you know about users?'
})
# Returns: {"response": "AI response text...", "context": [...]}
```

### LECTURE_MINDSPACE

Imports text corpus into a mindspace for semantic search and context retrieval.

```python
await client.lecture_mindspace({
    'mindspace_id': 'conversation-1',
    'corpus': 'User John Doe is a software engineer who specializes in Rust and distributed systems...'
})
```

---

## Real-time Notifications

Subscribe to real-time updates when an object changes.

```python
async def on_update(data):
    print('User updated!', data)

await client.notify('user:john', on_update)
```

---

## Schema Class

The `Schema` class provides an object-oriented way to model your data.

```python
from satori import Satori
from satori.schema import Schema
import asyncio

async def main():
    satori = Satori("username", "password", "ws://localhost:1234")
    await satori.connect()
    
    # Create a user schema
    user = Schema(satori, "user", key="user:john", body={"name": "John", "age": 30})
    await user.set()
    
    # Update the user
    await user.update("age", 31)
    
    # Get the user
    user_data = await user.get()
    
    # Add a friend vertex
    await user.set_vertex("user:jane", relation="friend", weight=1.0)
    
    # Get all friends
    friends = await user.get_vertex()
    
    # Add tags
    await user.push("premium", "tags")
    await user.push("active", "tags")
    
    # Find similar users
    similar = await user.get_similar(k=5)
    
    # Encrypt the user data
    await user.encrypt("my-secret-key")
    
    # Delete the user
    await user.delete()

asyncio.run(main())
```

### Schema Methods

- `set()` - Create the object
- `delete()` - Delete the object
- `get()` - Get object data
- `update(field, value)` - Update a field
- `encrypt(key)` - Encrypt the object
- `decrypt(key)` - Decrypt the object
- `set_vertex(vertex, relation, weight)` - Add a vertex
- `get_vertex()` - Get all vertices
- `delete_vertex(vertex)` - Delete a vertex
- `dfs(relation)` - Depth-first search
- `graph_bfs(relation)` - Breadth-first search
- `graph_shortest_path(target)` - Find shortest path
- `push(value, array)` - Add to array
- `pop(array)` - Remove last from array
- `splice(array)` - Remove first from array
- `remove(value, array)` - Remove specific value
- `ann(k, ef, use)` - Find similar (ANN)
- `get_similar(k, ef, use)` - Find similar (alias)

---

## Response Format

All successful responses follow this structure:

```python
{
    "type": "SUCCESS",      # "SUCCESS" or "ERROR"
    "message": "OK",        # Status message
    "id": "request-id",     # Request ID
    "key": "optional-key",  # Object key (if applicable)
    "data": {...}           # Response data (if applicable)
}
```

### Error Response

```python
{
    "type": "ERROR",
    "message": "Error description",
    "id": "request-id"
}
```

### AI Response (ask)

```python
{
    "type": "SUCCESS",
    "message": "SUCCESS",
    "id": "request-id",
    "data": {
        "response": "AI-generated answer...",
        "context": [...],
        "session": "session-id"
    AI }
}
```

### Response (query)

```python
{
    "type": "SUCCESS",
    "message": "OK",
    "id": "request-id",
    "data": {
        "result": "Operation result...",
        "status": "SUCCESS"
    }
}
```

### AI Response (ann/get_similar)

```python
{
    "type": "SUCCESS",
    "message": "OK",
    "id": "request-id",
    "data": [{"key": "object-key", "distance": 0.123}, ...]
}
```

---

## Complete Example

```python
import asyncio
from satori import Satori

async def main():
    # Initialize client
    client = Satori(
        username='user',
        password='password',
        host='ws://localhost:8000'
    )
    await client.connect()
    
    # Create a user
    await client.set({
        'key': 'user:john',
        'data': {'name': 'John Doe', 'email': 'john@example.com', 'age': 30},
        'type': 'user'
    })
    
    # Create another user and link them
    await client.set({
        'key': 'user:jane',
        'data': {'name': 'Jane Doe', 'email': 'jane@example.com', 'age': 28},
        'type': 'user'
    })
    
    # Create a relationship
    await client.set_vertex({
        'key': 'user:john',
        'vertex': {'vertex': 'user:jane', 'relation': 'friend', 'weight': 1.0}
    })
    
    # Get all friends of john
    friends = await client.get_vertex({'key': 'user:john'})
    print(f"John's friends: {friends}")
    
    # Find shortest path from john to jane
    path = await client.graph_shortest_path({
        'node': 'user:john',
        'target': 'user:jane'
    })
    print(f"Path: {path}")
    
    # Subscribe to updates
    async def on_update(data):
        print(f'John was updated: {data}')
    await client.notify('user:john', on_update)
    
    # Update john
    await client.put({
        'key': 'user:john',
        'replace_field': 'age',
        'replace_value': 31
    })

asyncio.run(main())
```

---

## 🧠 Key Concepts

- **key**: Unique identifier of the object
- **type**: Object type (e.g., 'user', 'post')
- **data**: Object payload
- **vertices**: Graph-like relationships between objects
- **field_array**: Advanced filters for bulk operations
- **encryption_key**: Key used for AES encryption/decryption
- **mindspace**: Cognitive context for AI operations

---

## 💬 Questions or Suggestions?

Feel free to open an issue or contribute!

With ❤️ from the Satori team.
