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.
- flowra/__init__.py +3 -0
- flowra/agent/__init__.py +70 -0
- flowra/agent/agent.py +21 -0
- flowra/agent/agent_def.py +213 -0
- flowra/agent/agent_registry.py +139 -0
- flowra/agent/agent_store.py +10 -0
- flowra/agent/compile.py +417 -0
- flowra/agent/interrupt_token.py +15 -0
- flowra/agent/service_locator.py +15 -0
- flowra/agent/step_decorator.py +28 -0
- flowra/agent/stored_values.py +114 -0
- flowra/lib/__init__.py +11 -0
- flowra/lib/chat/__init__.py +14 -0
- flowra/lib/chat/agent.py +75 -0
- flowra/lib/chat/config.py +29 -0
- flowra/lib/chat/hook_executor.py +21 -0
- flowra/lib/chat/hooks.py +43 -0
- flowra/lib/chat/spec.py +19 -0
- flowra/lib/config_value.py +19 -0
- flowra/lib/llm_config.py +13 -0
- flowra/lib/tool_loop/__init__.py +99 -0
- flowra/lib/tool_loop/_tool_call_agent.py +76 -0
- flowra/lib/tool_loop/agent.py +282 -0
- flowra/lib/tool_loop/cache.py +157 -0
- flowra/lib/tool_loop/config.py +26 -0
- flowra/lib/tool_loop/context.py +21 -0
- flowra/lib/tool_loop/hook_executor.py +189 -0
- flowra/lib/tool_loop/hooks.py +433 -0
- flowra/lib/tool_loop/spec.py +30 -0
- flowra/llm/__init__.py +28 -0
- flowra/llm/_base.py +10 -0
- flowra/llm/blocks.py +58 -0
- flowra/llm/messages.py +33 -0
- flowra/llm/pricing/__init__.py +3 -0
- flowra/llm/pricing/anthropic.py +64 -0
- flowra/llm/pricing/google.py +50 -0
- flowra/llm/pricing/openai.py +62 -0
- flowra/llm/provider.py +13 -0
- flowra/llm/providers/__init__.py +11 -0
- flowra/llm/providers/anthropic_vertex.py +343 -0
- flowra/llm/providers/google_vertex.py +379 -0
- flowra/llm/providers/openai.py +367 -0
- flowra/llm/request.py +23 -0
- flowra/llm/response.py +66 -0
- flowra/llm/schema_formatting.py +73 -0
- flowra/llm/schema_validation.py +33 -0
- flowra/llm/tools.py +14 -0
- flowra/py.typed +1 -0
- flowra/runtime/__init__.py +12 -0
- flowra/runtime/_sealed_scope.py +52 -0
- flowra/runtime/engine.py +328 -0
- flowra/runtime/execution.py +40 -0
- flowra/runtime/interrupt.py +46 -0
- flowra/runtime/runtime.py +280 -0
- flowra/runtime/runtime_scope.py +125 -0
- flowra/runtime/serialization.py +65 -0
- flowra/runtime/storage/__init__.py +5 -0
- flowra/runtime/storage/file.py +90 -0
- flowra/runtime/storage/in_memory.py +47 -0
- flowra/runtime/storage/session_storage.py +48 -0
- flowra/tools/__init__.py +24 -0
- flowra/tools/local_tool.py +313 -0
- flowra/tools/mcp_connection.py +389 -0
- flowra/tools/tool_group.py +38 -0
- flowra/tools/tool_registry.py +216 -0
- flowra/tools/types.py +18 -0
- flowra/version.py +2 -0
- flowra-0.0.1.dev1.dist-info/METADATA +18 -0
- flowra-0.0.1.dev1.dist-info/RECORD +70 -0
- flowra-0.0.1.dev1.dist-info/WHEEL +4 -0
flowra/__init__.py
ADDED
flowra/agent/__init__.py
ADDED
|
@@ -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)
|