cap-sdk-python 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.
- cap_sdk_python-0.1.0/PKG-INFO +59 -0
- cap_sdk_python-0.1.0/README.md +48 -0
- cap_sdk_python-0.1.0/cap/__init__.py +5 -0
- cap_sdk_python-0.1.0/cap/bus.py +31 -0
- cap_sdk_python-0.1.0/cap/client.py +18 -0
- cap_sdk_python-0.1.0/cap/pb/__init__.py +0 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/__init__.py +0 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/__init__.py +0 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/__init__.py +0 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/alert_pb2.py +27 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/alert_pb2_grpc.py +4 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/buspacket_pb2.py +31 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/buspacket_pb2_grpc.py +4 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/heartbeat_pb2.py +27 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/heartbeat_pb2_grpc.py +4 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/job_pb2.py +37 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/job_pb2_grpc.py +4 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/safety_pb2.py +34 -0
- cap_sdk_python-0.1.0/cap/pb/cortex/agent/v1/safety_pb2_grpc.py +69 -0
- cap_sdk_python-0.1.0/cap/worker.py +45 -0
- cap_sdk_python-0.1.0/cap_sdk_python.egg-info/PKG-INFO +59 -0
- cap_sdk_python-0.1.0/cap_sdk_python.egg-info/SOURCES.txt +25 -0
- cap_sdk_python-0.1.0/cap_sdk_python.egg-info/dependency_links.txt +1 -0
- cap_sdk_python-0.1.0/cap_sdk_python.egg-info/requires.txt +3 -0
- cap_sdk_python-0.1.0/cap_sdk_python.egg-info/top_level.txt +2 -0
- cap_sdk_python-0.1.0/pyproject.toml +21 -0
- cap_sdk_python-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cap-sdk-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CAP (Cortex Agent Protocol) Python SDK
|
|
5
|
+
Project-URL: Homepage, https://github.com/coretexos/cap
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: protobuf>=4.25.0
|
|
9
|
+
Requires-Dist: grpcio>=1.59.0
|
|
10
|
+
Requires-Dist: nats-py>=2.6.0
|
|
11
|
+
|
|
12
|
+
# CAP Python SDK
|
|
13
|
+
|
|
14
|
+
Asyncio-first SDK with NATS helpers for CAP workers and clients.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
1. Generate protobuf stubs into this SDK (one-time per proto change):
|
|
18
|
+
```bash
|
|
19
|
+
python -m grpc_tools.protoc \
|
|
20
|
+
-I../../proto \
|
|
21
|
+
--python_out=./cap/pb \
|
|
22
|
+
--grpc_python_out=./cap/pb \
|
|
23
|
+
../../proto/cortex/agent/v1/*.proto
|
|
24
|
+
```
|
|
25
|
+
(Or run `./tools/make_protos.sh` from repo root and copy `generated/python` into `sdk/python/cap/pb`.)
|
|
26
|
+
|
|
27
|
+
2. Install:
|
|
28
|
+
```bash
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
3. Run a worker:
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from cap import worker
|
|
36
|
+
from cap.pb.cortex.agent.v1 import job_pb2
|
|
37
|
+
|
|
38
|
+
async def handle(req: job_pb2.JobRequest):
|
|
39
|
+
return job_pb2.JobResult(
|
|
40
|
+
job_id=req.job_id,
|
|
41
|
+
status=job_pb2.JOB_STATUS_SUCCEEDED,
|
|
42
|
+
result_ptr=f"redis://res/{req.job_id}",
|
|
43
|
+
worker_id="worker-echo-1",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
asyncio.run(worker.run_worker("nats://127.0.0.1:4222", "job.echo", handle))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Files
|
|
50
|
+
- `cap/bus.py` — NATS connector.
|
|
51
|
+
- `cap/worker.py` — worker skeleton with handler hook.
|
|
52
|
+
- `cap/client.py` — publish JobRequest to `sys.job.submit`.
|
|
53
|
+
- `cap/pb/` — protobuf stubs (generated).
|
|
54
|
+
|
|
55
|
+
## Defaults
|
|
56
|
+
- Subjects: `sys.job.submit`, `sys.job.result`, `sys.heartbeat`.
|
|
57
|
+
- Protocol version: `1`.
|
|
58
|
+
|
|
59
|
+
Swap out `cap.bus` if you need a different transport.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CAP Python SDK
|
|
2
|
+
|
|
3
|
+
Asyncio-first SDK with NATS helpers for CAP workers and clients.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
1. Generate protobuf stubs into this SDK (one-time per proto change):
|
|
7
|
+
```bash
|
|
8
|
+
python -m grpc_tools.protoc \
|
|
9
|
+
-I../../proto \
|
|
10
|
+
--python_out=./cap/pb \
|
|
11
|
+
--grpc_python_out=./cap/pb \
|
|
12
|
+
../../proto/cortex/agent/v1/*.proto
|
|
13
|
+
```
|
|
14
|
+
(Or run `./tools/make_protos.sh` from repo root and copy `generated/python` into `sdk/python/cap/pb`.)
|
|
15
|
+
|
|
16
|
+
2. Install:
|
|
17
|
+
```bash
|
|
18
|
+
pip install -e .
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3. Run a worker:
|
|
22
|
+
```python
|
|
23
|
+
import asyncio
|
|
24
|
+
from cap import worker
|
|
25
|
+
from cap.pb.cortex.agent.v1 import job_pb2
|
|
26
|
+
|
|
27
|
+
async def handle(req: job_pb2.JobRequest):
|
|
28
|
+
return job_pb2.JobResult(
|
|
29
|
+
job_id=req.job_id,
|
|
30
|
+
status=job_pb2.JOB_STATUS_SUCCEEDED,
|
|
31
|
+
result_ptr=f"redis://res/{req.job_id}",
|
|
32
|
+
worker_id="worker-echo-1",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
asyncio.run(worker.run_worker("nats://127.0.0.1:4222", "job.echo", handle))
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Files
|
|
39
|
+
- `cap/bus.py` — NATS connector.
|
|
40
|
+
- `cap/worker.py` — worker skeleton with handler hook.
|
|
41
|
+
- `cap/client.py` — publish JobRequest to `sys.job.submit`.
|
|
42
|
+
- `cap/pb/` — protobuf stubs (generated).
|
|
43
|
+
|
|
44
|
+
## Defaults
|
|
45
|
+
- Subjects: `sys.job.submit`, `sys.job.result`, `sys.heartbeat`.
|
|
46
|
+
- Protocol version: `1`.
|
|
47
|
+
|
|
48
|
+
Swap out `cap.bus` if you need a different transport.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import nats
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NATSConfig:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
url: str,
|
|
11
|
+
token: Optional[str] = None,
|
|
12
|
+
username: Optional[str] = None,
|
|
13
|
+
password: Optional[str] = None,
|
|
14
|
+
name: str = "cap-sdk-python",
|
|
15
|
+
):
|
|
16
|
+
self.url = url
|
|
17
|
+
self.token = token
|
|
18
|
+
self.username = username
|
|
19
|
+
self.password = password
|
|
20
|
+
self.name = name
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def connect_nats(cfg: NATSConfig):
|
|
24
|
+
opts = {"servers": cfg.url, "name": cfg.name}
|
|
25
|
+
if cfg.token:
|
|
26
|
+
opts["token"] = cfg.token
|
|
27
|
+
if cfg.username:
|
|
28
|
+
opts["user"] = cfg.username
|
|
29
|
+
if cfg.password:
|
|
30
|
+
opts["password"] = cfg.password
|
|
31
|
+
return await nats.connect(**opts)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from google.protobuf import timestamp_pb2
|
|
2
|
+
from cap.pb.cortex.agent.v1 import buspacket_pb2
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
DEFAULT_PROTOCOL_VERSION = 1
|
|
6
|
+
SUBJECT_SUBMIT = "sys.job.submit"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def submit_job(nc, job_request, trace_id: str, sender_id: str):
|
|
10
|
+
ts = timestamp_pb2.Timestamp()
|
|
11
|
+
ts.GetCurrentTime()
|
|
12
|
+
packet = buspacket_pb2.BusPacket()
|
|
13
|
+
packet.trace_id = trace_id
|
|
14
|
+
packet.sender_id = sender_id
|
|
15
|
+
packet.created_at.CopyFrom(ts)
|
|
16
|
+
packet.protocol_version = DEFAULT_PROTOCOL_VERSION
|
|
17
|
+
packet.job_request.CopyFrom(job_request)
|
|
18
|
+
await nc.publish(SUBJECT_SUBMIT, packet.SerializeToString())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: cortex/agent/v1/alert.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x63ortex/agent/v1/alert.proto\x12\x0f\x63ortex.agent.v1\"N\n\x0bSystemAlert\x12\r\n\x05level\x18\x01 \x01(\t\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\tcomponent\x18\x03 \x01(\t\x12\x0c\n\x04\x63ode\x18\x04 \x01(\tB|\n\x16\x61i.cortex.cap.agent.v1P\x01Z(github.com/coretexos/cap/cortex/agent/v1\xaa\x02\x0f\x43ortex.Agent.V1\xca\x02\x0f\x43ortex\\Agent\\V1\xea\x02\x11\x43ortex::Agent::V1b\x06proto3')
|
|
18
|
+
|
|
19
|
+
_globals = globals()
|
|
20
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
21
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cortex.agent.v1.alert_pb2', _globals)
|
|
22
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
23
|
+
_globals['DESCRIPTOR']._options = None
|
|
24
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\026ai.cortex.cap.agent.v1P\001Z(github.com/coretexos/cap/cortex/agent/v1\252\002\017Cortex.Agent.V1\312\002\017Cortex\\Agent\\V1\352\002\021Cortex::Agent::V1'
|
|
25
|
+
_globals['_SYSTEMALERT']._serialized_start=48
|
|
26
|
+
_globals['_SYSTEMALERT']._serialized_end=126
|
|
27
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: cortex/agent/v1/buspacket.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
|
|
16
|
+
from cortex.agent.v1 import job_pb2 as cortex_dot_agent_dot_v1_dot_job__pb2
|
|
17
|
+
from cortex.agent.v1 import heartbeat_pb2 as cortex_dot_agent_dot_v1_dot_heartbeat__pb2
|
|
18
|
+
from cortex.agent.v1 import alert_pb2 as cortex_dot_agent_dot_v1_dot_alert__pb2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63ortex/agent/v1/buspacket.proto\x12\x0f\x63ortex.agent.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x19\x63ortex/agent/v1/job.proto\x1a\x1f\x63ortex/agent/v1/heartbeat.proto\x1a\x1b\x63ortex/agent/v1/alert.proto\"\xcb\x02\n\tBusPacket\x12\x10\n\x08trace_id\x18\x01 \x01(\t\x12\x11\n\tsender_id\x18\x02 \x01(\t\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x18\n\x10protocol_version\x18\x04 \x01(\x05\x12\x32\n\x0bjob_request\x18\n \x01(\x0b\x32\x1b.cortex.agent.v1.JobRequestH\x00\x12\x30\n\njob_result\x18\x0b \x01(\x0b\x32\x1a.cortex.agent.v1.JobResultH\x00\x12/\n\theartbeat\x18\x0c \x01(\x0b\x32\x1a.cortex.agent.v1.HeartbeatH\x00\x12-\n\x05\x61lert\x18\r \x01(\x0b\x32\x1c.cortex.agent.v1.SystemAlertH\x00\x42\t\n\x07payloadB|\n\x16\x61i.cortex.cap.agent.v1P\x01Z(github.com/coretexos/cap/cortex/agent/v1\xaa\x02\x0f\x43ortex.Agent.V1\xca\x02\x0f\x43ortex\\Agent\\V1\xea\x02\x11\x43ortex::Agent::V1b\x06proto3')
|
|
22
|
+
|
|
23
|
+
_globals = globals()
|
|
24
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
25
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cortex.agent.v1.buspacket_pb2', _globals)
|
|
26
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
27
|
+
_globals['DESCRIPTOR']._options = None
|
|
28
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\026ai.cortex.cap.agent.v1P\001Z(github.com/coretexos/cap/cortex/agent/v1\252\002\017Cortex.Agent.V1\312\002\017Cortex\\Agent\\V1\352\002\021Cortex::Agent::V1'
|
|
29
|
+
_globals['_BUSPACKET']._serialized_start=175
|
|
30
|
+
_globals['_BUSPACKET']._serialized_end=506
|
|
31
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: cortex/agent/v1/heartbeat.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63ortex/agent/v1/heartbeat.proto\x12\x0f\x63ortex.agent.v1\"\xbb\x01\n\tHeartbeat\x12\x11\n\tworker_id\x18\x01 \x01(\t\x12\x0e\n\x06region\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x10\n\x08\x63pu_load\x18\x04 \x01(\x02\x12\x17\n\x0fgpu_utilization\x18\x05 \x01(\x02\x12\x13\n\x0b\x61\x63tive_jobs\x18\x06 \x01(\x05\x12\x14\n\x0c\x63\x61pabilities\x18\x07 \x03(\t\x12\x0c\n\x04pool\x18\x0b \x01(\t\x12\x19\n\x11max_parallel_jobs\x18\x0c \x01(\x05\x42|\n\x16\x61i.cortex.cap.agent.v1P\x01Z(github.com/coretexos/cap/cortex/agent/v1\xaa\x02\x0f\x43ortex.Agent.V1\xca\x02\x0f\x43ortex\\Agent\\V1\xea\x02\x11\x43ortex::Agent::V1b\x06proto3')
|
|
18
|
+
|
|
19
|
+
_globals = globals()
|
|
20
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
21
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cortex.agent.v1.heartbeat_pb2', _globals)
|
|
22
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
23
|
+
_globals['DESCRIPTOR']._options = None
|
|
24
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\026ai.cortex.cap.agent.v1P\001Z(github.com/coretexos/cap/cortex/agent/v1\252\002\017Cortex.Agent.V1\312\002\017Cortex\\Agent\\V1\352\002\021Cortex::Agent::V1'
|
|
25
|
+
_globals['_HEARTBEAT']._serialized_start=53
|
|
26
|
+
_globals['_HEARTBEAT']._serialized_end=240
|
|
27
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: cortex/agent/v1/job.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x63ortex/agent/v1/job.proto\x12\x0f\x63ortex.agent.v1\"\xa3\x02\n\nJobRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\r\n\x05topic\x18\x02 \x01(\t\x12.\n\x08priority\x18\x03 \x01(\x0e\x32\x1c.cortex.agent.v1.JobPriority\x12\x13\n\x0b\x63ontext_ptr\x18\x04 \x01(\t\x12\x12\n\nadapter_id\x18\x05 \x01(\t\x12\x31\n\x03\x65nv\x18\x06 \x03(\x0b\x32$.cortex.agent.v1.JobRequest.EnvEntry\x12\x15\n\rparent_job_id\x18\x07 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x08 \x01(\t\x12\x12\n\nstep_index\x18\t \x01(\x05\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xaf\x01\n\tJobResult\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.cortex.agent.v1.JobStatus\x12\x12\n\nresult_ptr\x18\x03 \x01(\t\x12\x11\n\tworker_id\x18\x04 \x01(\t\x12\x14\n\x0c\x65xecution_ms\x18\x05 \x01(\x03\x12\x12\n\nerror_code\x18\x06 \x01(\t\x12\x15\n\rerror_message\x18\x07 \x01(\t*|\n\x0bJobPriority\x12\x1c\n\x18JOB_PRIORITY_UNSPECIFIED\x10\x00\x12\x1c\n\x18JOB_PRIORITY_INTERACTIVE\x10\x01\x12\x16\n\x12JOB_PRIORITY_BATCH\x10\x02\x12\x19\n\x15JOB_PRIORITY_CRITICAL\x10\x03*\x86\x02\n\tJobStatus\x12\x1a\n\x16JOB_STATUS_UNSPECIFIED\x10\x00\x12\x16\n\x12JOB_STATUS_PENDING\x10\x01\x12\x18\n\x14JOB_STATUS_SCHEDULED\x10\x02\x12\x19\n\x15JOB_STATUS_DISPATCHED\x10\x03\x12\x16\n\x12JOB_STATUS_RUNNING\x10\x04\x12\x18\n\x14JOB_STATUS_SUCCEEDED\x10\x05\x12\x15\n\x11JOB_STATUS_FAILED\x10\x06\x12\x18\n\x14JOB_STATUS_CANCELLED\x10\x07\x12\x15\n\x11JOB_STATUS_DENIED\x10\x08\x12\x16\n\x12JOB_STATUS_TIMEOUT\x10\tB|\n\x16\x61i.cortex.cap.agent.v1P\x01Z(github.com/coretexos/cap/cortex/agent/v1\xaa\x02\x0f\x43ortex.Agent.V1\xca\x02\x0f\x43ortex\\Agent\\V1\xea\x02\x11\x43ortex::Agent::V1b\x06proto3')
|
|
18
|
+
|
|
19
|
+
_globals = globals()
|
|
20
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
21
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cortex.agent.v1.job_pb2', _globals)
|
|
22
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
23
|
+
_globals['DESCRIPTOR']._options = None
|
|
24
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\026ai.cortex.cap.agent.v1P\001Z(github.com/coretexos/cap/cortex/agent/v1\252\002\017Cortex.Agent.V1\312\002\017Cortex\\Agent\\V1\352\002\021Cortex::Agent::V1'
|
|
25
|
+
_globals['_JOBREQUEST_ENVENTRY']._options = None
|
|
26
|
+
_globals['_JOBREQUEST_ENVENTRY']._serialized_options = b'8\001'
|
|
27
|
+
_globals['_JOBPRIORITY']._serialized_start=518
|
|
28
|
+
_globals['_JOBPRIORITY']._serialized_end=642
|
|
29
|
+
_globals['_JOBSTATUS']._serialized_start=645
|
|
30
|
+
_globals['_JOBSTATUS']._serialized_end=907
|
|
31
|
+
_globals['_JOBREQUEST']._serialized_start=47
|
|
32
|
+
_globals['_JOBREQUEST']._serialized_end=338
|
|
33
|
+
_globals['_JOBREQUEST_ENVENTRY']._serialized_start=296
|
|
34
|
+
_globals['_JOBREQUEST_ENVENTRY']._serialized_end=338
|
|
35
|
+
_globals['_JOBRESULT']._serialized_start=341
|
|
36
|
+
_globals['_JOBRESULT']._serialized_end=516
|
|
37
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: cortex/agent/v1/safety.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from cortex.agent.v1 import job_pb2 as cortex_dot_agent_dot_v1_dot_job__pb2
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x63ortex/agent/v1/safety.proto\x12\x0f\x63ortex.agent.v1\x1a\x19\x63ortex/agent/v1/job.proto\"\x8b\x01\n\x12PolicyCheckRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\r\n\x05topic\x18\x02 \x01(\t\x12\x0e\n\x06tenant\x18\x03 \x01(\t\x12.\n\x08priority\x18\x04 \x01(\x0e\x32\x1c.cortex.agent.v1.JobPriority\x12\x16\n\x0e\x65stimated_cost\x18\x05 \x01(\x01\"t\n\x13PolicyCheckResponse\x12/\n\x08\x64\x65\x63ision\x18\x01 \x01(\x0e\x32\x1d.cortex.agent.v1.DecisionType\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x1c\n\x14redacted_context_ptr\x18\x03 \x01(\t*\x9b\x01\n\x0c\x44\x65\x63isionType\x12\x1d\n\x19\x44\x45\x43ISION_TYPE_UNSPECIFIED\x10\x00\x12\x17\n\x13\x44\x45\x43ISION_TYPE_ALLOW\x10\x01\x12\x16\n\x12\x44\x45\x43ISION_TYPE_DENY\x10\x02\x12\x1f\n\x1b\x44\x45\x43ISION_TYPE_REQUIRE_HUMAN\x10\x03\x12\x1a\n\x16\x44\x45\x43ISION_TYPE_THROTTLE\x10\x04\x32\x62\n\x0cSafetyKernel\x12R\n\x05\x43heck\x12#.cortex.agent.v1.PolicyCheckRequest\x1a$.cortex.agent.v1.PolicyCheckResponseB|\n\x16\x61i.cortex.cap.agent.v1P\x01Z(github.com/coretexos/cap/cortex/agent/v1\xaa\x02\x0f\x43ortex.Agent.V1\xca\x02\x0f\x43ortex\\Agent\\V1\xea\x02\x11\x43ortex::Agent::V1b\x06proto3')
|
|
19
|
+
|
|
20
|
+
_globals = globals()
|
|
21
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
22
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cortex.agent.v1.safety_pb2', _globals)
|
|
23
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
24
|
+
_globals['DESCRIPTOR']._options = None
|
|
25
|
+
_globals['DESCRIPTOR']._serialized_options = b'\n\026ai.cortex.cap.agent.v1P\001Z(github.com/coretexos/cap/cortex/agent/v1\252\002\017Cortex.Agent.V1\312\002\017Cortex\\Agent\\V1\352\002\021Cortex::Agent::V1'
|
|
26
|
+
_globals['_DECISIONTYPE']._serialized_start=337
|
|
27
|
+
_globals['_DECISIONTYPE']._serialized_end=492
|
|
28
|
+
_globals['_POLICYCHECKREQUEST']._serialized_start=77
|
|
29
|
+
_globals['_POLICYCHECKREQUEST']._serialized_end=216
|
|
30
|
+
_globals['_POLICYCHECKRESPONSE']._serialized_start=218
|
|
31
|
+
_globals['_POLICYCHECKRESPONSE']._serialized_end=334
|
|
32
|
+
_globals['_SAFETYKERNEL']._serialized_start=494
|
|
33
|
+
_globals['_SAFETYKERNEL']._serialized_end=592
|
|
34
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
|
|
5
|
+
from cortex.agent.v1 import safety_pb2 as cortex_dot_agent_dot_v1_dot_safety__pb2
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SafetyKernelStub(object):
|
|
9
|
+
"""SafetyKernel defines the policy decision service.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, channel):
|
|
13
|
+
"""Constructor.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
channel: A grpc.Channel.
|
|
17
|
+
"""
|
|
18
|
+
self.Check = channel.unary_unary(
|
|
19
|
+
'/cortex.agent.v1.SafetyKernel/Check',
|
|
20
|
+
request_serializer=cortex_dot_agent_dot_v1_dot_safety__pb2.PolicyCheckRequest.SerializeToString,
|
|
21
|
+
response_deserializer=cortex_dot_agent_dot_v1_dot_safety__pb2.PolicyCheckResponse.FromString,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SafetyKernelServicer(object):
|
|
26
|
+
"""SafetyKernel defines the policy decision service.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def Check(self, request, context):
|
|
30
|
+
"""Missing associated documentation comment in .proto file."""
|
|
31
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
32
|
+
context.set_details('Method not implemented!')
|
|
33
|
+
raise NotImplementedError('Method not implemented!')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def add_SafetyKernelServicer_to_server(servicer, server):
|
|
37
|
+
rpc_method_handlers = {
|
|
38
|
+
'Check': grpc.unary_unary_rpc_method_handler(
|
|
39
|
+
servicer.Check,
|
|
40
|
+
request_deserializer=cortex_dot_agent_dot_v1_dot_safety__pb2.PolicyCheckRequest.FromString,
|
|
41
|
+
response_serializer=cortex_dot_agent_dot_v1_dot_safety__pb2.PolicyCheckResponse.SerializeToString,
|
|
42
|
+
),
|
|
43
|
+
}
|
|
44
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
45
|
+
'cortex.agent.v1.SafetyKernel', rpc_method_handlers)
|
|
46
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# This class is part of an EXPERIMENTAL API.
|
|
50
|
+
class SafetyKernel(object):
|
|
51
|
+
"""SafetyKernel defines the policy decision service.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def Check(request,
|
|
56
|
+
target,
|
|
57
|
+
options=(),
|
|
58
|
+
channel_credentials=None,
|
|
59
|
+
call_credentials=None,
|
|
60
|
+
insecure=False,
|
|
61
|
+
compression=None,
|
|
62
|
+
wait_for_ready=None,
|
|
63
|
+
timeout=None,
|
|
64
|
+
metadata=None):
|
|
65
|
+
return grpc.experimental.unary_unary(request, target, '/cortex.agent.v1.SafetyKernel/Check',
|
|
66
|
+
cortex_dot_agent_dot_v1_dot_safety__pb2.PolicyCheckRequest.SerializeToString,
|
|
67
|
+
cortex_dot_agent_dot_v1_dot_safety__pb2.PolicyCheckResponse.FromString,
|
|
68
|
+
options, channel_credentials,
|
|
69
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Callable, Awaitable
|
|
3
|
+
|
|
4
|
+
from google.protobuf import timestamp_pb2
|
|
5
|
+
from cap.pb.cortex.agent.v1 import buspacket_pb2, job_pb2
|
|
6
|
+
|
|
7
|
+
DEFAULT_PROTOCOL_VERSION = 1
|
|
8
|
+
SUBJECT_RESULT = "sys.job.result"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def run_worker(nats_url: str, subject: str, handler: Callable[[job_pb2.JobRequest], Awaitable[job_pb2.JobResult]]):
|
|
12
|
+
import nats
|
|
13
|
+
|
|
14
|
+
nc = await nats.connect(servers=nats_url, name="cap-worker")
|
|
15
|
+
|
|
16
|
+
async def on_msg(msg):
|
|
17
|
+
packet = buspacket_pb2.BusPacket()
|
|
18
|
+
packet.ParseFromString(msg.data)
|
|
19
|
+
req = packet.job_request
|
|
20
|
+
if not req.job_id:
|
|
21
|
+
return
|
|
22
|
+
try:
|
|
23
|
+
res = await handler(req)
|
|
24
|
+
except Exception as exc: # noqa: BLE001
|
|
25
|
+
res = job_pb2.JobResult(
|
|
26
|
+
job_id=req.job_id,
|
|
27
|
+
status=job_pb2.JOB_STATUS_FAILED,
|
|
28
|
+
error_message=str(exc),
|
|
29
|
+
)
|
|
30
|
+
ts = timestamp_pb2.Timestamp()
|
|
31
|
+
ts.GetCurrentTime()
|
|
32
|
+
out = buspacket_pb2.BusPacket()
|
|
33
|
+
out.trace_id = packet.trace_id
|
|
34
|
+
out.sender_id = "cap-worker"
|
|
35
|
+
out.protocol_version = DEFAULT_PROTOCOL_VERSION
|
|
36
|
+
out.created_at.CopyFrom(ts)
|
|
37
|
+
out.job_result.CopyFrom(res)
|
|
38
|
+
await nc.publish(SUBJECT_RESULT, out.SerializeToString())
|
|
39
|
+
|
|
40
|
+
await nc.subscribe(subject, queue=subject, cb=on_msg)
|
|
41
|
+
try:
|
|
42
|
+
while True:
|
|
43
|
+
await asyncio.sleep(1)
|
|
44
|
+
finally:
|
|
45
|
+
await nc.drain()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cap-sdk-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CAP (Cortex Agent Protocol) Python SDK
|
|
5
|
+
Project-URL: Homepage, https://github.com/coretexos/cap
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: protobuf>=4.25.0
|
|
9
|
+
Requires-Dist: grpcio>=1.59.0
|
|
10
|
+
Requires-Dist: nats-py>=2.6.0
|
|
11
|
+
|
|
12
|
+
# CAP Python SDK
|
|
13
|
+
|
|
14
|
+
Asyncio-first SDK with NATS helpers for CAP workers and clients.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
1. Generate protobuf stubs into this SDK (one-time per proto change):
|
|
18
|
+
```bash
|
|
19
|
+
python -m grpc_tools.protoc \
|
|
20
|
+
-I../../proto \
|
|
21
|
+
--python_out=./cap/pb \
|
|
22
|
+
--grpc_python_out=./cap/pb \
|
|
23
|
+
../../proto/cortex/agent/v1/*.proto
|
|
24
|
+
```
|
|
25
|
+
(Or run `./tools/make_protos.sh` from repo root and copy `generated/python` into `sdk/python/cap/pb`.)
|
|
26
|
+
|
|
27
|
+
2. Install:
|
|
28
|
+
```bash
|
|
29
|
+
pip install -e .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
3. Run a worker:
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from cap import worker
|
|
36
|
+
from cap.pb.cortex.agent.v1 import job_pb2
|
|
37
|
+
|
|
38
|
+
async def handle(req: job_pb2.JobRequest):
|
|
39
|
+
return job_pb2.JobResult(
|
|
40
|
+
job_id=req.job_id,
|
|
41
|
+
status=job_pb2.JOB_STATUS_SUCCEEDED,
|
|
42
|
+
result_ptr=f"redis://res/{req.job_id}",
|
|
43
|
+
worker_id="worker-echo-1",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
asyncio.run(worker.run_worker("nats://127.0.0.1:4222", "job.echo", handle))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Files
|
|
50
|
+
- `cap/bus.py` — NATS connector.
|
|
51
|
+
- `cap/worker.py` — worker skeleton with handler hook.
|
|
52
|
+
- `cap/client.py` — publish JobRequest to `sys.job.submit`.
|
|
53
|
+
- `cap/pb/` — protobuf stubs (generated).
|
|
54
|
+
|
|
55
|
+
## Defaults
|
|
56
|
+
- Subjects: `sys.job.submit`, `sys.job.result`, `sys.heartbeat`.
|
|
57
|
+
- Protocol version: `1`.
|
|
58
|
+
|
|
59
|
+
Swap out `cap.bus` if you need a different transport.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
cap/__init__.py
|
|
4
|
+
cap/bus.py
|
|
5
|
+
cap/client.py
|
|
6
|
+
cap/worker.py
|
|
7
|
+
cap/pb/__init__.py
|
|
8
|
+
cap/pb/cortex/__init__.py
|
|
9
|
+
cap/pb/cortex/agent/__init__.py
|
|
10
|
+
cap/pb/cortex/agent/v1/__init__.py
|
|
11
|
+
cap/pb/cortex/agent/v1/alert_pb2.py
|
|
12
|
+
cap/pb/cortex/agent/v1/alert_pb2_grpc.py
|
|
13
|
+
cap/pb/cortex/agent/v1/buspacket_pb2.py
|
|
14
|
+
cap/pb/cortex/agent/v1/buspacket_pb2_grpc.py
|
|
15
|
+
cap/pb/cortex/agent/v1/heartbeat_pb2.py
|
|
16
|
+
cap/pb/cortex/agent/v1/heartbeat_pb2_grpc.py
|
|
17
|
+
cap/pb/cortex/agent/v1/job_pb2.py
|
|
18
|
+
cap/pb/cortex/agent/v1/job_pb2_grpc.py
|
|
19
|
+
cap/pb/cortex/agent/v1/safety_pb2.py
|
|
20
|
+
cap/pb/cortex/agent/v1/safety_pb2_grpc.py
|
|
21
|
+
cap_sdk_python.egg-info/PKG-INFO
|
|
22
|
+
cap_sdk_python.egg-info/SOURCES.txt
|
|
23
|
+
cap_sdk_python.egg-info/dependency_links.txt
|
|
24
|
+
cap_sdk_python.egg-info/requires.txt
|
|
25
|
+
cap_sdk_python.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cap-sdk-python"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CAP (Cortex Agent Protocol) Python SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"protobuf>=4.25.0",
|
|
13
|
+
"grpcio>=1.59.0",
|
|
14
|
+
"nats-py>=2.6.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://github.com/coretexos/cap"
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["."]
|