trytet-client 0.2.0__py3-none-any.whl
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.
- trytet_client/__init__.py +45 -0
- trytet_client/client.py +169 -0
- trytet_client/models.py +151 -0
- trytet_client/telemetry.py +62 -0
- trytet_client-0.2.0.dist-info/METADATA +79 -0
- trytet_client-0.2.0.dist-info/RECORD +7 -0
- trytet_client-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Trytet Python SDK — client for the Trytet Engine API, MCP, and telemetry."""
|
|
2
|
+
|
|
3
|
+
from .client import TrytetClient
|
|
4
|
+
from .models import (
|
|
5
|
+
AgentManifest,
|
|
6
|
+
CartridgeInvocation,
|
|
7
|
+
CartridgeResult,
|
|
8
|
+
CrashReport,
|
|
9
|
+
EgressPolicy,
|
|
10
|
+
ExecutionStatus,
|
|
11
|
+
FuelVoucher,
|
|
12
|
+
McpPrompt,
|
|
13
|
+
McpResource,
|
|
14
|
+
McpTool,
|
|
15
|
+
NorthstarReport,
|
|
16
|
+
SnapshotResponse,
|
|
17
|
+
StructuredTelemetry,
|
|
18
|
+
TelemetryEvent,
|
|
19
|
+
TetExecutionRequest,
|
|
20
|
+
TetExecutionResult,
|
|
21
|
+
TopologyEdge,
|
|
22
|
+
)
|
|
23
|
+
from .telemetry import TelemetryStream
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"TrytetClient",
|
|
27
|
+
"TelemetryStream",
|
|
28
|
+
"AgentManifest",
|
|
29
|
+
"CartridgeInvocation",
|
|
30
|
+
"CartridgeResult",
|
|
31
|
+
"CrashReport",
|
|
32
|
+
"EgressPolicy",
|
|
33
|
+
"ExecutionStatus",
|
|
34
|
+
"FuelVoucher",
|
|
35
|
+
"McpPrompt",
|
|
36
|
+
"McpResource",
|
|
37
|
+
"McpTool",
|
|
38
|
+
"NorthstarReport",
|
|
39
|
+
"SnapshotResponse",
|
|
40
|
+
"StructuredTelemetry",
|
|
41
|
+
"TelemetryEvent",
|
|
42
|
+
"TetExecutionRequest",
|
|
43
|
+
"TetExecutionResult",
|
|
44
|
+
"TopologyEdge",
|
|
45
|
+
]
|
trytet_client/client.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Trytet API client with retry, MCP support, and cartridge management."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .models import (
|
|
9
|
+
CartridgeInvocation,
|
|
10
|
+
CartridgeResult,
|
|
11
|
+
McpPrompt,
|
|
12
|
+
McpResource,
|
|
13
|
+
McpTool,
|
|
14
|
+
NorthstarReport,
|
|
15
|
+
SnapshotResponse,
|
|
16
|
+
TetExecutionRequest,
|
|
17
|
+
TetExecutionResult,
|
|
18
|
+
TopologyEdge,
|
|
19
|
+
)
|
|
20
|
+
from .telemetry import TelemetryStream
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TrytetClient:
|
|
24
|
+
"""Async HTTP client for the Trytet Engine API."""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
base_url: str = "http://localhost:3000",
|
|
29
|
+
max_retries: int = 3,
|
|
30
|
+
initial_delay_ms: int = 100,
|
|
31
|
+
):
|
|
32
|
+
self._base = base_url.rstrip("/")
|
|
33
|
+
self._max_retries = max_retries
|
|
34
|
+
self._initial_delay = initial_delay_ms / 1000.0
|
|
35
|
+
self._client = httpx.AsyncClient(timeout=httpx.Timeout(30.0))
|
|
36
|
+
|
|
37
|
+
async def __aenter__(self) -> "TrytetClient":
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
41
|
+
await self.close()
|
|
42
|
+
|
|
43
|
+
async def close(self) -> None:
|
|
44
|
+
await self._client.aclose()
|
|
45
|
+
|
|
46
|
+
# -- retry helper -------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
async def _request(
|
|
49
|
+
self,
|
|
50
|
+
method: str,
|
|
51
|
+
path: str,
|
|
52
|
+
*,
|
|
53
|
+
json_body: Any = None,
|
|
54
|
+
retry_on_status: Optional[List[int]] = None,
|
|
55
|
+
) -> httpx.Response:
|
|
56
|
+
statuses = retry_on_status or [502, 503, 504]
|
|
57
|
+
delay = self._initial_delay
|
|
58
|
+
|
|
59
|
+
for attempt in range(self._max_retries + 1):
|
|
60
|
+
try:
|
|
61
|
+
resp = await self._client.request(
|
|
62
|
+
method, f"{self._base}{path}", json=json_body
|
|
63
|
+
)
|
|
64
|
+
if resp.status_code in statuses and attempt < self._max_retries:
|
|
65
|
+
raise httpx.TransportError(f"Retryable status {resp.status_code}")
|
|
66
|
+
return resp
|
|
67
|
+
except (httpx.TransportError, httpx.ConnectError) as e:
|
|
68
|
+
if attempt == self._max_retries:
|
|
69
|
+
raise
|
|
70
|
+
await asyncio.sleep(delay)
|
|
71
|
+
delay = min(delay * 2, 5.0)
|
|
72
|
+
|
|
73
|
+
raise RuntimeError("unreachable")
|
|
74
|
+
|
|
75
|
+
async def _post(self, path: str, body: Any = None) -> httpx.Response:
|
|
76
|
+
return await self._request("POST", path, json_body=body)
|
|
77
|
+
|
|
78
|
+
async def _get(self, path: str) -> httpx.Response:
|
|
79
|
+
return await self._request("GET", path)
|
|
80
|
+
|
|
81
|
+
async def _check(self, resp: httpx.Response) -> Dict[str, Any]:
|
|
82
|
+
if not resp.is_success:
|
|
83
|
+
text = resp.text[:500]
|
|
84
|
+
raise RuntimeError(f"Request failed ({resp.status_code}): {text}")
|
|
85
|
+
return resp.json()
|
|
86
|
+
|
|
87
|
+
# -- Agent lifecycle ----------------------------------------------------
|
|
88
|
+
|
|
89
|
+
async def execute(self, req: TetExecutionRequest) -> TetExecutionResult:
|
|
90
|
+
resp = await self._post("/v1/tet/execute", req.model_dump(exclude_none=True))
|
|
91
|
+
data = await self._check(resp)
|
|
92
|
+
return TetExecutionResult(**data)
|
|
93
|
+
|
|
94
|
+
async def snapshot(self, tet_id: str) -> SnapshotResponse:
|
|
95
|
+
resp = await self._post(f"/v1/tet/snapshot/{tet_id}")
|
|
96
|
+
data = await self._check(resp)
|
|
97
|
+
return SnapshotResponse(**data)
|
|
98
|
+
|
|
99
|
+
async def fork(
|
|
100
|
+
self, snapshot_id: str, req: TetExecutionRequest
|
|
101
|
+
) -> TetExecutionResult:
|
|
102
|
+
resp = await self._post(
|
|
103
|
+
f"/v1/tet/fork/{snapshot_id}", req.model_dump(exclude_none=True)
|
|
104
|
+
)
|
|
105
|
+
data = await self._check(resp)
|
|
106
|
+
return TetExecutionResult(**data)
|
|
107
|
+
|
|
108
|
+
async def teleport(self, alias: str, target_node: str) -> None:
|
|
109
|
+
resp = await self._post(f"/v1/tet/teleport/{alias}", target_node)
|
|
110
|
+
await self._check(resp)
|
|
111
|
+
|
|
112
|
+
async def get_topology(self) -> List[TopologyEdge]:
|
|
113
|
+
resp = await self._get("/v1/topology")
|
|
114
|
+
data = await self._check(resp)
|
|
115
|
+
return [TopologyEdge(**e) for e in data]
|
|
116
|
+
|
|
117
|
+
async def get_swarm_metrics(self) -> NorthstarReport:
|
|
118
|
+
resp = await self._get("/v1/swarm/metrics")
|
|
119
|
+
data = await self._check(resp)
|
|
120
|
+
return NorthstarReport(**data)
|
|
121
|
+
|
|
122
|
+
async def get_health(self) -> Dict[str, Any]:
|
|
123
|
+
resp = await self._get("/health")
|
|
124
|
+
return await self._check(resp)
|
|
125
|
+
|
|
126
|
+
# -- Cartridge management -----------------------------------------------
|
|
127
|
+
|
|
128
|
+
async def invoke_cartridge(self, invocation: CartridgeInvocation) -> CartridgeResult:
|
|
129
|
+
resp = await self._post(
|
|
130
|
+
"/v1/cartridge/invoke", invocation.model_dump()
|
|
131
|
+
)
|
|
132
|
+
data = await self._check(resp)
|
|
133
|
+
return CartridgeResult(**data)
|
|
134
|
+
|
|
135
|
+
# -- MCP ----------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
async def _mcp_call(self, method: str, params: Any = None) -> Any:
|
|
138
|
+
import time
|
|
139
|
+
|
|
140
|
+
request = {
|
|
141
|
+
"jsonrpc": "2.0",
|
|
142
|
+
"id": int(time.time() * 1000),
|
|
143
|
+
"method": method,
|
|
144
|
+
"params": params,
|
|
145
|
+
}
|
|
146
|
+
resp = await self._post("/v1/mcp", request)
|
|
147
|
+
data = await self._check(resp)
|
|
148
|
+
return data.get("result")
|
|
149
|
+
|
|
150
|
+
async def list_tools(self) -> List[McpTool]:
|
|
151
|
+
result = await self._mcp_call("tools/list")
|
|
152
|
+
return [McpTool(**t) for t in (result or {}).get("tools", [])]
|
|
153
|
+
|
|
154
|
+
async def call_tool(self, name: str, arguments: Dict[str, Any]) -> Any:
|
|
155
|
+
return await self._mcp_call("tools/call", {"name": name, "arguments": arguments})
|
|
156
|
+
|
|
157
|
+
async def list_resources(self) -> List[McpResource]:
|
|
158
|
+
result = await self._mcp_call("resources/list")
|
|
159
|
+
return [McpResource(**r) for r in (result or {}).get("resources", [])]
|
|
160
|
+
|
|
161
|
+
async def list_prompts(self) -> List[McpPrompt]:
|
|
162
|
+
result = await self._mcp_call("prompts/list")
|
|
163
|
+
return [McpPrompt(**p) for p in (result or {}).get("prompts", [])]
|
|
164
|
+
|
|
165
|
+
# -- Telemetry ----------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
def create_telemetry_stream(self) -> TelemetryStream:
|
|
168
|
+
ws_url = self._base.replace("http", "ws") + "/v1/swarm/stream"
|
|
169
|
+
return TelemetryStream(ws_url)
|
trytet_client/models.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Pydantic models for all Trytet API types."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Union, List, Dict, Any
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AgentManifestMetadata(BaseModel):
|
|
8
|
+
name: str
|
|
9
|
+
version: str
|
|
10
|
+
author_pubkey: Optional[str] = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AgentManifestConstraints(BaseModel):
|
|
14
|
+
max_memory_pages: int
|
|
15
|
+
fuel_limit: int
|
|
16
|
+
max_egress_bytes: int
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentManifestPermissions(BaseModel):
|
|
20
|
+
can_egress: List[str] = Field(default_factory=list)
|
|
21
|
+
can_persist: bool = False
|
|
22
|
+
can_teleport: bool = False
|
|
23
|
+
is_genesis_factory: bool = False
|
|
24
|
+
can_fork: bool = False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AgentManifest(BaseModel):
|
|
28
|
+
metadata: AgentManifestMetadata
|
|
29
|
+
constraints: AgentManifestConstraints
|
|
30
|
+
permissions: AgentManifestPermissions
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FuelVoucher(BaseModel):
|
|
34
|
+
tet_id: str
|
|
35
|
+
fuel_limit: int
|
|
36
|
+
nonce: int
|
|
37
|
+
signature: List[int]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class EgressPolicy(BaseModel):
|
|
41
|
+
allowed_domains: List[str]
|
|
42
|
+
max_daily_bytes: int
|
|
43
|
+
require_https: bool = True
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TetExecutionRequest(BaseModel):
|
|
47
|
+
payload: Optional[List[int]] = None
|
|
48
|
+
alias: Optional[str] = None
|
|
49
|
+
env: Optional[Dict[str, str]] = None
|
|
50
|
+
injected_files: Optional[Dict[str, str]] = None
|
|
51
|
+
allocated_fuel: Optional[int] = None
|
|
52
|
+
max_memory_mb: Optional[int] = None
|
|
53
|
+
parent_snapshot_id: Optional[str] = None
|
|
54
|
+
target_function: Optional[str] = None
|
|
55
|
+
call_depth: int = 0
|
|
56
|
+
voucher: Optional[FuelVoucher] = None
|
|
57
|
+
manifest: Optional[AgentManifest] = None
|
|
58
|
+
egress_policy: Optional[EgressPolicy] = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class StructuredTelemetry(BaseModel):
|
|
62
|
+
stdout_lines: List[str] = Field(default_factory=list)
|
|
63
|
+
stderr_lines: List[str] = Field(default_factory=list)
|
|
64
|
+
memory_used_kb: int = 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CrashReport(BaseModel):
|
|
68
|
+
error_type: str
|
|
69
|
+
message: str
|
|
70
|
+
instruction_offset: Optional[int] = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
ExecutionStatus = Union[
|
|
74
|
+
str, Dict[str, Any]
|
|
75
|
+
] # "Success" | "OutOfFuel" | ... | {"Crash": {...}}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TetExecutionResult(BaseModel):
|
|
79
|
+
tet_id: str
|
|
80
|
+
status: Any # ExecutionStatus
|
|
81
|
+
telemetry: StructuredTelemetry
|
|
82
|
+
execution_duration_us: int
|
|
83
|
+
fuel_consumed: int
|
|
84
|
+
mutated_files: Dict[str, str] = Field(default_factory=dict)
|
|
85
|
+
migrated_to: Optional[str] = None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class SnapshotResponse(BaseModel):
|
|
89
|
+
snapshot_id: str
|
|
90
|
+
size_bytes: int
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TopologyEdge(BaseModel):
|
|
94
|
+
source: str
|
|
95
|
+
target: str
|
|
96
|
+
latency_us: int
|
|
97
|
+
bytes_transferred: int
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class NorthstarReport(BaseModel):
|
|
101
|
+
teleport_warp_us: int
|
|
102
|
+
mitosis_constant_us: int
|
|
103
|
+
oracle_fidelity_us: int
|
|
104
|
+
market_evacuation_us: int
|
|
105
|
+
cartridge_spinup_us: int
|
|
106
|
+
timestamp: str
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CartridgeInvocation(BaseModel):
|
|
110
|
+
component_id: str
|
|
111
|
+
payload: str
|
|
112
|
+
fuel: int
|
|
113
|
+
max_memory_mb: int = 512
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CartridgeResult(BaseModel):
|
|
117
|
+
output: str
|
|
118
|
+
fuel_consumed: int
|
|
119
|
+
duration_us: int
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class McpTool(BaseModel):
|
|
123
|
+
name: str
|
|
124
|
+
description: str
|
|
125
|
+
inputSchema: Dict[str, Any] = Field(default_factory=dict)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class McpResource(BaseModel):
|
|
129
|
+
uri: str
|
|
130
|
+
name: str
|
|
131
|
+
description: Optional[str] = None
|
|
132
|
+
mimeType: Optional[str] = None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class McpPromptArgument(BaseModel):
|
|
136
|
+
name: str
|
|
137
|
+
description: Optional[str] = None
|
|
138
|
+
required: Optional[bool] = None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class McpPrompt(BaseModel):
|
|
142
|
+
name: str
|
|
143
|
+
description: Optional[str] = None
|
|
144
|
+
arguments: Optional[List[McpPromptArgument]] = None
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class TelemetryEvent(BaseModel):
|
|
148
|
+
event_type: str
|
|
149
|
+
tet_id: str
|
|
150
|
+
timestamp_us: int
|
|
151
|
+
data: Dict[str, Any] = Field(default_factory=dict)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""WebSocket telemetry stream for real-time Trytet observability."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any, Callable, Dict, Set
|
|
6
|
+
|
|
7
|
+
import websockets
|
|
8
|
+
from websockets.asyncio.client import ClientConnection
|
|
9
|
+
|
|
10
|
+
from .models import TelemetryEvent
|
|
11
|
+
|
|
12
|
+
TelemetryCallback = Callable[[TelemetryEvent], None]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TelemetryStream:
|
|
16
|
+
"""Async WebSocket client for the Trytet telemetry stream."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, ws_url: str):
|
|
19
|
+
self._url = ws_url
|
|
20
|
+
self._ws: ClientConnection | None = None
|
|
21
|
+
self._listeners: Set[TelemetryCallback] = set()
|
|
22
|
+
self._closed = False
|
|
23
|
+
self._reconnect_delay = 1.0
|
|
24
|
+
|
|
25
|
+
async def connect(self) -> None:
|
|
26
|
+
"""Connect and begin receiving telemetry events."""
|
|
27
|
+
while not self._closed:
|
|
28
|
+
try:
|
|
29
|
+
async with websockets.connect(self._url) as ws:
|
|
30
|
+
self._ws = ws
|
|
31
|
+
self._reconnect_delay = 1.0
|
|
32
|
+
await self._read_loop(ws)
|
|
33
|
+
except Exception:
|
|
34
|
+
if not self._closed:
|
|
35
|
+
await asyncio.sleep(self._reconnect_delay)
|
|
36
|
+
self._reconnect_delay = min(self._reconnect_delay * 2, 30.0)
|
|
37
|
+
|
|
38
|
+
async def _read_loop(self, ws: ClientConnection) -> None:
|
|
39
|
+
async for message in ws:
|
|
40
|
+
try:
|
|
41
|
+
data = json.loads(message)
|
|
42
|
+
event = TelemetryEvent(**data)
|
|
43
|
+
await self._emit(event)
|
|
44
|
+
except Exception:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
async def _emit(self, event: TelemetryEvent) -> None:
|
|
48
|
+
for listener in list(self._listeners):
|
|
49
|
+
try:
|
|
50
|
+
listener(event)
|
|
51
|
+
except Exception:
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def on_event(self, callback: TelemetryCallback) -> Callable[[], None]:
|
|
55
|
+
"""Register a callback. Returns an unregister function."""
|
|
56
|
+
self._listeners.add(callback)
|
|
57
|
+
return lambda: self._listeners.discard(callback)
|
|
58
|
+
|
|
59
|
+
def close(self) -> None:
|
|
60
|
+
"""Close the stream and stop reconnecting."""
|
|
61
|
+
self._closed = True
|
|
62
|
+
self._listeners.clear()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trytet-client
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python SDK for the Trytet Engine — sub-millisecond Wasm sandbox for AI agents
|
|
5
|
+
Author: Trytet
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: agent,llm,sandbox,trytet,wasm
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: httpx>=0.25
|
|
17
|
+
Requires-Dist: pydantic>=2.0
|
|
18
|
+
Requires-Dist: websockets>=12.0
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Trytet Python SDK
|
|
26
|
+
|
|
27
|
+
Official Python client for the [Trytet Engine](https://trytet.io) — a sub-millisecond, hyper-ephemeral Wasm execution substrate for AI agents.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install trytet-client
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import asyncio
|
|
39
|
+
from trytet_client import TrytetClient, TetExecutionRequest
|
|
40
|
+
|
|
41
|
+
async def main():
|
|
42
|
+
async with TrytetClient(base_url="http://localhost:3000") as client:
|
|
43
|
+
# Execute a Wasm payload
|
|
44
|
+
with open("my_agent.wasm", "rb") as f:
|
|
45
|
+
payload = list(f.read())
|
|
46
|
+
|
|
47
|
+
result = await client.execute(TetExecutionRequest(
|
|
48
|
+
payload=payload,
|
|
49
|
+
allocated_fuel=1_000_000,
|
|
50
|
+
alias="my-agent",
|
|
51
|
+
))
|
|
52
|
+
print(f"Status: {result.status}")
|
|
53
|
+
print(f"Fuel consumed: {result.fuel_consumed}")
|
|
54
|
+
|
|
55
|
+
# List available MCP tools
|
|
56
|
+
tools = await client.list_tools()
|
|
57
|
+
for tool in tools:
|
|
58
|
+
print(f" {tool.name}: {tool.description}")
|
|
59
|
+
|
|
60
|
+
# Invoke a cartridge
|
|
61
|
+
from trytet_client import CartridgeInvocation
|
|
62
|
+
cart_result = await client.invoke_cartridge(CartridgeInvocation(
|
|
63
|
+
component_id="js-evaluator",
|
|
64
|
+
payload="2 + 2",
|
|
65
|
+
fuel=1_000_000,
|
|
66
|
+
))
|
|
67
|
+
print(cart_result.output)
|
|
68
|
+
|
|
69
|
+
asyncio.run(main())
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
- Full async HTTP client with retry and backoff
|
|
75
|
+
- MCP (Model Context Protocol) support — tools, resources, prompts
|
|
76
|
+
- Cartridge management API
|
|
77
|
+
- WebSocket telemetry streaming
|
|
78
|
+
- Pydantic models for all API types
|
|
79
|
+
- Type-safe request/response handling
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
trytet_client/__init__.py,sha256=42WjFrZB3puFosTfxD3MznsIu1M8ARqG2OyCz8SI3EU,947
|
|
2
|
+
trytet_client/client.py,sha256=6tjuGTRqpvDslm9Q3xMKGwaUQXDuuRltE3QFHC95Bdc,5907
|
|
3
|
+
trytet_client/models.py,sha256=4j05SQlcKn3VP8kY4Lx5AZjXnv0X3OtExpx19bJJSZw,3468
|
|
4
|
+
trytet_client/telemetry.py,sha256=2uusXvANG_rYc-DA1PpKK-99SMUq8IgWpv4F8RWAG88,2063
|
|
5
|
+
trytet_client-0.2.0.dist-info/METADATA,sha256=poJDadTLPfZs6DMY3kpcGbHKgd_vf9taizIJJLDwD1E,2405
|
|
6
|
+
trytet_client-0.2.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
7
|
+
trytet_client-0.2.0.dist-info/RECORD,,
|