orkaia 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orkaia-0.1.0/.gitignore +19 -0
- orkaia-0.1.0/PKG-INFO +95 -0
- orkaia-0.1.0/README.md +82 -0
- orkaia-0.1.0/orka/__init__.py +25 -0
- orkaia-0.1.0/orka/client.py +91 -0
- orkaia-0.1.0/orka/decorators.py +123 -0
- orkaia-0.1.0/orka/exceptions.py +14 -0
- orkaia-0.1.0/pyproject.toml +23 -0
orkaia-0.1.0/.gitignore
ADDED
orkaia-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: orkaia
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Governance and audit SDK for AI agents
|
|
5
|
+
Project-URL: Homepage, https://orka.ia.br
|
|
6
|
+
Project-URL: Documentation, https://orka.ia.br/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/orka-runtime/orka-sdk
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: agents,ai,audit,compliance,eu-ai-act,governance
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: httpx>=0.27.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# orka-sdk
|
|
15
|
+
|
|
16
|
+
Governance and audit SDK for AI agents. Add policy enforcement, immutable audit trails, and trust scoring to any agent with one decorator.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install orka-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
import orka
|
|
28
|
+
|
|
29
|
+
# Initialize once at startup (get your API key at orka.ia.br)
|
|
30
|
+
orka.init(api_key="orka_your_key_here")
|
|
31
|
+
|
|
32
|
+
# Wrap any agent function
|
|
33
|
+
@orka.guard(agent_id="your-agent-uuid", task_type="summarize")
|
|
34
|
+
def summarize(text: str) -> str:
|
|
35
|
+
return openai_client.chat(text)
|
|
36
|
+
|
|
37
|
+
# Works with async too
|
|
38
|
+
@orka.guard(agent_id="your-agent-uuid", task_type="financial_transfer", risk="HIGH")
|
|
39
|
+
async def transfer_funds(amount: float, to_account: str) -> dict:
|
|
40
|
+
return await payment_api.transfer(amount, to_account)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That's it. Every call now:
|
|
44
|
+
- ✅ Checks your X-Shield policies before executing
|
|
45
|
+
- ✅ Logs to X-Ledger with immutable hash chain
|
|
46
|
+
- ✅ Updates the agent's trust score
|
|
47
|
+
- ✅ Blocks execution if policy denies it (raises `OrkaPolicyBlocked`)
|
|
48
|
+
|
|
49
|
+
## Handle policy blocks
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from orka import OrkaPolicyBlocked
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
result = transfer_funds(10000, "acc_123")
|
|
56
|
+
except OrkaPolicyBlocked as e:
|
|
57
|
+
print(f"Blocked: {e.reason}")
|
|
58
|
+
print(f"Policy: {e.policy_name}")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Works with LangChain, CrewAI, any framework
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from langchain.agents import AgentExecutor
|
|
65
|
+
import orka
|
|
66
|
+
|
|
67
|
+
orka.init(api_key="orka_...")
|
|
68
|
+
|
|
69
|
+
@orka.guard(agent_id="langchain-agent-uuid", task_type="web_search")
|
|
70
|
+
def run_agent(query: str) -> str:
|
|
71
|
+
return agent_executor.invoke({"input": query})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Never breaks your production code
|
|
75
|
+
|
|
76
|
+
If Orka is unreachable, your agent continues running. Governance failures are silent — your users never see Orka errors.
|
|
77
|
+
|
|
78
|
+
## Risk levels
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
@orka.guard(agent_id="...", task_type="...", risk="HIGH")
|
|
82
|
+
# EU AI Act risk classification: UNACCEPTABLE | HIGH | LIMITED | MINIMAL | NONE
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Environment variables
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
ORKA_API_KEY=orka_your_key
|
|
89
|
+
ORKA_BASE_URL=https://orka-backend.onrender.com/api/v1 # optional
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import os, orka
|
|
94
|
+
orka.init(api_key=os.environ["ORKA_API_KEY"])
|
|
95
|
+
```
|
orkaia-0.1.0/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# orka-sdk
|
|
2
|
+
|
|
3
|
+
Governance and audit SDK for AI agents. Add policy enforcement, immutable audit trails, and trust scoring to any agent with one decorator.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install orka-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import orka
|
|
15
|
+
|
|
16
|
+
# Initialize once at startup (get your API key at orka.ia.br)
|
|
17
|
+
orka.init(api_key="orka_your_key_here")
|
|
18
|
+
|
|
19
|
+
# Wrap any agent function
|
|
20
|
+
@orka.guard(agent_id="your-agent-uuid", task_type="summarize")
|
|
21
|
+
def summarize(text: str) -> str:
|
|
22
|
+
return openai_client.chat(text)
|
|
23
|
+
|
|
24
|
+
# Works with async too
|
|
25
|
+
@orka.guard(agent_id="your-agent-uuid", task_type="financial_transfer", risk="HIGH")
|
|
26
|
+
async def transfer_funds(amount: float, to_account: str) -> dict:
|
|
27
|
+
return await payment_api.transfer(amount, to_account)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That's it. Every call now:
|
|
31
|
+
- ✅ Checks your X-Shield policies before executing
|
|
32
|
+
- ✅ Logs to X-Ledger with immutable hash chain
|
|
33
|
+
- ✅ Updates the agent's trust score
|
|
34
|
+
- ✅ Blocks execution if policy denies it (raises `OrkaPolicyBlocked`)
|
|
35
|
+
|
|
36
|
+
## Handle policy blocks
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from orka import OrkaPolicyBlocked
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
result = transfer_funds(10000, "acc_123")
|
|
43
|
+
except OrkaPolicyBlocked as e:
|
|
44
|
+
print(f"Blocked: {e.reason}")
|
|
45
|
+
print(f"Policy: {e.policy_name}")
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Works with LangChain, CrewAI, any framework
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from langchain.agents import AgentExecutor
|
|
52
|
+
import orka
|
|
53
|
+
|
|
54
|
+
orka.init(api_key="orka_...")
|
|
55
|
+
|
|
56
|
+
@orka.guard(agent_id="langchain-agent-uuid", task_type="web_search")
|
|
57
|
+
def run_agent(query: str) -> str:
|
|
58
|
+
return agent_executor.invoke({"input": query})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Never breaks your production code
|
|
62
|
+
|
|
63
|
+
If Orka is unreachable, your agent continues running. Governance failures are silent — your users never see Orka errors.
|
|
64
|
+
|
|
65
|
+
## Risk levels
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
@orka.guard(agent_id="...", task_type="...", risk="HIGH")
|
|
69
|
+
# EU AI Act risk classification: UNACCEPTABLE | HIGH | LIMITED | MINIMAL | NONE
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Environment variables
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
ORKA_API_KEY=orka_your_key
|
|
76
|
+
ORKA_BASE_URL=https://orka-backend.onrender.com/api/v1 # optional
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
import os, orka
|
|
81
|
+
orka.init(api_key=os.environ["ORKA_API_KEY"])
|
|
82
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .client import OrkaClient
|
|
2
|
+
from .decorators import guard
|
|
3
|
+
from .exceptions import OrkaAuthError, OrkaConnectionError, OrkaPolicyBlocked
|
|
4
|
+
|
|
5
|
+
_client: OrkaClient | None = None
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def init(api_key: str, base_url: str = "https://orka-backend.onrender.com/api/v1") -> OrkaClient:
|
|
9
|
+
"""Initialize the Orka SDK. Call once at startup.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
api_key: Your Orka API key (from dashboard → API Keys)
|
|
13
|
+
base_url: Orka backend URL (default: production)
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
import orka
|
|
17
|
+
orka.init(api_key="orka_your_key_here")
|
|
18
|
+
"""
|
|
19
|
+
global _client
|
|
20
|
+
_client = OrkaClient(api_key=api_key, base_url=base_url)
|
|
21
|
+
return _client
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__all__ = ["init", "guard", "OrkaClient", "OrkaPolicyBlocked", "OrkaAuthError", "OrkaConnectionError"]
|
|
25
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .exceptions import OrkaAuthError, OrkaConnectionError, OrkaPolicyBlocked
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class OrkaClient:
|
|
12
|
+
def __init__(self, api_key: str, base_url: str = "https://orka-backend.onrender.com/api/v1"):
|
|
13
|
+
self.api_key = api_key
|
|
14
|
+
self.base_url = base_url.rstrip("/")
|
|
15
|
+
self._headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
|
16
|
+
|
|
17
|
+
def _sync_request(self, method: str, path: str, **kwargs) -> dict:
|
|
18
|
+
try:
|
|
19
|
+
with httpx.Client(timeout=10) as client:
|
|
20
|
+
r = client.request(method, f"{self.base_url}{path}", headers=self._headers, **kwargs)
|
|
21
|
+
if r.status_code == 401:
|
|
22
|
+
raise OrkaAuthError("Invalid API key")
|
|
23
|
+
if r.status_code == 403:
|
|
24
|
+
data = r.json()
|
|
25
|
+
raise OrkaPolicyBlocked(
|
|
26
|
+
reason=data.get("reason", "Policy denied"),
|
|
27
|
+
policy_name=data.get("policy_name"),
|
|
28
|
+
)
|
|
29
|
+
r.raise_for_status()
|
|
30
|
+
return r.json() if r.content else {}
|
|
31
|
+
except httpx.ConnectError as e:
|
|
32
|
+
raise OrkaConnectionError(f"Cannot reach Orka at {self.base_url}") from e
|
|
33
|
+
|
|
34
|
+
async def _async_request(self, method: str, path: str, **kwargs) -> dict:
|
|
35
|
+
try:
|
|
36
|
+
async with httpx.AsyncClient(timeout=10) as client:
|
|
37
|
+
r = await client.request(method, f"{self.base_url}{path}", headers=self._headers, **kwargs)
|
|
38
|
+
if r.status_code == 401:
|
|
39
|
+
raise OrkaAuthError("Invalid API key")
|
|
40
|
+
if r.status_code == 403:
|
|
41
|
+
data = r.json()
|
|
42
|
+
raise OrkaPolicyBlocked(
|
|
43
|
+
reason=data.get("reason", "Policy denied"),
|
|
44
|
+
policy_name=data.get("policy_name"),
|
|
45
|
+
)
|
|
46
|
+
r.raise_for_status()
|
|
47
|
+
return r.json() if r.content else {}
|
|
48
|
+
except httpx.ConnectError as e:
|
|
49
|
+
raise OrkaConnectionError(f"Cannot reach Orka at {self.base_url}") from e
|
|
50
|
+
|
|
51
|
+
def check_policy(self, agent_id: str, task_type: str, payload: dict) -> dict:
|
|
52
|
+
return self._sync_request("POST", "/xshield/check", json={
|
|
53
|
+
"agent_id": agent_id,
|
|
54
|
+
"task_type": task_type,
|
|
55
|
+
"payload": payload,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
async def check_policy_async(self, agent_id: str, task_type: str, payload: dict) -> dict:
|
|
59
|
+
return await self._async_request("POST", "/xshield/check", json={
|
|
60
|
+
"agent_id": agent_id,
|
|
61
|
+
"task_type": task_type,
|
|
62
|
+
"payload": payload,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
def log_execution(self, agent_id: str, task_type: str, status: str,
|
|
66
|
+
input_payload: dict, output_payload: Any = None,
|
|
67
|
+
error: str | None = None, duration_ms: int = 0) -> dict:
|
|
68
|
+
return self._sync_request("POST", "/executions", json={
|
|
69
|
+
"requesting_agent_id": agent_id,
|
|
70
|
+
"executing_agent_target": agent_id,
|
|
71
|
+
"task_type": task_type,
|
|
72
|
+
"task_payload": input_payload,
|
|
73
|
+
"status": status,
|
|
74
|
+
"output_payload": output_payload,
|
|
75
|
+
"error": error,
|
|
76
|
+
"duration_ms": duration_ms,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
async def log_execution_async(self, agent_id: str, task_type: str, status: str,
|
|
80
|
+
input_payload: dict, output_payload: Any = None,
|
|
81
|
+
error: str | None = None, duration_ms: int = 0) -> dict:
|
|
82
|
+
return await self._async_request("POST", "/executions", json={
|
|
83
|
+
"requesting_agent_id": agent_id,
|
|
84
|
+
"executing_agent_target": agent_id,
|
|
85
|
+
"task_type": task_type,
|
|
86
|
+
"task_payload": input_payload,
|
|
87
|
+
"status": status,
|
|
88
|
+
"output_payload": output_payload,
|
|
89
|
+
"error": error,
|
|
90
|
+
"duration_ms": duration_ms,
|
|
91
|
+
})
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import functools
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Callable, Literal
|
|
5
|
+
|
|
6
|
+
from .exceptions import OrkaPolicyBlocked
|
|
7
|
+
|
|
8
|
+
RiskLevel = Literal["UNACCEPTABLE", "HIGH", "LIMITED", "MINIMAL", "NONE"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def guard(
|
|
12
|
+
agent_id: str,
|
|
13
|
+
task_type: str,
|
|
14
|
+
risk: RiskLevel = "MINIMAL",
|
|
15
|
+
block_on_policy: bool = True,
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Decorator that wraps any function with Orka governance.
|
|
19
|
+
|
|
20
|
+
- Checks X-Shield policies before execution
|
|
21
|
+
- Logs result to X-Ledger after execution
|
|
22
|
+
- Updates trust score automatically
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
@orka.guard(agent_id="uuid", task_type="summarize")
|
|
26
|
+
def my_agent(text: str) -> str:
|
|
27
|
+
return llm.call(text)
|
|
28
|
+
|
|
29
|
+
@orka.guard(agent_id="uuid", task_type="transfer", risk="HIGH")
|
|
30
|
+
async def transfer_funds(amount: float) -> dict:
|
|
31
|
+
...
|
|
32
|
+
"""
|
|
33
|
+
from . import _client
|
|
34
|
+
|
|
35
|
+
def decorator(fn: Callable) -> Callable:
|
|
36
|
+
if asyncio.iscoroutinefunction(fn):
|
|
37
|
+
@functools.wraps(fn)
|
|
38
|
+
async def async_wrapper(*args, **kwargs):
|
|
39
|
+
if _client is None:
|
|
40
|
+
return await fn(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
input_payload = {"args": list(args), "kwargs": kwargs}
|
|
43
|
+
start = time.monotonic()
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
await _client.check_policy_async(agent_id, task_type, input_payload)
|
|
47
|
+
except OrkaPolicyBlocked:
|
|
48
|
+
if block_on_policy:
|
|
49
|
+
raise
|
|
50
|
+
except Exception:
|
|
51
|
+
pass # never block execution due to Orka connectivity issues
|
|
52
|
+
|
|
53
|
+
error = None
|
|
54
|
+
output = None
|
|
55
|
+
status = "COMPLETED"
|
|
56
|
+
try:
|
|
57
|
+
output = await fn(*args, **kwargs)
|
|
58
|
+
return output
|
|
59
|
+
except Exception as e:
|
|
60
|
+
error = str(e)
|
|
61
|
+
status = "FAILED"
|
|
62
|
+
raise
|
|
63
|
+
finally:
|
|
64
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
65
|
+
try:
|
|
66
|
+
await _client.log_execution_async(
|
|
67
|
+
agent_id=agent_id,
|
|
68
|
+
task_type=task_type,
|
|
69
|
+
status=status,
|
|
70
|
+
input_payload=input_payload,
|
|
71
|
+
output_payload=str(output) if output is not None else None,
|
|
72
|
+
error=error,
|
|
73
|
+
duration_ms=duration_ms,
|
|
74
|
+
)
|
|
75
|
+
except Exception:
|
|
76
|
+
pass # never break production code due to Orka
|
|
77
|
+
|
|
78
|
+
return async_wrapper
|
|
79
|
+
else:
|
|
80
|
+
@functools.wraps(fn)
|
|
81
|
+
def sync_wrapper(*args, **kwargs):
|
|
82
|
+
if _client is None:
|
|
83
|
+
return fn(*args, **kwargs)
|
|
84
|
+
|
|
85
|
+
input_payload = {"args": list(args), "kwargs": kwargs}
|
|
86
|
+
start = time.monotonic()
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
_client.check_policy(agent_id, task_type, input_payload)
|
|
90
|
+
except OrkaPolicyBlocked:
|
|
91
|
+
if block_on_policy:
|
|
92
|
+
raise
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
error = None
|
|
97
|
+
output = None
|
|
98
|
+
status = "COMPLETED"
|
|
99
|
+
try:
|
|
100
|
+
output = fn(*args, **kwargs)
|
|
101
|
+
return output
|
|
102
|
+
except Exception as e:
|
|
103
|
+
error = str(e)
|
|
104
|
+
status = "FAILED"
|
|
105
|
+
raise
|
|
106
|
+
finally:
|
|
107
|
+
duration_ms = int((time.monotonic() - start) * 1000)
|
|
108
|
+
try:
|
|
109
|
+
_client.log_execution(
|
|
110
|
+
agent_id=agent_id,
|
|
111
|
+
task_type=task_type,
|
|
112
|
+
status=status,
|
|
113
|
+
input_payload=input_payload,
|
|
114
|
+
output_payload=str(output) if output is not None else None,
|
|
115
|
+
error=error,
|
|
116
|
+
duration_ms=duration_ms,
|
|
117
|
+
)
|
|
118
|
+
except Exception:
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
return sync_wrapper
|
|
122
|
+
|
|
123
|
+
return decorator
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class OrkaError(Exception):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
class OrkaPolicyBlocked(OrkaError):
|
|
5
|
+
def __init__(self, reason: str, policy_name: str | None = None):
|
|
6
|
+
self.reason = reason
|
|
7
|
+
self.policy_name = policy_name
|
|
8
|
+
super().__init__(f"Blocked by policy: {reason}")
|
|
9
|
+
|
|
10
|
+
class OrkaAuthError(OrkaError):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class OrkaConnectionError(OrkaError):
|
|
14
|
+
pass
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "orkaia"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Governance and audit SDK for AI agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["ai", "agents", "governance", "compliance", "audit", "eu-ai-act"]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"httpx>=0.27.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[tool.hatch.build.targets.wheel]
|
|
18
|
+
packages = ["orka"]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://orka.ia.br"
|
|
22
|
+
Documentation = "https://orka.ia.br/docs"
|
|
23
|
+
Repository = "https://github.com/orka-runtime/orka-sdk"
|