Skip to content

Commit a59386b

Browse files
authored
Merge pull request #5 from onkernel/release-please--branches--main--changes--next
release: 0.1.0-alpha.4
2 parents 5f8fc19 + fa66332 commit a59386b

File tree

8 files changed

+190
-5
lines changed

8 files changed

+190
-5
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.1.0-alpha.3"
2+
".": "0.1.0-alpha.4"
33
}

.stats.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 4
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-d168b58fcf39dbd0458d132091793d3e2d0930070b7dda2d5f7f1baff20dd31b.yml
33
openapi_spec_hash: b7e0fd7ee1656d7dbad57209d1584d92
4-
config_hash: 75c0b894355904e2a91b70445072d4b4
4+
config_hash: c2bc5253d8afd6d67e031f73353c9b22

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 0.1.0-alpha.4 (2025-05-10)
4+
5+
Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/onkernel/kernel-python-sdk/compare/v0.1.0-alpha.3...v0.1.0-alpha.4)
6+
7+
### Features
8+
9+
* **api:** update via SDK Studio ([d93116e](https://github.com/onkernel/kernel-python-sdk/commit/d93116e633eb9503647acfbe3e9769f33fdd19ed))
10+
311
## 0.1.0-alpha.3 (2025-05-10)
412

513
Full Changelog: [v0.1.0-alpha.2...v0.1.0-alpha.3](https://github.com/onkernel/kernel-python-sdk/compare/v0.1.0-alpha.2...v0.1.0-alpha.3)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ It is generated with [Stainless](https://www.stainless.com/).
1010

1111
## Documentation
1212

13-
The full API of this library can be found in [api.md](api.md).
13+
The REST API documentation can be found on [docs.onkernel.com](https://docs.onkernel.com). The full API of this library can be found in [api.md](api.md).
1414

1515
## Installation
1616

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "kernel"
3-
version = "0.1.0-alpha.3"
3+
version = "0.1.0-alpha.4"
44
description = "The official Python library for the kernel API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/kernel/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@
2828
)
2929
from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient
3030
from ._utils._logs import setup_logging as _setup_logging
31+
from .app_framework import (
32+
App,
33+
KernelApp,
34+
KernelJson,
35+
KernelAction,
36+
KernelAppJson,
37+
KernelContext,
38+
KernelActionJson,
39+
KernelAppRegistry,
40+
app_registry,
41+
export_registry,
42+
)
3143

3244
__all__ = [
3345
"types",
@@ -68,6 +80,16 @@
6880
"DEFAULT_CONNECTION_LIMITS",
6981
"DefaultHttpxClient",
7082
"DefaultAsyncHttpxClient",
83+
"KernelContext",
84+
"KernelAction",
85+
"KernelActionJson",
86+
"KernelAppJson",
87+
"KernelJson",
88+
"KernelApp",
89+
"KernelAppRegistry",
90+
"App",
91+
"app_registry",
92+
"export_registry",
7193
]
7294

7395
if not _t.TYPE_CHECKING:

src/kernel/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "kernel"
4-
__version__ = "0.1.0-alpha.3" # x-release-please-version
4+
__version__ = "0.1.0-alpha.4" # x-release-please-version

src/kernel/app_framework.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import json
2+
import inspect
3+
import functools
4+
from typing import Any, Dict, List, Union, TypeVar, Callable, Optional
5+
from dataclasses import dataclass
6+
7+
T = TypeVar("T")
8+
9+
# Context definition
10+
@dataclass
11+
class KernelContext:
12+
"""Context object passed to action handlers"""
13+
invocation_id: str
14+
15+
# Action definition
16+
@dataclass
17+
class KernelAction:
18+
"""Action that can be invoked on a Kernel app"""
19+
name: str
20+
handler: Callable[..., Any]
21+
22+
# JSON interfaces
23+
@dataclass
24+
class KernelActionJson:
25+
"""JSON representation of a Kernel action"""
26+
name: str
27+
28+
@dataclass
29+
class KernelAppJson:
30+
"""JSON representation of a Kernel app"""
31+
name: str
32+
actions: List[KernelActionJson]
33+
34+
@dataclass
35+
class KernelJson:
36+
"""JSON representation of Kernel manifest"""
37+
apps: List[KernelAppJson]
38+
entrypoint: str
39+
40+
# App class
41+
class KernelApp:
42+
def __init__(self, name: str):
43+
self.name = name
44+
self.actions: Dict[str, KernelAction] = {}
45+
# Register this app in the global registry
46+
_app_registry.register_app(self)
47+
48+
def action(self, name_or_handler: Optional[Union[str, Callable[..., Any]]] = None) -> Callable[..., Any]:
49+
"""Decorator to register an action with the app"""
50+
if name_or_handler is None:
51+
# This is the @app.action() case, which should return the decorator
52+
def decorator(f: Callable[..., Any]) -> Callable[..., Any]:
53+
return self._register_action(f.__name__, f)
54+
return decorator
55+
elif callable(name_or_handler):
56+
# This is the @app.action case (handler passed directly)
57+
return self._register_action(name_or_handler.__name__, name_or_handler)
58+
else:
59+
# This is the @app.action("name") case (name_or_handler is a string)
60+
def decorator(f: Callable[..., Any]) -> Callable[..., Any]:
61+
return self._register_action(name_or_handler, f) # name_or_handler is the name string here
62+
return decorator
63+
64+
def _register_action(self, name: str, handler: Callable[..., Any]) -> Callable[..., Any]:
65+
"""Internal method to register an action"""
66+
67+
@functools.wraps(handler)
68+
def wrapper(*args: Any, **kwargs: Any) -> Any:
69+
# Determine if the original handler accepts context as first argument
70+
sig = inspect.signature(handler)
71+
param_names = list(sig.parameters.keys())
72+
param_count = len(param_names)
73+
74+
if param_count == 1:
75+
actual_input = None
76+
# The handler only takes input
77+
if len(args) > 0: # Prioritize args if context was implicitly passed
78+
# If context (args[0]) and input (args[1]) were provided, or just input (args[0])
79+
actual_input = args[1] if len(args) > 1 else args[0]
80+
elif kwargs:
81+
# Attempt to find the single expected kwarg
82+
if param_names: # Should always be true if param_count == 1
83+
param_name = param_names[0]
84+
if param_name in kwargs:
85+
actual_input = kwargs[param_name]
86+
elif kwargs: # Fallback if name doesn't match but kwargs exist
87+
actual_input = next(iter(kwargs.values()))
88+
elif kwargs: # param_names is empty but kwargs exist (unlikely for param_count==1)
89+
actual_input = next(iter(kwargs.values()))
90+
# If no args/kwargs, actual_input remains None, handler might raise error or accept None
91+
return handler(actual_input)
92+
else: # param_count == 0 or param_count > 1
93+
# Handler takes context and input (or more), or no args
94+
return handler(*args, **kwargs)
95+
96+
action = KernelAction(name=name, handler=wrapper)
97+
self.actions[name] = action
98+
return wrapper
99+
100+
def get_actions(self) -> List[KernelAction]:
101+
"""Get all actions for this app"""
102+
return list(self.actions.values())
103+
104+
def get_action(self, name: str) -> Optional[KernelAction]:
105+
"""Get an action by name"""
106+
return self.actions.get(name)
107+
108+
def to_dict(self) -> KernelAppJson:
109+
"""Export app information without handlers"""
110+
return KernelAppJson(
111+
name=self.name,
112+
actions=[KernelActionJson(name=action.name) for action in self.get_actions()]
113+
)
114+
115+
116+
# Registry for storing Kernel apps
117+
class KernelAppRegistry:
118+
def __init__(self) -> None:
119+
self.apps: Dict[str, KernelApp] = {}
120+
121+
def register_app(self, app: KernelApp) -> None:
122+
self.apps[app.name] = app
123+
124+
def get_apps(self) -> List[KernelApp]:
125+
return list(self.apps.values())
126+
127+
def get_app_by_name(self, name: str) -> Optional[KernelApp]:
128+
return self.apps.get(name)
129+
130+
def export(self, entrypoint_relpath: str) -> KernelJson:
131+
"""Export the registry as a KernelJson object"""
132+
apps = [app.to_dict() for app in self.get_apps()]
133+
return KernelJson(apps=apps, entrypoint=entrypoint_relpath)
134+
135+
def export_json(self, entrypoint_relpath: str) -> str:
136+
"""Export the registry as JSON"""
137+
kernel_json = self.export(entrypoint_relpath)
138+
return json.dumps(kernel_json.__dict__, indent=2)
139+
140+
141+
# Create singleton registry for apps
142+
_app_registry = KernelAppRegistry()
143+
144+
# Create a simple function for creating apps
145+
def App(name: str) -> KernelApp:
146+
"""Create a new Kernel app"""
147+
return KernelApp(name)
148+
149+
# Export the app registry for boot loader
150+
app_registry = _app_registry
151+
152+
# Function to export registry as JSON
153+
def export_registry(entrypoint_relpath: str) -> str:
154+
"""Export the registry as JSON"""
155+
return _app_registry.export_json(entrypoint_relpath)

0 commit comments

Comments
 (0)