nodnod 1.0.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.
Files changed (103) hide show
  1. nodnod-1.0.0/.claude/settings.local.json +14 -0
  2. nodnod-1.0.0/.codecov.yml +9 -0
  3. nodnod-1.0.0/.github/actions/install-dependencies/action.yml +21 -0
  4. nodnod-1.0.0/.github/workflows/tests.yml +50 -0
  5. nodnod-1.0.0/.gitignore +6 -0
  6. nodnod-1.0.0/PKG-INFO +15 -0
  7. nodnod-1.0.0/bug.py +26 -0
  8. nodnod-1.0.0/bug2.py +15 -0
  9. nodnod-1.0.0/docs/index.md +0 -0
  10. nodnod-1.0.0/main.py +172 -0
  11. nodnod-1.0.0/main2.py +79 -0
  12. nodnod-1.0.0/main3.py +107 -0
  13. nodnod-1.0.0/nodnod/__init__.py +31 -0
  14. nodnod-1.0.0/nodnod/agent/__init__.py +4 -0
  15. nodnod-1.0.0/nodnod/agent/base.py +15 -0
  16. nodnod-1.0.0/nodnod/agent/event_loop/__init__.py +0 -0
  17. nodnod-1.0.0/nodnod/agent/event_loop/agent.py +144 -0
  18. nodnod-1.0.0/nodnod/agent/event_loop/coroutine.py +128 -0
  19. nodnod-1.0.0/nodnod/agent/layer/__init__.py +0 -0
  20. nodnod-1.0.0/nodnod/agent/layer/agent.py +68 -0
  21. nodnod-1.0.0/nodnod/agent/layer/steps.py +26 -0
  22. nodnod-1.0.0/nodnod/builder/__init__.py +0 -0
  23. nodnod-1.0.0/nodnod/builder/build_parallels.py +40 -0
  24. nodnod-1.0.0/nodnod/builder/build_queue.py +30 -0
  25. nodnod-1.0.0/nodnod/builder/validate_graph.py +11 -0
  26. nodnod-1.0.0/nodnod/compose.py +74 -0
  27. nodnod-1.0.0/nodnod/error.py +49 -0
  28. nodnod-1.0.0/nodnod/interface/__init__.py +29 -0
  29. nodnod-1.0.0/nodnod/interface/agent_from_node.py +16 -0
  30. nodnod-1.0.0/nodnod/interface/composable.py +11 -0
  31. nodnod-1.0.0/nodnod/interface/compose_one.py +28 -0
  32. nodnod-1.0.0/nodnod/interface/create_result_node.py +44 -0
  33. nodnod-1.0.0/nodnod/interface/data.py +13 -0
  34. nodnod-1.0.0/nodnod/interface/either.py +49 -0
  35. nodnod-1.0.0/nodnod/interface/generic.py +81 -0
  36. nodnod-1.0.0/nodnod/interface/inject.py +31 -0
  37. nodnod-1.0.0/nodnod/interface/is_node.py +23 -0
  38. nodnod-1.0.0/nodnod/interface/node_constructor.py +65 -0
  39. nodnod-1.0.0/nodnod/interface/node_from_function.py +186 -0
  40. nodnod-1.0.0/nodnod/interface/option_node.py +81 -0
  41. nodnod-1.0.0/nodnod/interface/polymorphic.py +76 -0
  42. nodnod-1.0.0/nodnod/interface/result_node.py +26 -0
  43. nodnod-1.0.0/nodnod/interface/scalar.py +55 -0
  44. nodnod-1.0.0/nodnod/interface/union_node.py +94 -0
  45. nodnod-1.0.0/nodnod/node.py +249 -0
  46. nodnod-1.0.0/nodnod/node.pyi +32 -0
  47. nodnod-1.0.0/nodnod/scope.py +100 -0
  48. nodnod-1.0.0/nodnod/utils/__init__.py +0 -0
  49. nodnod-1.0.0/nodnod/utils/aio.py +16 -0
  50. nodnod-1.0.0/nodnod/utils/call.py +23 -0
  51. nodnod-1.0.0/nodnod/utils/create_node.py +54 -0
  52. nodnod-1.0.0/nodnod/utils/generator.py +21 -0
  53. nodnod-1.0.0/nodnod/utils/injection.py +30 -0
  54. nodnod-1.0.0/nodnod/utils/is_type.py +38 -0
  55. nodnod-1.0.0/nodnod/utils/misc.py +8 -0
  56. nodnod-1.0.0/nodnod/utils/prepare_values.py +18 -0
  57. nodnod-1.0.0/nodnod/utils/repr_type.py +8 -0
  58. nodnod-1.0.0/nodnod/utils/resolve_signature.py +155 -0
  59. nodnod-1.0.0/nodnod/utils/type_args.py +29 -0
  60. nodnod-1.0.0/nodnod/value.py +42 -0
  61. nodnod-1.0.0/pyproject.toml +118 -0
  62. nodnod-1.0.0/readme.md +5 -0
  63. nodnod-1.0.0/tests/conftest.py +0 -0
  64. nodnod-1.0.0/tests/test_agent_extended.py +23 -0
  65. nodnod-1.0.0/tests/test_aio_extended.py +61 -0
  66. nodnod-1.0.0/tests/test_build_queue.py +65 -0
  67. nodnod-1.0.0/tests/test_compose.py +112 -0
  68. nodnod-1.0.0/tests/test_compose_one.py +91 -0
  69. nodnod-1.0.0/tests/test_coroutine.py +258 -0
  70. nodnod-1.0.0/tests/test_create_node_from_composable.py +345 -0
  71. nodnod-1.0.0/tests/test_create_result_node_extended.py +48 -0
  72. nodnod-1.0.0/tests/test_either.py +90 -0
  73. nodnod-1.0.0/tests/test_either_extended.py +40 -0
  74. nodnod-1.0.0/tests/test_error.py +63 -0
  75. nodnod-1.0.0/tests/test_generators.py +48 -0
  76. nodnod-1.0.0/tests/test_generic.py +296 -0
  77. nodnod-1.0.0/tests/test_get_injection_type.py +26 -0
  78. nodnod-1.0.0/tests/test_initialize_forward_refs.py +161 -0
  79. nodnod-1.0.0/tests/test_injection.py +107 -0
  80. nodnod-1.0.0/tests/test_is_type_extended.py +50 -0
  81. nodnod-1.0.0/tests/test_node_basic.py +193 -0
  82. nodnod-1.0.0/tests/test_node_constructor.py +174 -0
  83. nodnod-1.0.0/tests/test_node_extended.py +218 -0
  84. nodnod-1.0.0/tests/test_node_from_function.py +288 -0
  85. nodnod-1.0.0/tests/test_node_repr.py +30 -0
  86. nodnod-1.0.0/tests/test_option_node_extended.py +32 -0
  87. nodnod-1.0.0/tests/test_option_result_nodes.py +135 -0
  88. nodnod-1.0.0/tests/test_polymorphic.py +87 -0
  89. nodnod-1.0.0/tests/test_polymorphic_extended.py +41 -0
  90. nodnod-1.0.0/tests/test_prepare_values.py +39 -0
  91. nodnod-1.0.0/tests/test_resolve_signature.py +82 -0
  92. nodnod-1.0.0/tests/test_resolve_signature_extended.py +144 -0
  93. nodnod-1.0.0/tests/test_result_node_extended.py +69 -0
  94. nodnod-1.0.0/tests/test_scalar.py +24 -0
  95. nodnod-1.0.0/tests/test_scope_extended.py +102 -0
  96. nodnod-1.0.0/tests/test_scope_inject.py +36 -0
  97. nodnod-1.0.0/tests/test_scope_repr.py +68 -0
  98. nodnod-1.0.0/tests/test_type_args.py +83 -0
  99. nodnod-1.0.0/tests/test_union_nodes.py +56 -0
  100. nodnod-1.0.0/tests/test_union_nodes_extended.py +75 -0
  101. nodnod-1.0.0/tests/test_value.py +25 -0
  102. nodnod-1.0.0/tests/test_value_extended.py +56 -0
  103. nodnod-1.0.0/uv.lock +259 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(cat:*)",
5
+ "Bash(python -m pytest --version)",
6
+ "Bash(uv add:*)",
7
+ "Bash(uv remove:*)",
8
+ "Bash(ls:*)",
9
+ "Bash(uv run:*)",
10
+ "Bash(find:*)"
11
+ ],
12
+ "deny": []
13
+ }
14
+ }
@@ -0,0 +1,9 @@
1
+ comment: false
2
+
3
+ coverage:
4
+ status:
5
+ project:
6
+ default:
7
+ target: auto
8
+ threshold: 1%
9
+ patch: off
@@ -0,0 +1,21 @@
1
+ name: Install uv and dependencies
2
+
3
+ inputs:
4
+ python-version:
5
+ description: "Python version."
6
+ required: false
7
+ default: "3.13"
8
+
9
+ runs:
10
+ using: composite
11
+ steps:
12
+ - uses: hynek/setup-cached-uv@v2
13
+ with:
14
+ cache-suffix: -${{ inputs.python-version }}-cache
15
+ cache-dependency-path: "**/uv.lock"
16
+
17
+ - name: "Install dependencies"
18
+ run: |
19
+ uv venv --python ${{ inputs.python-version }}
20
+ uv sync --all-groups --dev
21
+ shell: bash
@@ -0,0 +1,50 @@
1
+ name: tests and coverage
2
+
3
+ on:
4
+ push:
5
+
6
+ jobs:
7
+ lock-file:
8
+ name: "Lock uv"
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version:
13
+ - "3.13"
14
+ - "3.14"
15
+
16
+ steps:
17
+ - uses: asottile/workflows/.github/actions/fast-checkout@v1.10.0
18
+
19
+ - uses: ./.github/actions/install-dependencies
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+
23
+ - run: uv lock --locked
24
+
25
+ tests:
26
+ name: "Tests"
27
+ needs: lock-file
28
+ runs-on: ubuntu-latest
29
+ strategy:
30
+ matrix:
31
+ python-version:
32
+ - "3.13"
33
+ - "3.14"
34
+
35
+ steps:
36
+ - uses: asottile/workflows/.github/actions/fast-checkout@v1.10.0
37
+
38
+ - uses: ./.github/actions/install-dependencies
39
+ with:
40
+ python-version: ${{ matrix.python-version }}
41
+
42
+ - name: Run tests with coverage
43
+ run: |
44
+ uv run pytest --cov --cov-report=xml --cov-report=term-missing
45
+
46
+ - name: Upload coverage to Codecov
47
+ uses: codecov/codecov-action@v5
48
+ with:
49
+ files: ./coverage.xml
50
+ token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,6 @@
1
+ __pycache__
2
+ .venv
3
+ .coverage
4
+ .ruff_cache
5
+ .pytest_cache
6
+ .DS_Store
nodnod-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: nodnod
3
+ Version: 1.0.0
4
+ Summary: Programming in Python empowered with Nodes DI, implements efficient nodes computation agents and dependency scoping
5
+ Author: timoniq
6
+ Requires-Python: <4.0,>=3.13
7
+ Requires-Dist: kungfu-fp>=1.0.0
8
+ Requires-Dist: typing-extensions>=4.15.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # NodNod
12
+
13
+ 🧑‍🧑‍🧒‍🧒 Programming in Python enhanced with Nodes
14
+
15
+ [![codecov](https://codecov.io/gh/luwqz1/nodnod/branch/fix/graph/badge.svg)](https://codecov.io/gh/luwqz1/nodnod)
nodnod-1.0.0/bug.py ADDED
@@ -0,0 +1,26 @@
1
+ import asyncio
2
+
3
+ from nodnod.interface.node_from_function import Scope, create_node_from_function, inject_externals
4
+ from nodnod.interface.node_from_function import create_agent_from_node
5
+
6
+
7
+ def func(a: int | None, b: int) -> None:
8
+ print(a)
9
+ print(b)
10
+
11
+
12
+ async def main():
13
+ node = create_node_from_function(func)
14
+ agent = create_agent_from_node(node)
15
+ print(node.__dependencies__)
16
+ print(node.__externals__)
17
+ node.__dependencies__ = set()
18
+ scope = Scope(detail="test")
19
+
20
+ async with scope:
21
+ inject_externals(scope, dict(a=1, b=2))
22
+ await agent.run(scope, mapped_scopes={})
23
+ print(scope)
24
+
25
+
26
+ asyncio.run(main())
nodnod-1.0.0/bug2.py ADDED
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from nodnod.node import initialize_forward_refs
4
+ from nodnod.interface.node_from_function import create_node_from_function
5
+
6
+
7
+ def func(a: A) -> None: ... # ForwardRef(A)
8
+
9
+
10
+ class A:
11
+ pass
12
+
13
+
14
+
15
+ create_node_from_function(func)
File without changes
nodnod-1.0.0/main.py ADDED
@@ -0,0 +1,172 @@
1
+ import asyncio
2
+ import dataclasses
3
+ import typing
4
+
5
+ import kungfu
6
+
7
+ from nodnod import (
8
+ DataNode,
9
+ EventLoopAgent,
10
+ Node,
11
+ NodeConstructor,
12
+ NodeError,
13
+ Scalar,
14
+ Scope,
15
+ SequentialEither,
16
+ Value,
17
+ case,
18
+ generic_node,
19
+ polymorphic,
20
+ scalar_node,
21
+ )
22
+ from nodnod.utils.prepare_values import prepare_values
23
+
24
+
25
+ @generic_node
26
+ #@dataclasses.dataclass
27
+ class Lolik[T: (str, int), *Ts, X: str | int](DataNode, abstract=True):
28
+ def __init__(self, t: T, x: X):
29
+ self.t = t
30
+ self.x = x
31
+
32
+ @classmethod
33
+ async def __compose__(cls, t: type[T], y: type[X]) -> typing.Self:
34
+ return cls(t("11"), y("22"))
35
+
36
+
37
+ @generic_node
38
+ class TypeArgs[*Ts]:
39
+ def __init__(self, type_args: tuple[typing.Unpack[Ts]]) -> None:
40
+ self.type_args = type_args
41
+
42
+ @classmethod
43
+ def __compose__(cls, type_args: tuple[typing.Unpack[Ts]]) -> typing.Self:
44
+ return cls(type_args)
45
+
46
+
47
+ class Interface:
48
+ def get_lol(self) -> str:
49
+ ...
50
+
51
+
52
+ class MyInterface:
53
+ def get_lol(self) -> str:
54
+ return "megalol"
55
+
56
+
57
+ @scalar_node
58
+ class A:
59
+ @classmethod
60
+ async def __compose__(cls):
61
+ yield int(11)
62
+
63
+ @scalar_node
64
+ class Bitchnode:
65
+ @classmethod
66
+ async def __compose__(cls):
67
+ raise NodeError("dang")
68
+
69
+
70
+ @scalar_node
71
+ class B(Node[int]):
72
+ @classmethod
73
+ async def __compose__(cls):
74
+ yield 5
75
+
76
+
77
+ class AorB(SequentialEither):
78
+ __either__ = (A, B)
79
+
80
+
81
+ @scalar_node
82
+ @polymorphic[int]
83
+ class MyInt:
84
+ @case
85
+ def from_a(cls, a: A) -> int:
86
+ return a + 8
87
+
88
+ @case
89
+ def from_b(cls, b: B) -> int:
90
+ return b
91
+
92
+
93
+ @dataclasses.dataclass
94
+ class C(DataNode):
95
+ x: float
96
+ y: float
97
+
98
+ @classmethod
99
+ def __compose__(cls, aorb: AorB):
100
+ print(aorb)
101
+ print("calculating c")
102
+ print(aorb.value)
103
+ return cls(1.2, aorb.value / 2)
104
+
105
+
106
+ @scalar_node
107
+ class Lol:
108
+ @classmethod
109
+ def __compose__(cls, i: Interface) -> str:
110
+ return i.get_lol()
111
+
112
+
113
+ class MyNode(Node):
114
+ def __init__(self):
115
+ self.bro = 3
116
+
117
+
118
+ @scalar_node
119
+ class LOL:
120
+ @classmethod
121
+ def __compose__(
122
+ cls,
123
+ x: Lol,
124
+ mi: MyInt,
125
+ opt: kungfu.Option[A],
126
+ ar: kungfu.Result[Bitchnode, Exception],
127
+ ) -> str:
128
+ return x.upper() * mi + "d"
129
+
130
+
131
+ @scalar_node
132
+ class Jackpot:
133
+ @classmethod
134
+ def __compose__(cls) -> int:
135
+ return 777
136
+
137
+
138
+ class MultiplyBy(NodeConstructor):
139
+ def __init__(self, multiplier: int = 5) -> None:
140
+ self.multiplier = multiplier
141
+
142
+ def __compose__(self, jackpot: Jackpot) -> int:
143
+ return 123 * self.multiplier + jackpot
144
+
145
+
146
+ @scalar_node
147
+ class Sum:
148
+ @classmethod
149
+ def __compose__(cls, a: Scalar[int, MultiplyBy], b: Scalar[int, MultiplyBy[10]]) -> int:
150
+ return a + b
151
+
152
+
153
+ async def main():
154
+ agent = EventLoopAgent.build({TypeArgs[int, str], LOL, C, Lolik[str, float, str], Lolik[int, str, int], Sum}) # type: ignore
155
+
156
+ global_scope = Scope(detail="global")
157
+ global_scope.push(Value(Interface, MyInterface()))
158
+
159
+ async with global_scope.create_child("local") as scope:
160
+ await agent.run(
161
+ local_scope=scope,
162
+ mapped_scopes={A: global_scope},
163
+ )
164
+ print(prepare_values(scope.merge()))
165
+
166
+ await global_scope.close()
167
+
168
+ # Firstly closes C
169
+ # then A and B (any order because they are from the same layer of parallels)
170
+
171
+
172
+ asyncio.run(main())
nodnod-1.0.0/main2.py ADDED
@@ -0,0 +1,79 @@
1
+ import asyncio
2
+ import typing
3
+ import kungfu
4
+
5
+ from nodnod import Node
6
+ from nodnod.agent import EventLoopAgent
7
+ from nodnod.interface.scalar import scalar_node
8
+ from nodnod.node import is_type
9
+ from nodnod.scope import Scope
10
+
11
+
12
+ def self_hook(cls: type[Node], dep_name: str, dep_type: type[typing.Any]) -> kungfu.Pulse[str]:
13
+ if is_type(dep_type, typing.Self):
14
+ print(cls, dep_name, dep_type)
15
+ return kungfu.Ok()
16
+ return kungfu.Error("cannot process")
17
+
18
+
19
+ class NodeConstructor(Node, abstract=True):
20
+ __args__ = ()
21
+
22
+ @classmethod
23
+ def __construct__(cls, *args: typing.Any) -> typing.Self:
24
+ raise NotImplementedError
25
+
26
+ def __class_getitem__(cls, item: typing.Any | tuple[typing.Any, ...], /) -> typing.Any:
27
+ data = cls.__construct__(*((item,) if not isinstance(item, tuple) else item))
28
+ node = type(
29
+ cls.__name__,
30
+ (cls,),
31
+ dict(
32
+ __module__=cls.__module__,
33
+ __args__=data,
34
+ )
35
+ )
36
+ setattr(node, "__type__", node)
37
+ return node
38
+
39
+
40
+ @scalar_node
41
+ class A(Node):
42
+ @classmethod
43
+ def __compose__(cls) -> int:
44
+ return 123
45
+
46
+
47
+ class Lolik(NodeConstructor):
48
+ def __init__(self, x: type[str | int]) -> None:
49
+ self.x = x
50
+
51
+ @classmethod
52
+ def __construct__(cls, x: type[str | int] = str) -> typing.Self:
53
+ return cls(x)
54
+
55
+ def __compose__(self, a: A):
56
+ print(a)
57
+ return self.x
58
+
59
+
60
+ @scalar_node
61
+ class Boba:
62
+ @classmethod
63
+ def __compose__(cls, lolik: Lolik[int], lolik2: Lolik[str]) -> int:
64
+ print("lolik", lolik.x, "lolik2", lolik2.x)
65
+ return lolik
66
+
67
+
68
+ async def main():
69
+ agent = EventLoopAgent.build({Boba})
70
+ scope = Scope(detail="test")
71
+
72
+ async with scope:
73
+ await agent.run(scope, {})
74
+
75
+
76
+ asyncio.run(main())
77
+
78
+
79
+ __all__ = ("NodeConstructor",)
nodnod-1.0.0/main3.py ADDED
@@ -0,0 +1,107 @@
1
+ import asyncio
2
+ import dataclasses
3
+ import datetime
4
+ import typing
5
+
6
+ import kungfu
7
+
8
+ from nodnod import (
9
+ EventLoopAgent,
10
+ Externals,
11
+ NodeError,
12
+ Scope,
13
+ create_agent_from_node,
14
+ create_node_from_function,
15
+ inject_externals,
16
+ scalar_node,
17
+ )
18
+ from nodnod.interface.compose_one import compose_one
19
+
20
+
21
+ @dataclasses.dataclass
22
+ class User:
23
+ id: int
24
+ email: kungfu.Option[str]
25
+ last_active: datetime.datetime
26
+
27
+
28
+ @scalar_node
29
+ class UserId:
30
+ @classmethod
31
+ def __compose__(cls, user: User) -> int:
32
+ return user.id
33
+
34
+
35
+ @scalar_node
36
+ class Email(str):
37
+ def validate_email(self):
38
+ if "@" not in self:
39
+ raise NodeError("Email is in wrong format")
40
+
41
+ @classmethod
42
+ def __compose__(cls, user: User) -> str:
43
+ email = cls(user.email.expect(NodeError("User has no email")))
44
+ email.validate_email()
45
+ return email
46
+
47
+
48
+ @dataclasses.dataclass
49
+ class UserSource:
50
+ user: User
51
+
52
+ @classmethod
53
+ def __compose__(cls, user: User) -> typing.Self:
54
+ return cls(user)
55
+
56
+
57
+ @scalar_node
58
+ class EmailProvider:
59
+ @classmethod
60
+ def __compose__(cls, email: Email) -> str:
61
+ _, provider = email.split("@", 1)
62
+ if not provider:
63
+ raise NodeError("Email has no provider")
64
+ return provider
65
+
66
+
67
+ @scalar_node
68
+ class SinceActive:
69
+ @classmethod
70
+ def __compose__(cls, user: User) -> datetime.timedelta:
71
+ return datetime.datetime.now() - user.last_active
72
+
73
+
74
+ async def handler(email_provider: EmailProvider, boba: str, lol: str, since_active: SinceActive, user: UserSource):
75
+ print(email_provider, boba, lol, since_active, user.user)
76
+ return "handler result"
77
+
78
+
79
+ async def main():
80
+ # Compile time
81
+ handler_node = create_node_from_function(handler)
82
+ agent = create_agent_from_node(handler_node, EventLoopAgent)
83
+
84
+ user = User(1, kungfu.Some("lol@skibidi.org"), datetime.datetime.now())
85
+
86
+ # Run time
87
+ async with Scope() as scope:
88
+ scope.inject(User, user)
89
+ inject_externals(scope, {"boba": "ahah", "lol": "omg"})
90
+ await agent.run(scope, {})
91
+
92
+ print(
93
+ await compose_one(
94
+ SinceActive,
95
+ {User: User(2, kungfu.Nothing(), datetime.datetime.now())},
96
+ )
97
+ )
98
+
99
+ print(
100
+ await compose_one(
101
+ create_node_from_function(handler),
102
+ {User: user, Externals: {"boba": "tea", "lol": "0"}},
103
+ )
104
+ )
105
+
106
+
107
+ asyncio.run(main())
@@ -0,0 +1,31 @@
1
+ from .agent import Agent, EventLoopAgent
2
+ from .error import NodeError
3
+ from .interface import *
4
+ from .node import Injection, Node, Scalar
5
+ from .scope import Scope
6
+ from .value import Value
7
+
8
+ __all__ = (
9
+ "Agent",
10
+ "ConcurrentEither",
11
+ "DataNode",
12
+ "Externals",
13
+ "EventLoopAgent",
14
+ "Injection",
15
+ "NodeConstructor",
16
+ "Node",
17
+ "NodeError",
18
+ "Scalar",
19
+ "ResultNode",
20
+ "Scope",
21
+ "SequentialEither",
22
+ "Value",
23
+ "create_node_from_function",
24
+ "compose_one",
25
+ "inject_externals",
26
+ "inject_internals",
27
+ "generic_node",
28
+ "case",
29
+ "polymorphic",
30
+ "scalar_node",
31
+ )
@@ -0,0 +1,4 @@
1
+ from .base import Agent
2
+ from .event_loop.agent import EventLoopAgent
3
+
4
+ __all__ = ("Agent", "EventLoopAgent")
@@ -0,0 +1,15 @@
1
+ from nodnod.node import Node
2
+ from nodnod.scope import Scope
3
+ import typing
4
+
5
+
6
+ class Agent:
7
+ @classmethod
8
+ def build(cls, nodes: set[type[Node]]) -> typing.Self:
9
+ ...
10
+
11
+ def run(self, local_scope: Scope, mapped_scopes: dict[type[Node], Scope]) -> typing.Any:
12
+ ...
13
+
14
+
15
+ __all__ = ("Agent",)
File without changes