Sirapana Ai Engine
Project description
PyRuntime — Python API Reference
PyRuntime is the Python-facing async interface to the soulengine Rust core. Exposed via a PyO3 extension module, it manages the full lifecycle of users, agents, topic threads, and message memory.
Contents
- Installation & Import
- Initialisation
- User Management
- Topic Threads
- Agent Management
- Agent Episodes
- Block Format
- Configuration Reference
- Typical Usage Pattern
- Error Handling
- Thread Safety
Installation & Import
from soulengine import PyRuntime
Windows: Ensure PyTorch's DLL directory is added before importing.
import torch, os
os.add_dll_directory(os.path.join(os.path.dirname(torch.__file__), "lib"))
from soulengine import PyRuntime
Initialisation
PyRuntime.create(bind="127.0.0.1:3077") → PyRuntime
Creates and returns a new PyRuntime instance. This is the only constructor — do not call PyRuntime() directly. Also launches the HTTP API server at the given bind address.
runtime = await PyRuntime.create(bind="127.0.0.1:3077")
This is a staticmethod and must be awaited. It initialises the internal Rust Runtime wrapped in Arc<RwLock<>>.
Reads two config files at startup — both must exist and be valid:
configs/
├── inference_config.toml
└── agents_config.toml
User Management
create_user(user_name: str) → str
Registers a new user identity and returns a unique user_id.
user_id = await runtime.create_user("kattalaiUser")
| Parameter | Type | Description |
|---|---|---|
user_name |
str |
Display name for the user |
Returns: str — opaque user ID, required for insert_message.
Topic Threads
A topic thread is the shared conversation context that users and agents read from and write to.
create_topic_thread() → str
Creates a new topic thread and returns its topic_id.
topic_id = await runtime.create_topic_thread()
topic_history_len(topic_id: str) → int
Returns the total number of messages in the thread. Use this for polling — compare before and after insert_message to detect new activity.
length = await runtime.topic_history_len(topic_id)
iter_topic(topic_id: str, start_index: int) → str (JSON)
Returns a JSON-serialised list of messages from start_index onward.
raw = await runtime.iter_topic(topic_id, cursor)
entries = json.loads(raw) # list of dicts
Each entry contains:
| Key | Type | Description |
|---|---|---|
name |
str |
Source name (agent name or user handle) |
role |
str |
"user" or "assistant" |
content |
str |
Raw message text (may contain block markers) |
Incremental polling pattern:
# Snapshot length before sending
cursor = await runtime.topic_history_len(topic_id)
# ... send message, wait ...
# Fetch only new entries
new_entries = json.loads(await runtime.iter_topic(topic_id, cursor))
insert_message(topic_id: str, user_id: str, message: str) → str
Inserts a user message into the thread. This triggers attached agents to begin processing.
await runtime.insert_message(topic_id, user_id, "Summarise my unread emails")
| Parameter | Type | Description |
|---|---|---|
topic_id |
str |
Target topic thread |
user_id |
str |
Sender identity (from create_user) |
message |
str |
Message text |
Returns: str — confirmation from the Rust layer.
Raises: ValueError if the insert fails.
Agent Management
get_agent_list() → list[str]
Returns the list of agent names registered in the config.
agents = await runtime.get_agent_list()
# e.g. ["Researcher", "Coder", "DIA"]
deploy_agent(agent_name: str) → str
Deploys a named agent and returns its agent_id.
agent_id = await runtime.deploy_agent("Researcher")
| Parameter | Type | Description |
|---|---|---|
agent_name |
str |
Must match a name in get_agent_list() |
Returns: str — opaque agent ID used in topic and episode calls.
add_agent_to_topic(topic_id: str, agent_id: str) → str
Attaches an agent to a topic thread. The agent begins responding to new messages inserted into that topic.
await runtime.add_agent_to_topic(topic_id, agent_id)
Only one agent should be active on a topic at a time. Call
remove_agent_from_topicbefore switching agents.
Returns: str — confirmation. Raises: ValueError on failure.
remove_agent_from_topic(topic_id: str, agent_id: str) → str
Detaches an agent from a topic thread.
await runtime.remove_agent_from_topic(topic_id, agent_id)
Returns: str — confirmation. Raises: ValueError on failure.
is_agent_working_on_topic(topic_id: str, agent_id: str) → bool
Returns True if the agent is currently processing a message. Use this to drive a "thinking…" indicator.
thinking = await runtime.is_agent_working_on_topic(topic_id, agent_id)
Agent Episodes (Internal Memory)
An episode is the agent's internal working scratchpad for a topic — thoughts, tool calls, and intermediate outputs, separate from the shared topic history.
agent_episode_len(topic_id: str, agent_id: str) → int
Returns the number of entries in the agent's episode memory.
ep_len = await runtime.agent_episode_len(topic_id, agent_id)
iter_agent_episode(topic_id: str, agent_id: str, start_index: int) → str (JSON)
Returns a JSON-serialised list of the agent's internal episode entries from start_index onward. Same shape as iter_topic entries (name, role, content). The content field contains structured response blocks.
raw = await runtime.iter_agent_episode(topic_id, agent_id, cursor)
entries = json.loads(raw)
Block Format
Agent content fields use fenced-block conventions that the UI layer parses:
```thoughts
Agent reasoning goes here...
```
```terminal
&app_handle command arg
-> result
```
```output
Final response to the user.
```
```validation
Cross-check notes.
```
```followup_context
Handoff state for the next turn.
```
Parser:
import re
def parse_se_content(content: str) -> list[tuple[str, str]]:
pattern = re.compile(
r'```(thoughts|terminal|output|validation|followup_context)\s*\n(.*?)```',
re.DOTALL
)
matches = pattern.findall(content)
if matches:
return [(kind.strip(), body.strip()) for kind, body in matches if body.strip()]
return [("output", content.strip())] # plain-text fallback
Configuration Reference
Both files are read once inside PyRuntime.create(). Changes require a runtime restart — there is no hot-reload.
inference_config.toml
[[ollama_config]]
chat_api_url = "http://localhost:11434/api/chat"
generate_api_url = "http://localhost:11434/api/generate"
temperature = 0.1
[[gemini_config]]
api_key = "YOUR_GEMINI_API_KEY"
temperature = 0.1
[[huggingface_config]]
api_key = "YOUR_HF_API_KEY"
max_new_tokens = 8000
temperature = 0.1
[[sarvam_config]]
api_key = "YOUR_SARVAM_API_KEY"
max_new_tokens = 8000
temperature = 0.1
reasoning_effort = "high"
inference_provider |
Notes |
|---|---|
ollama |
Local inference, no API key. Requires Ollama on localhost:11434. |
gemini |
Google Gemini API. Requires api_key. |
huggingface |
HuggingFace Inference Router. Requires api_key. |
sarvam |
Sarvam AI API. Requires api_key. |
Minimum local-only config:
[[ollama_config]]
chat_api_url = "http://localhost:11434/api/chat"
generate_api_url = "http://localhost:11434/api/generate"
temperature = 0.1
agents_config.toml
[[agent_config]]
agent_name = "DIA"
agent_goal = "To assist user with their queries"
backstory = "You are an AI assistant"
reasoning_model = { inference_provider = "ollama", model_id = "qwen3:4b" }
nlp_model = { inference_provider = "ollama", model_id = "qwen3:0.6b" }
default_apps = ["clock_app"]
| Field | Type | Description |
|---|---|---|
agent_name |
str |
Name passed to deploy_agent() and returned by get_agent_list() |
agent_goal |
str |
Injected into the agent's system prompt as its objective |
backstory |
str |
Additional persona context in the system prompt |
reasoning_model |
inline table | Provider + model ID for the main thinking loop |
nlp_model |
inline table | Provider + model ID for fast NLP/routing decisions |
default_apps |
list[str] |
App handles loaded on deploy — must match app_handle_name in app TOMLs |
Mixing providers per agent:
reasoning_model = { inference_provider = "gemini", model_id = "gemini-2.5-flash" }
nlp_model = { inference_provider = "ollama", model_id = "qwen3:0.6b" }
App TOML Schema
The app_handle_name in an app's TOML is the string referenced in default_apps.
app_name = "Clock App"
app_path = "./apps/core_apps/clock_app/clock_app.py"
app_start_command = "python"
app_start_args = "./apps/core_apps/clock_app/clock_app.py"
app_handle_name = "clock_app"
app_launch_mode = "REPL"
app_usage_guideline = """
Use to get the current time or set timed reminders.
"""
[[app_command_signatures]]
command = "get_time"
consumes = []
produces = ["current_time"]
action = "read"
Built-in handle names (available after kattalai-setup):
| Handle | Description |
|---|---|
clock_app |
Current time and alarms |
notes_app |
Note CRUD and search |
grep_app |
File and stdin search |
calculator_app |
Arithmetic and variables |
stock_tracker |
Live quotes via yfinance |
webpage_reader |
Web extraction via Playwright |
Typical Usage Pattern
import asyncio, json
from soulengine import PyRuntime
async def main():
# 1. Bootstrap
runtime = await PyRuntime.create()
user_id = await runtime.create_user("alice")
topic_id = await runtime.create_topic_thread()
# 2. Deploy and attach an agent
agents = await runtime.get_agent_list()
agent_id = await runtime.deploy_agent(agents[0])
await runtime.add_agent_to_topic(topic_id, agent_id)
# 3. Snapshot cursor, send message
cursor = await runtime.topic_history_len(topic_id)
await runtime.insert_message(topic_id, user_id, "Hello!")
# 4. Poll until the agent finishes
while await runtime.is_agent_working_on_topic(topic_id, agent_id):
await asyncio.sleep(0.5)
# 5. Fetch new messages
entries = json.loads(await runtime.iter_topic(topic_id, cursor))
for entry in entries:
if entry["role"] != "user":
print(f"{entry['name']}: {entry['content']}")
asyncio.run(main())
Error Handling
All PyRuntime methods raise ValueError on internal Rust errors.
try:
await runtime.insert_message(topic_id, user_id, text)
except ValueError as e:
print(f"SoulEngine error: {e}")
Thread Safety
The Rust runtime uses Arc<RwLock<Runtime>> internally.
| Operations | Lock type |
|---|---|
create, deploy_agent, create_user, create_topic_thread |
Write lock |
insert_message, iter_topic, topic_history_len, all reads |
Read lock |
All methods are async — use with asyncio or an async framework like textual. Do not share a PyRuntime instance across threads without an async executor.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kattalai-0.4.1.3-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 24.6 MB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07dcdac3f0208f7a80f4cdac4504a8b3742e66ef63df6376b9cf0a1fc8e03a03
|
|
| MD5 |
746758e58707d194d5a5f143b55ad6ec
|
|
| BLAKE2b-256 |
f739109ffa39947bda1374c541ecab4c2d657e8f90b905fde3eb8176393e6d02
|
File details
Details for the file kattalai-0.4.1.3-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 24.1 MB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91ec977f67d6a3d137907003a09fa98a80fc38df27c37e2d70d8b2b3032c62f4
|
|
| MD5 |
30e310706baf27fb2fc9a47eb79e798f
|
|
| BLAKE2b-256 |
07bb959575905aa43a1ff7bc0a8317840aa655e16ca6c6e2a3dd1fe5904ee365
|
File details
Details for the file kattalai-0.4.1.3-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 24.6 MB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cce79b21b813ccbac45275be61a0c1fa814c491d1d5e4e3de78a6466c60ab7c6
|
|
| MD5 |
694107a792f45fc7c780a0c8751bde06
|
|
| BLAKE2b-256 |
54845bf6286e21c07135add979fa9b28917edeaf6acbc2b31af48a6fd0446090
|
File details
Details for the file kattalai-0.4.1.3-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp311-cp311-macosx_11_0_arm64.whl
- Upload date:
- Size: 24.1 MB
- Tags: CPython 3.11, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1502bd6a41bee930257ed4eae2c1a036fdef6bb26396154ba61399eddeb29806
|
|
| MD5 |
d18cf0f0d55f763a250b47a53118584f
|
|
| BLAKE2b-256 |
7c5db662362f197633d4e1c5f2d597f9f0bb753675301d4f64dca119f5f67333
|
File details
Details for the file kattalai-0.4.1.3-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 24.6 MB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1279e2ca338f3d5d20046fe3aeeb303f9688a930d436739b48141ece2601aec
|
|
| MD5 |
4b4fec5861ed626f0536b51d03fd45fe
|
|
| BLAKE2b-256 |
57c853ffb0a0cb10e579afdbf732399a85fb01deb0448d84f9b2ec70fa0484a1
|
File details
Details for the file kattalai-0.4.1.3-cp310-cp310-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp310-cp310-macosx_11_0_arm64.whl
- Upload date:
- Size: 24.1 MB
- Tags: CPython 3.10, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
570bc740f6582204a3e52b0cddf7dc8a85478a87a2076c6eed10d4909486c914
|
|
| MD5 |
6da96f8305a32f907af668d1ffcc5f3c
|
|
| BLAKE2b-256 |
8b036daa283b05ef448d15adc6166043798fa4e2c0be87207c7fc1891398c987
|
File details
Details for the file kattalai-0.4.1.3-cp39-cp39-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp39-cp39-win_amd64.whl
- Upload date:
- Size: 24.6 MB
- Tags: CPython 3.9, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2dc107bc4c0af4d834acd9847faeb53bedf0add0f3664906593dfd220eb17f8
|
|
| MD5 |
b9c0aea328cb5240d0c80cbfa79a0ba4
|
|
| BLAKE2b-256 |
272e6f271cafb670be29925333727a5e96c94fe2af4c37b7ea698c7fdd1c516a
|
File details
Details for the file kattalai-0.4.1.3-cp39-cp39-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1.3-cp39-cp39-macosx_11_0_arm64.whl
- Upload date:
- Size: 24.1 MB
- Tags: CPython 3.9, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c4f34a4cd86a617d9ad5f856424a10b5905c9e71a1d7092f9c9dbb29c0a0f56
|
|
| MD5 |
9e1ebca2e565db5913e6ad369f5fa154
|
|
| BLAKE2b-256 |
c44bda1b529b7079f7c109af3affe7d98c3dfd10d3a736315de95628e2512c2f
|