agently 4.0.6.10__py3-none-any.whl → 4.0.6.11__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 +38 -9
- agently/builtins/plugins/ModelRequester/OpenAICompatible.py +28 -0
- agently/builtins/plugins/ResponseParser/AgentlyResponseParser.py +22 -14
- agently/core/Agent.py +2 -4
- agently/core/ModelRequest.py +36 -11
- agently/core/PluginManager.py +2 -0
- agently/core/Prompt.py +7 -45
- agently/core/TriggerFlow/Chunk.py +3 -2
- agently/core/TriggerFlow/TriggerFlow.py +3 -4
- agently/core/TriggerFlow/process/BaseProcess.py +15 -6
- agently/integrations/chromadb.py +15 -0
- agently/types/data/response.py +10 -1
- agently/types/plugins/ResponseParser.py +26 -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.10.dist-info → agently-4.0.6.11.dist-info}/METADATA +1 -1
- {agently-4.0.6.10.dist-info → agently-4.0.6.11.dist-info}/RECORD +22 -21
- {agently-4.0.6.10.dist-info → agently-4.0.6.11.dist-info}/WHEEL +0 -0
- {agently-4.0.6.10.dist-info → agently-4.0.6.11.dist-info}/licenses/LICENSE +0 -0
agently/base.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from typing import Any, Literal, Type, TYPE_CHECKING, TypeVar, Generic, cast
|
|
16
16
|
|
|
17
|
-
from agently.utils import Settings, create_logger, FunctionShifter
|
|
17
|
+
from agently.utils import Settings, create_logger, FunctionShifter, DataFormatter
|
|
18
18
|
from agently.core import PluginManager, EventCenter, Tool, Prompt, ModelRequest, BaseAgent
|
|
19
19
|
from agently._default_init import _load_default_settings, _load_default_plugins, _hook_default_event_handlers
|
|
20
20
|
|
|
@@ -117,6 +117,8 @@ class AgentlyMain(Generic[A]):
|
|
|
117
117
|
self.tool = tool
|
|
118
118
|
self.AgentType = AgentType
|
|
119
119
|
|
|
120
|
+
self.set_settings = self.settings.set_settings
|
|
121
|
+
|
|
120
122
|
def set_debug_console(self, debug_console_status: Literal["ON", "OFF"]):
|
|
121
123
|
match debug_console_status:
|
|
122
124
|
case "OFF":
|
|
@@ -130,10 +132,6 @@ class AgentlyMain(Generic[A]):
|
|
|
130
132
|
self.logger.setLevel(log_level)
|
|
131
133
|
return self
|
|
132
134
|
|
|
133
|
-
def set_settings(self, key: str, value: "SerializableValue"):
|
|
134
|
-
self.settings.set_settings(key, value)
|
|
135
|
-
return self
|
|
136
|
-
|
|
137
135
|
def create_prompt(self, name: str = "agently_prompt") -> Prompt:
|
|
138
136
|
return Prompt(
|
|
139
137
|
self.plugin_manager,
|
|
@@ -21,6 +21,7 @@ from typing import Any
|
|
|
21
21
|
from json import JSONDecodeError
|
|
22
22
|
|
|
23
23
|
from agently.core import BaseAgent
|
|
24
|
+
from agently.utils import DataLocator
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class ConfigurePromptExtension(BaseAgent):
|
|
@@ -168,46 +169,74 @@ class ConfigurePromptExtension(BaseAgent):
|
|
|
168
169
|
variable_mappings,
|
|
169
170
|
)
|
|
170
171
|
|
|
171
|
-
def load_yaml_prompt(
|
|
172
|
+
def load_yaml_prompt(
|
|
173
|
+
self,
|
|
174
|
+
path_or_content: str | Path,
|
|
175
|
+
mappings: dict[str, Any] | None = None,
|
|
176
|
+
*,
|
|
177
|
+
prompt_key_path: str | None = None,
|
|
178
|
+
encoding: str | None = "utf-8",
|
|
179
|
+
):
|
|
172
180
|
path = Path(path_or_content)
|
|
173
181
|
if path.exists() and path.is_file():
|
|
174
182
|
try:
|
|
175
|
-
with path.open("r", encoding=
|
|
183
|
+
with path.open("r", encoding=encoding) as file:
|
|
176
184
|
prompt = yaml.safe_load(file)
|
|
177
185
|
except yaml.YAMLError as e:
|
|
178
186
|
raise ValueError(f"Cannot load YAML file '{ path_or_content }'.\nError: { e }")
|
|
179
187
|
else:
|
|
180
188
|
try:
|
|
181
|
-
prompt = yaml.safe_load(path_or_content)
|
|
189
|
+
prompt = yaml.safe_load(str(path_or_content))
|
|
182
190
|
except yaml.YAMLError as e:
|
|
183
191
|
raise ValueError(f"Cannot load YAML content or file path not existed.\nError: { e }")
|
|
192
|
+
if not isinstance(prompt, dict):
|
|
193
|
+
raise TypeError(
|
|
194
|
+
"Cannot execute YAML prompt configures, expect prompt configures as a dictionary data but got:"
|
|
195
|
+
f"{ prompt }"
|
|
196
|
+
)
|
|
197
|
+
if prompt_key_path is not None:
|
|
198
|
+
prompt = DataLocator.locate_path_in_dict(prompt, prompt_key_path)
|
|
184
199
|
if isinstance(prompt, dict):
|
|
185
200
|
self._execute_prompt_configure(prompt, mappings)
|
|
186
201
|
else:
|
|
187
202
|
raise TypeError(
|
|
188
|
-
"Cannot execute YAML prompt configures, expect prompt configures as a dictionary data but got:"
|
|
203
|
+
f"Cannot execute YAML prompt configures, expect prompt configures{ ' from [' + prompt_key_path + '] ' if prompt_key_path is not None else '' } as a dictionary data but got:"
|
|
189
204
|
f"{ prompt }"
|
|
190
205
|
)
|
|
191
206
|
return self
|
|
192
207
|
|
|
193
|
-
def load_json_prompt(
|
|
208
|
+
def load_json_prompt(
|
|
209
|
+
self,
|
|
210
|
+
path_or_content: str | Path,
|
|
211
|
+
mappings: dict[str, Any] | None = None,
|
|
212
|
+
*,
|
|
213
|
+
prompt_key_path: str | None = None,
|
|
214
|
+
encoding: str | None = "utf-8",
|
|
215
|
+
):
|
|
194
216
|
path = Path(path_or_content)
|
|
195
217
|
if path.exists() and path.is_file():
|
|
196
218
|
try:
|
|
197
|
-
with path.open("r", encoding=
|
|
219
|
+
with path.open("r", encoding=encoding) as file:
|
|
198
220
|
prompt = json5.load(file)
|
|
199
221
|
except JSONDecodeError as e:
|
|
200
222
|
raise ValueError(f"Cannot load JSON file '{ path_or_content }'.\nError: { e }")
|
|
201
223
|
else:
|
|
202
224
|
try:
|
|
203
|
-
prompt = json5.loads(path_or_content)
|
|
204
|
-
except
|
|
225
|
+
prompt = json5.loads(str(path_or_content))
|
|
226
|
+
except JSONDecodeError as e:
|
|
205
227
|
raise ValueError(f"Cannot load JSON content or file path not existed.\nError: { e }")
|
|
228
|
+
if not isinstance(prompt, dict):
|
|
229
|
+
raise TypeError(
|
|
230
|
+
"Cannot execute JSON prompt configures, expect prompt configures as a dictionary data but got:"
|
|
231
|
+
f"{ prompt }"
|
|
232
|
+
)
|
|
233
|
+
if prompt_key_path is not None:
|
|
234
|
+
prompt = DataLocator.locate_path_in_dict(prompt, prompt_key_path)
|
|
206
235
|
if isinstance(prompt, dict):
|
|
207
236
|
self._execute_prompt_configure(prompt, mappings)
|
|
208
237
|
else:
|
|
209
238
|
raise TypeError(
|
|
210
|
-
"Cannot execute JSON prompt configures, expect prompt configures as a dictionary data but got:"
|
|
239
|
+
f"Cannot execute JSON prompt configures, expect prompt configures{ ' from [' + prompt_key_path + '] ' if prompt_key_path is not None else '' }as a dictionary data but got:"
|
|
211
240
|
f"{ prompt }"
|
|
212
241
|
)
|
|
213
242
|
return self
|
|
@@ -45,6 +45,7 @@ if TYPE_CHECKING:
|
|
|
45
45
|
class ContentMapping(TypedDict):
|
|
46
46
|
id: str | None
|
|
47
47
|
role: str | None
|
|
48
|
+
reasoning: str | None
|
|
48
49
|
delta: str | None
|
|
49
50
|
tool_calls: str | None
|
|
50
51
|
done: str | None
|
|
@@ -114,6 +115,7 @@ class OpenAICompatible(ModelRequester):
|
|
|
114
115
|
"content_mapping": {
|
|
115
116
|
"id": "id",
|
|
116
117
|
"role": "choices[0].delta.role",
|
|
118
|
+
"reasoning": "choices[0].delta.reasoning_content",
|
|
117
119
|
"delta": "choices[0].delta.content",
|
|
118
120
|
"tool_calls": "choices[0].delta.tool_calls",
|
|
119
121
|
"done": None,
|
|
@@ -124,6 +126,7 @@ class OpenAICompatible(ModelRequester):
|
|
|
124
126
|
},
|
|
125
127
|
"extra_done": None,
|
|
126
128
|
},
|
|
129
|
+
"yield_extra_content_separately": True,
|
|
127
130
|
"content_mapping_style": "dot",
|
|
128
131
|
"timeout": {
|
|
129
132
|
"connect": 30.0,
|
|
@@ -505,6 +508,7 @@ class OpenAICompatible(ModelRequester):
|
|
|
505
508
|
async def broadcast_response(self, response_generator: AsyncGenerator) -> "AgentlyResponseGenerator":
|
|
506
509
|
meta = {}
|
|
507
510
|
message_record = {}
|
|
511
|
+
reasoning_buffer = ""
|
|
508
512
|
content_buffer = ""
|
|
509
513
|
|
|
510
514
|
content_mapping = cast(
|
|
@@ -516,6 +520,7 @@ class OpenAICompatible(ModelRequester):
|
|
|
516
520
|
)
|
|
517
521
|
id_mapping = content_mapping["id"]
|
|
518
522
|
role_mapping = content_mapping["role"]
|
|
523
|
+
reasoning_mapping = content_mapping["reasoning"]
|
|
519
524
|
delta_mapping = content_mapping["delta"]
|
|
520
525
|
tool_calls_mapping = content_mapping["tool_calls"]
|
|
521
526
|
done_mapping = content_mapping["done"]
|
|
@@ -523,6 +528,7 @@ class OpenAICompatible(ModelRequester):
|
|
|
523
528
|
finish_reason_mapping = content_mapping["finish_reason"]
|
|
524
529
|
extra_delta_mapping = content_mapping["extra_delta"]
|
|
525
530
|
extra_done_mapping = content_mapping["extra_done"]
|
|
531
|
+
yield_extra_content_separately = self.plugin_settings.get("yield_extra_content_separately", True)
|
|
526
532
|
|
|
527
533
|
content_mapping_style = str(self.plugin_settings.get("content_mapping_style"))
|
|
528
534
|
if content_mapping_style not in ("dot", "slash"):
|
|
@@ -552,6 +558,15 @@ class OpenAICompatible(ModelRequester):
|
|
|
552
558
|
)
|
|
553
559
|
if role:
|
|
554
560
|
meta.update({"role": role})
|
|
561
|
+
if reasoning_mapping:
|
|
562
|
+
reasoning = DataLocator.locate_path_in_dict(
|
|
563
|
+
loaded_message,
|
|
564
|
+
reasoning_mapping,
|
|
565
|
+
style=content_mapping_style,
|
|
566
|
+
)
|
|
567
|
+
if reasoning:
|
|
568
|
+
reasoning_buffer += str(reasoning)
|
|
569
|
+
yield "reasoning_delta", reasoning
|
|
555
570
|
if delta_mapping:
|
|
556
571
|
delta = DataLocator.locate_path_in_dict(
|
|
557
572
|
loaded_message,
|
|
@@ -578,6 +593,8 @@ class OpenAICompatible(ModelRequester):
|
|
|
578
593
|
)
|
|
579
594
|
if extra_value:
|
|
580
595
|
yield "extra", {extra_key: extra_value}
|
|
596
|
+
if yield_extra_content_separately:
|
|
597
|
+
yield extra_key, extra_value # type: ignore
|
|
581
598
|
else:
|
|
582
599
|
done_content = None
|
|
583
600
|
if self.model_type == "embeddings" and done_mapping is None:
|
|
@@ -593,6 +610,17 @@ class OpenAICompatible(ModelRequester):
|
|
|
593
610
|
yield "done", done_content
|
|
594
611
|
else:
|
|
595
612
|
yield "done", content_buffer
|
|
613
|
+
reasoning_content = None
|
|
614
|
+
if reasoning_mapping:
|
|
615
|
+
reasoning_content = DataLocator.locate_path_in_dict(
|
|
616
|
+
message_record,
|
|
617
|
+
reasoning_mapping,
|
|
618
|
+
style=content_mapping_style,
|
|
619
|
+
)
|
|
620
|
+
if reasoning_content:
|
|
621
|
+
yield "reasoning_done", reasoning_content
|
|
622
|
+
else:
|
|
623
|
+
yield "reasoning_done", reasoning_buffer
|
|
596
624
|
match self.model_type:
|
|
597
625
|
case "embeddings":
|
|
598
626
|
yield "original_done", message_record
|
|
@@ -282,8 +282,10 @@ class AgentlyResponseParser(ResponseParser):
|
|
|
282
282
|
|
|
283
283
|
async def get_async_generator(
|
|
284
284
|
self,
|
|
285
|
-
type: Literal['all', 'delta', '
|
|
286
|
-
content: Literal['all', 'delta', '
|
|
285
|
+
type: Literal['all', 'delta', 'specific', 'original', 'instant', 'streaming_parse'] | None = "delta",
|
|
286
|
+
content: Literal['all', 'delta', 'specific', 'original', 'instant', 'streaming_parse'] | None = "delta",
|
|
287
|
+
*,
|
|
288
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
287
289
|
) -> AsyncGenerator:
|
|
288
290
|
await self._ensure_consumer()
|
|
289
291
|
parsed_generator = cast(GeneratorConsumer, self._response_consumer).get_async_generator()
|
|
@@ -300,11 +302,13 @@ class AgentlyResponseParser(ResponseParser):
|
|
|
300
302
|
case "delta":
|
|
301
303
|
if event == "delta":
|
|
302
304
|
yield data
|
|
303
|
-
case "
|
|
304
|
-
if
|
|
305
|
-
|
|
306
|
-
elif
|
|
307
|
-
|
|
305
|
+
case "specific":
|
|
306
|
+
if specific is None:
|
|
307
|
+
specific = ["delta"]
|
|
308
|
+
elif isinstance(specific, str):
|
|
309
|
+
specific = [specific]
|
|
310
|
+
if event in specific:
|
|
311
|
+
yield event, data
|
|
308
312
|
case "instant" | "streaming_parse":
|
|
309
313
|
if self._streaming_json_parser is not None:
|
|
310
314
|
streaming_parsed = None
|
|
@@ -325,8 +329,10 @@ class AgentlyResponseParser(ResponseParser):
|
|
|
325
329
|
|
|
326
330
|
def get_generator(
|
|
327
331
|
self,
|
|
328
|
-
type: Literal['all', 'delta', '
|
|
329
|
-
content: Literal['all', 'delta', '
|
|
332
|
+
type: Literal['all', 'delta', 'specific', 'original', 'instant', 'streaming_parse'] | None = "delta",
|
|
333
|
+
content: Literal['all', 'delta', 'specific', 'original', 'instant', 'streaming_parse'] | None = "delta",
|
|
334
|
+
*,
|
|
335
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
330
336
|
) -> Generator:
|
|
331
337
|
asyncio.run(self._ensure_consumer())
|
|
332
338
|
parsed_generator = cast(GeneratorConsumer, self._response_consumer).get_generator()
|
|
@@ -343,11 +349,13 @@ class AgentlyResponseParser(ResponseParser):
|
|
|
343
349
|
case "delta":
|
|
344
350
|
if event == "delta":
|
|
345
351
|
yield data
|
|
346
|
-
case "
|
|
347
|
-
if
|
|
348
|
-
|
|
349
|
-
elif
|
|
350
|
-
|
|
352
|
+
case "specific":
|
|
353
|
+
if specific is None:
|
|
354
|
+
specific = ["delta"]
|
|
355
|
+
elif isinstance(specific, str):
|
|
356
|
+
specific = [specific]
|
|
357
|
+
if event in specific:
|
|
358
|
+
yield event, data
|
|
351
359
|
case "instant" | "streaming_parse":
|
|
352
360
|
if self._streaming_json_parser is not None:
|
|
353
361
|
streaming_parsed = None
|
agently/core/Agent.py
CHANGED
|
@@ -66,6 +66,8 @@ class BaseAgent:
|
|
|
66
66
|
self.request_prompt = self.request.prompt
|
|
67
67
|
self.prompt = self.request_prompt
|
|
68
68
|
|
|
69
|
+
self.set_settings = self.settings.set_settings
|
|
70
|
+
|
|
69
71
|
self.get_response = self.request.get_response
|
|
70
72
|
self.get_result = self.request.get_result
|
|
71
73
|
self.get_meta = self.request.get_meta
|
|
@@ -83,10 +85,6 @@ class BaseAgent:
|
|
|
83
85
|
self.async_start = self.async_get_data
|
|
84
86
|
|
|
85
87
|
# Basic Methods
|
|
86
|
-
def set_settings(self, key: str, value: "SerializableValue"):
|
|
87
|
-
self.settings.set_settings(key, value)
|
|
88
|
-
return self
|
|
89
|
-
|
|
90
88
|
def set_agent_prompt(
|
|
91
89
|
self,
|
|
92
90
|
key: "PromptStandardSlot | str",
|
agently/core/ModelRequest.py
CHANGED
|
@@ -425,14 +425,13 @@ class ModelRequest:
|
|
|
425
425
|
parent=parent_extension_handlers,
|
|
426
426
|
)
|
|
427
427
|
|
|
428
|
+
self.set_settings = self.settings.set_settings
|
|
429
|
+
|
|
428
430
|
self.get_meta = FunctionShifter.syncify(self.async_get_meta)
|
|
429
431
|
self.get_text = FunctionShifter.syncify(self.async_get_text)
|
|
430
432
|
self.get_data = FunctionShifter.syncify(self.async_get_data)
|
|
431
433
|
self.get_data_object = FunctionShifter.syncify(self.async_get_data_object)
|
|
432
434
|
|
|
433
|
-
def set_settings(self, key: str, value: "SerializableValue"):
|
|
434
|
-
self.settings.set_settings(key, value)
|
|
435
|
-
|
|
436
435
|
def set_prompt(
|
|
437
436
|
self,
|
|
438
437
|
key: "PromptStandardSlot | str",
|
|
@@ -590,58 +589,84 @@ class ModelRequest:
|
|
|
590
589
|
def get_generator(
|
|
591
590
|
self,
|
|
592
591
|
type: Literal["instant", "streaming_parse"],
|
|
592
|
+
*,
|
|
593
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
593
594
|
) -> Generator["StreamingData", None, None]: ...
|
|
594
595
|
|
|
595
596
|
@overload
|
|
596
597
|
def get_generator(
|
|
597
598
|
self,
|
|
598
599
|
type: Literal["all"],
|
|
600
|
+
*,
|
|
601
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
599
602
|
) -> Generator[tuple[str, Any], None, None]: ...
|
|
600
603
|
|
|
601
604
|
@overload
|
|
602
605
|
def get_generator(
|
|
603
606
|
self,
|
|
604
|
-
type: Literal["delta", "
|
|
607
|
+
type: Literal["delta", "specific", "original"],
|
|
608
|
+
*,
|
|
609
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
605
610
|
) -> Generator[str, None, None]: ...
|
|
606
611
|
|
|
607
612
|
@overload
|
|
608
613
|
def get_generator(
|
|
609
614
|
self,
|
|
610
|
-
type: Literal["all", "original", "delta", "
|
|
615
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
616
|
+
*,
|
|
617
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
611
618
|
) -> Generator: ...
|
|
612
619
|
|
|
613
620
|
def get_generator(
|
|
614
621
|
self,
|
|
615
|
-
type: Literal["all", "original", "delta", "
|
|
622
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
623
|
+
*,
|
|
624
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
616
625
|
) -> Generator:
|
|
617
|
-
return self.get_response().get_generator(
|
|
626
|
+
return self.get_response().get_generator(
|
|
627
|
+
type=type,
|
|
628
|
+
specific=specific,
|
|
629
|
+
)
|
|
618
630
|
|
|
619
631
|
@overload
|
|
620
632
|
def get_async_generator(
|
|
621
633
|
self,
|
|
622
634
|
type: Literal["instant", "streaming_parse"],
|
|
635
|
+
*,
|
|
636
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
623
637
|
) -> AsyncGenerator["StreamingData", None]: ...
|
|
624
638
|
|
|
625
639
|
@overload
|
|
626
640
|
def get_async_generator(
|
|
627
641
|
self,
|
|
628
642
|
type: Literal["all"],
|
|
643
|
+
*,
|
|
644
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
629
645
|
) -> AsyncGenerator[tuple[str, Any], None]: ...
|
|
630
646
|
|
|
631
647
|
@overload
|
|
632
648
|
def get_async_generator(
|
|
633
649
|
self,
|
|
634
|
-
type: Literal["delta", "
|
|
650
|
+
type: Literal["delta", "specific", "original"],
|
|
651
|
+
*,
|
|
652
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
635
653
|
) -> AsyncGenerator[str, None]: ...
|
|
636
654
|
|
|
637
655
|
@overload
|
|
638
656
|
def get_async_generator(
|
|
639
657
|
self,
|
|
640
|
-
type: Literal["all", "original", "delta", "
|
|
658
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
659
|
+
*,
|
|
660
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
641
661
|
) -> AsyncGenerator: ...
|
|
642
662
|
|
|
643
663
|
def get_async_generator(
|
|
644
664
|
self,
|
|
645
|
-
type: Literal["all", "original", "delta", "
|
|
665
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
666
|
+
*,
|
|
667
|
+
specific: list[str] | str | None = ["reasoning_delta", "delta", "reasoning_done", "done", "tool_calls"],
|
|
646
668
|
) -> AsyncGenerator:
|
|
647
|
-
return self.get_response().get_async_generator(
|
|
669
|
+
return self.get_response().get_async_generator(
|
|
670
|
+
type=type,
|
|
671
|
+
specific=specific,
|
|
672
|
+
)
|
agently/core/PluginManager.py
CHANGED
agently/core/Prompt.py
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
16
|
from textwrap import dedent
|
|
17
|
-
from typing import Any, Literal,
|
|
17
|
+
from typing import Any, Literal, TYPE_CHECKING, cast, overload, TypeVar
|
|
18
18
|
|
|
19
|
-
from agently.utils import RuntimeData, Settings
|
|
19
|
+
from agently.utils import RuntimeData, Settings, DataFormatter
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from agently.types.data.prompt import ChatMessage, PromptStandardSlot
|
|
@@ -80,8 +80,6 @@ class Prompt(RuntimeData):
|
|
|
80
80
|
):
|
|
81
81
|
super().__init__(prompt_dict, parent=parent_prompt, name=name)
|
|
82
82
|
|
|
83
|
-
self._placeholder_pattern = re.compile(r"\$\{\s*([^}]+?)\s*\}")
|
|
84
|
-
|
|
85
83
|
self.settings = Settings(
|
|
86
84
|
name="Prompt-Settings",
|
|
87
85
|
parent=parent_settings,
|
|
@@ -103,42 +101,6 @@ class Prompt(RuntimeData):
|
|
|
103
101
|
self.to_json_prompt = self.prompt_generator.to_json_prompt
|
|
104
102
|
self.to_yaml_prompt = self.prompt_generator.to_yaml_prompt
|
|
105
103
|
|
|
106
|
-
def _substitute_placeholder(self, obj: T, variable_mappings: dict[str, Any]) -> T | Any:
|
|
107
|
-
if not isinstance(variable_mappings, dict):
|
|
108
|
-
raise TypeError(f"Variable mappings require a dictionary but got: { variable_mappings }")
|
|
109
|
-
|
|
110
|
-
if isinstance(obj, str):
|
|
111
|
-
full_match = self._placeholder_pattern.fullmatch(obj)
|
|
112
|
-
if full_match:
|
|
113
|
-
key = full_match.group(1).strip()
|
|
114
|
-
return variable_mappings.get(key, obj)
|
|
115
|
-
else:
|
|
116
|
-
|
|
117
|
-
def replacer(match):
|
|
118
|
-
key = match.group(1).strip()
|
|
119
|
-
return str(variable_mappings.get(key, match.group(0)))
|
|
120
|
-
|
|
121
|
-
return self._placeholder_pattern.sub(replacer, obj)
|
|
122
|
-
|
|
123
|
-
if isinstance(obj, Mapping):
|
|
124
|
-
return {
|
|
125
|
-
self._substitute_placeholder(key, variable_mappings): self._substitute_placeholder(
|
|
126
|
-
value, variable_mappings
|
|
127
|
-
)
|
|
128
|
-
for key, value in obj.items()
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if isinstance(obj, Sequence) and not isinstance(obj, (str, bytes, bytearray)):
|
|
132
|
-
if isinstance(obj, tuple):
|
|
133
|
-
return tuple(self._substitute_placeholder(value, variable_mappings) for value in obj)
|
|
134
|
-
else:
|
|
135
|
-
return [self._substitute_placeholder(value, variable_mappings) for value in obj]
|
|
136
|
-
|
|
137
|
-
if isinstance(obj, set):
|
|
138
|
-
return {self._substitute_placeholder(value, variable_mappings) for value in obj}
|
|
139
|
-
|
|
140
|
-
return obj
|
|
141
|
-
|
|
142
104
|
@overload
|
|
143
105
|
def set(
|
|
144
106
|
self,
|
|
@@ -165,8 +127,8 @@ class Prompt(RuntimeData):
|
|
|
165
127
|
value = dedent(value.strip())
|
|
166
128
|
if mappings is not None:
|
|
167
129
|
super().set(
|
|
168
|
-
|
|
169
|
-
|
|
130
|
+
DataFormatter.substitute_placeholder(key, mappings),
|
|
131
|
+
DataFormatter.substitute_placeholder(value, mappings),
|
|
170
132
|
)
|
|
171
133
|
else:
|
|
172
134
|
super().set(key, value)
|
|
@@ -178,7 +140,7 @@ class Prompt(RuntimeData):
|
|
|
178
140
|
):
|
|
179
141
|
if mappings is not None:
|
|
180
142
|
super().update(
|
|
181
|
-
|
|
143
|
+
DataFormatter.substitute_placeholder(new, mappings),
|
|
182
144
|
)
|
|
183
145
|
else:
|
|
184
146
|
super().update(new)
|
|
@@ -193,8 +155,8 @@ class Prompt(RuntimeData):
|
|
|
193
155
|
value = dedent(value.strip())
|
|
194
156
|
if mappings is not None:
|
|
195
157
|
super().append(
|
|
196
|
-
|
|
197
|
-
|
|
158
|
+
DataFormatter.substitute_placeholder(key, mappings),
|
|
159
|
+
DataFormatter.substitute_placeholder(value, mappings),
|
|
198
160
|
)
|
|
199
161
|
else:
|
|
200
162
|
super().append(key, value)
|
|
@@ -30,9 +30,10 @@ 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)
|
|
@@ -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
|
|
|
@@ -17,7 +17,7 @@ import uuid
|
|
|
17
17
|
from asyncio import Event
|
|
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(
|
|
@@ -225,9 +233,10 @@ class TriggerFlowBaseProcess:
|
|
|
225
233
|
elif isinstance(chunk, tuple):
|
|
226
234
|
chunk_name = chunk[0]
|
|
227
235
|
chunk_func = chunk[1]
|
|
228
|
-
chunk =
|
|
236
|
+
chunk = self._flow_chunk(chunk_name)(chunk_func)
|
|
229
237
|
else:
|
|
230
|
-
chunk =
|
|
238
|
+
chunk = self._flow_chunk(name or chunk.__name__)(chunk) if callable(chunk) else chunk
|
|
239
|
+
assert isinstance(chunk, TriggerFlowChunk)
|
|
231
240
|
self._blue_print.add_handler(
|
|
232
241
|
self.trigger_type,
|
|
233
242
|
self.trigger_event,
|
|
@@ -280,9 +289,9 @@ class TriggerFlowBaseProcess:
|
|
|
280
289
|
if isinstance(chunk, tuple):
|
|
281
290
|
chunk_name = chunk[0]
|
|
282
291
|
chunk_func = chunk[1]
|
|
283
|
-
chunk =
|
|
292
|
+
chunk = self._flow_chunk(chunk_name)(chunk_func)
|
|
284
293
|
else:
|
|
285
|
-
chunk =
|
|
294
|
+
chunk = self._flow_chunk(chunk.__name__)(chunk) if callable(chunk) else chunk
|
|
286
295
|
triggers_to_wait[chunk.trigger] = False
|
|
287
296
|
trigger_to_chunk_name[chunk.trigger] = chunk.name
|
|
288
297
|
results[chunk.name] = None
|
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")
|
agently/types/data/response.py
CHANGED
|
@@ -24,7 +24,16 @@ if TYPE_CHECKING:
|
|
|
24
24
|
from agently.types.data.serializable import SerializableValue
|
|
25
25
|
|
|
26
26
|
AgentlyModelResponseEvent = Literal[
|
|
27
|
-
"error",
|
|
27
|
+
"error",
|
|
28
|
+
"original_delta",
|
|
29
|
+
"reasoning_delta",
|
|
30
|
+
"delta",
|
|
31
|
+
"tool_calls",
|
|
32
|
+
"original_done",
|
|
33
|
+
"reasoning_done",
|
|
34
|
+
"done",
|
|
35
|
+
"meta",
|
|
36
|
+
"extra",
|
|
28
37
|
]
|
|
29
38
|
|
|
30
39
|
AgentlyModelResponseMessage: TypeAlias = tuple[AgentlyModelResponseEvent, Any]
|
|
@@ -77,29 +77,39 @@ class ResponseParser(AgentlyPlugin, Protocol):
|
|
|
77
77
|
def get_async_generator(
|
|
78
78
|
self,
|
|
79
79
|
type: Literal["instant", "streaming_parse"],
|
|
80
|
+
*,
|
|
81
|
+
specific: list[str] | str | None = None,
|
|
80
82
|
) -> AsyncGenerator["StreamingData", None]: ...
|
|
81
83
|
|
|
82
84
|
@overload
|
|
83
85
|
def get_async_generator(
|
|
84
86
|
self,
|
|
85
87
|
type: Literal["all"],
|
|
88
|
+
*,
|
|
89
|
+
specific: list[str] | str | None = None,
|
|
86
90
|
) -> AsyncGenerator[tuple[str, Any], None]: ...
|
|
87
91
|
|
|
88
92
|
@overload
|
|
89
93
|
def get_async_generator(
|
|
90
94
|
self,
|
|
91
|
-
type: Literal["delta", "
|
|
95
|
+
type: Literal["delta", "specific", "original"],
|
|
96
|
+
*,
|
|
97
|
+
specific: list[str] | str | None = None,
|
|
92
98
|
) -> AsyncGenerator[str, None]: ...
|
|
93
99
|
|
|
94
100
|
@overload
|
|
95
101
|
def get_async_generator(
|
|
96
102
|
self,
|
|
97
|
-
type: Literal["all", "original", "delta", "
|
|
103
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
104
|
+
*,
|
|
105
|
+
specific: list[str] | str | None = None,
|
|
98
106
|
) -> AsyncGenerator: ...
|
|
99
107
|
|
|
100
108
|
def get_async_generator(
|
|
101
109
|
self,
|
|
102
|
-
type: Literal["all", "original", "delta", "
|
|
110
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
111
|
+
*,
|
|
112
|
+
specific: list[str] | str | None = None,
|
|
103
113
|
) -> AsyncGenerator:
|
|
104
114
|
"""
|
|
105
115
|
'instant' is Agently v3 compatible for 'streaming_parse'
|
|
@@ -110,29 +120,39 @@ class ResponseParser(AgentlyPlugin, Protocol):
|
|
|
110
120
|
def get_generator(
|
|
111
121
|
self,
|
|
112
122
|
type: Literal["instant", "streaming_parse"],
|
|
123
|
+
*,
|
|
124
|
+
specific: list[str] | str | None = None,
|
|
113
125
|
) -> Generator["StreamingData", None, None]: ...
|
|
114
126
|
|
|
115
127
|
@overload
|
|
116
128
|
def get_generator(
|
|
117
129
|
self,
|
|
118
130
|
type: Literal["all"],
|
|
131
|
+
*,
|
|
132
|
+
specific: list[str] | str | None = None,
|
|
119
133
|
) -> Generator[tuple[str, Any], None, None]: ...
|
|
120
134
|
|
|
121
135
|
@overload
|
|
122
136
|
def get_generator(
|
|
123
137
|
self,
|
|
124
|
-
type: Literal["delta", "
|
|
138
|
+
type: Literal["delta", "specific", "original"],
|
|
139
|
+
*,
|
|
140
|
+
specific: list[str] | str | None = None,
|
|
125
141
|
) -> Generator[str, None, None]: ...
|
|
126
142
|
|
|
127
143
|
@overload
|
|
128
144
|
def get_generator(
|
|
129
145
|
self,
|
|
130
|
-
type: Literal["all", "original", "delta", "
|
|
146
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
147
|
+
*,
|
|
148
|
+
specific: list[str] | str | None = None,
|
|
131
149
|
) -> Generator: ...
|
|
132
150
|
|
|
133
151
|
def get_generator(
|
|
134
152
|
self,
|
|
135
|
-
type: Literal["all", "original", "delta", "
|
|
153
|
+
type: Literal["all", "original", "delta", "specific", "instant", "streaming_parse"] | None = "delta",
|
|
154
|
+
*,
|
|
155
|
+
specific: list[str] | str | None = None,
|
|
136
156
|
) -> Generator:
|
|
137
157
|
"""
|
|
138
158
|
'instant' is Agently v3 compatible for 'streaming_parse'
|
agently/utils/DataFormatter.py
CHANGED
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import re
|
|
15
16
|
import datetime
|
|
16
17
|
import warnings
|
|
17
18
|
from typing import (
|
|
18
19
|
Any,
|
|
19
20
|
Literal,
|
|
20
21
|
Mapping,
|
|
22
|
+
Sequence,
|
|
21
23
|
Union,
|
|
22
24
|
get_origin,
|
|
23
25
|
get_args,
|
|
@@ -29,9 +31,12 @@ from pydantic import BaseModel
|
|
|
29
31
|
|
|
30
32
|
if TYPE_CHECKING:
|
|
31
33
|
from agently.types.data import SerializableValue, KwargsType
|
|
34
|
+
from re import Pattern
|
|
32
35
|
|
|
33
36
|
T = TypeVar("T")
|
|
34
37
|
|
|
38
|
+
DEFAULT_PLACEHOLDER_PATTERN = re.compile(r"\$\{\s*([^}]+?)\s*\}")
|
|
39
|
+
|
|
35
40
|
|
|
36
41
|
class DataFormatter:
|
|
37
42
|
@staticmethod
|
|
@@ -246,3 +251,75 @@ class DataFormatter:
|
|
|
246
251
|
return kwargs_format or None
|
|
247
252
|
|
|
248
253
|
return None
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def substitute_placeholder(
|
|
257
|
+
obj: T,
|
|
258
|
+
variable_mappings: dict[str, Any],
|
|
259
|
+
*,
|
|
260
|
+
placeholder_pattern: "Pattern | None" = None,
|
|
261
|
+
) -> T | Any:
|
|
262
|
+
if placeholder_pattern is None:
|
|
263
|
+
placeholder_pattern = DEFAULT_PLACEHOLDER_PATTERN
|
|
264
|
+
|
|
265
|
+
if not isinstance(variable_mappings, dict):
|
|
266
|
+
raise TypeError(f"Variable mappings require a dictionary but got: { variable_mappings }")
|
|
267
|
+
|
|
268
|
+
if isinstance(obj, str):
|
|
269
|
+
full_match = placeholder_pattern.fullmatch(obj)
|
|
270
|
+
if full_match:
|
|
271
|
+
key = full_match.group(1).strip()
|
|
272
|
+
return variable_mappings.get(key, obj)
|
|
273
|
+
else:
|
|
274
|
+
|
|
275
|
+
def replacer(match):
|
|
276
|
+
key = match.group(1).strip()
|
|
277
|
+
return str(variable_mappings.get(key, match.group(0)))
|
|
278
|
+
|
|
279
|
+
return placeholder_pattern.sub(replacer, obj)
|
|
280
|
+
|
|
281
|
+
if isinstance(obj, Mapping):
|
|
282
|
+
return {
|
|
283
|
+
DataFormatter.substitute_placeholder(
|
|
284
|
+
key,
|
|
285
|
+
variable_mappings,
|
|
286
|
+
placeholder_pattern=placeholder_pattern,
|
|
287
|
+
): DataFormatter.substitute_placeholder(
|
|
288
|
+
value,
|
|
289
|
+
variable_mappings,
|
|
290
|
+
placeholder_pattern=placeholder_pattern,
|
|
291
|
+
)
|
|
292
|
+
for key, value in obj.items()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if isinstance(obj, Sequence) and not isinstance(obj, (str, bytes, bytearray)):
|
|
296
|
+
if isinstance(obj, tuple):
|
|
297
|
+
return tuple(
|
|
298
|
+
DataFormatter.substitute_placeholder(
|
|
299
|
+
value,
|
|
300
|
+
variable_mappings,
|
|
301
|
+
placeholder_pattern=placeholder_pattern,
|
|
302
|
+
)
|
|
303
|
+
for value in obj
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
return [
|
|
307
|
+
DataFormatter.substitute_placeholder(
|
|
308
|
+
value,
|
|
309
|
+
variable_mappings,
|
|
310
|
+
placeholder_pattern=placeholder_pattern,
|
|
311
|
+
)
|
|
312
|
+
for value in obj
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
if isinstance(obj, set):
|
|
316
|
+
return {
|
|
317
|
+
DataFormatter.substitute_placeholder(
|
|
318
|
+
value,
|
|
319
|
+
variable_mappings,
|
|
320
|
+
placeholder_pattern=placeholder_pattern,
|
|
321
|
+
)
|
|
322
|
+
for value in obj
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return obj
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
|
|
16
|
+
from types import MappingProxyType
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
SAFE_BUILTINS = {
|
|
20
|
+
"abs": abs,
|
|
21
|
+
"min": min,
|
|
22
|
+
"max": max,
|
|
23
|
+
"sum": sum,
|
|
24
|
+
"len": len,
|
|
25
|
+
"range": range,
|
|
26
|
+
"enumerate": enumerate,
|
|
27
|
+
"list": list,
|
|
28
|
+
"dict": dict,
|
|
29
|
+
"set": set,
|
|
30
|
+
"tuple": tuple,
|
|
31
|
+
"print": print,
|
|
32
|
+
"sorted": sorted,
|
|
33
|
+
"str": str,
|
|
34
|
+
"int": int,
|
|
35
|
+
"float": float,
|
|
36
|
+
"list": list,
|
|
37
|
+
"dict": dict,
|
|
38
|
+
"bool": bool,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
SAFE_TYPES = [int, float, str, list, dict, set, tuple, bool, type(None)]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PythonSandbox:
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
preset_objects: dict[str, object] | None = None,
|
|
48
|
+
base_vars: dict[str, Any] | None = None,
|
|
49
|
+
allowed_return_types: list[type] = SAFE_TYPES,
|
|
50
|
+
):
|
|
51
|
+
self.preset_objects = preset_objects or {}
|
|
52
|
+
self.base_vars = base_vars or {}
|
|
53
|
+
self.allowed_return_types = allowed_return_types
|
|
54
|
+
|
|
55
|
+
self.safe_globals = MappingProxyType(
|
|
56
|
+
{
|
|
57
|
+
"__builtins__": SAFE_BUILTINS,
|
|
58
|
+
**self.preset_objects,
|
|
59
|
+
**self.base_vars,
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def _check_safe_value(self, value):
|
|
64
|
+
if isinstance(value, tuple(self.allowed_return_types)):
|
|
65
|
+
return value
|
|
66
|
+
raise ValueError(f"Type of return '{ type(value) }' can not be used in Python Sandbox.")
|
|
67
|
+
|
|
68
|
+
def _wrap_obj(self, obj):
|
|
69
|
+
sandbox = self
|
|
70
|
+
|
|
71
|
+
class ObjectWrapper:
|
|
72
|
+
def __init__(self, obj):
|
|
73
|
+
self._obj = obj
|
|
74
|
+
|
|
75
|
+
def __getattr__(self, name):
|
|
76
|
+
if name.startswith("_"):
|
|
77
|
+
raise AttributeError(f"Can not access private attribute '{name}'.")
|
|
78
|
+
attr = getattr(self._obj, name)
|
|
79
|
+
if callable(attr):
|
|
80
|
+
|
|
81
|
+
def method(*args, **kwargs):
|
|
82
|
+
result = attr(*args, **kwargs)
|
|
83
|
+
return sandbox._check_safe_value(result)
|
|
84
|
+
|
|
85
|
+
return method
|
|
86
|
+
return sandbox._check_safe_value(attr)
|
|
87
|
+
|
|
88
|
+
self.allowed_return_types.append(ObjectWrapper)
|
|
89
|
+
|
|
90
|
+
return ObjectWrapper(obj)
|
|
91
|
+
|
|
92
|
+
def run(self, code: str):
|
|
93
|
+
safe_objects = {k: self._wrap_obj(v) for k, v in self.preset_objects.items()}
|
|
94
|
+
globals_dict = dict(self.safe_globals)
|
|
95
|
+
globals_dict.update(safe_objects)
|
|
96
|
+
|
|
97
|
+
local_vars = {}
|
|
98
|
+
exec(code, globals_dict, local_vars)
|
|
99
|
+
for k, v in local_vars.items():
|
|
100
|
+
self._check_safe_value(v)
|
|
101
|
+
return local_vars
|
agently/utils/Settings.py
CHANGED
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import re
|
|
15
16
|
import json
|
|
16
17
|
import yaml
|
|
17
18
|
import toml
|
|
18
19
|
from typing import TYPE_CHECKING, Literal, cast
|
|
19
|
-
from
|
|
20
|
+
from .SerializableRuntimeData import SerializableRuntimeData, SerializableRuntimeDataNamespace
|
|
21
|
+
from .LazyImport import LazyImport
|
|
22
|
+
from .DataFormatter import DataFormatter
|
|
20
23
|
|
|
21
24
|
if TYPE_CHECKING:
|
|
22
25
|
from agently.types.data import SerializableData, SerializableValue
|
|
@@ -114,7 +117,21 @@ class Settings(SerializableRuntimeData):
|
|
|
114
117
|
else:
|
|
115
118
|
raise TypeError(f"[Agently Settings] Cannot load parsed data, expect dictionary type, got: { type(data) }")
|
|
116
119
|
|
|
117
|
-
def set_settings(self, key: str, value: "SerializableValue"):
|
|
120
|
+
def set_settings(self, key: str, value: "SerializableValue", *, auto_load_env: bool = False):
|
|
121
|
+
if auto_load_env:
|
|
122
|
+
import os
|
|
123
|
+
|
|
124
|
+
LazyImport.import_package("dotenv")
|
|
125
|
+
from dotenv import load_dotenv, find_dotenv
|
|
126
|
+
|
|
127
|
+
load_dotenv(find_dotenv())
|
|
128
|
+
|
|
129
|
+
environ = dict(os.environ)
|
|
130
|
+
value = DataFormatter.substitute_placeholder(
|
|
131
|
+
value,
|
|
132
|
+
environ,
|
|
133
|
+
placeholder_pattern=re.compile(r"\$\{\s*ENV\.([^}]+?)\s*\}"),
|
|
134
|
+
)
|
|
118
135
|
if key in self._path_mappings:
|
|
119
136
|
self.update({str(self._path_mappings[key]): value})
|
|
120
137
|
return self
|
agently/utils/__init__.py
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
1
|
agently/__init__.py,sha256=Gf0LL7Czqeuf6hfvHfEGlACLg0d0osQupyMATB0EBlc,884
|
|
2
2
|
agently/_default_init.py,sha256=AhYwzZYOxqDeIoVb8cvPjJ2BjE5V7wxeuH7R-MNZWyg,2057
|
|
3
3
|
agently/_default_settings.yaml,sha256=6woqJ2tjg_jl6kwSOwmTMETfVraHidort2smf0Is_qQ,1357
|
|
4
|
-
agently/base.py,sha256=
|
|
4
|
+
agently/base.py,sha256=U92SWzoU7h7Gl92r8bEv-7wsGUPrUlxXyDohLX-2qCo,4629
|
|
5
5
|
agently/builtins/agent_extensions/AutoFuncExtension.py,sha256=TmwMazwPzb5WXfDqfedY5yZOOMTFIHqaB9Bte29adUc,2433
|
|
6
6
|
agently/builtins/agent_extensions/ChatSessionExtension.py,sha256=Y6mvnsfAY0rykKtfp-tApwJy5O4SS-YEt2-jaWr83uc,12034
|
|
7
|
-
agently/builtins/agent_extensions/ConfigurePromptExtension.py,sha256=
|
|
7
|
+
agently/builtins/agent_extensions/ConfigurePromptExtension.py,sha256=9wy2zHIDVHbUlj5sI0A03SscUWSzZNc9hNJSEdXFXd0,11390
|
|
8
8
|
agently/builtins/agent_extensions/KeyWaiterExtension.py,sha256=Rf8dB8Yt3_9IJifpiE-Rn6lLIXqZjaNp94lnX6Betgw,5555
|
|
9
9
|
agently/builtins/agent_extensions/ToolExtension.py,sha256=S3jjumHiauEQ-m46Zkh-1I9ih02kKoj8sBEU82woz1E,6886
|
|
10
10
|
agently/builtins/agent_extensions/__init__.py,sha256=IxWRQogF8PCVNXeY7D4qhGukEx3JFvfLlUW2x0FbyfA,852
|
|
11
11
|
agently/builtins/hookers/ConsoleHooker.py,sha256=aJdDj_nG8CiwyelA505zvtpzBSwD52nFIkBRDJGgq3Y,8099
|
|
12
12
|
agently/builtins/hookers/PureLoggerHooker.py,sha256=fzN0OfhQzgns4KeCNH-qcdm-BdQT0W2kqEmt3Zp2pYI,1906
|
|
13
13
|
agently/builtins/hookers/SystemMessageHooker.py,sha256=1nh1FY70PYyZOAQGfQiGnwIvo4ZF3NSAjeghI3sInn4,7207
|
|
14
|
-
agently/builtins/plugins/ModelRequester/OpenAICompatible.py,sha256=
|
|
14
|
+
agently/builtins/plugins/ModelRequester/OpenAICompatible.py,sha256=CFTMZHENOi6qsrUfWtgq19P6Ec7H-8vRybVVr4RZfJQ,26773
|
|
15
15
|
agently/builtins/plugins/PromptGenerator/AgentlyPromptGenerator.py,sha256=GRRR9uxgyCXHKgt_r2BinWnhKy8rYUN8CG9ld2P8n4E,30841
|
|
16
|
-
agently/builtins/plugins/ResponseParser/AgentlyResponseParser.py,sha256=
|
|
16
|
+
agently/builtins/plugins/ResponseParser/AgentlyResponseParser.py,sha256=5iF6NLoMjTtEC3hc-DmatFmSWZK2nKkoiyqHK7Q3Yj4,17847
|
|
17
17
|
agently/builtins/plugins/ToolManager/AgentlyToolManager.py,sha256=oaqte5LAryZQMD6vuEbKhe6kOLUyZTRZswC1MDFiYxw,9138
|
|
18
18
|
agently/builtins/plugins/__init__.py,sha256=wj4_U9TTekc2CmjppbXKUREDFRXFX1y0ySOW-CxQuok,801
|
|
19
19
|
agently/builtins/tools/Browse.py,sha256=gIePs-gtsqOI_ZTReGqEcoKvhs4FkBzTxow--QS5_ek,3469
|
|
20
20
|
agently/builtins/tools/Search.py,sha256=tUynNiW_ZMAGaB2ua3HRcY_trIbLEoASFE-p2QMQ0Zg,7362
|
|
21
21
|
agently/builtins/tools/__init__.py,sha256=pFOWgH2C3xRvgQo3UVdkj4yHjF9nNtmoVHmOZfoGsyU,647
|
|
22
|
-
agently/core/Agent.py,sha256=
|
|
22
|
+
agently/core/Agent.py,sha256=LWkzWG_XXrC4oVkWg4ebnAqAJ81O9UEo1n-qJj7IIrc,10251
|
|
23
23
|
agently/core/EventCenter.py,sha256=sknU5w9MpGDQgMOF9c5k4PfM4SNT5X_LrpYte2HaFNM,10861
|
|
24
24
|
agently/core/ExtensionHandlers.py,sha256=88iSAW50bgMshB56cTgKg30eOjZQyXiJY1en4w7afWY,2076
|
|
25
|
-
agently/core/ModelRequest.py,sha256=
|
|
26
|
-
agently/core/PluginManager.py,sha256=
|
|
27
|
-
agently/core/Prompt.py,sha256=
|
|
25
|
+
agently/core/ModelRequest.py,sha256=KNNjO8BiV1qS9jcFcp0V7ACXUC4CcZRdmcXjPUNqHbw,24443
|
|
26
|
+
agently/core/PluginManager.py,sha256=fwRxvqPMgXYIrclhRHtkaPsyvn6SaeBFqvL7tTzYwck,4410
|
|
27
|
+
agently/core/Prompt.py,sha256=uvGGvbND08b0OhkD-UXY0J65yLedllBt4lMqk4NYl1U,6893
|
|
28
28
|
agently/core/Tool.py,sha256=PNYf_BwVefr8IOqf5asLaVq2fU7hQaFJwJVj3S4fq84,1871
|
|
29
29
|
agently/core/TriggerFlow/BluePrint.py,sha256=Lz-wbA0f79t7VjvX0knH-9AC_Qz9wts0QFemL97R3jo,4810
|
|
30
|
-
agently/core/TriggerFlow/Chunk.py,sha256=
|
|
30
|
+
agently/core/TriggerFlow/Chunk.py,sha256=xPWr_ofpl-iG4jHIJfB5mPanmn70pq7x8GCcz3G8NPc,1583
|
|
31
31
|
agently/core/TriggerFlow/Execution.py,sha256=sXxDt5l9m2ZWMG6JZjnt50akyDFEu4I-YB2yhW-UP5E,11640
|
|
32
32
|
agently/core/TriggerFlow/Process.py,sha256=doIDUa7x0j1TVuOhp-hraC0hHLwpistF-Dksf76NJvQ,837
|
|
33
|
-
agently/core/TriggerFlow/TriggerFlow.py,sha256=
|
|
33
|
+
agently/core/TriggerFlow/TriggerFlow.py,sha256=lu0MSaIfQ7HwELuWjIBQHvkdC0L9d5x7mX-eNEmi3aQ,7628
|
|
34
34
|
agently/core/TriggerFlow/__init__.py,sha256=eHc6ldUIS0kka_1fZXkdaHFnSDoXaGSvXggwVszMAJQ,911
|
|
35
|
-
agently/core/TriggerFlow/process/BaseProcess.py,sha256=
|
|
35
|
+
agently/core/TriggerFlow/process/BaseProcess.py,sha256=3pf2t6-tTKIttAzFbL89RdmzjISKys4N65UGzPnFAIc,14847
|
|
36
36
|
agently/core/TriggerFlow/process/ForEachProcess.py,sha256=rjUVouZENnuGFp7GXGTgWJT7uMEhczHamWr9cFugsh0,4549
|
|
37
37
|
agently/core/TriggerFlow/process/MatchCaseProcess.py,sha256=MKY5Yh66JiMABhCzamRl8UZOBjbD75TFp84Jw6o_t68,7900
|
|
38
38
|
agently/core/TriggerFlow/process/__init__.py,sha256=BP5bAr9LRVVD83KFqXeprgTmXA1iCSOSsD509BtoX_E,753
|
|
39
39
|
agently/core/__init__.py,sha256=CPglSpW5oEEmWpCpdvv9wK4myXmVipjuZm5HtMq6Vxo,1214
|
|
40
|
-
agently/integrations/chromadb.py,sha256=
|
|
40
|
+
agently/integrations/chromadb.py,sha256=iULT9sK2MIZe5pyqMdL9M1yBkWqQhShwEyLmGnKUuew,10390
|
|
41
41
|
agently/types/__init__.py,sha256=xb8GMY-ULncO_PY9rfRUsyi12wAQQJx8gAAnoM30uZA,592
|
|
42
42
|
agently/types/data/__init__.py,sha256=qladqSEqcAUW_XzdTDl4cvaraq_DpANy3aZbIPxoygk,1627
|
|
43
43
|
agently/types/data/event.py,sha256=LFQW7MN_QGOis3XV-8K6jNXWsLvT7tYxo4BZbUBCpfI,1790
|
|
44
44
|
agently/types/data/prompt.py,sha256=DiszVM_3OHe66waf-99mBH7bzRr0cpbCHSpDI-2EjPs,5163
|
|
45
45
|
agently/types/data/request.py,sha256=Do-9g5QxZRMYjaoHCZYwHbj28r-t4noAAtOebw764P4,1924
|
|
46
|
-
agently/types/data/response.py,sha256=
|
|
46
|
+
agently/types/data/response.py,sha256=QYrrZXh_fXsHRltKtd6FKIfmJlgV-stsg6B0AIXueto,3774
|
|
47
47
|
agently/types/data/serializable.py,sha256=v2KlyKNOKp4L6J_Ueupb-gCyrnngvBskFUwNPSJQgnA,844
|
|
48
48
|
agently/types/data/tool.py,sha256=wE8Dda2JtY5cojpHUuQrw7PNeVZ6Zma968bn-pUmS7I,1529
|
|
49
49
|
agently/types/plugins/EventHooker.py,sha256=kb80-baVc3fVlrddW5syv9uSD8a2Mcw8Fd3I2HQhY_Y,1030
|
|
50
50
|
agently/types/plugins/ModelRequester.py,sha256=urG1zFX0b4U6ZKSO50IbW5IHK3ydmRgUom7O7Niqk8s,3875
|
|
51
51
|
agently/types/plugins/PromptGenerator.py,sha256=V8kqT0Eeq09AQqfGA-SZ5mNKeit1UrmqlDQCquSMzUU,4752
|
|
52
|
-
agently/types/plugins/ResponseParser.py,sha256=
|
|
52
|
+
agently/types/plugins/ResponseParser.py,sha256=6dCVWz61gaHOxsX9e5sYFqcWRZ5hBnNXAarT0-9uCUY,4566
|
|
53
53
|
agently/types/plugins/ToolManager.py,sha256=q1Y3G_tzh1AU3s13H-zTDZIkR4W1mjh9E6AKudFOvyg,2421
|
|
54
54
|
agently/types/plugins/__init__.py,sha256=gz_EpgBQGndIQHY5vJB2YRzAN5yIb3FZZG7pC8lB1lM,848
|
|
55
55
|
agently/types/plugins/base.py,sha256=AoNLwsH5IZBQt7_NZfxMWMhAk6PJSOFHR0IYOXp1imI,1167
|
|
56
56
|
agently/types/trigger_flow/__init__.py,sha256=Gj31SmWBC4qtrOqQedyGsnCfeSkUf3XvZNFrJ2QbMNw,777
|
|
57
57
|
agently/types/trigger_flow/trigger_flow.py,sha256=6lvhDwizIV5p3h61l1GsmJU_9Tw8v3u-SnHuygkSJdo,3799
|
|
58
|
-
agently/utils/DataFormatter.py,sha256=
|
|
58
|
+
agently/utils/DataFormatter.py,sha256=qdPtLPIQs9eCZ-hPBphan5CcPrR7Nz8h8cb7zF8F1j0,12050
|
|
59
59
|
agently/utils/DataLocator.py,sha256=ss8OLet9HN0U1PZb-OCHS6KL54kv7vFZph6G0-GBidk,6015
|
|
60
60
|
agently/utils/DataPathBuilder.py,sha256=sEzE1i2EWn7NMkCCXDT50gR6_qMzcZ0y0YGkYbXdB3s,10007
|
|
61
61
|
agently/utils/FunctionShifter.py,sha256=quwugTmf-vzHzRR_2wdv14AxLpr0lwxdUtVoX7Jeq48,5839
|
|
@@ -63,14 +63,15 @@ agently/utils/GeneratorConsumer.py,sha256=EXpz2XGnv6rPdz8bPetJu3LpWIVhMvIi8GLG1B
|
|
|
63
63
|
agently/utils/LazyImport.py,sha256=PfXc2iILXb7WVj6UD45_3qInow6z0cvhFlDqxTK-HfY,9120
|
|
64
64
|
agently/utils/Logger.py,sha256=reIj6a7mNtLYDx3brLKEf0I8LbNkhXmL8Yc-DXnnsCU,2967
|
|
65
65
|
agently/utils/Messenger.py,sha256=dLasJvDt1HxJttt6X9dutwGPvyAtL7yp6BZ3TDxuFDI,729
|
|
66
|
+
agently/utils/PythonSandbox.py,sha256=6HYbd64p8BnTysYQjUvumLf0YzEOq-7uhCuObAG3Q6U,3037
|
|
66
67
|
agently/utils/RuntimeData.py,sha256=SewZ8D1fljuDwfVZTAqZ0XTNEcU2cuAr7QlVqk0vzrE,21925
|
|
67
68
|
agently/utils/SerializableRuntimeData.py,sha256=bVVwin50VnOs30W881ClFepSXAK8GCOUZnVd-SiolRw,3314
|
|
68
|
-
agently/utils/Settings.py,sha256=
|
|
69
|
+
agently/utils/Settings.py,sha256=0vWNhVBKZLRKwuIKoXn-tYNZMajMQHLHdqrGhBA2S3Q,5854
|
|
69
70
|
agently/utils/Storage.py,sha256=E7QyNJ9T0yOUafPgdP90La698hgLMSGjhJ7qCEHzxxw,9438
|
|
70
71
|
agently/utils/StreamingJSONCompleter.py,sha256=aZ9zuGUTQlP-QKbXHUZCf6EtVuG49MKn8xdhw0VhDEA,4292
|
|
71
72
|
agently/utils/StreamingJSONParser.py,sha256=sPPJOtj5OYvsrukRErcoxRl4yuV1zDuf7pQ_pvw_Zow,21116
|
|
72
|
-
agently/utils/__init__.py,sha256=
|
|
73
|
-
agently-4.0.6.
|
|
74
|
-
agently-4.0.6.
|
|
75
|
-
agently-4.0.6.
|
|
76
|
-
agently-4.0.6.
|
|
73
|
+
agently/utils/__init__.py,sha256=Uq3uQdk2_OX_m6gF9wAvs4_scC-tsE4EjNTxN_oDagw,1321
|
|
74
|
+
agently-4.0.6.11.dist-info/METADATA,sha256=goiOxI7yRqvMAMM62e5Af2MsiIt7tgSVHgn1OpDismA,7113
|
|
75
|
+
agently-4.0.6.11.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
76
|
+
agently-4.0.6.11.dist-info/licenses/LICENSE,sha256=Y5ZgAdYgMFigPT8dhN18dTLRtBshOSfWhTDRO1t0Cq4,11360
|
|
77
|
+
agently-4.0.6.11.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|