agently 4.0.6.7__py3-none-any.whl → 4.0.7__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.
- agently/base.py +3 -5
- agently/builtins/agent_extensions/ConfigurePromptExtension.py +40 -9
- agently/builtins/hookers/SystemMessageHooker.py +51 -7
- agently/builtins/plugins/ModelRequester/OpenAICompatible.py +32 -0
- agently/builtins/plugins/PromptGenerator/AgentlyPromptGenerator.py +64 -1
- agently/builtins/plugins/ResponseParser/AgentlyResponseParser.py +22 -14
- agently/core/Agent.py +67 -27
- agently/core/ModelRequest.py +216 -26
- agently/core/PluginManager.py +2 -0
- agently/core/Prompt.py +15 -45
- agently/core/TriggerFlow/BluePrint.py +2 -0
- agently/core/TriggerFlow/Chunk.py +5 -4
- agently/core/TriggerFlow/Execution.py +29 -12
- agently/core/TriggerFlow/TriggerFlow.py +24 -10
- agently/core/TriggerFlow/process/BaseProcess.py +63 -21
- agently/core/TriggerFlow/process/ForEachProcess.py +30 -24
- agently/core/TriggerFlow/process/MatchCaseProcess.py +6 -6
- agently/integrations/chromadb.py +15 -0
- agently/types/data/response.py +10 -1
- agently/types/plugins/PromptGenerator.py +5 -1
- agently/types/plugins/ResponseParser.py +26 -6
- agently/types/trigger_flow/trigger_flow.py +6 -6
- agently/utils/DataFormatter.py +77 -0
- agently/utils/PythonSandbox.py +101 -0
- agently/utils/Settings.py +19 -2
- agently/utils/__init__.py +1 -0
- {agently-4.0.6.7.dist-info → agently-4.0.7.dist-info}/METADATA +1 -1
- {agently-4.0.6.7.dist-info → agently-4.0.7.dist-info}/RECORD +30 -29
- {agently-4.0.6.7.dist-info → agently-4.0.7.dist-info}/WHEEL +0 -0
- {agently-4.0.6.7.dist-info → agently-4.0.7.dist-info}/licenses/LICENSE +0 -0
agently/core/Prompt.py
CHANGED
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
|
-
from
|
|
16
|
+
from textwrap import dedent
|
|
17
|
+
from typing import Any, Literal, TYPE_CHECKING, cast, overload, TypeVar
|
|
17
18
|
|
|
18
|
-
from agently.utils import RuntimeData, Settings
|
|
19
|
+
from agently.utils import RuntimeData, Settings, DataFormatter
|
|
19
20
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
21
22
|
from agently.types.data.prompt import ChatMessage, PromptStandardSlot
|
|
@@ -79,8 +80,6 @@ class Prompt(RuntimeData):
|
|
|
79
80
|
):
|
|
80
81
|
super().__init__(prompt_dict, parent=parent_prompt, name=name)
|
|
81
82
|
|
|
82
|
-
self._placeholder_pattern = re.compile(r"\$\{\s*([^}]+?)\s*\}")
|
|
83
|
-
|
|
84
83
|
self.settings = Settings(
|
|
85
84
|
name="Prompt-Settings",
|
|
86
85
|
parent=parent_settings,
|
|
@@ -98,42 +97,9 @@ class Prompt(RuntimeData):
|
|
|
98
97
|
self.to_messages = self.prompt_generator.to_messages
|
|
99
98
|
self.to_prompt_object = self.prompt_generator.to_prompt_object
|
|
100
99
|
self.to_output_model = self.prompt_generator.to_output_model
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
raise TypeError(f"Variable mappings require a dictionary but got: { variable_mappings }")
|
|
105
|
-
|
|
106
|
-
if isinstance(obj, str):
|
|
107
|
-
full_match = self._placeholder_pattern.fullmatch(obj)
|
|
108
|
-
if full_match:
|
|
109
|
-
key = full_match.group(1).strip()
|
|
110
|
-
return variable_mappings.get(key, obj)
|
|
111
|
-
else:
|
|
112
|
-
|
|
113
|
-
def replacer(match):
|
|
114
|
-
key = match.group(1).strip()
|
|
115
|
-
return str(variable_mappings.get(key, match.group(0)))
|
|
116
|
-
|
|
117
|
-
return self._placeholder_pattern.sub(replacer, obj)
|
|
118
|
-
|
|
119
|
-
if isinstance(obj, Mapping):
|
|
120
|
-
return {
|
|
121
|
-
self._substitute_placeholder(key, variable_mappings): self._substitute_placeholder(
|
|
122
|
-
value, variable_mappings
|
|
123
|
-
)
|
|
124
|
-
for key, value in obj.items()
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if isinstance(obj, Sequence) and not isinstance(obj, (str, bytes, bytearray)):
|
|
128
|
-
if isinstance(obj, tuple):
|
|
129
|
-
return tuple(self._substitute_placeholder(value, variable_mappings) for value in obj)
|
|
130
|
-
else:
|
|
131
|
-
return [self._substitute_placeholder(value, variable_mappings) for value in obj]
|
|
132
|
-
|
|
133
|
-
if isinstance(obj, set):
|
|
134
|
-
return {self._substitute_placeholder(value, variable_mappings) for value in obj}
|
|
135
|
-
|
|
136
|
-
return obj
|
|
100
|
+
self.to_serializable_prompt_data = self.prompt_generator.to_serializable_prompt_data
|
|
101
|
+
self.to_json_prompt = self.prompt_generator.to_json_prompt
|
|
102
|
+
self.to_yaml_prompt = self.prompt_generator.to_yaml_prompt
|
|
137
103
|
|
|
138
104
|
@overload
|
|
139
105
|
def set(
|
|
@@ -157,10 +123,12 @@ class Prompt(RuntimeData):
|
|
|
157
123
|
value: Any,
|
|
158
124
|
mappings: dict[str, Any] | None = None,
|
|
159
125
|
):
|
|
126
|
+
if isinstance(value, str):
|
|
127
|
+
value = dedent(value.strip())
|
|
160
128
|
if mappings is not None:
|
|
161
129
|
super().set(
|
|
162
|
-
|
|
163
|
-
|
|
130
|
+
DataFormatter.substitute_placeholder(key, mappings),
|
|
131
|
+
DataFormatter.substitute_placeholder(value, mappings),
|
|
164
132
|
)
|
|
165
133
|
else:
|
|
166
134
|
super().set(key, value)
|
|
@@ -172,7 +140,7 @@ class Prompt(RuntimeData):
|
|
|
172
140
|
):
|
|
173
141
|
if mappings is not None:
|
|
174
142
|
super().update(
|
|
175
|
-
|
|
143
|
+
DataFormatter.substitute_placeholder(new, mappings),
|
|
176
144
|
)
|
|
177
145
|
else:
|
|
178
146
|
super().update(new)
|
|
@@ -183,10 +151,12 @@ class Prompt(RuntimeData):
|
|
|
183
151
|
value: Any,
|
|
184
152
|
mappings: dict[str, Any] | None = None,
|
|
185
153
|
):
|
|
154
|
+
if isinstance(value, str):
|
|
155
|
+
value = dedent(value.strip())
|
|
186
156
|
if mappings is not None:
|
|
187
157
|
super().append(
|
|
188
|
-
|
|
189
|
-
|
|
158
|
+
DataFormatter.substitute_placeholder(key, mappings),
|
|
159
|
+
DataFormatter.substitute_placeholder(value, mappings),
|
|
190
160
|
)
|
|
191
161
|
else:
|
|
192
162
|
super().append(key, value)
|
|
@@ -132,6 +132,7 @@ class TriggerFlowBluePrint:
|
|
|
132
132
|
*,
|
|
133
133
|
execution_id: str | None = None,
|
|
134
134
|
skip_exceptions: bool = False,
|
|
135
|
+
concurrency: int | None = None,
|
|
135
136
|
):
|
|
136
137
|
handlers_snapshot: TriggerFlowAllHandlers = {
|
|
137
138
|
"event": {k: v.copy() for k, v in self._handlers["event"].items()},
|
|
@@ -143,6 +144,7 @@ class TriggerFlowBluePrint:
|
|
|
143
144
|
trigger_flow=trigger_flow,
|
|
144
145
|
id=execution_id,
|
|
145
146
|
skip_exceptions=skip_exceptions,
|
|
147
|
+
concurrency=concurrency,
|
|
146
148
|
)
|
|
147
149
|
|
|
148
150
|
def copy(self, *, name: str | None = None):
|
|
@@ -30,16 +30,17 @@ class TriggerFlowChunk:
|
|
|
30
30
|
*,
|
|
31
31
|
name: str | None = None,
|
|
32
32
|
):
|
|
33
|
-
self.
|
|
33
|
+
self.id = uuid.uuid4().hex
|
|
34
|
+
self.name = name if name is not None else self.id
|
|
34
35
|
self._handler = handler
|
|
35
|
-
self.trigger = f"Chunk[{ handler.__name__ }]-{ self.
|
|
36
|
+
self.trigger = f"Chunk[{ handler.__name__ }]-{ self.id }"
|
|
36
37
|
|
|
37
38
|
async def async_call(self, data: "TriggerFlowEventData"):
|
|
38
39
|
result = await FunctionShifter.asyncify(self._handler)(data)
|
|
39
|
-
await data.async_emit(self.trigger, result,
|
|
40
|
+
await data.async_emit(self.trigger, result, _layer_marks=data._layer_marks.copy())
|
|
40
41
|
return result
|
|
41
42
|
|
|
42
43
|
def call(self, data: "TriggerFlowEventData"):
|
|
43
44
|
result = FunctionShifter.syncify(self._handler)(data)
|
|
44
|
-
data.emit(self.trigger, result,
|
|
45
|
+
data.emit(self.trigger, result, _layer_marks=data._layer_marks.copy())
|
|
45
46
|
return result
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import uuid
|
|
17
17
|
import asyncio
|
|
18
18
|
import warnings
|
|
19
|
+
from contextvars import ContextVar
|
|
19
20
|
|
|
20
21
|
from typing import Any, Literal, TYPE_CHECKING
|
|
21
22
|
|
|
@@ -37,6 +38,7 @@ class TriggerFlowExecution:
|
|
|
37
38
|
trigger_flow: "TriggerFlow",
|
|
38
39
|
id: str | None = None,
|
|
39
40
|
skip_exceptions: bool = False,
|
|
41
|
+
concurrency: int | None = None,
|
|
40
42
|
):
|
|
41
43
|
# Basic Attributions
|
|
42
44
|
self.id = id if id is not None else uuid.uuid4().hex
|
|
@@ -45,6 +47,11 @@ class TriggerFlowExecution:
|
|
|
45
47
|
self._runtime_data = RuntimeData()
|
|
46
48
|
self._system_runtime_data = RuntimeData()
|
|
47
49
|
self._skip_exceptions = skip_exceptions
|
|
50
|
+
self._concurrency_semaphore = asyncio.Semaphore(concurrency) if concurrency and concurrency > 0 else None
|
|
51
|
+
self._concurrency_depth = ContextVar(
|
|
52
|
+
f"trigger_flow_execution_concurrency_depth_{ self.id }",
|
|
53
|
+
default=0,
|
|
54
|
+
)
|
|
48
55
|
|
|
49
56
|
# Settings
|
|
50
57
|
self.settings = Settings(
|
|
@@ -97,7 +104,7 @@ class TriggerFlowExecution:
|
|
|
97
104
|
self,
|
|
98
105
|
trigger_event: str,
|
|
99
106
|
value: Any = None,
|
|
100
|
-
|
|
107
|
+
_layer_marks: list[str] | None = None,
|
|
101
108
|
*,
|
|
102
109
|
trigger_type: Literal["event", "runtime_data", "flow_data"] = "event",
|
|
103
110
|
):
|
|
@@ -126,19 +133,29 @@ class TriggerFlowExecution:
|
|
|
126
133
|
},
|
|
127
134
|
self.settings,
|
|
128
135
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
async def run_handler(handler_func):
|
|
137
|
+
if self._concurrency_semaphore is None:
|
|
138
|
+
return await handler_func
|
|
139
|
+
depth = self._concurrency_depth.get()
|
|
140
|
+
token = self._concurrency_depth.set(depth + 1)
|
|
141
|
+
try:
|
|
142
|
+
if depth > 0:
|
|
143
|
+
return await handler_func
|
|
144
|
+
async with self._concurrency_semaphore:
|
|
145
|
+
return await handler_func
|
|
146
|
+
finally:
|
|
147
|
+
self._concurrency_depth.reset(token)
|
|
148
|
+
|
|
149
|
+
handler_task = FunctionShifter.asyncify(handler)(
|
|
150
|
+
TriggerFlowEventData(
|
|
151
|
+
trigger_event=trigger_event,
|
|
152
|
+
trigger_type=trigger_type,
|
|
153
|
+
value=value,
|
|
154
|
+
execution=self,
|
|
155
|
+
_layer_marks=_layer_marks,
|
|
140
156
|
)
|
|
141
157
|
)
|
|
158
|
+
tasks.append(asyncio.ensure_future(run_handler(handler_task)))
|
|
142
159
|
|
|
143
160
|
if tasks:
|
|
144
161
|
await asyncio.gather(*tasks, return_exceptions=self._skip_exceptions)
|
|
@@ -51,6 +51,7 @@ class TriggerFlow:
|
|
|
51
51
|
self._skip_exceptions = skip_exceptions
|
|
52
52
|
self._executions: dict[str, "TriggerFlowExecution"] = {}
|
|
53
53
|
self._start_process = TriggerFlowProcess(
|
|
54
|
+
flow_chunk=self.chunk,
|
|
54
55
|
trigger_event="START",
|
|
55
56
|
blue_print=self._blue_print,
|
|
56
57
|
block_data=TriggerFlowBlockData(
|
|
@@ -60,6 +61,8 @@ class TriggerFlow:
|
|
|
60
61
|
|
|
61
62
|
self.chunks = self._blue_print.chunks
|
|
62
63
|
|
|
64
|
+
self.set_settings = self.settings.set_settings
|
|
65
|
+
|
|
63
66
|
self.get_flow_data = self._flow_data.get
|
|
64
67
|
self.set_flow_data = FunctionShifter.syncify(self.async_set_flow_data)
|
|
65
68
|
self.append_flow_data = FunctionShifter.syncify(self.async_append_flow_data)
|
|
@@ -74,10 +77,6 @@ class TriggerFlow:
|
|
|
74
77
|
self.start_execution = FunctionShifter.syncify(self.async_start_execution)
|
|
75
78
|
self.start = FunctionShifter.syncify(self.async_start)
|
|
76
79
|
|
|
77
|
-
def set_settings(self, key: str, value: "SerializableValue"):
|
|
78
|
-
self.settings.set_settings(key, value)
|
|
79
|
-
return self
|
|
80
|
-
|
|
81
80
|
@overload
|
|
82
81
|
def chunk(self, handler_or_name: "TriggerFlowHandler") -> TriggerFlowChunk: ...
|
|
83
82
|
|
|
@@ -100,13 +99,19 @@ class TriggerFlow:
|
|
|
100
99
|
self._blue_print.chunks[handler_or_name.__name__] = chunk
|
|
101
100
|
return chunk
|
|
102
101
|
|
|
103
|
-
def create_execution(
|
|
102
|
+
def create_execution(
|
|
103
|
+
self,
|
|
104
|
+
*,
|
|
105
|
+
skip_exceptions: bool | None = None,
|
|
106
|
+
concurrency: int | None = None,
|
|
107
|
+
):
|
|
104
108
|
execution_id = uuid.uuid4().hex
|
|
105
109
|
skip_exceptions = skip_exceptions if skip_exceptions is not None else self._skip_exceptions
|
|
106
110
|
execution = self._blue_print.create_execution(
|
|
107
111
|
self,
|
|
108
112
|
execution_id=execution_id,
|
|
109
113
|
skip_exceptions=skip_exceptions,
|
|
114
|
+
concurrency=concurrency,
|
|
110
115
|
)
|
|
111
116
|
self._executions[execution_id] = execution
|
|
112
117
|
return execution
|
|
@@ -119,8 +124,14 @@ class TriggerFlow:
|
|
|
119
124
|
if execution.id in self._executions:
|
|
120
125
|
del self._executions[execution.id]
|
|
121
126
|
|
|
122
|
-
async def async_start_execution(
|
|
123
|
-
|
|
127
|
+
async def async_start_execution(
|
|
128
|
+
self,
|
|
129
|
+
initial_value: Any,
|
|
130
|
+
*,
|
|
131
|
+
wait_for_result: bool = False,
|
|
132
|
+
concurrency: int | None = None,
|
|
133
|
+
):
|
|
134
|
+
execution = self.create_execution(concurrency=concurrency)
|
|
124
135
|
await execution.async_start(initial_value, wait_for_result=wait_for_result)
|
|
125
136
|
return execution
|
|
126
137
|
|
|
@@ -193,8 +204,9 @@ class TriggerFlow:
|
|
|
193
204
|
*,
|
|
194
205
|
wait_for_result: bool = True,
|
|
195
206
|
timeout: int | None = 10,
|
|
207
|
+
concurrency: int | None = None,
|
|
196
208
|
):
|
|
197
|
-
execution = await self.async_start_execution(initial_value)
|
|
209
|
+
execution = await self.async_start_execution(initial_value, concurrency=concurrency)
|
|
198
210
|
if wait_for_result:
|
|
199
211
|
return await execution.async_get_result(timeout=timeout)
|
|
200
212
|
|
|
@@ -203,8 +215,9 @@ class TriggerFlow:
|
|
|
203
215
|
initial_value: Any = None,
|
|
204
216
|
*,
|
|
205
217
|
timeout: int | None = 10,
|
|
218
|
+
concurrency: int | None = None,
|
|
206
219
|
):
|
|
207
|
-
execution = self.create_execution()
|
|
220
|
+
execution = self.create_execution(concurrency=concurrency)
|
|
208
221
|
return execution.get_async_runtime_stream(
|
|
209
222
|
initial_value,
|
|
210
223
|
timeout=timeout,
|
|
@@ -215,8 +228,9 @@ class TriggerFlow:
|
|
|
215
228
|
initial_value: Any = None,
|
|
216
229
|
*,
|
|
217
230
|
timeout: int | None = 10,
|
|
231
|
+
concurrency: int | None = None,
|
|
218
232
|
):
|
|
219
|
-
execution = self.create_execution()
|
|
233
|
+
execution = self.create_execution(concurrency=concurrency)
|
|
220
234
|
return execution.get_runtime_stream(
|
|
221
235
|
initial_value,
|
|
222
236
|
timeout=timeout,
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
import uuid
|
|
17
|
-
from asyncio import Event
|
|
17
|
+
from asyncio import Event, Semaphore
|
|
18
18
|
from threading import Lock
|
|
19
19
|
|
|
20
|
-
from typing import Any, Literal, TYPE_CHECKING, overload
|
|
20
|
+
from typing import Callable, Any, Literal, TYPE_CHECKING, overload, cast
|
|
21
21
|
from typing_extensions import Self
|
|
22
22
|
|
|
23
23
|
|
|
@@ -31,15 +31,18 @@ from agently.types.trigger_flow import TriggerFlowBlockData
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class TriggerFlowBaseProcess:
|
|
34
|
+
|
|
34
35
|
def __init__(
|
|
35
36
|
self,
|
|
36
37
|
*,
|
|
38
|
+
flow_chunk,
|
|
37
39
|
trigger_event: str,
|
|
38
40
|
blue_print: "TriggerFlowBluePrint",
|
|
39
41
|
block_data: "TriggerFlowBlockData",
|
|
40
42
|
trigger_type: Literal["event", "runtime_data", "flow_data"] = "event",
|
|
41
43
|
**options,
|
|
42
44
|
):
|
|
45
|
+
self._flow_chunk = flow_chunk
|
|
43
46
|
self.trigger_event = trigger_event
|
|
44
47
|
self.trigger_type: Literal["event", "runtime_data", "flow_data"] = trigger_type
|
|
45
48
|
self._blue_print = blue_print
|
|
@@ -55,6 +58,7 @@ class TriggerFlowBaseProcess:
|
|
|
55
58
|
**options,
|
|
56
59
|
):
|
|
57
60
|
return type(self)(
|
|
61
|
+
flow_chunk=self._flow_chunk,
|
|
58
62
|
trigger_event=trigger_event,
|
|
59
63
|
trigger_type=trigger_type,
|
|
60
64
|
blue_print=blue_print,
|
|
@@ -112,8 +116,12 @@ class TriggerFlowBaseProcess:
|
|
|
112
116
|
if isinstance(trigger_or_triggers, TriggerFlowChunk):
|
|
113
117
|
trigger_or_triggers = trigger_or_triggers.trigger
|
|
114
118
|
if isinstance(trigger_or_triggers, str):
|
|
119
|
+
if trigger_or_triggers in self._blue_print.chunks:
|
|
120
|
+
trigger = self._blue_print.chunks[trigger_or_triggers].trigger
|
|
121
|
+
else:
|
|
122
|
+
trigger = trigger_or_triggers
|
|
115
123
|
return self._new(
|
|
116
|
-
trigger_event=
|
|
124
|
+
trigger_event=trigger,
|
|
117
125
|
trigger_type="event",
|
|
118
126
|
blue_print=self._blue_print,
|
|
119
127
|
block_data=TriggerFlowBlockData(
|
|
@@ -178,7 +186,7 @@ class TriggerFlowBaseProcess:
|
|
|
178
186
|
if mode == "simple_or"
|
|
179
187
|
else (data.trigger_type, data.trigger_event, data.value)
|
|
180
188
|
),
|
|
181
|
-
|
|
189
|
+
_layer_marks=data._layer_marks.copy(),
|
|
182
190
|
)
|
|
183
191
|
case "and":
|
|
184
192
|
if data.trigger_type in values and data.trigger_event in values[trigger_type]: # type: ignore
|
|
@@ -191,7 +199,7 @@ class TriggerFlowBaseProcess:
|
|
|
191
199
|
await data.async_emit(
|
|
192
200
|
when_trigger,
|
|
193
201
|
values,
|
|
194
|
-
|
|
202
|
+
_layer_marks=data._layer_marks.copy(),
|
|
195
203
|
)
|
|
196
204
|
|
|
197
205
|
for trigger_type, trigger_event_dict in values.items():
|
|
@@ -213,15 +221,22 @@ class TriggerFlowBaseProcess:
|
|
|
213
221
|
|
|
214
222
|
def to(
|
|
215
223
|
self,
|
|
216
|
-
chunk: "TriggerFlowChunk | TriggerFlowHandler | str",
|
|
224
|
+
chunk: "TriggerFlowChunk | TriggerFlowHandler | str | tuple[str, TriggerFlowHandler]",
|
|
217
225
|
side_branch: bool = False,
|
|
226
|
+
name: str | None = None,
|
|
218
227
|
):
|
|
219
228
|
if isinstance(chunk, str):
|
|
220
229
|
if chunk in self._blue_print.chunks:
|
|
221
230
|
chunk = self._blue_print.chunks[chunk]
|
|
222
231
|
else:
|
|
223
232
|
raise NotImplementedError(f"Cannot find chunk named '{ chunk }'")
|
|
224
|
-
|
|
233
|
+
elif isinstance(chunk, tuple):
|
|
234
|
+
chunk_name = chunk[0]
|
|
235
|
+
chunk_func = chunk[1]
|
|
236
|
+
chunk = self._flow_chunk(chunk_name)(chunk_func)
|
|
237
|
+
else:
|
|
238
|
+
chunk = self._flow_chunk(name or chunk.__name__)(chunk) if callable(chunk) else chunk
|
|
239
|
+
assert isinstance(chunk, TriggerFlowChunk)
|
|
225
240
|
self._blue_print.add_handler(
|
|
226
241
|
self.trigger_type,
|
|
227
242
|
self.trigger_event,
|
|
@@ -235,38 +250,65 @@ class TriggerFlowBaseProcess:
|
|
|
235
250
|
**self._options,
|
|
236
251
|
)
|
|
237
252
|
|
|
238
|
-
def side_branch(
|
|
239
|
-
|
|
253
|
+
def side_branch(
|
|
254
|
+
self,
|
|
255
|
+
chunk: "TriggerFlowChunk | TriggerFlowHandler",
|
|
256
|
+
*,
|
|
257
|
+
name: str | None = None,
|
|
258
|
+
):
|
|
259
|
+
return self.to(
|
|
260
|
+
chunk,
|
|
261
|
+
side_branch=True,
|
|
262
|
+
name=name,
|
|
263
|
+
)
|
|
240
264
|
|
|
241
265
|
def batch(
|
|
242
266
|
self,
|
|
243
|
-
*chunks: "TriggerFlowChunk | TriggerFlowHandler",
|
|
267
|
+
*chunks: "TriggerFlowChunk | TriggerFlowHandler | tuple[str, TriggerFlowHandler]",
|
|
244
268
|
side_branch: bool = False,
|
|
269
|
+
concurrency: int | None = None,
|
|
245
270
|
):
|
|
246
271
|
batch_trigger = f"Batch-{ uuid.uuid4().hex }"
|
|
247
272
|
results = {}
|
|
248
|
-
|
|
273
|
+
triggers_to_wait = {}
|
|
274
|
+
trigger_to_chunk_name = {}
|
|
275
|
+
semaphore = Semaphore(concurrency) if concurrency and concurrency > 0 else None
|
|
249
276
|
|
|
250
277
|
async def wait_all_chunks(data: "TriggerFlowEventData"):
|
|
251
|
-
if data.event in
|
|
252
|
-
results[data.event] = data.value
|
|
253
|
-
|
|
254
|
-
for done in
|
|
278
|
+
if data.event in triggers_to_wait:
|
|
279
|
+
results[trigger_to_chunk_name[data.event]] = data.value
|
|
280
|
+
triggers_to_wait[data.event] = True
|
|
281
|
+
for done in triggers_to_wait.values():
|
|
255
282
|
if done is False:
|
|
256
283
|
return
|
|
257
284
|
await data.async_emit(
|
|
258
285
|
batch_trigger,
|
|
259
286
|
results,
|
|
260
|
-
|
|
287
|
+
_layer_marks=data._layer_marks.copy(),
|
|
261
288
|
)
|
|
262
289
|
|
|
263
290
|
for chunk in chunks:
|
|
264
|
-
|
|
265
|
-
|
|
291
|
+
if isinstance(chunk, tuple):
|
|
292
|
+
chunk_name = chunk[0]
|
|
293
|
+
chunk_func = chunk[1]
|
|
294
|
+
chunk = self._flow_chunk(chunk_name)(chunk_func)
|
|
295
|
+
else:
|
|
296
|
+
chunk = self._flow_chunk(chunk.__name__)(chunk) if callable(chunk) else chunk
|
|
297
|
+
triggers_to_wait[chunk.trigger] = False
|
|
298
|
+
trigger_to_chunk_name[chunk.trigger] = chunk.name
|
|
299
|
+
results[chunk.name] = None
|
|
300
|
+
|
|
301
|
+
if semaphore is None:
|
|
302
|
+
handler = chunk.async_call
|
|
303
|
+
else:
|
|
304
|
+
async def handler(data: "TriggerFlowEventData", _chunk=chunk):
|
|
305
|
+
async with semaphore:
|
|
306
|
+
return await _chunk.async_call(data)
|
|
307
|
+
|
|
266
308
|
self._blue_print.add_handler(
|
|
267
309
|
self.trigger_type,
|
|
268
310
|
self.trigger_event,
|
|
269
|
-
|
|
311
|
+
handler,
|
|
270
312
|
)
|
|
271
313
|
self._blue_print.add_event_handler(chunk.trigger, wait_all_chunks)
|
|
272
314
|
|
|
@@ -299,13 +341,13 @@ class TriggerFlowBaseProcess:
|
|
|
299
341
|
await data.async_emit(
|
|
300
342
|
collect_trigger,
|
|
301
343
|
self._block_data.global_data.get(f"collections.{ collection_name}"),
|
|
302
|
-
|
|
344
|
+
_layer_marks=data._layer_marks.copy(),
|
|
303
345
|
)
|
|
304
346
|
elif mode == "filled_then_empty":
|
|
305
347
|
await data.async_emit(
|
|
306
348
|
collect_trigger,
|
|
307
349
|
self._block_data.global_data.get(f"collections.{ collection_name}"),
|
|
308
|
-
|
|
350
|
+
_layer_marks=data._layer_marks.copy(),
|
|
309
351
|
)
|
|
310
352
|
del self._block_data.global_data[f"collections.{ collection_name}"]
|
|
311
353
|
|
|
@@ -22,7 +22,7 @@ from agently.utils import RuntimeDataNamespace
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class TriggerFlowForEachProcess(TriggerFlowBaseProcess):
|
|
25
|
-
def for_each(self):
|
|
25
|
+
def for_each(self, *, concurrency: int | None = None):
|
|
26
26
|
for_each_id = uuid.uuid4().hex
|
|
27
27
|
for_each_block_data = TriggerFlowBlockData(
|
|
28
28
|
outer_block=self._block_data,
|
|
@@ -31,6 +31,7 @@ class TriggerFlowForEachProcess(TriggerFlowBaseProcess):
|
|
|
31
31
|
},
|
|
32
32
|
)
|
|
33
33
|
send_item_trigger = f"ForEach-{ for_each_id }-Send"
|
|
34
|
+
semaphore = asyncio.Semaphore(concurrency) if concurrency and concurrency > 0 else None
|
|
34
35
|
|
|
35
36
|
async def send_items(data: "TriggerFlowEventData"):
|
|
36
37
|
data.layer_in()
|
|
@@ -38,33 +39,38 @@ class TriggerFlowForEachProcess(TriggerFlowBaseProcess):
|
|
|
38
39
|
assert for_each_instance_id is not None
|
|
39
40
|
|
|
40
41
|
send_tasks = []
|
|
41
|
-
|
|
42
|
-
items = list(data.value)
|
|
43
|
-
for item in items:
|
|
44
|
-
data.layer_in()
|
|
45
|
-
item_id = data.layer_mark
|
|
46
|
-
assert item_id is not None
|
|
47
|
-
data._system_runtime_data.set(f"for_each_results.{ for_each_instance_id }.{ item_id }", EMPTY)
|
|
48
|
-
send_tasks.append(
|
|
49
|
-
data.async_emit(
|
|
50
|
-
send_item_trigger,
|
|
51
|
-
item,
|
|
52
|
-
data.layer_marks.copy(),
|
|
53
|
-
)
|
|
54
|
-
)
|
|
55
|
-
data.layer_out()
|
|
56
|
-
await asyncio.gather(*send_tasks)
|
|
57
|
-
else:
|
|
42
|
+
def prepare_item(item):
|
|
58
43
|
data.layer_in()
|
|
59
44
|
item_id = data.layer_mark
|
|
60
45
|
assert item_id is not None
|
|
46
|
+
layer_marks = data._layer_marks.copy()
|
|
61
47
|
data._system_runtime_data.set(f"for_each_results.{ for_each_instance_id }.{ item_id }", EMPTY)
|
|
62
|
-
await data.async_emit(
|
|
63
|
-
send_item_trigger,
|
|
64
|
-
data.value,
|
|
65
|
-
data.layer_marks.copy(),
|
|
66
|
-
)
|
|
67
48
|
data.layer_out()
|
|
49
|
+
return item_id, layer_marks, item
|
|
50
|
+
|
|
51
|
+
async def emit_item(item, layer_marks):
|
|
52
|
+
if semaphore is None:
|
|
53
|
+
await data.async_emit(
|
|
54
|
+
send_item_trigger,
|
|
55
|
+
item,
|
|
56
|
+
layer_marks,
|
|
57
|
+
)
|
|
58
|
+
else:
|
|
59
|
+
async with semaphore:
|
|
60
|
+
await data.async_emit(
|
|
61
|
+
send_item_trigger,
|
|
62
|
+
item,
|
|
63
|
+
layer_marks,
|
|
64
|
+
)
|
|
65
|
+
if not isinstance(data.value, str) and isinstance(data.value, Sequence):
|
|
66
|
+
items = list(data.value)
|
|
67
|
+
for item in items:
|
|
68
|
+
_, layer_marks, item_value = prepare_item(item)
|
|
69
|
+
send_tasks.append(emit_item(item_value, layer_marks))
|
|
70
|
+
await asyncio.gather(*send_tasks)
|
|
71
|
+
else:
|
|
72
|
+
_, layer_marks, item_value = prepare_item(data.value)
|
|
73
|
+
await emit_item(item_value, layer_marks)
|
|
68
74
|
|
|
69
75
|
self.to(send_items)
|
|
70
76
|
|
|
@@ -103,7 +109,7 @@ class TriggerFlowForEachProcess(TriggerFlowBaseProcess):
|
|
|
103
109
|
await data.async_emit(
|
|
104
110
|
end_for_each_trigger,
|
|
105
111
|
list(for_each_results[for_each_instance_id].values()),
|
|
106
|
-
data.
|
|
112
|
+
data._layer_marks.copy(),
|
|
107
113
|
)
|
|
108
114
|
for_each_results.delete(for_each_instance_id)
|
|
109
115
|
|
|
@@ -58,7 +58,7 @@ class TriggerFlowMatchCaseProcess(TriggerFlowBaseProcess):
|
|
|
58
58
|
await data.async_emit(
|
|
59
59
|
f"Match-{ match_id }-Case-{ case_id }",
|
|
60
60
|
data.value,
|
|
61
|
-
|
|
61
|
+
_layer_marks=data._layer_marks.copy(),
|
|
62
62
|
)
|
|
63
63
|
return
|
|
64
64
|
elif mode == "hit_all":
|
|
@@ -71,7 +71,7 @@ class TriggerFlowMatchCaseProcess(TriggerFlowBaseProcess):
|
|
|
71
71
|
data.async_emit(
|
|
72
72
|
f"Match-{ match_id }-Case-{ case_id }",
|
|
73
73
|
data.value,
|
|
74
|
-
|
|
74
|
+
_layer_marks=data._layer_marks.copy(),
|
|
75
75
|
)
|
|
76
76
|
)
|
|
77
77
|
data.layer_out()
|
|
@@ -81,13 +81,13 @@ class TriggerFlowMatchCaseProcess(TriggerFlowBaseProcess):
|
|
|
81
81
|
await data.async_emit(
|
|
82
82
|
f"Match-{ match_id }-Else",
|
|
83
83
|
data.value,
|
|
84
|
-
|
|
84
|
+
_layer_marks=data._layer_marks.copy(),
|
|
85
85
|
)
|
|
86
86
|
else:
|
|
87
87
|
await data.async_emit(
|
|
88
88
|
f"Match-{ match_id }-Result",
|
|
89
89
|
data.value,
|
|
90
|
-
|
|
90
|
+
_layer_marks=data._layer_marks.copy(),
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
self.to(match_case)
|
|
@@ -164,7 +164,7 @@ class TriggerFlowMatchCaseProcess(TriggerFlowBaseProcess):
|
|
|
164
164
|
await data.async_emit(
|
|
165
165
|
f"Match-{ match_id }-Result",
|
|
166
166
|
list(match_results.values()),
|
|
167
|
-
|
|
167
|
+
_layer_marks=data._layer_marks.copy(),
|
|
168
168
|
)
|
|
169
169
|
del data._system_runtime_data[f"match_results.{ data.upper_layer_mark }"]
|
|
170
170
|
else:
|
|
@@ -172,7 +172,7 @@ class TriggerFlowMatchCaseProcess(TriggerFlowBaseProcess):
|
|
|
172
172
|
await data.async_emit(
|
|
173
173
|
f"Match-{ match_id }-Result",
|
|
174
174
|
data.value,
|
|
175
|
-
|
|
175
|
+
_layer_marks=data._layer_marks.copy(),
|
|
176
176
|
)
|
|
177
177
|
|
|
178
178
|
for trigger in branch_ends:
|
agently/integrations/chromadb.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2023-2025 AgentEra(Agently.Tech)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
from agently.utils import LazyImport
|
|
2
17
|
|
|
3
18
|
LazyImport.import_package("chromadb")
|