cortexflow-sdk 0.1.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.
@@ -0,0 +1,38 @@
1
+ """cortexflow-sdk — typed interfaces for building CortexFlow plugins.
2
+
3
+ Install this package (not the full ``cortexflow`` gateway) to write a
4
+ plugin, tool, or channel adapter that CortexFlow can load::
5
+
6
+ pip install cortexflow-sdk
7
+
8
+ Then subclass one of:
9
+
10
+ Plugin — the entry point every plugin package registers
11
+ Tool — a discrete capability the agent can invoke
12
+ ChannelAdapter — a messaging platform integration
13
+
14
+ See https://github.com/TheAmitChandra/CortexFlow for the full gateway
15
+ and plugin-loading documentation.
16
+ """
17
+
18
+ from cortexflow_sdk.channels import (
19
+ Attachment,
20
+ ChannelAdapter,
21
+ InboundMessage,
22
+ MessageHandler,
23
+ )
24
+ from cortexflow_sdk.plugins import Plugin, PluginMetadata
25
+ from cortexflow_sdk.tools import Tool, ToolResult
26
+
27
+ __all__ = [
28
+ "Attachment",
29
+ "ChannelAdapter",
30
+ "InboundMessage",
31
+ "MessageHandler",
32
+ "Plugin",
33
+ "PluginMetadata",
34
+ "Tool",
35
+ "ToolResult",
36
+ ]
37
+
38
+ __version__ = "0.1.0"
@@ -0,0 +1,99 @@
1
+ """Channel adapter abstract base class and shared data types."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from abc import ABC, abstractmethod
7
+ from dataclasses import dataclass, field
8
+ from typing import Any, Awaitable, Callable
9
+
10
+
11
+ @dataclass
12
+ class Attachment:
13
+ """A file or media attachment from a channel message."""
14
+
15
+ type: str # "image" | "audio" | "video" | "document"
16
+ url: str | None = None
17
+ data: bytes | None = None
18
+ filename: str | None = None
19
+ mime_type: str | None = None
20
+
21
+
22
+ @dataclass
23
+ class InboundMessage:
24
+ """Normalised inbound message from any channel adapter."""
25
+
26
+ channel: str
27
+ sender_id: str
28
+ sender_name: str
29
+ text: str | None
30
+ attachments: list[Attachment] = field(default_factory=list)
31
+ thread_id: str | None = None
32
+ reply_to_id: str | None = None
33
+ timestamp: float = field(default_factory=time.time)
34
+ raw: dict[str, Any] = field(default_factory=dict)
35
+
36
+
37
+ #: A coroutine function that handles an inbound message.
38
+ MessageHandler = Callable[[InboundMessage], Awaitable[None]]
39
+
40
+
41
+ class ChannelAdapter(ABC):
42
+ """Abstract base for all platform channel adapters.
43
+
44
+ Subclasses must set ``channel_id`` as a class attribute and implement
45
+ ``connect``, ``disconnect``, and ``send``.
46
+ """
47
+
48
+ channel_id: str # e.g. "telegram" | "discord" | "slack"
49
+
50
+ def __init__(self, config: dict[str, Any]) -> None:
51
+ self.config = config
52
+ self._handler: MessageHandler | None = None
53
+
54
+ @abstractmethod
55
+ async def connect(self) -> None:
56
+ """Establish connection to the platform. Raises on failure."""
57
+ ...
58
+
59
+ @abstractmethod
60
+ async def disconnect(self) -> None:
61
+ """Gracefully disconnect from the platform."""
62
+ ...
63
+
64
+ @abstractmethod
65
+ async def send(
66
+ self,
67
+ target: str,
68
+ text: str,
69
+ *,
70
+ reply_to: str | None = None,
71
+ attachments: list[Attachment] | None = None,
72
+ ) -> str | None:
73
+ """Send a message to *target* (platform-specific ID).
74
+
75
+ Returns the sent message ID if the platform provides one.
76
+ """
77
+ ...
78
+
79
+ def on_message(self, handler: MessageHandler) -> None:
80
+ """Register the handler that receives all inbound messages."""
81
+ self._handler = handler
82
+
83
+ async def _dispatch(self, message: InboundMessage) -> None:
84
+ """Forward *message* to the registered handler (if any)."""
85
+ if self._handler is not None:
86
+ await self._handler(message)
87
+
88
+ def get_config_schema(self) -> dict[str, Any]:
89
+ """Return a JSON Schema describing this adapter's config options."""
90
+ return {
91
+ "type": "object",
92
+ "properties": {
93
+ "enabled": {"type": "boolean", "default": False},
94
+ },
95
+ "required": [],
96
+ }
97
+
98
+ def __repr__(self) -> str:
99
+ return f"{self.__class__.__name__}(channel_id={self.channel_id!r})"
@@ -0,0 +1,101 @@
1
+ """Plugin base class — the contract every CortexFlow plugin must satisfy.
2
+
3
+ A plugin is a Python package installed via ``pip install <name>`` that:
4
+ - Declares a ``cortexflow.plugins`` entry point pointing to a Plugin subclass
5
+ - Provides one or more tools, channel adapters, or TTS/STT backends
6
+ - Declares its capabilities and required permissions upfront
7
+
8
+ Plugin types:
9
+ "tool" — adds Tool instances to the gateway's ToolRegistry
10
+ "channel" — adds a ChannelAdapter to the gateway
11
+ "tts" — alternative TTS backend
12
+ "stt" — alternative STT backend
13
+ "memory" — alternative memory tier
14
+ "generic" — anything else (lifecycle hooks, middleware)
15
+
16
+ Example plugin (in a separate package)::
17
+
18
+ # cortexflow_github/plugin.py
19
+ from cortexflow_sdk import Plugin, PluginMetadata
20
+
21
+ class GitHubPlugin(Plugin):
22
+ metadata = PluginMetadata(
23
+ name="cortexflow-github",
24
+ version="1.0.0",
25
+ plugin_type="tool",
26
+ description="GitHub integration — PRs, issues, commits",
27
+ permissions=["network"],
28
+ )
29
+
30
+ def get_tools(self):
31
+ from .tools import GitHubTool
32
+ return [GitHubTool()]
33
+
34
+ # In pyproject.toml:
35
+ # [project.entry-points."cortexflow.plugins"]
36
+ # cortexflow-github = "cortexflow_github.plugin:GitHubPlugin"
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ from abc import ABC
42
+ from dataclasses import dataclass, field
43
+ from typing import TYPE_CHECKING, Any
44
+
45
+ if TYPE_CHECKING:
46
+ from cortexflow_sdk.channels import ChannelAdapter
47
+ from cortexflow_sdk.tools import Tool
48
+
49
+
50
+ @dataclass
51
+ class PluginMetadata:
52
+ """Metadata that every plugin must declare."""
53
+
54
+ name: str
55
+ version: str
56
+ plugin_type: str # "tool" | "channel" | "tts" | "stt" | "memory" | "generic"
57
+ description: str
58
+ permissions: list[str] = field(default_factory=list)
59
+ author: str = ""
60
+ homepage: str = ""
61
+
62
+
63
+ class Plugin(ABC):
64
+ """Abstract base for all CortexFlow plugins.
65
+
66
+ Subclass this and implement the methods that apply to your plugin_type.
67
+ Unimplemented optional methods return empty lists/None by default.
68
+ """
69
+
70
+ metadata: PluginMetadata
71
+
72
+ # ------------------------------------------------------------------
73
+ # Lifecycle
74
+ # ------------------------------------------------------------------
75
+
76
+ async def on_load(self) -> None:
77
+ """Called once when the plugin is loaded. Perform async init here."""
78
+
79
+ async def on_unload(self) -> None:
80
+ """Called when the plugin is unloaded (e.g. on gateway shutdown)."""
81
+
82
+ # ------------------------------------------------------------------
83
+ # Type-specific contribution methods
84
+ # ------------------------------------------------------------------
85
+
86
+ def get_tools(self) -> list["Tool"]:
87
+ """Return Tool instances contributed by this plugin (type=tool)."""
88
+ return []
89
+
90
+ def get_channel_adapter(self) -> "ChannelAdapter | None":
91
+ """Return a ChannelAdapter contributed by this plugin (type=channel)."""
92
+ return None
93
+
94
+ def get_config_schema(self) -> dict[str, Any]:
95
+ """Return JSON Schema for plugin-specific config options."""
96
+ return {"type": "object", "properties": {}}
97
+
98
+ # ------------------------------------------------------------------
99
+
100
+ def __repr__(self) -> str:
101
+ return f"Plugin({self.metadata.name!r} v{self.metadata.version}, type={self.metadata.plugin_type!r})"
@@ -0,0 +1,116 @@
1
+ """Tool abstract base class and shared data types.
2
+
3
+ A Tool is a discrete capability an agent can invoke during a pipeline run.
4
+ Tools are:
5
+ - Declared with a name, description, and typed parameter schema
6
+ - Invoked with a plain dict of arguments
7
+ - Sandboxed: each tool declares what permissions it needs
8
+ - Stateless: tools must not store state between calls
9
+
10
+ Tool authors subclass ``Tool`` and implement ``execute``.
11
+
12
+ Example::
13
+
14
+ from cortexflow_sdk import Tool, ToolResult
15
+
16
+ class MyTool(Tool):
17
+ name = "my_tool"
18
+ description = "Does something useful."
19
+ parameters = {
20
+ "query": {"type": "str", "description": "The search query"},
21
+ }
22
+ permissions = ["network"]
23
+
24
+ async def execute(self, query: str) -> ToolResult:
25
+ result = await some_api(query)
26
+ return ToolResult(output=result, tool=self.name)
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from abc import ABC, abstractmethod
32
+ from dataclasses import dataclass, field
33
+ from typing import Any
34
+
35
+
36
+ @dataclass
37
+ class ToolResult:
38
+ """Returned by every tool execution."""
39
+
40
+ tool: str
41
+ output: Any
42
+ error: str | None = None
43
+ metadata: dict[str, Any] = field(default_factory=dict)
44
+
45
+ @property
46
+ def success(self) -> bool:
47
+ return self.error is None
48
+
49
+ def to_prompt_block(self) -> str:
50
+ """Serialise for injection into the LLM prompt."""
51
+ if self.error:
52
+ return f"[TOOL:{self.tool} ERROR] {self.error}"
53
+ output = str(self.output) if not isinstance(self.output, str) else self.output
54
+ return f"[TOOL:{self.tool}]\n{output}"
55
+
56
+
57
+ class Tool(ABC):
58
+ """Abstract base for all CortexFlow tools.
59
+
60
+ Class attributes (declare on subclass):
61
+ name: Unique tool identifier, snake_case.
62
+ description: One sentence used by the LLM to decide when to call it.
63
+ parameters: Dict of {param_name: {"type": str, "description": str, "required": bool}}.
64
+ permissions: List of required permission strings, e.g. ["network", "filesystem:read"].
65
+ """
66
+
67
+ name: str
68
+ description: str
69
+ parameters: dict[str, dict[str, Any]] = {}
70
+ permissions: list[str] = []
71
+
72
+ @abstractmethod
73
+ async def execute(self, **kwargs: Any) -> ToolResult:
74
+ """Run the tool with the given arguments.
75
+
76
+ Args should match the keys declared in ``parameters``.
77
+ Returns a ToolResult — never raises; wrap errors in ToolResult.error.
78
+ """
79
+ ...
80
+
81
+ def get_schema(self) -> dict[str, Any]:
82
+ """Return JSON Schema describing the tool for LLM function calling."""
83
+ props: dict[str, Any] = {}
84
+ required: list[str] = []
85
+ for param_name, spec in self.parameters.items():
86
+ props[param_name] = {
87
+ "type": _py_to_json_type(spec.get("type", "str")),
88
+ "description": spec.get("description", ""),
89
+ }
90
+ if spec.get("required", True):
91
+ required.append(param_name)
92
+
93
+ return {
94
+ "name": self.name,
95
+ "description": self.description,
96
+ "parameters": {
97
+ "type": "object",
98
+ "properties": props,
99
+ "required": required,
100
+ },
101
+ }
102
+
103
+ def __repr__(self) -> str:
104
+ return f"{self.__class__.__name__}(name={self.name!r})"
105
+
106
+
107
+ def _py_to_json_type(py_type: str) -> str:
108
+ mapping = {
109
+ "str": "string",
110
+ "int": "integer",
111
+ "float": "number",
112
+ "bool": "boolean",
113
+ "list": "array",
114
+ "dict": "object",
115
+ }
116
+ return mapping.get(py_type, "string")
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: cortexflow-sdk
3
+ Version: 0.1.0
4
+ Summary: Typed interfaces for building CortexFlow plugins, tools, and channel adapters — no gateway dependencies required.
5
+ Author-email: Amit Chandra <amit.vervebot@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=8.2.0; extra == "dev"
12
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
13
+ Dynamic: license-file
14
+
15
+ # cortexflow-sdk
16
+
17
+ Typed interfaces for building [CortexFlow](https://github.com/TheAmitChandra/CortexFlow) plugins — without installing the full gateway.
18
+
19
+ CortexFlow's gateway depends on FastAPI, Redis, Qdrant, and SDKs for all 14
20
+ supported channels. None of that is needed to *write* a plugin — only to
21
+ *run* the gateway that loads it. This package contains just the three base
22
+ classes a plugin author needs, with zero third-party dependencies.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install cortexflow-sdk
28
+ ```
29
+
30
+ ## Writing a plugin
31
+
32
+ Every plugin package registers a `Plugin` subclass via a `cortexflow.plugins`
33
+ entry point:
34
+
35
+ ```python
36
+ # my_plugin/plugin.py
37
+ from cortexflow_sdk import Plugin, PluginMetadata, Tool, ToolResult
38
+
39
+
40
+ class WeatherTool(Tool):
41
+ name = "get_weather"
42
+ description = "Get the current weather for a city."
43
+ parameters = {
44
+ "city": {"type": "str", "description": "City name", "required": True},
45
+ }
46
+ permissions = ["network"]
47
+
48
+ async def execute(self, city: str) -> ToolResult:
49
+ # ... call a weather API ...
50
+ return ToolResult(tool=self.name, output=f"Sunny in {city}")
51
+
52
+
53
+ class WeatherPlugin(Plugin):
54
+ metadata = PluginMetadata(
55
+ name="cortexflow-weather",
56
+ version="1.0.0",
57
+ plugin_type="tool",
58
+ description="Adds a get_weather tool.",
59
+ permissions=["network"],
60
+ )
61
+
62
+ def get_tools(self):
63
+ return [WeatherTool()]
64
+ ```
65
+
66
+ ```toml
67
+ # my_plugin/pyproject.toml
68
+ [project.entry-points."cortexflow.plugins"]
69
+ cortexflow-weather = "my_plugin.plugin:WeatherPlugin"
70
+ ```
71
+
72
+ Once published to PyPI and installed alongside the CortexFlow gateway,
73
+ `cortex plugin add cortexflow-weather` discovers and loads it.
74
+
75
+ ## Plugin types
76
+
77
+ | `plugin_type` | Implement | Contributes |
78
+ |---|---|---|
79
+ | `tool` | `get_tools()` | One or more `Tool` instances |
80
+ | `channel` | `get_channel_adapter()` | A `ChannelAdapter` instance |
81
+ | `tts` / `stt` / `memory` | — | Loaded by name; see gateway docs |
82
+ | `generic` | `on_load()` / `on_unload()` | Lifecycle hooks only |
83
+
84
+ ## Writing a channel adapter plugin
85
+
86
+ ```python
87
+ from cortexflow_sdk import ChannelAdapter, InboundMessage
88
+
89
+
90
+ class MyChannelAdapter(ChannelAdapter):
91
+ channel_id = "my_channel"
92
+
93
+ async def connect(self) -> None: ...
94
+ async def disconnect(self) -> None: ...
95
+ async def send(self, target, text, *, reply_to=None, attachments=None):
96
+ ...
97
+
98
+ async def _on_platform_event(self, raw_event: dict) -> None:
99
+ await self._dispatch(InboundMessage(
100
+ channel=self.channel_id,
101
+ sender_id=raw_event["user_id"],
102
+ sender_name=raw_event["user_name"],
103
+ text=raw_event["text"],
104
+ ))
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1,9 @@
1
+ cortexflow_sdk/__init__.py,sha256=4UWcL5iE_qeZ_Fz7b6SaUoQq87qgkFWc_qNQLeGk0CU,976
2
+ cortexflow_sdk/channels.py,sha256=iJ2kJYnYlu4B7cEPHf8Ur4ntum8oPXlV0SWxEU0kuvg,2907
3
+ cortexflow_sdk/plugins.py,sha256=NgNAQFuiTmVYIaFKQMfiSQKu2p8icDfYCmgl9wBeOPA,3471
4
+ cortexflow_sdk/tools.py,sha256=YCIznkTA_uZ52cEnSih981gnb5uJGZHzr7s6JdZamO0,3597
5
+ cortexflow_sdk-0.1.0.dist-info/licenses/LICENSE,sha256=7Dz5G7L1S2nxC1KQDChyyWKVx4NDmJJDf-KNruUtrHk,1069
6
+ cortexflow_sdk-0.1.0.dist-info/METADATA,sha256=aBz71SglE4-H0_lNjQzNa_TKAeZFsVy44s3LTjlWXlc,3168
7
+ cortexflow_sdk-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
8
+ cortexflow_sdk-0.1.0.dist-info/top_level.txt,sha256=mpyjX-TvTeBifQZpJ5vUoKg-wn4pG902vhcio7YjWHw,15
9
+ cortexflow_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amit Chandra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ cortexflow_sdk