echotools 2.3.30__tar.gz → 2.3.31__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 (188) hide show
  1. {echotools-2.3.30 → echotools-2.3.31}/PKG-INFO +1 -1
  2. {echotools-2.3.30 → echotools-2.3.31}/pyproject.toml +1 -1
  3. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/prompt/history_format.py +11 -0
  4. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/prompt/inject.py +5 -0
  5. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/prompt/templates.py +9 -0
  6. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml.py +7 -1
  7. {echotools-2.3.30 → echotools-2.3.31}/src/echotools.egg-info/PKG-INFO +1 -1
  8. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_entml_protocol.py +43 -0
  9. {echotools-2.3.30 → echotools-2.3.31}/README.md +0 -0
  10. {echotools-2.3.30 → echotools-2.3.31}/docs/README.md +0 -0
  11. {echotools-2.3.30 → echotools-2.3.31}/setup.cfg +0 -0
  12. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/__init__.py +0 -0
  13. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/cache/__init__.py +0 -0
  14. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/cache/list_cache.py +0 -0
  15. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/cache/memory_cache.py +0 -0
  16. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/__init__.py +0 -0
  17. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/base.py +0 -0
  18. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/center.py +0 -0
  19. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/center_notify.py +0 -0
  20. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/center_template.py +0 -0
  21. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/loader.py +0 -0
  22. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/config/merge.py +0 -0
  23. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/errors/__init__.py +0 -0
  24. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/errors/base.py +0 -0
  25. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/errors/classify.py +0 -0
  26. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/errors/common.py +0 -0
  27. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/errors/http.py +0 -0
  28. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/ids/__init__.py +0 -0
  29. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/ids/generator.py +0 -0
  30. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/io/__init__.py +0 -0
  31. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/io/io_utils.py +0 -0
  32. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/io/printstream.py +0 -0
  33. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/logger/__init__.py +0 -0
  34. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/logger/manager.py +0 -0
  35. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/retry/__init__.py +0 -0
  36. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/base/retry/retry.py +0 -0
  37. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/compat.py +0 -0
  38. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/__init__.py +0 -0
  39. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/candidate.py +0 -0
  40. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/dispatcher.py +0 -0
  41. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
  42. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/race.py +0 -0
  43. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/selector.py +0 -0
  44. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/dispatch/usage.py +0 -0
  45. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/__init__.py +0 -0
  46. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
  47. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/parsers/stream.py +0 -0
  48. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
  49. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
  50. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
  51. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/prompt/history.py +0 -0
  52. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
  53. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
  54. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
  55. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
  56. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_thinking.py +0 -0
  57. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_thinking_history.py +0 -0
  58. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_thinking_parse.py +0 -0
  59. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
  60. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
  61. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/registry.py +0 -0
  62. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/shared/__init__.py +0 -0
  63. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/shared/coercion.py +0 -0
  64. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
  65. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/shared/normalization.py +0 -0
  66. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
  67. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
  68. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/keys.py +0 -0
  69. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/lifecycle/__init__.py +0 -0
  70. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/lifecycle/manager.py +0 -0
  71. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/lifecycle/updater.py +0 -0
  72. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
  73. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/process/__init__.py +0 -0
  74. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/process/port.py +0 -0
  75. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/protocol/__init__.py +0 -0
  76. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/protocol/base.py +0 -0
  77. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/__init__.py +0 -0
  78. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/conpty.py +0 -0
  79. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/local/__init__.py +0 -0
  80. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/local/core.py +0 -0
  81. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/local/proc.py +0 -0
  82. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/local/unix.py +0 -0
  83. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/local/win.py +0 -0
  84. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/sanitize.py +0 -0
  85. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/session/__init__.py +0 -0
  86. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/session/base.py +0 -0
  87. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/session/client.py +0 -0
  88. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/ssh.py +0 -0
  89. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/exec/terminal/tmux.py +0 -0
  90. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/console/__init__.py +0 -0
  91. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/console/charmap.py +0 -0
  92. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/console/spinner.py +0 -0
  93. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/console/ui.py +0 -0
  94. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/events/__init__.py +0 -0
  95. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/events/bus.py +0 -0
  96. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/events/event.py +0 -0
  97. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/spinner/__init__.py +0 -0
  98. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/spinner/spinner.py +0 -0
  99. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/spinner/spinner_color.py +0 -0
  100. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/tracing/__init__.py +0 -0
  101. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/tracing/context.py +0 -0
  102. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/tracing/span.py +0 -0
  103. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/tracing/tracer.py +0 -0
  104. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/translate.py +0 -0
  105. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/watcher/__init__.py +0 -0
  106. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/watcher/file_watcher.py +0 -0
  107. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/__init__.py +0 -0
  108. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/application.py +0 -0
  109. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/broker.py +0 -0
  110. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/app.js +0 -0
  111. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/filezone.js +0 -0
  112. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
  113. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
  114. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
  115. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
  116. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/textinput.js +0 -0
  117. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
  118. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
  119. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
  120. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
  121. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/fonts/tailwind.css +0 -0
  122. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/inputbox.css +0 -0
  123. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/inputbox.js +0 -0
  124. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/style.css +0 -0
  125. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/input_box/voicegif.js +0 -0
  126. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/middleware.py +0 -0
  127. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/stats.py +0 -0
  128. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/media/web/utils.py +0 -0
  129. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/files/__init__.py +0 -0
  130. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/files/file_util.py +0 -0
  131. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/network/__init__.py +0 -0
  132. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/network/http_utils.py +0 -0
  133. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/plugin/__init__.py +0 -0
  134. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/plugin/base.py +0 -0
  135. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/plugin/discovery.py +0 -0
  136. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/plugin/registry.py +0 -0
  137. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/plugin/registry_ops.py +0 -0
  138. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/proxy/__init__.py +0 -0
  139. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/proxy/manager.py +0 -0
  140. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/runtime/__init__.py +0 -0
  141. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/runtime/collector.py +0 -0
  142. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/scheduler/__init__.py +0 -0
  143. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/scheduler/scheduler.py +0 -0
  144. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/sdk/__init__.py +0 -0
  145. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/plat/sdk/facade.py +0 -0
  146. {echotools-2.3.30 → echotools-2.3.31}/src/echotools/version.py +0 -0
  147. {echotools-2.3.30 → echotools-2.3.31}/src/echotools.egg-info/SOURCES.txt +0 -0
  148. {echotools-2.3.30 → echotools-2.3.31}/src/echotools.egg-info/dependency_links.txt +0 -0
  149. {echotools-2.3.30 → echotools-2.3.31}/src/echotools.egg-info/requires.txt +0 -0
  150. {echotools-2.3.30 → echotools-2.3.31}/src/echotools.egg-info/top_level.txt +0 -0
  151. {echotools-2.3.30 → echotools-2.3.31}/src/tests/bench_import.py +0 -0
  152. {echotools-2.3.30 → echotools-2.3.31}/src/tests/conftest.py +0 -0
  153. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_cache_retry.py +0 -0
  154. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_completion.py +0 -0
  155. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_config.py +0 -0
  156. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_config_extended.py +0 -0
  157. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_console.py +0 -0
  158. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_coverage_core.py +0 -0
  159. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_dispatch.py +0 -0
  160. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_dispatch_extended.py +0 -0
  161. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_dispatch_race.py +0 -0
  162. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_entml_thinking_history.py +0 -0
  163. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_entml_thinking_parse.py +0 -0
  164. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_entml_tools.py +0 -0
  165. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_errors.py +0 -0
  166. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_errors_common.py +0 -0
  167. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_events.py +0 -0
  168. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_events_event.py +0 -0
  169. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_facade.py +0 -0
  170. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_fncall.py +0 -0
  171. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_ids.py +0 -0
  172. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_imports.py +0 -0
  173. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_io.py +0 -0
  174. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_keys.py +0 -0
  175. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_lifecycle.py +0 -0
  176. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_logger.py +0 -0
  177. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_merge.py +0 -0
  178. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_plugin.py +0 -0
  179. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_printstream.py +0 -0
  180. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_protocols.py +0 -0
  181. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_proxy.py +0 -0
  182. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_proxy_selector.py +0 -0
  183. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_runtime.py +0 -0
  184. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_scheduler.py +0 -0
  185. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_tracing.py +0 -0
  186. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_usage.py +0 -0
  187. {echotools-2.3.30 → echotools-2.3.31}/src/tests/test_web.py +0 -0
  188. {echotools-2.3.30 → echotools-2.3.31}/src/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.30
3
+ Version: 2.3.31
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.30"
7
+ version = "2.3.31"
8
8
  description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
9
9
  readme = "docs/README.md"
10
10
  requires-python = ">=3.8"
@@ -15,6 +15,17 @@ from .prompt_helpers import (
15
15
  )
16
16
 
17
17
 
18
+ def history_contains_tool_calls(messages: List[Dict[str, Any]]) -> bool:
19
+ """历史消息中是否包含 assistant tool_calls 或 tool 结果。"""
20
+ for m in messages:
21
+ role = m.get("role") or "user"
22
+ if role == "assistant" and m.get("tool_calls"):
23
+ return True
24
+ if role == "tool":
25
+ return True
26
+ return False
27
+
28
+
18
29
  def _format_conversation_history(
19
30
  messages: List[Dict[str, Any]],
20
31
  protocol: Optional[Any] = None,
@@ -12,6 +12,7 @@ from echotools.exec.fncall.prompt.history import (
12
12
  )
13
13
  from echotools.exec.fncall.prompt.history_format import (
14
14
  _format_conversation_history,
15
+ history_contains_tool_calls,
15
16
  )
16
17
  from echotools.exec.fncall.prompt.prompt_helpers import (
17
18
  build_no_tools_prompt,
@@ -61,6 +62,7 @@ def build_tools_prompt(
61
62
  current_user_message: str,
62
63
  loop_detection_threshold: int,
63
64
  protocol_options: Optional[Dict[str, Any]] = None,
65
+ history_has_tool_calls: bool = False,
64
66
  ) -> str:
65
67
  loop_warning = ""
66
68
  if loop_detection_threshold > 0:
@@ -77,6 +79,8 @@ def build_tools_prompt(
77
79
  if protocol_options is not None and protocol.id == "entml"
78
80
  else {}
79
81
  )
82
+ if protocol.id == "entml":
83
+ extra["history_has_tool_calls"] = history_has_tool_calls
80
84
  return protocol.render_prompt(
81
85
  tool_descs=tool_descs,
82
86
  lang=lang,
@@ -133,6 +137,7 @@ def inject_fncall(
133
137
  prompt = build_tools_prompt(
134
138
  protocol, tools, normalized, lang, user_system_prompt,
135
139
  history_text, current_user_message, loop_detection_threshold, protocol_options,
140
+ history_has_tool_calls=history_contains_tool_calls(history_messages),
136
141
  )
137
142
  if dump_prompt:
138
143
  _maybe_dump_prompt(prompt, dump_dir)
@@ -118,6 +118,15 @@ _HISTORY_CLARIFY_EN = (
118
118
  "The user's latest message follows below."
119
119
  )
120
120
 
121
+ _HISTORY_TOOL_INVOKE_REMINDER_EN = (
122
+ "Reminder — tool notation in the conversation history above: prior tool "
123
+ "calls appear as compact pseudocode (e.g. `[function_name(arg=\"value\")]` "
124
+ "and optional `→ Result:` / `→ Error:` lines). That transcript is for "
125
+ "context only; it is not parsed and must not be copied. To invoke a tool "
126
+ "in your reply, output a valid `<entml:invoke>` block exactly as specified "
127
+ "in the tool instructions at the top of this prompt."
128
+ )
129
+
121
130
  _HISTORY_CLARIFY_ZH = (
122
131
  "以下是已完成的交互记录。"
123
132
  "此处展示的所有工具调用及其结果均已执行完毕,请勿重复调用。"
@@ -10,7 +10,10 @@ from __future__ import annotations
10
10
  import json as _json
11
11
  from typing import Any, Dict, List, Optional, Tuple
12
12
 
13
- from echotools.exec.fncall.prompt.templates import _HISTORY_CLARIFY_EN
13
+ from echotools.exec.fncall.prompt.templates import (
14
+ _HISTORY_CLARIFY_EN,
15
+ _HISTORY_TOOL_INVOKE_REMINDER_EN,
16
+ )
14
17
  from echotools.exec.fncall.protocols.entml_invoke import (
15
18
  format_entml_tool_calls,
16
19
  parse_entml_tool_calls,
@@ -117,6 +120,7 @@ class EntmlProtocol(ToolProtocol):
117
120
  loop_warning: str = "",
118
121
  current_user_message: str = "",
119
122
  protocol_options: Optional[Dict[str, Any]] = None,
123
+ history_has_tool_calls: bool = False,
120
124
  ) -> str:
121
125
  if tool_descs:
122
126
  sections: List[str] = [
@@ -138,6 +142,8 @@ class EntmlProtocol(ToolProtocol):
138
142
  sections.append(
139
143
  format_entml_conversation_history(history_text, _HISTORY_CLARIFY_EN)
140
144
  )
145
+ if tool_descs and history_has_tool_calls:
146
+ sections.append(_HISTORY_TOOL_INVOKE_REMINDER_EN)
141
147
 
142
148
  if loop_warning:
143
149
  sections.append(f"<loop_warning>\n{loop_warning}\n</loop_warning>")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echotools
3
- Version: 2.3.30
3
+ Version: 2.3.31
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Author: nichengfuben
6
6
  License: MIT
@@ -207,6 +207,49 @@ def test_entml_history_clarify_always_english() -> None:
207
207
  content = inject_fncall(msgs, tools, proto, lang="zh")[0]["content"]
208
208
  assert "The following is a transcript of completed interactions." in content
209
209
  assert "以下是已完成的交互记录" not in content
210
+ assert "Reminder — tool notation in the conversation history above" not in content
211
+
212
+
213
+ def test_entml_history_tool_invoke_reminder_when_tools_in_history() -> None:
214
+ proto = get_protocol("entml")
215
+ tools = [
216
+ {
217
+ "type": "function",
218
+ "function": {
219
+ "name": "search",
220
+ "description": "Search",
221
+ "parameters": {
222
+ "type": "object",
223
+ "properties": {"query": {"type": "string"}},
224
+ },
225
+ },
226
+ }
227
+ ]
228
+ msgs = [
229
+ {"role": "user", "content": "find docs"},
230
+ {
231
+ "role": "assistant",
232
+ "content": "",
233
+ "tool_calls": [{
234
+ "id": "call_1",
235
+ "type": "function",
236
+ "function": {
237
+ "name": "search",
238
+ "arguments": json.dumps({"query": "docs"}),
239
+ },
240
+ }],
241
+ },
242
+ {"role": "tool", "tool_call_id": "call_1", "content": "found 3"},
243
+ {"role": "user", "content": "summarize"},
244
+ ]
245
+ content = inject_fncall(msgs, tools, proto)[0]["content"]
246
+ assert "[search(query=\"docs\")]" in content
247
+ assert "Reminder — tool notation in the conversation history above" in content
248
+ assert "output a valid `<entml:invoke>` block" in content
249
+ hist_idx = content.index("<entml:conversation_history>")
250
+ reminder_idx = content.index("Reminder — tool notation")
251
+ user_idx = content.index("<current_user_message>")
252
+ assert hist_idx < reminder_idx < user_idx
210
253
 
211
254
 
212
255
  @pytest.mark.parametrize(
File without changes
File without changes
File without changes