Sirapana Ai Engine
Project description
PyRuntime — Python API Reference
PyRuntime is the Python-facing async interface to the SoulEngine Rust core. It is exposed via a PyO3 extension module (soulengine) and manages the full lifecycle of users, agents, topic threads, and message memory.
Installation & Import
from soulengine import PyRuntime
Note: The
soulengineextension module requires a compatible build of the Rust core. On Windows, ensuretorch's DLL directory is added before importing if you are using torch-backed models.
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 this launches the api server of the runtime
runtime = await PyRuntime.create(bind="127.0.0.1:3077")
This is a staticmethod and must be awaited. It initialises the internal Rust Runtime and wraps it in an async-safe Arc<RwLock<>>.
User Management
create_user(user_name: str) → str
Registers a new user identity within the runtime 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, used in insert_message.
Topic Threads
A topic thread is the shared conversation context that users and agents write to and read from.
create_topic_thread() → str
Creates a new topic thread and returns its topic_id.
topic_id = await runtime.create_topic_thread()
Returns: str — opaque topic ID.
topic_history_len(topic_id: str) → int
Returns the total number of messages currently in the topic thread.
length = await runtime.topic_history_len(topic_id)
Useful for polling — compare the value before and after a insert_message call to detect new activity.
iter_topic(topic_id: str, start_index: int) → str (JSON)
Returns a JSON-serialised list of messages starting from start_index.
raw = await runtime.iter_topic(topic_id, cursor)
entries = json.loads(raw) # list of dicts
Each entry dict 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) |
Tip: Track a cursor integer and pass it as start_index to fetch only new messages since the last poll:
cursor = await runtime.topic_history_len(topic_id)
# ... wait for activity ...
new_raw = await runtime.iter_topic(topic_id, cursor)
new_entries = json.loads(new_raw)
insert_message(topic_id: str, user_id: str, message: str) → str
Inserts a user message into the topic thread. This triggers any agents currently attached to the topic 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 value 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 runtime's configuration.
agents = await runtime.get_agent_list()
# e.g. ["Researcher", "Coder", "Writer"]
deploy_agent(agent_name: str) → str
Deploys a named agent from the config registry 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 a deployed agent to a topic thread. The agent will start responding to new messages inserted into that topic.
await runtime.add_agent_to_topic(topic_id, agent_id)
Returns: str — confirmation message.
Raises: ValueError on failure.
Only one agent should be active on a topic at a time in typical usage. Call
remove_agent_from_topicbefore switching agents.
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 message.
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 on the given topic thread. Use this to drive a "thinking..." indicator in your UI.
thinking = await runtime.is_agent_working_on_topic(topic_id, agent_id)
Agent Episodes (Internal Memory)
An episode is the agent's internal working memory for a topic — its scratchpad of 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 for a given topic.
ep_len = await runtime.agent_episode_len(topic_id, agent_id)
Use this alongside iter_agent_episode for incremental polling (same cursor pattern as iter_topic).
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.
raw = await runtime.iter_agent_episode(topic_id, agent_id, cursor)
entries = json.loads(raw)
Each entry has the same shape as iter_topic entries (name, role, content). The content field typically contains structured response blocks (see Block Format below).
Block Format
Agent message content uses a fenced block convention that the UI layer parses:
```thoughts
Agent reasoning goes here...
```
```terminal
tool_call(arg="value")
-> result
```
```output
Final response to the user.
```
```validation
Cross-check notes here.
```
```followup_context
Handoff state for the next turn.
```
A regex parser extracts these blocks for display:
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 — Required TOML Files
PyRuntime.create() reads two TOML files at startup. Both must exist and be valid before calling the constructor or it will fail to initialise. If you installed from PyPI, run kattalai-setup once then kattalai-folder to open the directory where these files live. If you built from source, they are already present at ./configs/ in the repo root.
configs/
├── inference_config.toml ← LLM provider credentials & settings
└── agents_config.toml ← Agent definitions (name, goal, models, apps)
configs/inference_config.toml
Defines one or more LLM providers. The runtime reads this to know which inference backends are available. You only need to fill in the providers you actually intend to use — unused blocks can be left out or left with empty API keys.
[[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"
Supported inference_provider values (referenced in agents_config.toml):
| Value | Notes |
|---|---|
ollama |
Local inference, no API key needed. Requires Ollama running 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 setup (local-only, no API keys): Install Ollama, pull a model, and only include [[ollama_config]]:
ollama pull qwen3:4b # reasoning model
ollama pull qwen3:0.6b # lightweight NLP model
[[ollama_config]]
chat_api_url = "http://localhost:11434/api/chat"
generate_api_url = "http://localhost:11434/api/generate"
temperature = 0.1
configs/agents_config.toml
Defines the agents that deploy_agent(agent_name) can load. Each [[agent_config]] block creates one deployable agent. The agent_name field here is exactly what you pass to deploy_agent() and what get_agent_list() returns.
[[agent_config]]
agent_name = "DIA"
agent_goal = "To assist user with their queries"
backstory = "You are an AI assistant"
# Primary model — used for multi-step reasoning and planning
reasoning_model = { inference_provider = "ollama", model_id = "qwen3:4b" }
# Lightweight model — used for fast NLP classification and tool routing
nlp_model = { inference_provider = "ollama", model_id = "qwen3:0.6b" }
# Apps available to this agent at deploy time
default_apps = ["clock_app"]
Key fields:
| Field | Type | Description |
|---|---|---|
agent_name |
str |
Must match the name passed to deploy_agent() |
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 handle names loaded on deploy (must match app_handle_name in app TOMLs) |
Mixing providers per agent is supported — e.g. Gemini for reasoning and Ollama for NLP:
reasoning_model = { inference_provider = "gemini", model_id = "gemini-2.5-flash" }
nlp_model = { inference_provider = "ollama", model_id = "qwen3:0.6b" }
Multiple agents — add more [[agent_config]] blocks. All defined agents show up in get_agent_list():
[[agent_config]]
agent_name = "Researcher"
agent_goal = "Research topics thoroughly and summarise findings"
backstory = "You are a careful research assistant"
reasoning_model = { inference_provider = "ollama", model_id = "qwen3:4b" }
nlp_model = { inference_provider = "ollama", model_id = "qwen3:0.6b" }
default_apps = ["grep_app", "notes_app"]
[[agent_config]]
agent_name = "Coder"
agent_goal = "Write and debug Python code"
backstory = "You are a senior software engineer"
reasoning_model = { inference_provider = "gemini", model_id = "gemini-2.5-flash" }
nlp_model = { inference_provider = "ollama", model_id = "qwen3:0.6b" }
default_apps = ["calculator_app", "grep_app"]
App TOML Schema (for default_apps)
Each app listed in default_apps must have a corresponding TOML file under apps/. The runtime auto-discovers all *.toml files under ./apps/ at startup — no registration step needed. The app_handle_name in the app TOML is the string you reference 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" # ← this string goes in default_apps
app_launch_mode = "REPL" # "REPL" (persistent) or "ONE_SHOT"
app_usage_guideline = """
Use this app to get the current time or set timed reminders.
"""
[[app_command_signatures]]
command = "get_time"
consumes = []
produces = ["current_time"]
action = "read"
[[app_command_signatures]]
command = "set_alarm"
consumes = ["duration", "label"]
produces = ["alarm_confirmation"]
action = "write"
Built-in app handles (available after kattalai-setup):
app_handle_name |
Description |
|---|---|
clock_app |
Current time and timed alarms |
notes_app |
Note creation, search, and backup |
grep_app |
Regex/literal file search |
calculator_app |
Arithmetic and expression evaluation |
stock_tracker |
Live quotes and watchlists via yfinance |
webpage_reader |
Web page extraction via Playwright |
Configuration → PyRuntime relationship
The diagram below shows exactly which config values feed into which PyRuntime calls:
inference_config.toml agents_config.toml
───────────────────── ──────────────────
[[ollama_config]] ┐ agent_name ──────────── deploy_agent("DIA")
[[gemini_config]] ├──────► reasoning_model get_agent_list() → ["DIA", ...]
[[huggingface_config]] │ nlp_model
[[sarvam_config]] ┘ default_apps ─────────── app TOMLs in ./apps/
│
└── agent_goal ─────── system prompt (internal)
backstory ─────── system prompt (internal)
Both files are read once inside PyRuntime.create(). Changing them requires restarting the runtime — there is no hot-reload.
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. Send a message and poll for reply
cursor = await runtime.topic_history_len(topic_id)
await runtime.insert_message(topic_id, user_id, "Hello!")
while True:
await asyncio.sleep(0.5)
new_len = await runtime.topic_history_len(topic_id)
if new_len > cursor:
break
# 4. 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. Wrap calls in try/except:
try:
await runtime.insert_message(topic_id, user_id, text)
except ValueError as e:
print(f"SoulEngine error: {e}")
Thread Safety Notes
- The Rust runtime uses
Arc<RwLock<Runtime>>internally. create,deploy_agent,create_user,create_topic_threadacquire a write lock.insert_message,iter_topic,topic_history_len, and all read operations acquire a read lock.- All methods are
async— use withasyncioor an async framework liketextual.
Writing a New App
Apps are the tools agents use to act in the world. Each app is a self-contained Python script paired with a TOML config. The runtime discovers apps automatically — there is no registration step. Drop a folder with a .py + .toml pair under apps/ and it will be available to any agent whose default_apps list references its handle.
Run kattalai-folder to open the install directory, then navigate to apps/ to see the existing core apps as reference implementations.
Step 1 — Create the folder
apps/
└── core_apps/ # or other_apps/ for optional/experimental apps
└── my_new_app/
├── my_new_app.py # the app script
└── my_new_app.toml # the capability config
Both files must sit in the same folder. The folder name, script name, and TOML name do not need to match each other, but it is conventional to keep them consistent.
Step 2 — Write the TOML config
The TOML config is what the runtime embeds and uses for tool routing. Write it carefully — the app_usage_guideline text is the primary signal the agent uses to decide when to invoke this app.
app_name = "Weather App"
app_path = "./apps/core_apps/weather_app/weather_app.py"
app_start_command = "python"
app_start_args = "./apps/core_apps/weather_app/weather_app.py"
app_handle_name = "weather_app" # unique handle — used in default_apps and agent commands
app_launch_mode = "REPL" # "REPL" or "ONE_SHOT" — see below
app_usage_guideline = """
Use this app to get current weather conditions or forecasts for any city.
Invoke when the user asks about weather, temperature, rain, or climate.
"""
[[app_command_signatures]]
command = "current"
consumes = ["city_name"]
produces = ["temperature", "conditions", "humidity"]
action = "read"
[[app_command_signatures]]
command = "forecast"
consumes = ["city_name", "days"]
produces = ["forecast_list"]
action = "read"
Field reference:
| Field | Required | Description |
|---|---|---|
app_name |
✓ | Human-readable display name |
app_path |
✓ | Path to the Python script, relative to the runtime working directory |
app_start_command |
✓ | Interpreter — almost always "python" |
app_start_args |
✓ | Arguments passed to app_start_command — typically same as app_path |
app_handle_name |
✓ | Unique short identifier. Must be a valid identifier string with no spaces. Referenced in default_apps and in agent commands as &<handle_name> |
app_launch_mode |
✓ | "REPL" — process is started once and kept alive. "ONE_SHOT" — process is spawned fresh per invocation |
app_usage_guideline |
✓ | Free text description embedded by the runtime. This is the primary signal for semantic app selection — be specific about when to use the app |
[[app_command_signatures]] |
✓ (≥1) | One block per command the app accepts |
command |
✓ | Command name string — must match what the Python script expects |
consumes |
✓ | Input type names (can be empty list []) |
produces |
✓ | Output type names |
action |
✓ | 1–2 word verb describing the operation: read, write, compute, search, fetch, send, delete |
app_launch_mode guidance:
| Mode | When to use |
|---|---|
REPL |
App maintains state between calls (e.g. clock alarms, open file handles, cached data). The process stays running and listens for repeated commands on stdin. |
ONE_SHOT |
App is stateless or each call is independent (e.g. notes lookup, single calculation). A fresh Python process is spawned per invocation. |
Step 3 — Write the Python script
The script communicates with the Rust terminal layer through a structured message protocol on stdin/stdout. The soul_engine_app helper from se_app_utils manages the REPL loop and protocol framing for you.
Import pattern:
import json
import sys
import asyncio
from pathlib import Path
# Walk up to the apps/ root so se_app_utils is importable
apps_path = Path(__file__).resolve().parent.parent.parent
sys.path.append(str(apps_path))
import se_app_utils
from se_app_utils.soulengine import soul_engine_app
Path(__file__).resolve().parent.parent.parentassumes the script is three levels deep underapps/(e.g.apps/core_apps/my_app/my_app.py). Adjust.parentcount if your layout differs.
Minimal REPL app skeleton:
import json
import sys
import asyncio
from pathlib import Path
apps_path = Path(__file__).resolve().parent.parent.parent
sys.path.append(str(apps_path))
import se_app_utils
from se_app_utils.soulengine import soul_engine_app
async def process_command(se_interface, args):
"""
Called by soul_engine_app for every inbound invocation.
Args:
se_interface – messaging handle; call se_interface.send_message(json_str) to reply
args – list of string tokens from the agent's command invocation;
args[0] is typically the subcommand, args[1:] are parameters
"""
if not args:
se_interface.send_message(json.dumps({
"status": "error",
"message": "No command provided"
}))
return
command = args[0]
if command == "current":
city = args[1] if len(args) > 1 else "unknown"
# Replace with real implementation
se_interface.send_message(json.dumps({
"status": "ok",
"city": city,
"temperature": "28°C",
"conditions": "partly cloudy",
"humidity": "65%"
}))
elif command == "forecast":
city = args[1] if len(args) > 1 else "unknown"
se_interface.send_message(json.dumps({
"status": "ok",
"city": city,
"forecast": ["Mon 28°C", "Tue 27°C", "Wed 29°C"]
}))
else:
se_interface.send_message(json.dumps({
"status": "error",
"message": f"Unknown command: {command}"
}))
if __name__ == "__main__":
soul_app = soul_engine_app(app_name="Weather App")
soul_app.run_repl(main_fn=process_command)
What soul_engine_app does for you:
- Reads JSON-encoded commands from stdin in the format the Rust terminal layer sends.
- Calls your
process_command(se_interface, args)coroutine with a messaging handle and the parsed argument list. - Keeps the process alive in a loop for
REPLmode. se_interface.send_message(json_str)writes the response back to stdout wrapped in the[#APP_MESSAGE>...]protocol markers that the Rust layer reads.
Protocol message format (for reference — handled by soul_engine_app automatically):
# Inbound (Rust → Python, on stdin):
[#APP_INVOKE>{"command": "current", "args": ["Chennai"]}]
# Outbound (Python → Rust, on stdout):
[#APP_MESSAGE>{"status": "ok", "temperature": "32°C", "conditions": "sunny"}]
You only need to interact with the protocol directly if you are not using soul_engine_app.
Async support: process_command is an async def — you can await asyncio.sleep(...) or any other coroutine inside it. This is particularly useful for timer-based apps that need to send an acknowledgement, wait, then fire a follow-up message:
async def process_command(se_interface, args):
# ... parse and validate args ...
# Send acknowledgement immediately
se_interface.send_message(json.dumps({
"status": "alarm_set",
"fires_at": target_str,
"message": alarm_msg
}))
await asyncio.sleep(seconds) # non-blocking wait
# Fire the follow-up after the delay
se_interface.send_message(json.dumps({
"status": "alarm_fired",
"message": alarm_msg
}))
Step 4 — Register the handle in agents_config.toml
Add your new app_handle_name to the default_apps list of any agent that should have access to it:
[[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", "notes_app", "weather_app"] # ← add here
The app_handle_name in the TOML and the string in default_apps must match exactly.
Step 5 — Verify discovery
Restart the runtime and check the Logs tab (or cargo run --release output) for a line like:
[appstore] loaded: weather_app (REPL) commands: current, forecast
If the app does not appear, common causes are:
- The TOML file is not under the
./apps/directory tree. app_handle_namecontains spaces or special characters.- A syntax error in the TOML — run
python -c "import tomllib; tomllib.load(open('my_new_app.toml','rb'))"to validate. - The
app_pathis wrong relative to the runtime working directory (always the repo/install root, not the app folder).
Complete working example — calculator_app
This is one of the built-in core apps and is a good reference for a minimal correct implementation.
apps/core_apps/calculator_app/calculator_app.toml:
app_name = "Calculator App"
app_path = "./apps/core_apps/calculator_app/calculator_app.py"
app_start_command = "python"
app_start_args = "./apps/core_apps/calculator_app/calculator_app.py"
app_handle_name = "calculator_app"
app_launch_mode = "REPL"
app_usage_guideline = """
Use this app for arithmetic calculations, expression evaluation, and storing
named variables for reuse across computations. Invoke when the user asks to
calculate, compute, evaluate an expression, or do maths.
"""
[[app_command_signatures]]
command = "calculate"
consumes = ["expression"]
produces = ["result"]
action = "compute"
[[app_command_signatures]]
command = "store"
consumes = ["variable_name", "value"]
produces = ["confirmation"]
action = "write"
[[app_command_signatures]]
command = "recall"
consumes = ["variable_name"]
produces = ["value"]
action = "read"
apps/core_apps/calculator_app/calculator_app.py:
import json
import sys
import asyncio
from pathlib import Path
apps_path = Path(__file__).resolve().parent.parent.parent
sys.path.append(str(apps_path))
import se_app_utils
from se_app_utils.soulengine import soul_engine_app
_vars: dict = {} # persistent variable store across REPL calls
async def process_command(se_interface, args):
"""
Commands:
calculate <expression> → evaluate expression (may reference stored vars)
store <variable_name> <value> → store a named variable
recall <variable_name> → retrieve a stored variable
"""
if not args:
se_interface.send_message(json.dumps({
"status": "error",
"message": "No command provided. Use: calculate | store | recall"
}))
return
command = args[0]
if command == "calculate":
expr = " ".join(args[1:]).strip()
if not expr:
se_interface.send_message(json.dumps({
"status": "error",
"message": "No expression provided."
}))
return
try:
result = eval(expr, {"__builtins__": {}}, _vars)
se_interface.send_message(json.dumps({
"status": "ok",
"expression": expr,
"result": result
}))
except Exception as e:
se_interface.send_message(json.dumps({
"status": "error",
"message": str(e)
}))
elif command == "store":
if len(args) < 3:
se_interface.send_message(json.dumps({
"status": "error",
"message": "Usage: store <variable_name> <value>"
}))
return
name = args[1]
value = args[2]
try:
_vars[name] = eval(value, {"__builtins__": {}}, _vars)
except Exception:
_vars[name] = value # store as string if eval fails
se_interface.send_message(json.dumps({
"status": "ok",
"stored": name,
"value": _vars[name]
}))
elif command == "recall":
if len(args) < 2:
se_interface.send_message(json.dumps({
"status": "error",
"message": "Usage: recall <variable_name>"
}))
return
name = args[1]
if name in _vars:
se_interface.send_message(json.dumps({
"status": "ok",
"variable": name,
"value": _vars[name]
}))
else:
se_interface.send_message(json.dumps({
"status": "error",
"message": f"Variable '{name}' not found."
}))
else:
se_interface.send_message(json.dumps({
"status": "error",
"message": f"Unknown command: {command}"
}))
if __name__ == "__main__":
soul_app = soul_engine_app(app_name="Calculator App")
soul_app.run_repl(main_fn=process_command)
App authoring checklist
Before testing your new app end-to-end with the runtime:
- Folder created under
apps/core_apps/orapps/other_apps/ - TOML has unique
app_handle_namewith no spaces -
app_pathis relative to the runtime working directory (repo/install root), not to the app folder -
app_usage_guidelineclearly describes when to use the app — this is embedded for semantic search - At least one
[[app_command_signatures]]block defined - Python script resolves
apps_pathviaPath(__file__).resolve().parent.parent.parentand appends it tosys.path - Python script imports
soul_engine_appfromse_app_utils.soulengine - Entry point is
async def process_command(se_interface, args)— all replies viase_interface.send_message(json.dumps({...})) - Script bottom is
soul_engine_app(app_name="...").run_repl(main_fn=process_command) -
app_handle_nameadded todefault_appsinagents_config.tomlfor the relevant agent - Restart runtime and confirm the app appears in Logs tab
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-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
715fb9abfb1f0d5f0264dfa66050e02c28e137c28156e0491464e63c47c883cd
|
|
| MD5 |
1503015dd094a3239c320fb1a54d9c18
|
|
| BLAKE2b-256 |
a7074c03fa5252f9cc15dbbaad38e6cfdd3d3e49c1f5a76463ab1f6fd0fb528e
|
File details
Details for the file kattalai-0.4.1-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
a39186f93b03a2ad91b1d7ccff73a1391aef3e6b2d99c7cdedd0c7c3852d1626
|
|
| MD5 |
8a1eef97d73653e062a7331790ab0bd0
|
|
| BLAKE2b-256 |
716a4c791bdc750265b62675d8327acc43ce99bec55f987fc7a46d5c43a6f9c8
|
File details
Details for the file kattalai-0.4.1-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
f23b5406ded467d500c977f6710e61838f98c18a3e3a66a4e9b7d10430dd1514
|
|
| MD5 |
77bfc24ac9b905353bb910dc4a3a296b
|
|
| BLAKE2b-256 |
cb31383c5293e26e96195c69d435fc550a254d187e854eea7fc20445d20ff51c
|
File details
Details for the file kattalai-0.4.1-cp311-cp311-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
80d24ed607210fe6d23dbe8692f76f0d68aaf41cd39e4f9f1d54bee57ddfa7cc
|
|
| MD5 |
5a933fe6cea178dfe15155d6162111a2
|
|
| BLAKE2b-256 |
d4425c66fca07dc13307256743036da144185b235c9c126cd409603890e5aea4
|
File details
Details for the file kattalai-0.4.1-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
a25201978b0105979c6a060a94bf6c604e2b1eddbb1e2479c1ef6235d53a6447
|
|
| MD5 |
ff7ecb47d2fbb2b213cbe94df61b219c
|
|
| BLAKE2b-256 |
bccc8ac6199df46bc256cc0e89f70b887203649a4dbdc9fd447c6ebdfabf5fa5
|
File details
Details for the file kattalai-0.4.1-cp310-cp310-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
281941d4a38ae9bc1fc848ee59746ff4cf6b03cd48421555ba186ef980a88dba
|
|
| MD5 |
ecb1d0ad7942e6268ee00d6672991d1c
|
|
| BLAKE2b-256 |
b4c5830bcf36b45fd3ae0ce079557d89f20d573097c5bd261895752264011bfa
|
File details
Details for the file kattalai-0.4.1-cp39-cp39-win_amd64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
f14936429abc7898c43b253806321206dcb1db689c53fd615075486ee182dfa7
|
|
| MD5 |
78e094637a6657a598a0850003a50be1
|
|
| BLAKE2b-256 |
c596ad4cddef13090b6b3a35dce13f3d1e78b7c1ea7bc21f964facd2e40e95f5
|
File details
Details for the file kattalai-0.4.1-cp39-cp39-macosx_11_0_arm64.whl.
File metadata
- Download URL: kattalai-0.4.1-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 |
cfab303e5786b288f5bfae493fdddaf57d346dd164d0d4aeaf66fc884da92b09
|
|
| MD5 |
b3d04a0c1dbb2da0e3d917aca3b6a80d
|
|
| BLAKE2b-256 |
47855613bb2dabe838e049f9dbdfe5d79976f23e85f15fc442493c67d52132c9
|