aient 1.2.1__tar.gz → 1.2.3__tar.gz

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.
Files changed (56) hide show
  1. {aient-1.2.1 → aient-1.2.3}/PKG-INFO +1 -1
  2. {aient-1.2.1 → aient-1.2.3}/aient/architext/architext/core.py +67 -21
  3. {aient-1.2.1 → aient-1.2.3}/aient/architext/test/test.py +56 -0
  4. {aient-1.2.1 → aient-1.2.3}/aient.egg-info/PKG-INFO +1 -1
  5. {aient-1.2.1 → aient-1.2.3}/pyproject.toml +1 -1
  6. {aient-1.2.1 → aient-1.2.3}/LICENSE +0 -0
  7. {aient-1.2.1 → aient-1.2.3}/README.md +0 -0
  8. {aient-1.2.1 → aient-1.2.3}/aient/__init__.py +0 -0
  9. {aient-1.2.1 → aient-1.2.3}/aient/architext/architext/__init__.py +0 -0
  10. {aient-1.2.1 → aient-1.2.3}/aient/architext/test/openai_client.py +0 -0
  11. {aient-1.2.1 → aient-1.2.3}/aient/architext/test/test_save_load.py +0 -0
  12. {aient-1.2.1 → aient-1.2.3}/aient/core/__init__.py +0 -0
  13. {aient-1.2.1 → aient-1.2.3}/aient/core/log_config.py +0 -0
  14. {aient-1.2.1 → aient-1.2.3}/aient/core/models.py +0 -0
  15. {aient-1.2.1 → aient-1.2.3}/aient/core/request.py +0 -0
  16. {aient-1.2.1 → aient-1.2.3}/aient/core/response.py +0 -0
  17. {aient-1.2.1 → aient-1.2.3}/aient/core/test/test_base_api.py +0 -0
  18. {aient-1.2.1 → aient-1.2.3}/aient/core/test/test_geminimask.py +0 -0
  19. {aient-1.2.1 → aient-1.2.3}/aient/core/test/test_image.py +0 -0
  20. {aient-1.2.1 → aient-1.2.3}/aient/core/test/test_payload.py +0 -0
  21. {aient-1.2.1 → aient-1.2.3}/aient/core/utils.py +0 -0
  22. {aient-1.2.1 → aient-1.2.3}/aient/models/__init__.py +0 -0
  23. {aient-1.2.1 → aient-1.2.3}/aient/models/audio.py +0 -0
  24. {aient-1.2.1 → aient-1.2.3}/aient/models/base.py +0 -0
  25. {aient-1.2.1 → aient-1.2.3}/aient/models/chatgpt.py +0 -0
  26. {aient-1.2.1 → aient-1.2.3}/aient/plugins/__init__.py +0 -0
  27. {aient-1.2.1 → aient-1.2.3}/aient/plugins/arXiv.py +0 -0
  28. {aient-1.2.1 → aient-1.2.3}/aient/plugins/config.py +0 -0
  29. {aient-1.2.1 → aient-1.2.3}/aient/plugins/excute_command.py +0 -0
  30. {aient-1.2.1 → aient-1.2.3}/aient/plugins/get_time.py +0 -0
  31. {aient-1.2.1 → aient-1.2.3}/aient/plugins/image.py +0 -0
  32. {aient-1.2.1 → aient-1.2.3}/aient/plugins/list_directory.py +0 -0
  33. {aient-1.2.1 → aient-1.2.3}/aient/plugins/read_file.py +0 -0
  34. {aient-1.2.1 → aient-1.2.3}/aient/plugins/read_image.py +0 -0
  35. {aient-1.2.1 → aient-1.2.3}/aient/plugins/readonly.py +0 -0
  36. {aient-1.2.1 → aient-1.2.3}/aient/plugins/registry.py +0 -0
  37. {aient-1.2.1 → aient-1.2.3}/aient/plugins/run_python.py +0 -0
  38. {aient-1.2.1 → aient-1.2.3}/aient/plugins/websearch.py +0 -0
  39. {aient-1.2.1 → aient-1.2.3}/aient/plugins/write_file.py +0 -0
  40. {aient-1.2.1 → aient-1.2.3}/aient/utils/__init__.py +0 -0
  41. {aient-1.2.1 → aient-1.2.3}/aient/utils/prompt.py +0 -0
  42. {aient-1.2.1 → aient-1.2.3}/aient/utils/scripts.py +0 -0
  43. {aient-1.2.1 → aient-1.2.3}/aient.egg-info/SOURCES.txt +0 -0
  44. {aient-1.2.1 → aient-1.2.3}/aient.egg-info/dependency_links.txt +0 -0
  45. {aient-1.2.1 → aient-1.2.3}/aient.egg-info/requires.txt +0 -0
  46. {aient-1.2.1 → aient-1.2.3}/aient.egg-info/top_level.txt +0 -0
  47. {aient-1.2.1 → aient-1.2.3}/setup.cfg +0 -0
  48. {aient-1.2.1 → aient-1.2.3}/test/test_Web_crawler.py +0 -0
  49. {aient-1.2.1 → aient-1.2.3}/test/test_ddg_search.py +0 -0
  50. {aient-1.2.1 → aient-1.2.3}/test/test_google_search.py +0 -0
  51. {aient-1.2.1 → aient-1.2.3}/test/test_ollama.py +0 -0
  52. {aient-1.2.1 → aient-1.2.3}/test/test_plugin.py +0 -0
  53. {aient-1.2.1 → aient-1.2.3}/test/test_search.py +0 -0
  54. {aient-1.2.1 → aient-1.2.3}/test/test_url.py +0 -0
  55. {aient-1.2.1 → aient-1.2.3}/test/test_whisper.py +0 -0
  56. {aient-1.2.1 → aient-1.2.3}/test/test_yjh.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aient
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: Aient: The Awakening of Agent.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -142,6 +142,15 @@ class Texts(ContextProvider):
142
142
  async def render(self) -> Optional[str]:
143
143
  return self.content
144
144
 
145
+ def __eq__(self, other):
146
+ if not isinstance(other, Texts):
147
+ return NotImplemented
148
+ # If either object is dynamic, they are only equal if they are the exact same object.
149
+ if self._is_dynamic or (hasattr(other, '_is_dynamic') and other._is_dynamic):
150
+ return self is other
151
+ # For static content, compare the actual content.
152
+ return self.content == other.content
153
+
145
154
  class Tools(ContextProvider):
146
155
  def __init__(self, tools_json: Optional[List[Dict]] = None, name: str = "tools"):
147
156
  super().__init__(name)
@@ -154,6 +163,11 @@ class Tools(ContextProvider):
154
163
  return None
155
164
  return f"<tools>{str(self._tools_json)}</tools>"
156
165
 
166
+ def __eq__(self, other):
167
+ if not isinstance(other, Tools):
168
+ return NotImplemented
169
+ return self._tools_json == other._tools_json
170
+
157
171
  class Files(ContextProvider):
158
172
  def __init__(self, *paths: Union[str, List[str]], name: str = "files"):
159
173
  super().__init__(name)
@@ -230,6 +244,11 @@ class Files(ContextProvider):
230
244
  if not self._files: return None
231
245
  return "<latest_file_content>" + "\n".join([f"<file><file_path>{p}</file_path><file_content>{c}</file_content></file>" for p, c in self._files.items()]) + "\n</latest_file_content>"
232
246
 
247
+ def __eq__(self, other):
248
+ if not isinstance(other, Files):
249
+ return NotImplemented
250
+ return self._files == other._files
251
+
233
252
  class Images(ContextProvider):
234
253
  def __init__(self, url: str, name: Optional[str] = None):
235
254
  super().__init__(name or url)
@@ -250,6 +269,11 @@ class Images(ContextProvider):
250
269
  logging.warning(f"Image file not found: {self.url}. Skipping.")
251
270
  return None # Or handle error appropriately
252
271
 
272
+ def __eq__(self, other):
273
+ if not isinstance(other, Images):
274
+ return NotImplemented
275
+ return self.url == other.url
276
+
253
277
  # 3. 消息类 (已合并 MessageContent)
254
278
  class Message(ABC):
255
279
  def __init__(self, role: str, *initial_items: Union[ContextProvider, str, list]):
@@ -297,6 +321,16 @@ class Message(ABC):
297
321
  self._items: List[ContextProvider] = processed_items
298
322
  self._parent_messages: Optional['Messages'] = None
299
323
 
324
+ @property
325
+ def content(self) -> Optional[Union[str, List[Dict[str, Any]]]]:
326
+ """
327
+ Renders the message content.
328
+ For simple text messages, returns a string.
329
+ For multimodal messages, returns a list of content blocks.
330
+ """
331
+ rendered_dict = self.to_dict()
332
+ return rendered_dict.get('content') if rendered_dict else None
333
+
300
334
  def _render_content(self) -> str:
301
335
  final_parts = []
302
336
  for item in self._items:
@@ -346,26 +380,36 @@ class Message(ABC):
346
380
  return type(self)(*new_items)
347
381
  return NotImplemented
348
382
 
349
- def __getitem__(self, key: str) -> Any:
383
+ def __getitem__(self, key: Union[str, int]) -> Any:
350
384
  """
351
- 使得 Message 对象支持字典风格的访问 (e.g., message['content'])
385
+ 使得 Message 对象支持字典风格的访问 (e.g., message['content'])
386
+ 和列表风格的索引访问 (e.g., message[-1])。
352
387
  """
353
- if key == 'role':
354
- return self.role
355
- elif key == 'content':
356
- # 直接调用 to_dict 并提取 'content',确保逻辑一致
357
- rendered_dict = self.to_dict()
358
- return rendered_dict.get('content') if rendered_dict else None
359
- # 对于 tool_calls 等特殊属性,也通过 to_dict 获取
360
- elif hasattr(self, key):
361
- rendered_dict = self.to_dict()
362
- if rendered_dict and key in rendered_dict:
363
- return rendered_dict[key]
364
-
365
- # 如果在对象本身或其 to_dict() 中都找不到,则引发 KeyError
366
- if hasattr(self, key):
367
- return getattr(self, key)
368
- raise KeyError(f"'{key}'")
388
+ if isinstance(key, str):
389
+ if key == 'role':
390
+ return self.role
391
+ elif key == 'content':
392
+ # 直接调用 to_dict 并提取 'content',确保逻辑一致
393
+ rendered_dict = self.to_dict()
394
+ return rendered_dict.get('content') if rendered_dict else None
395
+ # 对于 tool_calls 等特殊属性,也通过 to_dict 获取
396
+ elif hasattr(self, key):
397
+ rendered_dict = self.to_dict()
398
+ if rendered_dict and key in rendered_dict:
399
+ return rendered_dict[key]
400
+
401
+ # 如果在对象本身或其 to_dict() 中都找不到,则引发 KeyError
402
+ if hasattr(self, key):
403
+ return getattr(self, key)
404
+ raise KeyError(f"'{key}'")
405
+ elif isinstance(key, int):
406
+ return self._items[key]
407
+ else:
408
+ raise TypeError(f"Message indices must be integers or strings, not {type(key).__name__}")
409
+
410
+ def __len__(self) -> int:
411
+ """返回消息中 provider 的数量。"""
412
+ return len(self._items)
369
413
 
370
414
  def __repr__(self): return f"Message(role='{self.role}', items={[i.name for i in self._items]})"
371
415
  def __bool__(self) -> bool:
@@ -444,15 +488,17 @@ class ToolCalls(Message):
444
488
  class ToolResults(Message):
445
489
  """Represents a tool message with the result of a single tool call."""
446
490
  def __init__(self, tool_call_id: str, content: str):
447
- super().__init__("tool")
491
+ # We pass a Texts provider to the parent so it can be rendered,
492
+ # but the primary way to access content for ToolResults is via its dict representation.
493
+ super().__init__("tool", Texts(text=content))
448
494
  self.tool_call_id = tool_call_id
449
- self.content = content
495
+ self._content = content
450
496
 
451
497
  def to_dict(self) -> Dict[str, Any]:
452
498
  return {
453
499
  "role": self.role,
454
500
  "tool_call_id": self.tool_call_id,
455
- "content": self.content
501
+ "content": self._content
456
502
  }
457
503
 
458
504
  # 4. 顶层容器: Messages
@@ -1102,6 +1102,62 @@ Current time: {Texts(lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"))}
1102
1102
  self.assertIn("Third explanation.", rendered_final[0]['content'])
1103
1103
  self.assertIn("Some other text.", rendered_final[0]['content'])
1104
1104
 
1105
+ async def test_z9_rolemessage_content_access(self):
1106
+ """测试是否支持 RoleMessage.content 来访问渲染好的内容"""
1107
+ # 1. 创建一个简单的 UserMessage
1108
+ user_message = UserMessage("你好, Architext!")
1109
+ # 对于简单的 Texts, refresh 不是必须的, 但这是个好习惯
1110
+ # Message 类本身没有 refresh, 调用其 providers 的 refresh
1111
+ for p in user_message.providers():
1112
+ await p.refresh()
1113
+
1114
+ # 2. 直接访问 .content 属性
1115
+ # 在实现该功能前,这行代码会因 AttributeError 而失败
1116
+ self.assertEqual(user_message.content, "你好, Architext!")
1117
+
1118
+ # 3. 创建一个多模态消息
1119
+ multimodal_message = AssistantMessage(
1120
+ "这是一张图片:",
1121
+ Images(url="data:image/png;base64,FAKE_IMG_DATA")
1122
+ )
1123
+ for p in multimodal_message.providers():
1124
+ await p.refresh()
1125
+
1126
+ # 4. 访问多模态消息的 .content 属性,期望返回一个列表
1127
+ content_list = multimodal_message.content
1128
+ self.assertIsInstance(content_list, list)
1129
+ self.assertEqual(len(content_list), 2)
1130
+ self.assertEqual(content_list[0]['type'], 'text')
1131
+ self.assertEqual(content_list[1]['type'], 'image_url')
1132
+
1133
+ # 5. 测试通过 RoleMessage 工厂创建的消息
1134
+ role_message = RoleMessage('user', "通过工厂创建的内容")
1135
+ for p in role_message.providers():
1136
+ await p.refresh()
1137
+ self.assertEqual(role_message.content, "通过工厂创建的内容")
1138
+
1139
+ async def test_za_message_indexing_and_length(self):
1140
+ """测试 Message 对象是否支持通过索引访问 provider 以及获取长度"""
1141
+ # 1. 创建一个 UserMessage
1142
+ mess = UserMessage(
1143
+ Texts("some instruction"),
1144
+ Texts("hi", name="done")
1145
+ )
1146
+
1147
+ # 2. 测试获取长度
1148
+ # 这在实现 __len__ 之前会失败
1149
+ self.assertEqual(len(mess), 2)
1150
+
1151
+ # 3. 测试通过索引访问
1152
+ # 这在修改 __getitem__ 之前会失败
1153
+ self.assertEqual(mess[-1].name, "done")
1154
+ self.assertEqual(mess[0].name, Texts("some instruction").name)
1155
+ self.assertEqual(mess[0], Texts("some instruction"))
1156
+
1157
+ # 4. 测试索引越界
1158
+ with self.assertRaises(IndexError):
1159
+ _ = mess[2]
1160
+
1105
1161
  # ==============================================================================
1106
1162
  # 6. 演示
1107
1163
  # ==============================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aient
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: Aient: The Awakening of Agent.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aient"
3
- version = "1.2.1"
3
+ version = "1.2.3"
4
4
  description = "Aient: The Awakening of Agent."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes