flowra 0.0.1.dev1__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.
Files changed (70) hide show
  1. flowra/__init__.py +3 -0
  2. flowra/agent/__init__.py +70 -0
  3. flowra/agent/agent.py +21 -0
  4. flowra/agent/agent_def.py +213 -0
  5. flowra/agent/agent_registry.py +139 -0
  6. flowra/agent/agent_store.py +10 -0
  7. flowra/agent/compile.py +417 -0
  8. flowra/agent/interrupt_token.py +15 -0
  9. flowra/agent/service_locator.py +15 -0
  10. flowra/agent/step_decorator.py +28 -0
  11. flowra/agent/stored_values.py +114 -0
  12. flowra/lib/__init__.py +11 -0
  13. flowra/lib/chat/__init__.py +14 -0
  14. flowra/lib/chat/agent.py +75 -0
  15. flowra/lib/chat/config.py +29 -0
  16. flowra/lib/chat/hook_executor.py +21 -0
  17. flowra/lib/chat/hooks.py +43 -0
  18. flowra/lib/chat/spec.py +19 -0
  19. flowra/lib/config_value.py +19 -0
  20. flowra/lib/llm_config.py +13 -0
  21. flowra/lib/tool_loop/__init__.py +99 -0
  22. flowra/lib/tool_loop/_tool_call_agent.py +76 -0
  23. flowra/lib/tool_loop/agent.py +282 -0
  24. flowra/lib/tool_loop/cache.py +157 -0
  25. flowra/lib/tool_loop/config.py +26 -0
  26. flowra/lib/tool_loop/context.py +21 -0
  27. flowra/lib/tool_loop/hook_executor.py +189 -0
  28. flowra/lib/tool_loop/hooks.py +433 -0
  29. flowra/lib/tool_loop/spec.py +30 -0
  30. flowra/llm/__init__.py +28 -0
  31. flowra/llm/_base.py +10 -0
  32. flowra/llm/blocks.py +58 -0
  33. flowra/llm/messages.py +33 -0
  34. flowra/llm/pricing/__init__.py +3 -0
  35. flowra/llm/pricing/anthropic.py +64 -0
  36. flowra/llm/pricing/google.py +50 -0
  37. flowra/llm/pricing/openai.py +62 -0
  38. flowra/llm/provider.py +13 -0
  39. flowra/llm/providers/__init__.py +11 -0
  40. flowra/llm/providers/anthropic_vertex.py +343 -0
  41. flowra/llm/providers/google_vertex.py +379 -0
  42. flowra/llm/providers/openai.py +367 -0
  43. flowra/llm/request.py +23 -0
  44. flowra/llm/response.py +66 -0
  45. flowra/llm/schema_formatting.py +73 -0
  46. flowra/llm/schema_validation.py +33 -0
  47. flowra/llm/tools.py +14 -0
  48. flowra/py.typed +1 -0
  49. flowra/runtime/__init__.py +12 -0
  50. flowra/runtime/_sealed_scope.py +52 -0
  51. flowra/runtime/engine.py +328 -0
  52. flowra/runtime/execution.py +40 -0
  53. flowra/runtime/interrupt.py +46 -0
  54. flowra/runtime/runtime.py +280 -0
  55. flowra/runtime/runtime_scope.py +125 -0
  56. flowra/runtime/serialization.py +65 -0
  57. flowra/runtime/storage/__init__.py +5 -0
  58. flowra/runtime/storage/file.py +90 -0
  59. flowra/runtime/storage/in_memory.py +47 -0
  60. flowra/runtime/storage/session_storage.py +48 -0
  61. flowra/tools/__init__.py +24 -0
  62. flowra/tools/local_tool.py +313 -0
  63. flowra/tools/mcp_connection.py +389 -0
  64. flowra/tools/tool_group.py +38 -0
  65. flowra/tools/tool_registry.py +216 -0
  66. flowra/tools/types.py +18 -0
  67. flowra/version.py +2 -0
  68. flowra-0.0.1.dev1.dist-info/METADATA +18 -0
  69. flowra-0.0.1.dev1.dist-info/RECORD +70 -0
  70. flowra-0.0.1.dev1.dist-info/WHEEL +4 -0
flowra/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from . import agent, lib, llm, runtime, tools
2
+
3
+ __all__ = ["agent", "lib", "llm", "runtime", "tools"]
@@ -0,0 +1,70 @@
1
+ from .agent import Agent
2
+ from .agent_def import (
3
+ NEW_SCOPE,
4
+ AbstractAgent,
5
+ AgentDefinition,
6
+ AgentDefinitionRef,
7
+ AgentInstance,
8
+ AgentRef,
9
+ AgentResult,
10
+ Call,
11
+ CallStepHandler,
12
+ CreateInstance,
13
+ Goto,
14
+ SlotContainer,
15
+ SlotDef,
16
+ SlotKind,
17
+ Spawn,
18
+ StepAction,
19
+ StepMeta,
20
+ StepMethod,
21
+ StepRef,
22
+ StepResult,
23
+ StepSpec,
24
+ resolve_step_owner,
25
+ resolve_step_ref,
26
+ )
27
+ from .agent_registry import AgentRegistry, ResolvedAgent
28
+ from .agent_store import AgentStore
29
+ from .compile import compile_agent
30
+ from .interrupt_token import InterruptToken
31
+ from .service_locator import ServiceLocator
32
+ from .step_decorator import step
33
+ from .stored_values import AppendOnlyList, Scalar, slot
34
+
35
+ __all__ = [
36
+ "NEW_SCOPE",
37
+ "AbstractAgent",
38
+ "Agent",
39
+ "AgentDefinition",
40
+ "AgentDefinitionRef",
41
+ "AgentInstance",
42
+ "AgentRef",
43
+ "AgentRegistry",
44
+ "AgentResult",
45
+ "AgentStore",
46
+ "AppendOnlyList",
47
+ "Call",
48
+ "CallStepHandler",
49
+ "CreateInstance",
50
+ "Goto",
51
+ "InterruptToken",
52
+ "ResolvedAgent",
53
+ "Scalar",
54
+ "ServiceLocator",
55
+ "SlotContainer",
56
+ "SlotDef",
57
+ "SlotKind",
58
+ "Spawn",
59
+ "StepAction",
60
+ "StepMeta",
61
+ "StepMethod",
62
+ "StepRef",
63
+ "StepResult",
64
+ "StepSpec",
65
+ "compile_agent",
66
+ "resolve_step_owner",
67
+ "resolve_step_ref",
68
+ "slot",
69
+ "step",
70
+ ]
flowra/agent/agent.py ADDED
@@ -0,0 +1,21 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from .agent_def import AbstractAgent, AgentDefinitionRef, get_step_tag, set_agent_class
4
+ from .compile import compile_agent
5
+
6
+ __all__ = ["Agent"]
7
+
8
+
9
+ class Agent(AbstractAgent):
10
+ __slots__ = ("__slot_store__",)
11
+ __subagents__: ClassVar[dict[str, AgentDefinitionRef]] = {}
12
+
13
+ def __init_subclass__(cls, **kwargs: Any) -> None:
14
+ super().__init_subclass__(**kwargs)
15
+ for attr_name in vars(cls):
16
+ val = getattr(cls, attr_name, None)
17
+ if callable(val) and get_step_tag(val) is not None:
18
+ set_agent_class(val, cls)
19
+
20
+ subagents = getattr(cls, "__subagents__", None)
21
+ cls.__agent_definition__ = compile_agent(cls, subagents)
@@ -0,0 +1,213 @@
1
+ import dataclasses
2
+ import enum
3
+ from collections.abc import Awaitable, Callable
4
+ from typing import Any, ClassVar
5
+
6
+ from .service_locator import ServiceLocator
7
+ from .stored_values import AppendOnlyList, Scalar
8
+
9
+ __all__ = [
10
+ "NEW_SCOPE",
11
+ "AbstractAgent",
12
+ "AgentDefinition",
13
+ "AgentDefinitionRef",
14
+ "AgentInstance",
15
+ "AgentRef",
16
+ "AgentResult",
17
+ "Call",
18
+ "CallStepHandler",
19
+ "CreateInstance",
20
+ "Goto",
21
+ "SlotContainer",
22
+ "SlotDef",
23
+ "SlotKind",
24
+ "Spawn",
25
+ "StepAction",
26
+ "StepMeta",
27
+ "StepMethod",
28
+ "StepRef",
29
+ "StepResult",
30
+ "StepSpec",
31
+ "get_step_tag",
32
+ "resolve_step_owner",
33
+ "resolve_step_ref",
34
+ "set_agent_class",
35
+ "set_step_tag",
36
+ ]
37
+
38
+ _STEP_TAG_ATTR = "__step_tag__"
39
+ _AGENT_CLASS_ATTR = "__agent_class__"
40
+
41
+
42
+ def get_step_tag(fn: Callable[..., Any]) -> str | None:
43
+ return getattr(fn, _STEP_TAG_ATTR, None)
44
+
45
+
46
+ def set_step_tag(fn: Callable[..., Any], tag: str) -> None:
47
+ setattr(fn, _STEP_TAG_ATTR, tag)
48
+
49
+
50
+ def set_agent_class(fn: Callable[..., Any], cls: type) -> None:
51
+ setattr(fn, _AGENT_CLASS_ATTR, cls)
52
+
53
+
54
+ def resolve_step_ref(ref: "StepRef") -> str:
55
+ if isinstance(ref, str):
56
+ return ref
57
+ func = getattr(ref, "__func__", ref)
58
+ tag = getattr(func, _STEP_TAG_ATTR, None)
59
+ if tag is None:
60
+ raise TypeError(f"{ref!r} is not a @step-decorated method")
61
+ return tag
62
+
63
+
64
+ def resolve_step_owner(ref: "StepRef") -> type | None:
65
+ if isinstance(ref, str):
66
+ return None
67
+ bound_self = getattr(ref, "__self__", None)
68
+ if bound_self is not None:
69
+ return type(bound_self)
70
+ func = getattr(ref, "__func__", ref)
71
+ return getattr(func, _AGENT_CLASS_ATTR, None)
72
+
73
+
74
+ def _resolve_step_with_agent(step: "StepRef", agent: "AgentRef | None") -> str:
75
+ if not isinstance(step, str):
76
+ bound_self = getattr(step, "__self__", None)
77
+ if bound_self is None and agent is None:
78
+ msg = (
79
+ f"Unbound method {step!r} requires explicit agent parameter. "
80
+ f"Use agent=AgentClass or agent='agent_name', or use bound method self.{step.__name__}"
81
+ )
82
+ raise TypeError(msg)
83
+ step = resolve_step_ref(step)
84
+ return step
85
+
86
+
87
+ class AbstractAgent:
88
+ __slots__ = ()
89
+ __agent_definition__: ClassVar["AgentDefinition"]
90
+
91
+
92
+ type AgentRef = str | type[AbstractAgent]
93
+
94
+
95
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
96
+ class StepSpec:
97
+ pass
98
+
99
+
100
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
101
+ class AgentResult:
102
+ pass
103
+
104
+
105
+ class _NewScope(enum.Enum):
106
+ NEW_SCOPE = "NEW_SCOPE"
107
+
108
+
109
+ NEW_SCOPE = _NewScope.NEW_SCOPE
110
+
111
+
112
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True, init=False)
113
+ class Goto:
114
+ agent: AgentRef | None
115
+ step: str
116
+ spec: StepSpec | None
117
+ storage_key: str | _NewScope | None
118
+
119
+ def __init__(
120
+ self,
121
+ *,
122
+ step: "StepRef",
123
+ agent: AgentRef | None = None,
124
+ spec: StepSpec | None = None,
125
+ storage_key: str | _NewScope | None = None,
126
+ ) -> None:
127
+ object.__setattr__(self, "agent", agent)
128
+ object.__setattr__(self, "step", _resolve_step_with_agent(step, agent))
129
+ object.__setattr__(self, "spec", spec)
130
+ object.__setattr__(self, "storage_key", storage_key)
131
+
132
+
133
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True, init=False)
134
+ class Call:
135
+ agent: AgentRef | None
136
+ step: str
137
+ spec: StepSpec | None
138
+ storage_key: str | _NewScope | None
139
+
140
+ def __init__(
141
+ self,
142
+ *,
143
+ step: "StepRef",
144
+ agent: AgentRef | None = None,
145
+ spec: StepSpec | None = None,
146
+ storage_key: str | _NewScope | None = None,
147
+ ) -> None:
148
+ object.__setattr__(self, "agent", agent)
149
+ object.__setattr__(self, "step", _resolve_step_with_agent(step, agent))
150
+ object.__setattr__(self, "spec", spec)
151
+ object.__setattr__(self, "storage_key", storage_key)
152
+
153
+
154
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True, init=False)
155
+ class Spawn:
156
+ children: list[Call]
157
+ then: str
158
+
159
+ def __init__(
160
+ self,
161
+ *,
162
+ children: list[Call],
163
+ then: "StepRef",
164
+ ) -> None:
165
+ object.__setattr__(self, "children", children)
166
+ object.__setattr__(self, "then", resolve_step_ref(then))
167
+
168
+
169
+ type StepAction = Goto | Spawn
170
+ type StepResult = StepAction | AgentResult | None
171
+ type StepMethod = Callable[..., StepResult | Awaitable[StepResult]]
172
+ type StepRef = str | StepMethod
173
+
174
+ type CallStepHandler = Callable[[str, StepSpec | None, list[AgentResult] | None], Awaitable[StepResult]]
175
+
176
+ type SlotContainer = Scalar | AppendOnlyList
177
+
178
+ type CreateInstance = Callable[[ServiceLocator, dict[str, SlotContainer]], AgentInstance]
179
+
180
+
181
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
182
+ class AgentInstance:
183
+ call_step: CallStepHandler
184
+
185
+
186
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
187
+ class StepMeta:
188
+ step_spec: type[StepSpec] | None = None
189
+ result_types: frozenset[type[AgentResult]] = frozenset()
190
+
191
+
192
+ class SlotKind(enum.StrEnum):
193
+ SCALAR = "scalar"
194
+ APPEND_ONLY = "append_only"
195
+
196
+
197
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
198
+ class SlotDef:
199
+ key: str
200
+ kind: SlotKind
201
+ value_type: type
202
+
203
+
204
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
205
+ class AgentDefinition:
206
+ steps: dict[str, StepMeta]
207
+ slots: dict[str, SlotDef]
208
+ type_registry: dict[str, type]
209
+ create_instance: CreateInstance
210
+ subagents: "dict[str, AgentDefinitionRef]" = dataclasses.field(default_factory=dict)
211
+
212
+
213
+ type AgentDefinitionRef = AgentDefinition | type[AbstractAgent]
@@ -0,0 +1,139 @@
1
+ import dataclasses
2
+ import re
3
+
4
+ from .agent_def import AbstractAgent, AgentDefinition, AgentDefinitionRef, AgentRef
5
+
6
+ __all__ = ["AgentRegistry", "ResolvedAgent"]
7
+
8
+
9
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
10
+ class ResolvedAgent:
11
+ name: str
12
+ definition: AgentDefinition
13
+
14
+
15
+ class AgentRegistry:
16
+ __VALID_NAME = re.compile(r"^[a-zA-Z0-9_-]+$")
17
+
18
+ __slots__ = ("__definitions", "__type_maps")
19
+
20
+ def __init__(self, agents: dict[str, AgentDefinitionRef]) -> None:
21
+ self.__definitions: dict[str, AgentDefinition] = {}
22
+ self.__type_maps: dict[str, dict[type[AbstractAgent], str]] = {}
23
+ for name, ref in agents.items():
24
+ self.__register("", name, ref)
25
+
26
+ def resolve(
27
+ self,
28
+ ref: AgentRef,
29
+ *,
30
+ context: str | None = None,
31
+ ) -> ResolvedAgent:
32
+ if isinstance(ref, str):
33
+ name = self.__resolve_name(ref, context=context)
34
+ defn = self.__definitions.get(name)
35
+ if defn is None:
36
+ raise KeyError(f"Unknown agent: {name!r}")
37
+ return ResolvedAgent(name=name, definition=defn)
38
+
39
+ return self.__resolve_type(ref, context or "")
40
+
41
+ def get(self, name: str) -> AgentDefinition:
42
+ defn = self.__definitions.get(name)
43
+ if defn is None:
44
+ raise KeyError(f"Unknown agent: {name!r}")
45
+ return defn
46
+
47
+ def all_definitions(self) -> list[AgentDefinition]:
48
+ return list(self.__definitions.values())
49
+
50
+ def all_named_definitions(self) -> list[tuple[str, AgentDefinition]]:
51
+ return list(self.__definitions.items())
52
+
53
+ def name_for_type(
54
+ self,
55
+ agent_type: type[AbstractAgent],
56
+ *,
57
+ context: str = "",
58
+ ) -> str:
59
+ return self.__resolve_type(agent_type, context).name
60
+
61
+ def __register(
62
+ self,
63
+ context: str,
64
+ local_name: str,
65
+ ref: AgentDefinitionRef,
66
+ ) -> None:
67
+ self.__validate_name(local_name)
68
+ full_name = f"{context}/{local_name}" if context else local_name
69
+
70
+ if isinstance(ref, type):
71
+ agent_def = ref.__agent_definition__
72
+ type_map = self.__type_maps.setdefault(context, {})
73
+ if ref in type_map:
74
+ msg = f"Duplicate agent class {ref.__name__!r} at level {context!r}"
75
+ raise ValueError(msg)
76
+ type_map[ref] = local_name
77
+ else:
78
+ agent_def = ref
79
+
80
+ self.__definitions[full_name] = agent_def
81
+
82
+ for subname, subref in agent_def.subagents.items():
83
+ self.__register(full_name, subname, subref)
84
+
85
+ def __resolve_type(
86
+ self,
87
+ agent_type: type[AbstractAgent],
88
+ context: str,
89
+ ) -> ResolvedAgent:
90
+ current = context
91
+ while True:
92
+ type_map = self.__type_maps.get(current, {})
93
+ local_name = type_map.get(agent_type)
94
+ if local_name is not None:
95
+ full_name = f"{current}/{local_name}" if current else local_name
96
+ return ResolvedAgent(name=full_name, definition=self.__definitions[full_name])
97
+ if not current:
98
+ break
99
+ slash = current.rfind("/")
100
+ current = current[:slash] if slash >= 0 else ""
101
+ msg = f"Unregistered agent class: {agent_type.__name__}"
102
+ raise KeyError(msg)
103
+
104
+ @staticmethod
105
+ def __resolve_name(ref: str, *, context: str | None) -> str:
106
+ if not ref.startswith("."):
107
+ return ref
108
+
109
+ if context is None:
110
+ msg = f"Relative agent reference {ref!r} requires a context"
111
+ raise ValueError(msg)
112
+
113
+ if ref.startswith("./"):
114
+ suffix = ref[2:]
115
+ return f"{context}/{suffix}"
116
+
117
+ if ref.startswith("../"):
118
+ parts = context.split("/")
119
+ remaining = ref
120
+ while remaining.startswith("../"):
121
+ remaining = remaining[3:]
122
+ if not parts or (len(parts) == 1 and parts[0] == ""):
123
+ msg = f"Relative reference {ref!r} goes above root from context {context!r}"
124
+ raise ValueError(msg)
125
+ parts.pop()
126
+ if not remaining:
127
+ msg = f"Relative reference {ref!r} resolves to a context, not an agent name"
128
+ raise ValueError(msg)
129
+ parent = "/".join(parts)
130
+ return f"{parent}/{remaining}" if parent else remaining
131
+
132
+ msg = f"Invalid relative reference {ref!r}: use './' or '../' prefix"
133
+ raise ValueError(msg)
134
+
135
+ @classmethod
136
+ def __validate_name(cls, name: str) -> None:
137
+ if not cls.__VALID_NAME.match(name):
138
+ msg = f"Invalid agent name {name!r}: must contain only alphanumeric characters, underscores, and hyphens"
139
+ raise ValueError(msg)
@@ -0,0 +1,10 @@
1
+ import abc
2
+
3
+ __all__ = ["AgentStore"]
4
+
5
+
6
+ class AgentStore(abc.ABC):
7
+ __slots__ = ()
8
+
9
+ @abc.abstractmethod
10
+ async def flush(self) -> None: ...