echotools 2.3.2__tar.gz → 2.3.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 (150) hide show
  1. {echotools-2.3.2 → echotools-2.3.3}/PKG-INFO +1 -1
  2. {echotools-2.3.2 → echotools-2.3.3}/pyproject.toml +1 -1
  3. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/prompt/inject.py +29 -16
  4. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/protocols/entml.py +33 -3
  5. {echotools-2.3.2 → echotools-2.3.3}/src/echotools.egg-info/PKG-INFO +1 -1
  6. {echotools-2.3.2 → echotools-2.3.3}/README.md +0 -0
  7. {echotools-2.3.2 → echotools-2.3.3}/setup.cfg +0 -0
  8. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/__init__.py +0 -0
  9. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/_version.py +0 -0
  10. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/cache/__init__.py +0 -0
  11. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/cache/list_cache.py +0 -0
  12. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/cache/memory_cache.py +0 -0
  13. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/config/__init__.py +0 -0
  14. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/config/base.py +0 -0
  15. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/config/center.py +0 -0
  16. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/config/loader.py +0 -0
  17. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/config/merge.py +0 -0
  18. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/dispatch/__init__.py +0 -0
  19. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/dispatch/candidate.py +0 -0
  20. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/dispatch/dispatcher.py +0 -0
  21. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/dispatch/proxy_selector.py +0 -0
  22. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/dispatch/selector.py +0 -0
  23. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/dispatch/usage.py +0 -0
  24. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/errors/__init__.py +0 -0
  25. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/errors/base.py +0 -0
  26. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/errors/classify.py +0 -0
  27. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/errors/common.py +0 -0
  28. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/errors/http.py +0 -0
  29. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/events/__init__.py +0 -0
  30. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/events/bus.py +0 -0
  31. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/events/event.py +0 -0
  32. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/files/__init__.py +0 -0
  33. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/files/file_util.py +0 -0
  34. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/__init__.py +0 -0
  35. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/parsers/__init__.py +0 -0
  36. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/parsers/stream.py +0 -0
  37. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/parsers/xml_parser.py +0 -0
  38. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/prompt/__init__.py +0 -0
  39. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/prompt/history.py +0 -0
  40. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/prompt/templates.py +0 -0
  41. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/protocols/__init__.py +0 -0
  42. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/registry.py +0 -0
  43. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/shared/__init__.py +0 -0
  44. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/shared/coercion.py +0 -0
  45. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/shared/loop_detect.py +0 -0
  46. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/shared/normalization.py +0 -0
  47. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/fncall/shared/xml_helpers.py +0 -0
  48. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/ids/__init__.py +0 -0
  49. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/ids/generator.py +0 -0
  50. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/io/__init__.py +0 -0
  51. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/io/io_utils.py +0 -0
  52. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/io/printstream.py +0 -0
  53. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/keys.py +0 -0
  54. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/lifecycle/__init__.py +0 -0
  55. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/lifecycle/manager.py +0 -0
  56. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/lifecycle/updater.py +0 -0
  57. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/logger/__init__.py +0 -0
  58. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/logger/manager.py +0 -0
  59. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/network/__init__.py +0 -0
  60. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/network/http_utils.py +0 -0
  61. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/plugin/__init__.py +0 -0
  62. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/plugin/base.py +0 -0
  63. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/plugin/discovery.py +0 -0
  64. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/plugin/registry.py +0 -0
  65. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/process/__init__.py +0 -0
  66. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/process/port.py +0 -0
  67. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/protocol/__init__.py +0 -0
  68. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/protocol/base.py +0 -0
  69. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/proxy/__init__.py +0 -0
  70. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/proxy/manager.py +0 -0
  71. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/py.typed +0 -0
  72. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/retry/__init__.py +0 -0
  73. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/retry/retry.py +0 -0
  74. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/runtime/__init__.py +0 -0
  75. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/runtime/collector.py +0 -0
  76. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/scheduler/__init__.py +0 -0
  77. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/scheduler/scheduler.py +0 -0
  78. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/sdk/__init__.py +0 -0
  79. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/sdk/facade.py +0 -0
  80. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/__init__.py +0 -0
  81. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/conpty.py +0 -0
  82. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/local.py +0 -0
  83. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/sanitize.py +0 -0
  84. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/session.py +0 -0
  85. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/ssh.py +0 -0
  86. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/terminal/tmux.py +0 -0
  87. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/tracing/__init__.py +0 -0
  88. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/tracing/context.py +0 -0
  89. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/tracing/span.py +0 -0
  90. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/tracing/tracer.py +0 -0
  91. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/translate.py +0 -0
  92. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/watcher/__init__.py +0 -0
  93. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/watcher/file_watcher.py +0 -0
  94. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/__init__.py +0 -0
  95. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/application.py +0 -0
  96. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/broker.py +0 -0
  97. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/app.js +0 -0
  98. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/file-zone/file-zone.js +0 -0
  99. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/fonts/inter-400.woff2 +0 -0
  100. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/fonts/inter-500.woff2 +0 -0
  101. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/fonts/inter-600.woff2 +0 -0
  102. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/fonts/tailwind.min.css +0 -0
  103. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/input-box.css +0 -0
  104. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/input-box.js +0 -0
  105. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/motion-kit/motion-kit.js +0 -0
  106. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/send-button/send-button.js +0 -0
  107. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/sortable-list/sortable-list.css +0 -0
  108. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/sortable-list/sortable-list.js +0 -0
  109. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/style.css +0 -0
  110. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/text-input/text-input.js +0 -0
  111. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/input_box/voice-input/voice-input.js +0 -0
  112. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/middleware.py +0 -0
  113. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/stats.py +0 -0
  114. {echotools-2.3.2 → echotools-2.3.3}/src/echotools/web/utils.py +0 -0
  115. {echotools-2.3.2 → echotools-2.3.3}/src/echotools.egg-info/SOURCES.txt +0 -0
  116. {echotools-2.3.2 → echotools-2.3.3}/src/echotools.egg-info/dependency_links.txt +0 -0
  117. {echotools-2.3.2 → echotools-2.3.3}/src/echotools.egg-info/requires.txt +0 -0
  118. {echotools-2.3.2 → echotools-2.3.3}/src/echotools.egg-info/top_level.txt +0 -0
  119. {echotools-2.3.2 → echotools-2.3.3}/tests/test_cache_retry.py +0 -0
  120. {echotools-2.3.2 → echotools-2.3.3}/tests/test_completion.py +0 -0
  121. {echotools-2.3.2 → echotools-2.3.3}/tests/test_config.py +0 -0
  122. {echotools-2.3.2 → echotools-2.3.3}/tests/test_config_extended.py +0 -0
  123. {echotools-2.3.2 → echotools-2.3.3}/tests/test_coverage_core.py +0 -0
  124. {echotools-2.3.2 → echotools-2.3.3}/tests/test_dispatch.py +0 -0
  125. {echotools-2.3.2 → echotools-2.3.3}/tests/test_dispatch_extended.py +0 -0
  126. {echotools-2.3.2 → echotools-2.3.3}/tests/test_dispatch_race.py +0 -0
  127. {echotools-2.3.2 → echotools-2.3.3}/tests/test_errors.py +0 -0
  128. {echotools-2.3.2 → echotools-2.3.3}/tests/test_errors_common.py +0 -0
  129. {echotools-2.3.2 → echotools-2.3.3}/tests/test_events.py +0 -0
  130. {echotools-2.3.2 → echotools-2.3.3}/tests/test_events_event.py +0 -0
  131. {echotools-2.3.2 → echotools-2.3.3}/tests/test_facade.py +0 -0
  132. {echotools-2.3.2 → echotools-2.3.3}/tests/test_fncall.py +0 -0
  133. {echotools-2.3.2 → echotools-2.3.3}/tests/test_ids.py +0 -0
  134. {echotools-2.3.2 → echotools-2.3.3}/tests/test_imports.py +0 -0
  135. {echotools-2.3.2 → echotools-2.3.3}/tests/test_io.py +0 -0
  136. {echotools-2.3.2 → echotools-2.3.3}/tests/test_keys.py +0 -0
  137. {echotools-2.3.2 → echotools-2.3.3}/tests/test_lifecycle.py +0 -0
  138. {echotools-2.3.2 → echotools-2.3.3}/tests/test_logger.py +0 -0
  139. {echotools-2.3.2 → echotools-2.3.3}/tests/test_merge.py +0 -0
  140. {echotools-2.3.2 → echotools-2.3.3}/tests/test_plugin.py +0 -0
  141. {echotools-2.3.2 → echotools-2.3.3}/tests/test_printstream.py +0 -0
  142. {echotools-2.3.2 → echotools-2.3.3}/tests/test_protocols.py +0 -0
  143. {echotools-2.3.2 → echotools-2.3.3}/tests/test_proxy.py +0 -0
  144. {echotools-2.3.2 → echotools-2.3.3}/tests/test_proxy_selector.py +0 -0
  145. {echotools-2.3.2 → echotools-2.3.3}/tests/test_runtime.py +0 -0
  146. {echotools-2.3.2 → echotools-2.3.3}/tests/test_scheduler.py +0 -0
  147. {echotools-2.3.2 → echotools-2.3.3}/tests/test_tracing.py +0 -0
  148. {echotools-2.3.2 → echotools-2.3.3}/tests/test_usage.py +0 -0
  149. {echotools-2.3.2 → echotools-2.3.3}/tests/test_web.py +0 -0
  150. {echotools-2.3.2 → echotools-2.3.3}/tests/test_web_broker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echotools
3
- Version: 2.3.2
3
+ Version: 2.3.3
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Author: nichengfuben
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "echotools"
7
- version = "2.3.2"
7
+ version = "2.3.3"
8
8
  description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -50,7 +50,9 @@ def inject_fncall(
50
50
  dump_dir: Optional[str] = None,
51
51
  ) -> List[Dict[str, Any]]:
52
52
  """注入工具定义为单条 user 消息。
53
-
53
+
54
+ 无论是否有 tools,都构建包含历史的 prompt 返回单条 user 消息。
55
+
54
56
  Args:
55
57
  messages: 消息列表。
56
58
  tools: 工具定义。
@@ -64,26 +66,18 @@ def inject_fncall(
64
66
  Returns:
65
67
  单条 user 消息列表。
66
68
  """
67
- if not tools:
68
- return list(messages)
69
69
  normalized = _normalize_messages(list(messages))
70
- loop_warning = ""
71
- if loop_detection_threshold > 0:
72
- result = detect_tool_loop(normalized, loop_detection_threshold)
73
- if result.is_looping:
74
- logger.debug(
75
- "检测到工具循环(%d 次)", result.repeat_count
76
- )
77
- loop_warning = result.suggestion
70
+
71
+ # 提取最后一条 user 消息和历史
78
72
  last_user_idx: Optional[int] = None
79
73
  for i in range(len(normalized) - 1, -1, -1):
80
74
  if (normalized[i].get("role") or "user") == "user":
81
75
  last_user_idx = i
82
76
  break
77
+
83
78
  if last_user_idx is not None:
84
79
  history_messages = (
85
- normalized[:last_user_idx]
86
- + normalized[last_user_idx + 1 :]
80
+ normalized[:last_user_idx] + normalized[last_user_idx + 1:]
87
81
  )
88
82
  current_user_message = normalize_content(
89
83
  normalized[last_user_idx].get("content", "")
@@ -91,13 +85,30 @@ def inject_fncall(
91
85
  else:
92
86
  history_messages = normalized
93
87
  current_user_message = ""
88
+
89
+ # 构建历史文本
90
+ history_text = _format_conversation_history(
91
+ history_messages, protocol=protocol
92
+ ).strip()
93
+
94
+ # 如果没有 tools,直接拼历史 + 当前消息,不注入工具定义
95
+ if not tools:
96
+ prompt = f"<conversation_history>\n{history_text}\n</conversation_history>\n\n<current_user_message>\n{current_user_message}\n</current_user_message>"
97
+ return [{"role": "user", "content": prompt}]
98
+
99
+ # 有 tools:走原有逻辑
100
+ loop_warning = ""
101
+ if loop_detection_threshold > 0:
102
+ result = detect_tool_loop(normalized, loop_detection_threshold)
103
+ if result.is_looping:
104
+ logger.debug("检测到工具循环(%d 次)", result.repeat_count)
105
+ loop_warning = result.suggestion
106
+
94
107
  if hasattr(protocol, 'format_tool_descs'):
95
108
  tool_descs = protocol.format_tool_descs(tools)
96
109
  else:
97
110
  tool_descs = format_tool_descs(tools)
98
- history_text = _format_conversation_history(
99
- history_messages, protocol=protocol
100
- ).strip()
111
+
101
112
  prompt = protocol.render_prompt(
102
113
  tool_descs=tool_descs,
103
114
  lang=lang,
@@ -106,6 +117,8 @@ def inject_fncall(
106
117
  loop_warning=loop_warning,
107
118
  current_user_message=current_user_message,
108
119
  )
120
+
109
121
  if dump_prompt:
110
122
  _maybe_dump_prompt(prompt, dump_dir)
123
+
111
124
  return [{"role": "user", "content": prompt}]
@@ -75,6 +75,29 @@ _PARAMETERS_RE = re.compile(
75
75
  r'<entml:parameters>([\s\S]*?)</entml:parameters>',
76
76
  re.DOTALL,
77
77
  )
78
+ # 用于解析 <entml:parameters> 内的子标签
79
+ _SUB_TAG_RE = re.compile(
80
+ r'<([^>]+)>([\s\S]*?)</\1>',
81
+ re.DOTALL,
82
+ )
83
+
84
+
85
+ def _parse_sub_tags(content: str, schema_index: Optional[Dict[str, Any]] = None, func_name: str = "") -> Dict[str, Any]:
86
+ """解析 <entml:parameters> 内的子标签,返回参数字典。"""
87
+ args: Dict[str, Any] = {}
88
+ for m in _SUB_TAG_RE.finditer(content):
89
+ pname = m.group(1).strip()
90
+ pval = m.group(2).strip()
91
+ pschema = schema_index.get(func_name, {}).get(pname, {}) if schema_index else {}
92
+ if pschema:
93
+ args[pname] = _coerce_param_value(pval, pschema)
94
+ else:
95
+ try:
96
+ args[pname] = json.loads(pval)
97
+ except json.JSONDecodeError:
98
+ args[pname] = pval
99
+ return args
100
+
78
101
 
79
102
  # ---------------------------------------------------------------------------
80
103
  # Entml 协议
@@ -179,11 +202,18 @@ class EntmlProtocol(ToolProtocol):
179
202
  # Try <entml:parameters>{json}</entml:parameters> format first
180
203
  params_m = _PARAMETERS_RE.search(body)
181
204
  if params_m:
182
- params_json = params_m.group(1).strip()
205
+ params_content = params_m.group(1).strip()
183
206
  try:
184
- args = json.loads(params_json)
207
+ # 尝试 JSON 解析
208
+ args = json.loads(params_content)
209
+ if not isinstance(args, dict):
210
+ args = {"value": args}
185
211
  except json.JSONDecodeError:
186
- args = {"value": params_json}
212
+ # 不是 JSON,尝试解析子标签
213
+ args = _parse_sub_tags(params_content, schema_index, name)
214
+ # 如果子标签解析也没结果,整个当作值
215
+ if not args:
216
+ args = {"value": params_content}
187
217
  else:
188
218
  # Try <entml:parameter name="...">value</entml:parameter> format
189
219
  for param_m in _PARAM_RE.finditer(body):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echotools
3
- Version: 2.3.2
3
+ Version: 2.3.3
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Author: nichengfuben
6
6
  License: MIT
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