agently 4.0.7__py3-none-any.whl → 4.0.7.2__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/_default_init.py +4 -0
- agently/_default_settings.yaml +3 -1
- agently/base.py +19 -1
- agently/builtins/agent_extensions/ChatSessionExtension.py +2 -2
- agently/builtins/agent_extensions/SessionExtension.py +294 -0
- agently/builtins/agent_extensions/__init__.py +1 -0
- agently/builtins/plugins/PromptGenerator/AgentlyPromptGenerator.py +57 -17
- agently/builtins/plugins/Session/AgentlyMemoSession.py +652 -0
- agently/builtins/tools/Browse.py +11 -3
- agently/builtins/tools/Cmd.py +112 -0
- agently/builtins/tools/Search.py +28 -2
- agently/builtins/tools/__init__.py +1 -0
- agently/core/Agent.py +7 -7
- agently/core/ModelRequest.py +6 -5
- agently/core/Prompt.py +1 -1
- agently/core/Session.py +85 -0
- agently/core/TriggerFlow/TriggerFlow.py +1 -1
- agently/core/TriggerFlow/process/BaseProcess.py +8 -4
- agently/integrations/chromadb.py +4 -4
- agently/types/data/__init__.py +2 -0
- agently/types/data/prompt.py +6 -1
- agently/types/data/tool.py +9 -0
- agently/types/plugins/BuiltInTool.py +22 -0
- agently/types/plugins/Session.py +159 -0
- agently/types/plugins/__init__.py +21 -0
- agently/types/plugins/base.py +1 -1
- agently/utils/AGENT_UTILS_GUIDE.md +175 -0
- agently/utils/DataFormatter.py +14 -4
- agently/utils/DataLocator.py +108 -31
- agently/utils/FunctionShifter.py +3 -2
- agently/utils/TimeInfo.py +22 -0
- agently/utils/__init__.py +1 -0
- agently-4.0.7.2.dist-info/METADATA +433 -0
- {agently-4.0.7.dist-info → agently-4.0.7.2.dist-info}/RECORD +36 -28
- {agently-4.0.7.dist-info → agently-4.0.7.2.dist-info}/WHEEL +1 -1
- agently-4.0.7.dist-info/METADATA +0 -194
- {agently-4.0.7.dist-info → agently-4.0.7.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
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
|
+
import shlex
|
|
17
|
+
import subprocess
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Iterable, Sequence
|
|
20
|
+
|
|
21
|
+
from agently.types.plugins import BuiltInTool
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Cmd(BuiltInTool):
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
*,
|
|
28
|
+
allowed_cmd_prefixes: Sequence[str] | None = None,
|
|
29
|
+
allowed_workdir_roots: Iterable[str | Path] | None = None,
|
|
30
|
+
timeout: int = 20,
|
|
31
|
+
env: dict[str, str] | None = None,
|
|
32
|
+
):
|
|
33
|
+
self.tool_info_list = [
|
|
34
|
+
{
|
|
35
|
+
"name": "cmd",
|
|
36
|
+
"desc": "Run a shell command with allowlist checks.",
|
|
37
|
+
"kwargs": {
|
|
38
|
+
"cmd": ("str | list[str]", "Command to run"),
|
|
39
|
+
"workdir": ("str | None", "Working directory"),
|
|
40
|
+
"allow_unsafe": ("bool", "Allow command outside allowlist"),
|
|
41
|
+
},
|
|
42
|
+
"func": self.run,
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
self.allowed_cmd_prefixes = set(
|
|
47
|
+
allowed_cmd_prefixes
|
|
48
|
+
if allowed_cmd_prefixes is not None
|
|
49
|
+
else ["ls", "rg", "cat", "pwd", "whoami", "date", "head", "tail"]
|
|
50
|
+
)
|
|
51
|
+
roots = allowed_workdir_roots if allowed_workdir_roots is not None else [Path.cwd()]
|
|
52
|
+
self.allowed_workdir_roots = [Path(root).resolve() for root in roots]
|
|
53
|
+
self.timeout = timeout
|
|
54
|
+
self.env = env
|
|
55
|
+
|
|
56
|
+
def _normalize_cmd(self, cmd: str | Sequence[str]) -> list[str]:
|
|
57
|
+
if isinstance(cmd, str):
|
|
58
|
+
return shlex.split(cmd)
|
|
59
|
+
return list(cmd)
|
|
60
|
+
|
|
61
|
+
def _is_cmd_allowed(self, args: list[str]) -> bool:
|
|
62
|
+
if not args:
|
|
63
|
+
return False
|
|
64
|
+
cmd = args[0]
|
|
65
|
+
base = Path(cmd).name
|
|
66
|
+
return base in self.allowed_cmd_prefixes
|
|
67
|
+
|
|
68
|
+
def _is_workdir_allowed(self, workdir: str | Path | None) -> bool:
|
|
69
|
+
workdir_path = Path(workdir or Path.cwd()).resolve()
|
|
70
|
+
for root in self.allowed_workdir_roots:
|
|
71
|
+
try:
|
|
72
|
+
workdir_path.relative_to(root)
|
|
73
|
+
return True
|
|
74
|
+
except ValueError:
|
|
75
|
+
continue
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
async def run(
|
|
79
|
+
self,
|
|
80
|
+
cmd: str | Sequence[str],
|
|
81
|
+
workdir: str | Path | None = None,
|
|
82
|
+
allow_unsafe: bool = False,
|
|
83
|
+
) -> dict:
|
|
84
|
+
args = self._normalize_cmd(cmd)
|
|
85
|
+
if not self._is_workdir_allowed(workdir):
|
|
86
|
+
return {
|
|
87
|
+
"ok": False,
|
|
88
|
+
"need_approval": True,
|
|
89
|
+
"reason": "workdir_not_allowed",
|
|
90
|
+
"workdir": str(workdir or Path.cwd()),
|
|
91
|
+
}
|
|
92
|
+
if not self._is_cmd_allowed(args) and not allow_unsafe:
|
|
93
|
+
return {
|
|
94
|
+
"ok": False,
|
|
95
|
+
"need_approval": True,
|
|
96
|
+
"reason": "cmd_not_allowed",
|
|
97
|
+
"cmd": args,
|
|
98
|
+
}
|
|
99
|
+
result = subprocess.run(
|
|
100
|
+
args,
|
|
101
|
+
cwd=str(Path(workdir).resolve()) if workdir else None,
|
|
102
|
+
capture_output=True,
|
|
103
|
+
text=True,
|
|
104
|
+
timeout=self.timeout,
|
|
105
|
+
env=self.env,
|
|
106
|
+
)
|
|
107
|
+
return {
|
|
108
|
+
"ok": result.returncode == 0,
|
|
109
|
+
"returncode": result.returncode,
|
|
110
|
+
"stdout": result.stdout,
|
|
111
|
+
"stderr": result.stderr,
|
|
112
|
+
}
|
agently/builtins/tools/Search.py
CHANGED
|
@@ -19,7 +19,6 @@ from agently.utils import LazyImport, FunctionShifter
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class Search:
|
|
22
|
-
|
|
23
22
|
def __init__(
|
|
24
23
|
self,
|
|
25
24
|
*,
|
|
@@ -103,7 +102,34 @@ class Search:
|
|
|
103
102
|
] = "us-en",
|
|
104
103
|
options: dict[str, Any] | None = None,
|
|
105
104
|
):
|
|
106
|
-
|
|
105
|
+
self.tool_info_list = [
|
|
106
|
+
{
|
|
107
|
+
"name": "search",
|
|
108
|
+
"desc": "Search the web with {query}",
|
|
109
|
+
"kwargs": {"query": [("str", "search query")]},
|
|
110
|
+
"func": self.search,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "search_news",
|
|
114
|
+
"desc": "Search news with {query}",
|
|
115
|
+
"kwargs": {"query": [("str", "search query")]},
|
|
116
|
+
"func": self.search_news,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "search_wikipedia",
|
|
120
|
+
"desc": "Search wikipedia with {query}",
|
|
121
|
+
"kwargs": {"query": [("str", "search query")]},
|
|
122
|
+
"func": self.search_wikipedia,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "search_arxiv",
|
|
126
|
+
"desc": "Search arXiv with {query}",
|
|
127
|
+
"kwargs": {"query": [("str", "search query")]},
|
|
128
|
+
"func": self.search_arxiv,
|
|
129
|
+
},
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
LazyImport.import_package("ddgs", version_constraint=">=9.10.0")
|
|
107
133
|
from ddgs import DDGS
|
|
108
134
|
|
|
109
135
|
self.proxy = proxy
|
agently/core/Agent.py
CHANGED
|
@@ -16,7 +16,7 @@ import uuid
|
|
|
16
16
|
import yaml
|
|
17
17
|
import json
|
|
18
18
|
|
|
19
|
-
from typing import Any, TYPE_CHECKING
|
|
19
|
+
from typing import Any, Sequence, TYPE_CHECKING
|
|
20
20
|
|
|
21
21
|
from agently.core import Prompt, ExtensionHandlers, ModelRequest
|
|
22
22
|
from agently.utils import Settings
|
|
@@ -116,15 +116,15 @@ class BaseAgent:
|
|
|
116
116
|
self.agent_prompt.set("chat_history", [])
|
|
117
117
|
return self
|
|
118
118
|
|
|
119
|
-
def set_chat_history(self, chat_history: "
|
|
119
|
+
def set_chat_history(self, chat_history: "Sequence[dict[str, Any] | ChatMessage]"):
|
|
120
120
|
self.reset_chat_history()
|
|
121
|
-
if not isinstance(chat_history,
|
|
121
|
+
if not isinstance(chat_history, Sequence):
|
|
122
122
|
chat_history = [chat_history]
|
|
123
123
|
self.agent_prompt.set("chat_history", chat_history)
|
|
124
124
|
return self
|
|
125
125
|
|
|
126
|
-
def add_chat_history(self, chat_history: "
|
|
127
|
-
if not isinstance(chat_history,
|
|
126
|
+
def add_chat_history(self, chat_history: "Sequence[dict[str, Any] | ChatMessage] | dict[str, Any] | ChatMessage"):
|
|
127
|
+
if not isinstance(chat_history, Sequence):
|
|
128
128
|
chat_history = [chat_history]
|
|
129
129
|
self.agent_prompt.set("chat_history", chat_history)
|
|
130
130
|
return self
|
|
@@ -151,9 +151,9 @@ class BaseAgent:
|
|
|
151
151
|
always: bool = False,
|
|
152
152
|
):
|
|
153
153
|
if always:
|
|
154
|
-
self.agent_prompt.set("
|
|
154
|
+
self.agent_prompt.set("system", prompt, mappings)
|
|
155
155
|
else:
|
|
156
|
-
self.request.prompt.set("
|
|
156
|
+
self.request.prompt.set("system", prompt, mappings)
|
|
157
157
|
return self
|
|
158
158
|
|
|
159
159
|
def rule(
|
agently/core/ModelRequest.py
CHANGED
|
@@ -61,10 +61,6 @@ class ModelResponseResult:
|
|
|
61
61
|
self.async_get_meta = self._response_parser.async_get_meta
|
|
62
62
|
self.get_text = self._response_parser.get_text
|
|
63
63
|
self.async_get_text = self._response_parser.async_get_text
|
|
64
|
-
# self.get_data = self._response_parser.get_data
|
|
65
|
-
# self.async_get_data = self._response_parser.async_get_data
|
|
66
|
-
# self.get_data_object = self._response_parser.get_data_object
|
|
67
|
-
# self.async_get_data_object = self._response_parser.async_get_data_object
|
|
68
64
|
self.get_data = FunctionShifter.syncify(self.async_get_data)
|
|
69
65
|
self.get_data_object = FunctionShifter.syncify(self.async_get_data_object)
|
|
70
66
|
self.get_generator = self._response_parser.get_generator
|
|
@@ -153,6 +149,11 @@ class ModelResponseResult:
|
|
|
153
149
|
return await self._response_parser.async_get_data(type=type)
|
|
154
150
|
return await self._response_parser.async_get_data(type=type)
|
|
155
151
|
|
|
152
|
+
@overload
|
|
153
|
+
async def async_get_data_object(
|
|
154
|
+
self,
|
|
155
|
+
) -> "BaseModel | None": ...
|
|
156
|
+
|
|
156
157
|
@overload
|
|
157
158
|
async def async_get_data_object(
|
|
158
159
|
self,
|
|
@@ -467,7 +468,7 @@ class ModelRequest:
|
|
|
467
468
|
prompt: Any,
|
|
468
469
|
mappings: dict[str, Any] | None = None,
|
|
469
470
|
):
|
|
470
|
-
self.prompt.set("system", ["YOU MUST REACT AND RESPOND AS {system.
|
|
471
|
+
self.prompt.set("system", ["YOU MUST REACT AND RESPOND AS {system.your_role}!"])
|
|
471
472
|
self.prompt.set("system.your_role", prompt, mappings)
|
|
472
473
|
return self
|
|
473
474
|
|
agently/core/Prompt.py
CHANGED
|
@@ -161,5 +161,5 @@ class Prompt(RuntimeData):
|
|
|
161
161
|
else:
|
|
162
162
|
super().append(key, value)
|
|
163
163
|
|
|
164
|
-
def get(self, key: "PromptStandardSlot | None" = None, default: T = None, inherit: bool = True) -> Any | T:
|
|
164
|
+
def get(self, key: "PromptStandardSlot | str | None" = None, default: T = None, inherit: bool = True) -> Any | T:
|
|
165
165
|
return super().get(key, default=default, inherit=inherit)
|
agently/core/Session.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING, Literal, cast
|
|
18
|
+
|
|
19
|
+
from agently.utils import Settings
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from agently.core import PluginManager, BaseAgent
|
|
23
|
+
from agently.types.plugins import (
|
|
24
|
+
SessionProtocol,
|
|
25
|
+
MemoResizePolicyHandler,
|
|
26
|
+
MemoResizeHandler,
|
|
27
|
+
AttachmentSummaryHandler,
|
|
28
|
+
MemoUpdateHandler,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Session:
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
policy_handler: "MemoResizePolicyHandler | None" = None,
|
|
37
|
+
resize_handlers: "dict[Literal['lite', 'deep'] | str, MemoResizeHandler] | None" = None,
|
|
38
|
+
attachment_summary_handler: "AttachmentSummaryHandler | None" = None,
|
|
39
|
+
memo_update_handler: "MemoUpdateHandler | None" = None,
|
|
40
|
+
parent_settings: Settings | None = None,
|
|
41
|
+
agent: "BaseAgent | None" = None,
|
|
42
|
+
plugin_manager: "PluginManager | None" = None,
|
|
43
|
+
):
|
|
44
|
+
if plugin_manager is None:
|
|
45
|
+
from agently.base import plugin_manager as global_plugin_manager, settings as global_settings
|
|
46
|
+
|
|
47
|
+
plugin_manager = global_plugin_manager
|
|
48
|
+
if parent_settings is None:
|
|
49
|
+
parent_settings = global_settings
|
|
50
|
+
|
|
51
|
+
if parent_settings is None:
|
|
52
|
+
parent_settings = Settings(name="Session-Settings")
|
|
53
|
+
|
|
54
|
+
session_settings = Settings(
|
|
55
|
+
name="Session-Settings",
|
|
56
|
+
parent=parent_settings,
|
|
57
|
+
)
|
|
58
|
+
plugin_name = str(session_settings.get("plugins.Session.activate", "AgentlyMemoSession"))
|
|
59
|
+
SessionPlugin = cast(
|
|
60
|
+
type["SessionProtocol"],
|
|
61
|
+
plugin_manager.get_plugin("Session", plugin_name),
|
|
62
|
+
)
|
|
63
|
+
impl = SessionPlugin(
|
|
64
|
+
policy_handler=policy_handler,
|
|
65
|
+
resize_handlers=resize_handlers,
|
|
66
|
+
attachment_summary_handler=attachment_summary_handler,
|
|
67
|
+
memo_update_handler=memo_update_handler,
|
|
68
|
+
parent_settings=session_settings,
|
|
69
|
+
agent=agent,
|
|
70
|
+
)
|
|
71
|
+
object.__setattr__(self, "_impl", impl)
|
|
72
|
+
object.__setattr__(self, "settings", impl.settings)
|
|
73
|
+
object.__setattr__(self, "plugin_manager", plugin_manager)
|
|
74
|
+
|
|
75
|
+
def __getattr__(self, name: str):
|
|
76
|
+
return getattr(self._impl, name)
|
|
77
|
+
|
|
78
|
+
def __setattr__(self, name: str, value):
|
|
79
|
+
if name in {"_impl", "settings", "plugin_manager"}:
|
|
80
|
+
object.__setattr__(self, name, value)
|
|
81
|
+
return
|
|
82
|
+
if "_impl" in self.__dict__ and hasattr(self._impl, name):
|
|
83
|
+
setattr(self._impl, name, value)
|
|
84
|
+
return
|
|
85
|
+
object.__setattr__(self, name, value)
|
|
@@ -294,16 +294,20 @@ class TriggerFlowBaseProcess:
|
|
|
294
294
|
chunk = self._flow_chunk(chunk_name)(chunk_func)
|
|
295
295
|
else:
|
|
296
296
|
chunk = self._flow_chunk(chunk.__name__)(chunk) if callable(chunk) else chunk
|
|
297
|
+
typed_chunk = cast(TriggerFlowChunk, chunk)
|
|
297
298
|
triggers_to_wait[chunk.trigger] = False
|
|
298
299
|
trigger_to_chunk_name[chunk.trigger] = chunk.name
|
|
299
300
|
results[chunk.name] = None
|
|
300
301
|
|
|
301
302
|
if semaphore is None:
|
|
302
|
-
handler =
|
|
303
|
+
handler = typed_chunk.async_call
|
|
303
304
|
else:
|
|
304
|
-
|
|
305
|
-
async
|
|
306
|
-
|
|
305
|
+
def make_handler(bound_chunk: TriggerFlowChunk):
|
|
306
|
+
async def handler(data: "TriggerFlowEventData"):
|
|
307
|
+
async with semaphore:
|
|
308
|
+
return await bound_chunk.async_call(data)
|
|
309
|
+
return handler
|
|
310
|
+
handler = make_handler(typed_chunk)
|
|
307
311
|
|
|
308
312
|
self._blue_print.add_handler(
|
|
309
313
|
self.trigger_type,
|
agently/integrations/chromadb.py
CHANGED
|
@@ -38,7 +38,7 @@ if TYPE_CHECKING:
|
|
|
38
38
|
)
|
|
39
39
|
from chromadb.api.collection_configuration import CreateCollectionConfiguration
|
|
40
40
|
from chromadb.api import ClientAPI
|
|
41
|
-
from agently.
|
|
41
|
+
from agently.base import Agent
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class ChromaDataDictOptional(TypedDict, total=False):
|
|
@@ -57,7 +57,7 @@ class ChromaData:
|
|
|
57
57
|
original_data: ChromaDataDict | list[ChromaDataDict],
|
|
58
58
|
*,
|
|
59
59
|
embedding_function: "Callable[[str | list[str]], Embeddings] | None" = None,
|
|
60
|
-
agent: "
|
|
60
|
+
agent: "Agent | None" = None,
|
|
61
61
|
):
|
|
62
62
|
self._original_data = original_data if isinstance(original_data, list) else [original_data]
|
|
63
63
|
if embedding_function:
|
|
@@ -157,7 +157,7 @@ class ChromaEmbeddingFunction(EmbeddingFunction):
|
|
|
157
157
|
def __init__(
|
|
158
158
|
self,
|
|
159
159
|
*,
|
|
160
|
-
embedding_agent: "
|
|
160
|
+
embedding_agent: "Agent",
|
|
161
161
|
):
|
|
162
162
|
def embedding_function_by_agent(texts: list[str]) -> "Embeddings":
|
|
163
163
|
return embedding_agent.input(texts).start()
|
|
@@ -180,7 +180,7 @@ class ChromaCollection:
|
|
|
180
180
|
schema: "Schema | None" = None,
|
|
181
181
|
configuration: "CreateCollectionConfiguration | None" = None,
|
|
182
182
|
metadata: dict[str, Any] | None = None,
|
|
183
|
-
embedding_agent: "
|
|
183
|
+
embedding_agent: "Agent | None" = None,
|
|
184
184
|
data_loader: "DataLoader[Loadable] | None" = None,
|
|
185
185
|
get_or_create: bool = False,
|
|
186
186
|
hnsw_space: Literal["l2", "cosine", "ip"] = "cosine",
|
agently/types/data/__init__.py
CHANGED
|
@@ -36,6 +36,7 @@ EMPTY = AVOID_COPY()
|
|
|
36
36
|
from .serializable import SerializableData, SerializableValue
|
|
37
37
|
from .prompt import (
|
|
38
38
|
ChatMessage,
|
|
39
|
+
ChatMessageDict,
|
|
39
40
|
ChatMessageContent,
|
|
40
41
|
TextMessageContent,
|
|
41
42
|
PromptModel,
|
|
@@ -71,4 +72,5 @@ from .tool import (
|
|
|
71
72
|
ReturnType,
|
|
72
73
|
MCPConfig,
|
|
73
74
|
MCPConfigs,
|
|
75
|
+
ToolInfo,
|
|
74
76
|
)
|
agently/types/data/prompt.py
CHANGED
|
@@ -52,8 +52,13 @@ ChatMessageContent = TextMessageContent | AttachmentMessageContent
|
|
|
52
52
|
ChatMessageContentAdapter = TypeAdapter(Annotated[ChatMessageContent, Field(union_mode="left_to_right")])
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
class ChatMessageDict(TypedDict):
|
|
56
|
+
role: Literal["system", "developer", "tool", "user", "assistant"] | str
|
|
57
|
+
content: str | list[dict[str, Any] | ChatMessageContent]
|
|
58
|
+
|
|
59
|
+
|
|
55
60
|
class ChatMessage(BaseModel):
|
|
56
|
-
role: str = "user"
|
|
61
|
+
role: Literal["system", "developer", "tool", "user", "assistant"] | str = "user"
|
|
57
62
|
content: str | list[dict[str, Any] | ChatMessageContent]
|
|
58
63
|
|
|
59
64
|
model_config = ConfigDict(extra="allow")
|
agently/types/data/tool.py
CHANGED
|
@@ -40,3 +40,12 @@ class MCPConfig(TypedDict):
|
|
|
40
40
|
|
|
41
41
|
class MCPConfigs(TypedDict):
|
|
42
42
|
mcpServers: dict[str, MCPConfig]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ToolInfo(TypedDict, total=False):
|
|
46
|
+
name: str
|
|
47
|
+
desc: str
|
|
48
|
+
kwargs: dict[str, Any]
|
|
49
|
+
func: Callable[..., Any]
|
|
50
|
+
returns: ReturnType
|
|
51
|
+
tags: str | list[str]
|
|
@@ -0,0 +1,22 @@
|
|
|
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 typing import Protocol
|
|
17
|
+
|
|
18
|
+
from agently.types.data import ToolInfo
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BuiltInTool(Protocol):
|
|
22
|
+
tool_info_list: list[ToolInfo]
|
|
@@ -0,0 +1,159 @@
|
|
|
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 __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Any, Literal, Awaitable, Callable, TypeAlias, TYPE_CHECKING, Protocol
|
|
19
|
+
from typing_extensions import TypedDict, NotRequired, Self
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from agently.utils import Settings
|
|
23
|
+
from agently.types.data import SerializableData, ChatMessage
|
|
24
|
+
|
|
25
|
+
MemoResizeType: TypeAlias = Literal["lite", "deep"] | str
|
|
26
|
+
SessionMode: TypeAlias = Literal["lite", "memo"] | str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SessionLimit(TypedDict, total=False):
|
|
30
|
+
chars: int
|
|
31
|
+
messages: int
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class SessionConfig(TypedDict, total=False):
|
|
35
|
+
mode: SessionMode
|
|
36
|
+
limit: SessionLimit
|
|
37
|
+
every_n_turns: int
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MemoResizeDecision(TypedDict):
|
|
41
|
+
type: MemoResizeType
|
|
42
|
+
reason: NotRequired[str]
|
|
43
|
+
severity: NotRequired[int]
|
|
44
|
+
meta: NotRequired[dict[str, Any]]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
MemoResizePolicyResult: TypeAlias = "MemoResizeType | MemoResizeDecision | None"
|
|
48
|
+
|
|
49
|
+
MemoResizePolicyHandler: TypeAlias = (
|
|
50
|
+
"Callable[[list[ChatMessage], list[ChatMessage], Settings], MemoResizePolicyResult | Awaitable[MemoResizePolicyResult]]"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
MemoResizePolicyAsyncHandler: TypeAlias = (
|
|
54
|
+
"Callable[[list[ChatMessage], list[ChatMessage], Settings], Awaitable[MemoResizePolicyResult]]"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
MemoResizeHandlerResult: TypeAlias = "tuple[list[ChatMessage], list[ChatMessage], SerializableData]"
|
|
58
|
+
|
|
59
|
+
MemoResizeHandler: TypeAlias = (
|
|
60
|
+
"Callable[[list[ChatMessage], list[ChatMessage], SerializableData, Settings], MemoResizeHandlerResult | Awaitable[MemoResizeHandlerResult]]"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
MemoResizeAsyncHandler: TypeAlias = (
|
|
64
|
+
"Callable[[list[ChatMessage], list[ChatMessage], SerializableData, Settings], Awaitable[MemoResizeHandlerResult]]"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
MemoUpdateResult: TypeAlias = "dict[str, Any]"
|
|
68
|
+
MemoUpdateHandler: TypeAlias = (
|
|
69
|
+
"Callable[[dict[str, Any], list[ChatMessage], list[AttachmentSummary], Settings], MemoUpdateResult | Awaitable[MemoUpdateResult]]"
|
|
70
|
+
)
|
|
71
|
+
MemoUpdateAsyncHandler: TypeAlias = (
|
|
72
|
+
"Callable[[dict[str, Any], list[ChatMessage], list[AttachmentSummary], Settings], Awaitable[MemoUpdateResult]]"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
AttachmentSummary: TypeAlias = "dict[str, Any]"
|
|
76
|
+
AttachmentSummaryHandler: TypeAlias = (
|
|
77
|
+
"Callable[[ChatMessage], list[AttachmentSummary] | Awaitable[list[AttachmentSummary]]]"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
AttachmentSummaryAsyncHandler: TypeAlias = "Callable[[ChatMessage], Awaitable[list[AttachmentSummary]]]"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SessionProtocol(Protocol):
|
|
84
|
+
id: str
|
|
85
|
+
settings: "Settings"
|
|
86
|
+
memo: "SerializableData"
|
|
87
|
+
full_chat_history: "list[ChatMessage]"
|
|
88
|
+
current_chat_history: "list[ChatMessage]"
|
|
89
|
+
set_settings: Callable[..., Any]
|
|
90
|
+
judge_resize: Callable[..., Any]
|
|
91
|
+
resize: Callable[..., Any]
|
|
92
|
+
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
*,
|
|
96
|
+
policy_handler: MemoResizePolicyHandler | None = None,
|
|
97
|
+
resize_handlers: dict[Literal["lite", "deep"] | str, MemoResizeHandler] | None = None,
|
|
98
|
+
attachment_summary_handler: AttachmentSummaryHandler | None = None,
|
|
99
|
+
memo_update_handler: MemoUpdateHandler | None = None,
|
|
100
|
+
parent_settings: "Settings | None" = None,
|
|
101
|
+
agent: Any | None = None,
|
|
102
|
+
): ...
|
|
103
|
+
|
|
104
|
+
def configure(
|
|
105
|
+
self,
|
|
106
|
+
*,
|
|
107
|
+
mode: SessionMode | None = None,
|
|
108
|
+
limit: SessionLimit | None = None,
|
|
109
|
+
every_n_turns: int | None = None,
|
|
110
|
+
) -> Self: ...
|
|
111
|
+
|
|
112
|
+
def set_limit(
|
|
113
|
+
self,
|
|
114
|
+
*,
|
|
115
|
+
chars: int | None = None,
|
|
116
|
+
messages: int | None = None,
|
|
117
|
+
) -> Self: ...
|
|
118
|
+
|
|
119
|
+
def use_lite(
|
|
120
|
+
self,
|
|
121
|
+
*,
|
|
122
|
+
chars: int | None = None,
|
|
123
|
+
messages: int | None = None,
|
|
124
|
+
every_n_turns: int | None = None,
|
|
125
|
+
) -> Self: ...
|
|
126
|
+
|
|
127
|
+
def use_memo(
|
|
128
|
+
self,
|
|
129
|
+
*,
|
|
130
|
+
chars: int | None = None,
|
|
131
|
+
messages: int | None = None,
|
|
132
|
+
every_n_turns: int | None = None,
|
|
133
|
+
) -> Self: ...
|
|
134
|
+
|
|
135
|
+
def append_message(self, message: "ChatMessage | dict[str, Any]") -> Self: ...
|
|
136
|
+
|
|
137
|
+
def set_policy_handler(self, policy_handler: MemoResizePolicyHandler) -> Self: ...
|
|
138
|
+
|
|
139
|
+
def set_resize_handlers(
|
|
140
|
+
self,
|
|
141
|
+
resize_type: Literal["lite", "deep"] | str,
|
|
142
|
+
resize_handler: MemoResizeHandler,
|
|
143
|
+
) -> Self: ...
|
|
144
|
+
|
|
145
|
+
def set_attachment_summary_handler(self, attachment_summary_handler: AttachmentSummaryHandler) -> Self: ...
|
|
146
|
+
|
|
147
|
+
def set_memo_update_handler(self, memo_update_handler: MemoUpdateHandler) -> Self: ...
|
|
148
|
+
|
|
149
|
+
async def async_judge_resize(self, force: Literal["lite", "deep", False, None] | str = False): ...
|
|
150
|
+
|
|
151
|
+
async def async_resize(self, force: Literal["lite", "deep", False, None] | str = False): ...
|
|
152
|
+
|
|
153
|
+
def to_json(self) -> str: ...
|
|
154
|
+
|
|
155
|
+
def to_yaml(self) -> str: ...
|
|
156
|
+
|
|
157
|
+
def load_json(self, value: str) -> Self: ...
|
|
158
|
+
|
|
159
|
+
def load_yaml(self, value: str) -> Self: ...
|
|
@@ -18,3 +18,24 @@ from .PromptGenerator import PromptGenerator
|
|
|
18
18
|
from .ModelRequester import ModelRequester
|
|
19
19
|
from .ResponseParser import ResponseParser
|
|
20
20
|
from .ToolManager import ToolManager
|
|
21
|
+
from .BuiltInTool import BuiltInTool
|
|
22
|
+
from .Session import (
|
|
23
|
+
SessionProtocol,
|
|
24
|
+
SessionMode,
|
|
25
|
+
SessionLimit,
|
|
26
|
+
SessionConfig,
|
|
27
|
+
MemoResizePolicyHandler,
|
|
28
|
+
MemoResizePolicyAsyncHandler,
|
|
29
|
+
MemoResizePolicyResult,
|
|
30
|
+
MemoResizeHandler,
|
|
31
|
+
MemoResizeAsyncHandler,
|
|
32
|
+
MemoResizeHandlerResult,
|
|
33
|
+
MemoResizeType,
|
|
34
|
+
MemoResizeDecision,
|
|
35
|
+
MemoUpdateHandler,
|
|
36
|
+
MemoUpdateAsyncHandler,
|
|
37
|
+
MemoUpdateResult,
|
|
38
|
+
AttachmentSummaryHandler,
|
|
39
|
+
AttachmentSummaryAsyncHandler,
|
|
40
|
+
AttachmentSummary,
|
|
41
|
+
)
|
agently/types/plugins/base.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from typing import Any, Literal, Protocol, runtime_checkable
|
|
16
16
|
|
|
17
|
-
AgentlyPluginType = Literal["PromptGenerator", "ModelRequester", "ResponseParser", "ToolManager"]
|
|
17
|
+
AgentlyPluginType = Literal["PromptGenerator", "ModelRequester", "ResponseParser", "ToolManager", "Session"]
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@runtime_checkable
|