From 732aa9b0c5f94374115294a90676589a2578d9c8 Mon Sep 17 00:00:00 2001 From: Brent Edwards Date: Tue, 19 May 2026 01:46:35 +0000 Subject: [PATCH 1/3] docs(my-actor.yaml): ad an example actor ISSUES CLOSED: #1 --- docs/my-actor.yaml | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/my-actor.yaml diff --git a/docs/my-actor.yaml b/docs/my-actor.yaml new file mode 100644 index 0000000..ab53622 --- /dev/null +++ b/docs/my-actor.yaml @@ -0,0 +1,64 @@ +# Simple Graph Actor - Sequential Processing +# Demonstrates a simple 3-node graph with linear execution + +name: local/proofreader +type: graph +description: Simple proofreader +version: "1.0.0" + +# LLM model +provider: openai +model: gpt-3.5-turbo + +# Graph topology +route: + nodes: + # Node 1: Extract text from document + - id: extractor + type: tool + name: extractor + description: Extracts text from various document formats + config: + tool_name: documents/extract_text + parameters: + formats: + - pdf + - docx + - txt + + # Node 2: Analyze content + - id: proofreader + type: agent + name: Proofreader + description: Proofreads a document. + config: + model: gpt-3.5-turbo + prompt: | + You are an expert proofreader. Read the given text and correct: + - misspellings + - incorrect grammar + - incorrect punctuation + - incorrect capitalization + - duplicate words + - misplaced modifiers + + Provide constructive feedback with specific suppestions for improvement. + + # Linear edges + edges: + - from_node: extractor + to_node: proofreader + + # Entry and exit + entry_node: extractor + exit_nodes: + - proofreader + +# Context settings +context_view: executor +memory: + enabled: true + max_messages: 10 + +context: + max_context_tokens: 4000 -- 2.52.0 From f49ca537b923abba211c2e8e75703485f6cf7eed Mon Sep 17 00:00:00 2001 From: Brent Edwards Date: Tue, 19 May 2026 20:35:04 +0000 Subject: [PATCH 2/3] docs(index.html): add my-actor.html ISSUES CLOSED: #1 --- features/environment.py | 2 +- features/steps/smoke_steps.py | 4 ++-- src/cleveractors/actor/compiler.py | 4 +--- src/cleveractors/core/exceptions.py | 2 ++ src/cleveractors/langgraph/__init__.py | 19 ------------------- 5 files changed, 6 insertions(+), 25 deletions(-) diff --git a/features/environment.py b/features/environment.py index abd89fc..0e339de 100644 --- a/features/environment.py +++ b/features/environment.py @@ -13,5 +13,5 @@ def before_all(context: Any) -> None: """Hook invoked once before any features run.""" -def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001 +def before_scenario(context: Any, scenario: Any) -> None: """Per-scenario reset of any context state.""" diff --git a/features/steps/smoke_steps.py b/features/steps/smoke_steps.py index 54e23e1..221edab 100644 --- a/features/steps/smoke_steps.py +++ b/features/steps/smoke_steps.py @@ -75,7 +75,7 @@ def when_compiled(context: Any) -> None: context.actor_raw ) context.compiled = compile_actor(context.actor_config) - except Exception as exc: # noqa: BLE001 (test capture) + except Exception as exc: context.compile_exc = exc @@ -84,7 +84,7 @@ def when_validated(context: Any) -> None: context.validation_exc = None try: ActorConfigSchema.model_validate(context.actor_raw) - except Exception as exc: # noqa: BLE001 + except Exception as exc: context.validation_exc = exc diff --git a/src/cleveractors/actor/compiler.py b/src/cleveractors/actor/compiler.py index dd17534..5b7a206 100644 --- a/src/cleveractors/actor/compiler.py +++ b/src/cleveractors/actor/compiler.py @@ -179,7 +179,6 @@ def _extract_lsp_bindings(node: NodeDefinition) -> list[LspBinding]: def _detect_subgraph_cycles( - actor_name: str, route_nodes: list[NodeDefinition], resolver: ActorResolver | None, visited: frozenset[str], @@ -212,7 +211,6 @@ def _detect_subgraph_cycles( continue deeper = _detect_subgraph_cycles( - ref_name, referenced.route.nodes, resolver, visited | {ref_name}, @@ -271,7 +269,7 @@ def compile_actor( # Detect cross-actor subgraph cycles cross_cycles = _detect_subgraph_cycles( - config.name, route.nodes, actor_resolver, frozenset({config.name}) + route.nodes, actor_resolver, frozenset({config.name}) ) if cross_cycles: raise SubgraphCycleError( diff --git a/src/cleveractors/core/exceptions.py b/src/cleveractors/core/exceptions.py index 84b1adf..40a9592 100644 --- a/src/cleveractors/core/exceptions.py +++ b/src/cleveractors/core/exceptions.py @@ -30,6 +30,8 @@ class ValidationError(Exception): - Subgraph cycle detected by the compiler. - Invalid entry / exit points in the graph route. """ + def __init__(self, details: map[str, str], *args: object) -> None: + super().__init__(details, *args) class NotFoundError(Exception): diff --git a/src/cleveractors/langgraph/__init__.py b/src/cleveractors/langgraph/__init__.py index 89a431f..496a91d 100644 --- a/src/cleveractors/langgraph/__init__.py +++ b/src/cleveractors/langgraph/__init__.py @@ -1,32 +1,13 @@ """LangGraph primitives adapted for actor-first routing. - -``graph`` sub-module is lazy-loaded because it pulls in -``langchain_core``, ``langsmith``, and ``rx.operators`` which add -multiple seconds of import overhead. Lightweight symbols from -``nodes`` and ``state`` are available eagerly. """ from .nodes import Edge, NodeConfig, NodeType from .state import GraphState -def __getattr__(name: str): - _GRAPH_ATTRS = {"GraphConfig", "LangGraph"} - if name in _GRAPH_ATTRS: - from .graph import GraphConfig as _GraphConfig - from .graph import LangGraph as _LangGraph - - globals()["GraphConfig"] = _GraphConfig - globals()["LangGraph"] = _LangGraph - return globals()[name] - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - - __all__ = [ "Edge", - "GraphConfig", "GraphState", - "LangGraph", "NodeConfig", "NodeType", ] -- 2.52.0 From 60f79cbe5d9f01127cb6d71d79bcdecd2a694dd8 Mon Sep 17 00:00:00 2001 From: Brent Edwards Date: Wed, 20 May 2026 00:22:57 +0000 Subject: [PATCH 3/3] docs(index.html): Add my-actor.yaml ISSUES CLOSED: #1 --- features/steps/smoke_steps.py | 4 +--- src/cleveractors/actor/compiler.py | 12 ++++++++++++ src/cleveractors/actor/loader.py | 13 +++++++++---- src/cleveractors/core/exceptions.py | 3 ++- src/cleveractors/langgraph/__init__.py | 4 +--- src/cleveractors/langgraph/state.py | 4 ++-- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/features/steps/smoke_steps.py b/features/steps/smoke_steps.py index 221edab..34833f9 100644 --- a/features/steps/smoke_steps.py +++ b/features/steps/smoke_steps.py @@ -71,9 +71,7 @@ def when_compiled(context: Any) -> None: try: # NB: ``context.config`` is a reserved attribute on Behave's # Context; using ``actor_config`` keeps us out of its way. - context.actor_config = ActorConfigSchema.model_validate( - context.actor_raw - ) + context.actor_config = ActorConfigSchema.model_validate(context.actor_raw) context.compiled = compile_actor(context.actor_config) except Exception as exc: context.compile_exc = exc diff --git a/src/cleveractors/actor/compiler.py b/src/cleveractors/actor/compiler.py index 5b7a206..f51abce 100644 --- a/src/cleveractors/actor/compiler.py +++ b/src/cleveractors/actor/compiler.py @@ -44,6 +44,18 @@ from cleveractors.lsp.models import LspBinding class ActorCompilationError(ValidationError): """Raised when actor compilation fails.""" + def __init__( + self, message: str | None = None, details: dict[str, str] | None = None + ) -> None: + if message is not None and details is not None: + super().__init__(details={"details": message, **details}) + elif message is not None: + super().__init__(details={"details": message}) + elif details is not None: + super().__init__(details=details) + else: + super().__init__(details={}) + class SubgraphCycleError(ActorCompilationError): """Raised when subgraph references form a cycle.""" diff --git a/src/cleveractors/actor/loader.py b/src/cleveractors/actor/loader.py index 0061939..77b4068 100644 --- a/src/cleveractors/actor/loader.py +++ b/src/cleveractors/actor/loader.py @@ -184,14 +184,19 @@ class ActorLoader: if duplicate_msgs: all_errors = errors + duplicate_msgs raise ValidationError( - "Actor discovery failed:\n" + "\n".join(all_errors), - details={"duplicates": duplicate_msgs, "errors": errors}, + details={ + "message": "Actor discovery failed:\n" + "\n".join(all_errors), + "duplicates": ", ".join(duplicate_msgs), + "errors": ", ".join(errors), + } ) if errors: raise ValidationError( - "Actor discovery failed:\n" + "\n".join(errors), - details={"errors": errors}, + details={ + "message": "Actor discovery failed:\n" + "\n".join(errors), + "errors": ", ".join(errors), + }, ) new_actors: dict[str, _CacheEntry] = {} diff --git a/src/cleveractors/core/exceptions.py b/src/cleveractors/core/exceptions.py index 40a9592..d2808d3 100644 --- a/src/cleveractors/core/exceptions.py +++ b/src/cleveractors/core/exceptions.py @@ -30,7 +30,8 @@ class ValidationError(Exception): - Subgraph cycle detected by the compiler. - Invalid entry / exit points in the graph route. """ - def __init__(self, details: map[str, str], *args: object) -> None: + + def __init__(self, details: dict[str, str], *args: object) -> None: super().__init__(details, *args) diff --git a/src/cleveractors/langgraph/__init__.py b/src/cleveractors/langgraph/__init__.py index 496a91d..e92d3a3 100644 --- a/src/cleveractors/langgraph/__init__.py +++ b/src/cleveractors/langgraph/__init__.py @@ -1,10 +1,8 @@ -"""LangGraph primitives adapted for actor-first routing. -""" +"""LangGraph primitives adapted for actor-first routing.""" from .nodes import Edge, NodeConfig, NodeType from .state import GraphState - __all__ = [ "Edge", "GraphState", diff --git a/src/cleveractors/langgraph/state.py b/src/cleveractors/langgraph/state.py index bb6e6fe..a338629 100644 --- a/src/cleveractors/langgraph/state.py +++ b/src/cleveractors/langgraph/state.py @@ -26,6 +26,8 @@ from pydantic import BaseModel, ConfigDict, Field T = TypeVar("T", bound="GraphState") logger = logging.getLogger(__name__) + + class StateUpdateMode(Enum): REPLACE = "replace" MERGE = "merge" @@ -91,5 +93,3 @@ class GraphState(BaseModel): @classmethod def from_dict(cls: type[T], data: dict[str, Any]) -> T: return cls(**data) - - -- 2.52.0