Skip to content

Commit eee1257

Browse files
committed
feat: update changelog for version 0.4.0 with streaming support and new features
1 parent c3ce9c0 commit eee1257

File tree

5 files changed

+52
-15
lines changed

5 files changed

+52
-15
lines changed

CHANGELOG.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,47 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88

9+
## [0.4.0] - 2026-02-20
10+
11+
### Added
12+
13+
#### Streaming Support
14+
- **Executor.stream()** - New async generator method for streaming module execution
15+
- Implements same 6-step pipeline as `call_async()` (context, safety, lookup, ACL, input validation, middleware before)
16+
- Falls back to `call_async()` yielding single chunk for non-streaming modules
17+
- For streaming modules, iterates `module.stream()` and yields each chunk
18+
- Accumulates chunks via shallow merge for output validation and after-middleware
19+
- Full error handling with middleware recovery
20+
- **ModuleAnnotations.streaming** - New `streaming: bool = False` field to indicate if a module supports streaming execution
21+
- **Test coverage** - Added 5 comprehensive tests in `test_executor_stream.py`:
22+
- Fallback behavior for non-streaming modules
23+
- Multi-chunk streaming
24+
- Module not found error handling
25+
- Before/after middleware integration
26+
- Disjoint key accumulation via shallow merge
27+
28+
29+
## [0.3.0] - 2026-02-20
30+
31+
### Added
32+
33+
#### Public API Extensions
34+
- **ErrorCodes** - New `ErrorCodes` class with all framework error code constants; replaces hardcoded error strings
35+
- **ContextFactory Protocol** - New `ContextFactory` protocol for creating Context from framework-specific requests (e.g., Django, FastAPI)
36+
- **Registry constants** - Exported `REGISTRY_EVENTS` dict and `MODULE_ID_PATTERN` regex for consistent module ID validation
37+
- **Executor.from_registry()** - Convenience factory method for creating an Executor from a Registry with optional middlewares, ACL, and config
38+
39+
#### Schema System
40+
- **Comprehensive schema system** - Full implementation with loading, validation, and export capabilities
41+
- Schema loading from JSON/YAML files
42+
- Runtime schema validation
43+
- Schema export functionality
44+
45+
### Fixed
46+
- **ErrorCodes class** - Prevent attribute deletion to ensure error code constants remain immutable
47+
- **Planning documentation** - Updated progress bar style in overview.md
48+
49+
950
## [0.2.3] - 2026-02-20
1051

1152
### Added

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "apcore"
7-
version = "0.3.0"
7+
version = "0.4.0"
88
description = "Schema-driven module development framework for AI-perceivable interfaces"
99
readme = "README.md"
1010
requires-python = ">=3.11"

src/apcore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
TracingMiddleware,
6262
)
6363

64-
__version__ = "0.3.0"
64+
__version__ = "0.4.0"
6565

6666
__all__ = [
6767
# Core

src/apcore/executor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,11 @@ async def stream(
756756
# Error handling with middleware recovery
757757
if executed_middlewares:
758758
recovery = await self._execute_on_error_async(
759-
module_id, effective_inputs, exc, ctx, executed_middlewares,
759+
module_id,
760+
effective_inputs,
761+
exc,
762+
ctx,
763+
executed_middlewares,
760764
)
761765
if recovery is not None:
762766
yield recovery

tests/test_executor_stream.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ def __init__(self) -> None:
4747
def execute(self, inputs: dict[str, Any], context: Context) -> dict[str, Any]:
4848
return {"value": inputs["count"]}
4949

50-
async def stream(
51-
self, inputs: dict[str, Any], context: Context
52-
) -> AsyncIterator[dict[str, Any]]:
50+
async def stream(self, inputs: dict[str, Any], context: Context) -> AsyncIterator[dict[str, Any]]:
5351
for i in range(1, inputs["count"] + 1):
5452
yield {"value": i}
5553

@@ -64,9 +62,7 @@ def __init__(self) -> None:
6462
def execute(self, inputs: dict[str, Any], context: Context) -> dict[str, Any]:
6563
return {"a": "val_a", "b": "val_b"}
6664

67-
async def stream(
68-
self, inputs: dict[str, Any], context: Context
69-
) -> AsyncIterator[dict[str, Any]]:
65+
async def stream(self, inputs: dict[str, Any], context: Context) -> AsyncIterator[dict[str, Any]]:
7066
yield {"a": "val_a"}
7167
yield {"b": "val_b"}
7268

@@ -138,9 +134,7 @@ def after(
138134
log.append("after")
139135
return None
140136

141-
ex = _make_executor(
142-
module=mod, module_id="counter", middlewares=[TrackingMiddleware()]
143-
)
137+
ex = _make_executor(module=mod, module_id="counter", middlewares=[TrackingMiddleware()])
144138

145139
chunks: list[dict[str, Any]] = []
146140
async for chunk in ex.stream("counter", {"count": 2}):
@@ -166,9 +160,7 @@ def after(
166160
after_output = dict(output)
167161
return None
168162

169-
ex = _make_executor(
170-
module=mod, module_id="disjoint", middlewares=[CaptureAfter()]
171-
)
163+
ex = _make_executor(module=mod, module_id="disjoint", middlewares=[CaptureAfter()])
172164

173165
chunks: list[dict[str, Any]] = []
174166
async for chunk in ex.stream("disjoint", {}):

0 commit comments

Comments
 (0)