aient 1.1.92__py3-none-any.whl → 1.1.94__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.
- aient/architext/architext/core.py +25 -6
- aient/architext/test/test.py +80 -0
- {aient-1.1.92.dist-info → aient-1.1.94.dist-info}/METADATA +1 -1
- {aient-1.1.92.dist-info → aient-1.1.94.dist-info}/RECORD +7 -7
- {aient-1.1.92.dist-info → aient-1.1.94.dist-info}/WHEEL +0 -0
- {aient-1.1.92.dist-info → aient-1.1.94.dist-info}/licenses/LICENSE +0 -0
- {aient-1.1.92.dist-info → aient-1.1.94.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ import hashlib
|
|
6
6
|
import mimetypes
|
7
7
|
from dataclasses import dataclass
|
8
8
|
from abc import ABC, abstractmethod
|
9
|
-
from typing import List, Dict, Any, Optional, Union
|
9
|
+
from typing import List, Dict, Any, Optional, Union, Callable
|
10
10
|
|
11
11
|
# 1. 核心数据结构: ContentBlock
|
12
12
|
@dataclass
|
@@ -32,20 +32,39 @@ class ContextProvider(ABC):
|
|
32
32
|
return None
|
33
33
|
|
34
34
|
class Texts(ContextProvider):
|
35
|
-
def __init__(self, text: str, name: Optional[str] = None):
|
35
|
+
def __init__(self, text: Optional[Union[str, Callable[[], str]]] = None, name: Optional[str] = None):
|
36
|
+
if text is None and name is None:
|
37
|
+
raise ValueError("Either 'text' or 'name' must be provided.")
|
38
|
+
|
36
39
|
self._text = text
|
40
|
+
self._is_dynamic = callable(self._text)
|
41
|
+
|
37
42
|
if name is None:
|
38
|
-
|
39
|
-
|
43
|
+
if self._is_dynamic:
|
44
|
+
import uuid
|
45
|
+
_name = f"dynamic_text_{uuid.uuid4().hex[:8]}"
|
46
|
+
else:
|
47
|
+
# Handle the case where text is None during initialization
|
48
|
+
h = hashlib.sha1(self._text.encode() if self._text else b'').hexdigest()
|
49
|
+
_name = f"text_{h[:8]}"
|
40
50
|
else:
|
41
51
|
_name = name
|
42
52
|
super().__init__(_name)
|
43
53
|
|
44
|
-
def
|
54
|
+
async def refresh(self):
|
55
|
+
if self._is_dynamic:
|
56
|
+
self._is_stale = True
|
57
|
+
await super().refresh()
|
58
|
+
|
59
|
+
def update(self, text: Union[str, Callable[[], str]]):
|
45
60
|
self._text = text
|
61
|
+
self._is_dynamic = callable(self._text)
|
46
62
|
self.mark_stale()
|
47
63
|
|
48
|
-
async def render(self) -> str:
|
64
|
+
async def render(self) -> Optional[str]:
|
65
|
+
if self._is_dynamic:
|
66
|
+
return self._text()
|
67
|
+
return self._text
|
49
68
|
|
50
69
|
class Tools(ContextProvider):
|
51
70
|
def __init__(self, tools_json: List[Dict]): super().__init__("tools"); self._tools_json = tools_json
|
aient/architext/test/test.py
CHANGED
@@ -819,6 +819,86 @@ class TestContextManagement(unittest.IsolatedAsyncioTestCase):
|
|
819
819
|
if os.path.exists(test_file):
|
820
820
|
os.remove(test_file)
|
821
821
|
|
822
|
+
async def test_z_dynamic_texts_provider(self):
|
823
|
+
"""测试 Texts provider 是否支持可调用对象以实现动态内容"""
|
824
|
+
import time
|
825
|
+
from datetime import datetime
|
826
|
+
|
827
|
+
# 1. 使用 lambda 函数创建一个动态的 Texts provider
|
828
|
+
# 每次调用 render 时,它都应该返回当前时间
|
829
|
+
dynamic_text_provider = Texts(lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
830
|
+
messages = Messages(UserMessage(dynamic_text_provider))
|
831
|
+
|
832
|
+
# 2. 第一次渲染
|
833
|
+
rendered1 = await messages.render_latest()
|
834
|
+
time1_str = rendered1[0]['content']
|
835
|
+
self.assertIsNotNone(time1_str)
|
836
|
+
|
837
|
+
# 3. 等待一秒钟
|
838
|
+
time.sleep(1)
|
839
|
+
|
840
|
+
# 4. 第二次渲染,并期望内容已更新
|
841
|
+
rendered2 = await messages.render_latest()
|
842
|
+
time2_str = rendered2[0]['content']
|
843
|
+
self.assertIsNotNone(time2_str)
|
844
|
+
|
845
|
+
# 5. 验证两次渲染的时间戳不同
|
846
|
+
self.assertNotEqual(time1_str, time2_str, "动态 Texts provider 的内容在两次渲染之间应该更新")
|
847
|
+
|
848
|
+
async def test_z2_dynamic_texts_with_prefix(self):
|
849
|
+
"""测试动态 Texts provider 包含静态前缀时也能正确更新"""
|
850
|
+
import time
|
851
|
+
from datetime import datetime
|
852
|
+
import platform
|
853
|
+
|
854
|
+
# 1. 创建一个包含静态前缀和动态内容的 provider
|
855
|
+
# 正确的用法是将整个表达式放入 lambda
|
856
|
+
dynamic_provider = Texts(lambda: f"平台信息:{platform.platform()}, 时间:{datetime.now().isoformat()}")
|
857
|
+
messages = Messages(UserMessage(dynamic_provider))
|
858
|
+
|
859
|
+
# 2. 第一次渲染
|
860
|
+
rendered1 = await messages.render_latest()
|
861
|
+
content1 = rendered1[0]['content']
|
862
|
+
self.assertIn("平台信息:", content1)
|
863
|
+
|
864
|
+
# 3. 等待一秒
|
865
|
+
time.sleep(1)
|
866
|
+
|
867
|
+
# 4. 第二次渲染
|
868
|
+
rendered2 = await messages.render_latest()
|
869
|
+
content2 = rendered2[0]['content']
|
870
|
+
self.assertIn("平台信息:", content2)
|
871
|
+
|
872
|
+
# 5. 验证两次内容不同(因为时间戳变了)
|
873
|
+
self.assertNotEqual(content1, content2, "包含静态前缀的动态 provider 内容应该更新")
|
874
|
+
|
875
|
+
async def test_z3_deferred_text_update_via_provider(self):
|
876
|
+
"""测试 Texts(name=...) 初始化, 然后通过 provider 更新内容"""
|
877
|
+
# This test is expected to fail with a TypeError on the next line
|
878
|
+
# because the current Texts.__init__ requires 'text'.
|
879
|
+
deferred_text_provider = Texts(name="deferred_content")
|
880
|
+
|
881
|
+
messages = Messages(UserMessage(deferred_text_provider))
|
882
|
+
|
883
|
+
# Initial render: with no text, it should probably render to an empty string.
|
884
|
+
# If there's no content, the message itself might not be rendered.
|
885
|
+
# Let's assume an empty provider results in the message not rendering.
|
886
|
+
await deferred_text_provider.refresh()
|
887
|
+
self.assertIsNone(deferred_text_provider.get_content_block())
|
888
|
+
|
889
|
+
rendered_initial = await messages.render_latest()
|
890
|
+
self.assertEqual(len(rendered_initial), 0)
|
891
|
+
|
892
|
+
# 3. Get provider and update content
|
893
|
+
provider = messages.provider("deferred_content")
|
894
|
+
self.assertIsNotNone(provider)
|
895
|
+
provider.update("This is the new content.")
|
896
|
+
|
897
|
+
# 4. Re-render and validate
|
898
|
+
rendered_updated = await messages.render_latest()
|
899
|
+
self.assertEqual(len(rendered_updated), 1)
|
900
|
+
self.assertEqual(rendered_updated[0]['content'], "This is the new content.")
|
901
|
+
|
822
902
|
|
823
903
|
# ==============================================================================
|
824
904
|
# 6. 演示
|
@@ -1,8 +1,8 @@
|
|
1
1
|
aient/__init__.py,sha256=SRfF7oDVlOOAi6nGKiJIUK6B_arqYLO9iSMp-2IZZps,21
|
2
2
|
aient/architext/architext/__init__.py,sha256=79Ih1151rfcqZdr7F8HSZSTs_iT2SKd1xCkehMsXeXs,19
|
3
|
-
aient/architext/architext/core.py,sha256=
|
3
|
+
aient/architext/architext/core.py,sha256=CUO2-fvJTnaL1WrtInZ_BK15sCKBe-is961SLCrjZ24,18620
|
4
4
|
aient/architext/test/openai_client.py,sha256=Dqtbmubv6vwF8uBqcayG0kbsiO65of7sgU2-DRBi-UM,4590
|
5
|
-
aient/architext/test/test.py,sha256=
|
5
|
+
aient/architext/test/test.py,sha256=KwDKwApzPvniHX3epEROyZiMuTUKKzHnSsWrbWf-CUI,44074
|
6
6
|
aient/architext/test/test_save_load.py,sha256=o8DqH6gDYZkFkQy-a7blqLtJTRj5e4a-Lil48pJ0V3g,3260
|
7
7
|
aient/core/__init__.py,sha256=NxjebTlku35S4Dzr16rdSqSTWUvvwEeACe8KvHJnjPg,34
|
8
8
|
aient/core/log_config.py,sha256=kz2_yJv1p-o3lUQOwA3qh-LSc3wMHv13iCQclw44W9c,274
|
@@ -35,8 +35,8 @@ aient/plugins/write_file.py,sha256=Jt8fOEwqhYiSWpCbwfAr1xoi_BmFnx3076GMhuL06uI,3
|
|
35
35
|
aient/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
36
36
|
aient/utils/prompt.py,sha256=UcSzKkFE4-h_1b6NofI6xgk3GoleqALRKY8VBaXLjmI,11311
|
37
37
|
aient/utils/scripts.py,sha256=VqtK4RFEx7KxkmcqG3lFDS1DxoNlFFGErEjopVcc8IE,40974
|
38
|
-
aient-1.1.
|
39
|
-
aient-1.1.
|
40
|
-
aient-1.1.
|
41
|
-
aient-1.1.
|
42
|
-
aient-1.1.
|
38
|
+
aient-1.1.94.dist-info/licenses/LICENSE,sha256=XNdbcWldt0yaNXXWB_Bakoqnxb3OVhUft4MgMA_71ds,1051
|
39
|
+
aient-1.1.94.dist-info/METADATA,sha256=CATxAXUOZ7AHgyICbtyHoHcwzdFtaFLUertAGTQ6lQs,4842
|
40
|
+
aient-1.1.94.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
41
|
+
aient-1.1.94.dist-info/top_level.txt,sha256=3oXzrP5sAVvyyqabpeq8A2_vfMtY554r4bVE-OHBrZk,6
|
42
|
+
aient-1.1.94.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|