echotools 2.3.67__tar.gz → 2.3.70__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 (236) hide show
  1. {echotools-2.3.67 → echotools-2.3.70}/PKG-INFO +1 -1
  2. {echotools-2.3.67 → echotools-2.3.70}/pyproject.toml +1 -1
  3. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/prompt/inject.py +3 -1
  4. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml.py +1 -1
  5. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_think/core.py +31 -1
  6. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_think/hist.py +12 -0
  7. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/history_markup.py +112 -4
  8. {echotools-2.3.67 → echotools-2.3.70}/src/echotools.egg-info/PKG-INFO +1 -1
  9. {echotools-2.3.67 → echotools-2.3.70}/src/echotools.egg-info/SOURCES.txt +4 -0
  10. echotools-2.3.70/src/tests/fixtures/simulated_fake_history_markup_responses.py +407 -0
  11. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_protocol.py +106 -0
  12. echotools-2.3.70/src/tests/test_fake_history_markup_batch.py +75 -0
  13. echotools-2.3.70/src/tests/test_fake_history_markup_invoke_preserve.py +130 -0
  14. echotools-2.3.70/src/tests/test_fake_history_markup_stream.py +151 -0
  15. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_history_markup_filter.py +8 -0
  16. {echotools-2.3.67 → echotools-2.3.70}/README.md +0 -0
  17. {echotools-2.3.67 → echotools-2.3.70}/docs/README.md +0 -0
  18. {echotools-2.3.67 → echotools-2.3.70}/setup.cfg +0 -0
  19. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/__init__.py +0 -0
  20. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/cache/__init__.py +0 -0
  21. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/cache/list_cache.py +0 -0
  22. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/cache/memory_cache.py +0 -0
  23. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/__init__.py +0 -0
  24. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/base.py +0 -0
  25. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/center.py +0 -0
  26. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/center_notify.py +0 -0
  27. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/center_template.py +0 -0
  28. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/loader.py +0 -0
  29. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/config/merge.py +0 -0
  30. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/errors/__init__.py +0 -0
  31. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/errors/base.py +0 -0
  32. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/errors/classify.py +0 -0
  33. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/errors/common.py +0 -0
  34. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/errors/http.py +0 -0
  35. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/ids/__init__.py +0 -0
  36. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/ids/generator.py +0 -0
  37. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/io/__init__.py +0 -0
  38. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/io/io_utils.py +0 -0
  39. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/io/printstream.py +0 -0
  40. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/logger/__init__.py +0 -0
  41. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/logger/manager.py +0 -0
  42. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/retry/__init__.py +0 -0
  43. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/base/retry/retry.py +0 -0
  44. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/compat.py +0 -0
  45. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/__init__.py +0 -0
  46. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/candidate.py +0 -0
  47. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/dispatcher.py +0 -0
  48. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
  49. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/race.py +0 -0
  50. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/selector.py +0 -0
  51. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/dispatch/usage.py +0 -0
  52. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/__init__.py +0 -0
  53. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
  54. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/parsers/stream.py +0 -0
  55. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
  56. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
  57. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
  58. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/prompt/history.py +0 -0
  59. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
  60. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
  61. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/prompt/templates.py +0 -0
  62. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
  63. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
  64. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
  65. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
  66. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
  67. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_think/parse.py +0 -0
  68. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
  69. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
  70. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/registry.py +0 -0
  71. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/__init__.py +0 -0
  72. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/coercion.py +0 -0
  73. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
  74. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/normalization.py +0 -0
  75. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
  76. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
  77. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/keys.py +0 -0
  78. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/lifecycle/__init__.py +0 -0
  79. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/lifecycle/manager.py +0 -0
  80. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/lifecycle/updater.py +0 -0
  81. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
  82. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/process/__init__.py +0 -0
  83. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/process/port.py +0 -0
  84. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/protocol/__init__.py +0 -0
  85. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/protocol/base.py +0 -0
  86. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/__init__.py +0 -0
  87. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/conpty.py +0 -0
  88. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/local/__init__.py +0 -0
  89. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/local/core.py +0 -0
  90. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/local/proc.py +0 -0
  91. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/local/unix.py +0 -0
  92. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/local/win.py +0 -0
  93. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/sanitize.py +0 -0
  94. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/session/__init__.py +0 -0
  95. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/session/base.py +0 -0
  96. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/session/client.py +0 -0
  97. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/ssh.py +0 -0
  98. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/exec/terminal/tmux.py +0 -0
  99. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/__init__.py +0 -0
  100. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/charmap.py +0 -0
  101. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/spinner.py +0 -0
  102. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/ui.py +0 -0
  103. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
  104. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
  105. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
  106. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
  107. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
  108. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
  109. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/ui_console.py +0 -0
  110. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/ui_io.py +0 -0
  111. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/ui_log.py +0 -0
  112. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/ui_platform.py +0 -0
  113. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/ui_text.py +0 -0
  114. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uicore/ui_types.py +0 -0
  115. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
  116. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
  117. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
  118. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
  119. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uilayout/ui_table.py +0 -0
  120. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
  121. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
  122. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
  123. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
  124. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
  125. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
  126. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/events/__init__.py +0 -0
  127. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/events/bus.py +0 -0
  128. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/events/event.py +0 -0
  129. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/spinner/__init__.py +0 -0
  130. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/spinner/spinner.py +0 -0
  131. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/spinner/spinner_color.py +0 -0
  132. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/tracing/__init__.py +0 -0
  133. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/tracing/context.py +0 -0
  134. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/tracing/span.py +0 -0
  135. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/tracing/tracer.py +0 -0
  136. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/translate.py +0 -0
  137. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/watcher/__init__.py +0 -0
  138. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/watcher/file_watcher.py +0 -0
  139. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/__init__.py +0 -0
  140. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/application.py +0 -0
  141. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/broker.py +0 -0
  142. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/app.js +0 -0
  143. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/filezone.js +0 -0
  144. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
  145. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
  146. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
  147. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
  148. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/textinput.js +0 -0
  149. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
  150. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
  151. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
  152. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
  153. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/inputbox.css +0 -0
  154. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/inputbox.js +0 -0
  155. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/style.css +0 -0
  156. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/input_box/voicegif.js +0 -0
  157. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/middleware.py +0 -0
  158. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/stats.py +0 -0
  159. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/media/web/utils.py +0 -0
  160. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/files/__init__.py +0 -0
  161. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/files/file_util.py +0 -0
  162. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/network/__init__.py +0 -0
  163. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/network/http_utils.py +0 -0
  164. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/plugin/__init__.py +0 -0
  165. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/plugin/base.py +0 -0
  166. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/plugin/discovery.py +0 -0
  167. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/plugin/registry.py +0 -0
  168. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/plugin/registry_ops.py +0 -0
  169. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/proxy/__init__.py +0 -0
  170. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/proxy/manager.py +0 -0
  171. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/runtime/__init__.py +0 -0
  172. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/runtime/collector.py +0 -0
  173. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/scheduler/__init__.py +0 -0
  174. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/scheduler/scheduler.py +0 -0
  175. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/sdk/__init__.py +0 -0
  176. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/plat/sdk/facade.py +0 -0
  177. {echotools-2.3.67 → echotools-2.3.70}/src/echotools/version.py +0 -0
  178. {echotools-2.3.67 → echotools-2.3.70}/src/echotools.egg-info/dependency_links.txt +0 -0
  179. {echotools-2.3.67 → echotools-2.3.70}/src/echotools.egg-info/requires.txt +0 -0
  180. {echotools-2.3.67 → echotools-2.3.70}/src/echotools.egg-info/top_level.txt +0 -0
  181. {echotools-2.3.67 → echotools-2.3.70}/src/tests/bench_import.py +0 -0
  182. {echotools-2.3.67 → echotools-2.3.70}/src/tests/conftest.py +0 -0
  183. {echotools-2.3.67 → echotools-2.3.70}/src/tests/fixtures/__init__.py +0 -0
  184. {echotools-2.3.67 → echotools-2.3.70}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
  185. {echotools-2.3.67 → echotools-2.3.70}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
  186. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_adversarial_stream_splits.py +0 -0
  187. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_cache_retry.py +0 -0
  188. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_completion.py +0 -0
  189. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_config.py +0 -0
  190. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_config_extended.py +0 -0
  191. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_console.py +0 -0
  192. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_coverage_core.py +0 -0
  193. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_dispatch.py +0 -0
  194. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_dispatch_extended.py +0 -0
  195. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_dispatch_race.py +0 -0
  196. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_agent_output_formats.py +0 -0
  197. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_bare_invoke_format.py +0 -0
  198. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_parser_comprehensive.py +0 -0
  199. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_stream_json.py +0 -0
  200. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
  201. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_thinking_history.py +0 -0
  202. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_thinking_parse.py +0 -0
  203. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_tool_parse_leak.py +0 -0
  204. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_tool_pipeline.py +0 -0
  205. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_entml_tools.py +0 -0
  206. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_errors.py +0 -0
  207. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_errors_common.py +0 -0
  208. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_events.py +0 -0
  209. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_events_event.py +0 -0
  210. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_facade.py +0 -0
  211. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_fault_thinking_close_batch.py +0 -0
  212. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_fault_thinking_close_stream.py +0 -0
  213. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_fncall.py +0 -0
  214. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_fncall_stream_thinking.py +0 -0
  215. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_ids.py +0 -0
  216. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_imports.py +0 -0
  217. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_io.py +0 -0
  218. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_keys.py +0 -0
  219. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_lifecycle.py +0 -0
  220. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_logger.py +0 -0
  221. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_merge.py +0 -0
  222. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_model_branch_matrix.py +0 -0
  223. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_plugin.py +0 -0
  224. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_printstream.py +0 -0
  225. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_protocols.py +0 -0
  226. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_proxy.py +0 -0
  227. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_proxy_selector.py +0 -0
  228. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_runtime.py +0 -0
  229. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_scheduler.py +0 -0
  230. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_simulated_llm_tool_parse.py +0 -0
  231. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_stream_partial_json_parity.py +0 -0
  232. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_stream_realtime_parse.py +0 -0
  233. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_tracing.py +0 -0
  234. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_usage.py +0 -0
  235. {echotools-2.3.67 → echotools-2.3.70}/src/tests/test_web.py +0 -0
  236. {echotools-2.3.67 → echotools-2.3.70}/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.67
3
+ Version: 2.3.70
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Home-page: https://github.com/nichengfuben/echotools
6
6
  Author: nichengfuben
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "echotools"
7
- version = "2.3.67"
7
+ version = "2.3.70"
8
8
  description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
9
9
  readme = "docs/README.md"
10
10
  requires-python = ">=3.8"
@@ -166,7 +166,9 @@ def inject_fncall(
166
166
  sections.append(format_entml_current_user_message(current_user_message))
167
167
  # thinking 放在最后,超限截断时优先保留在 send_text 尾部
168
168
  thinking_section = build_entml_thinking_section(
169
- protocol_options, has_tools=False
169
+ protocol_options,
170
+ has_tools=False,
171
+ history_text=history_text,
170
172
  )
171
173
  if thinking_section:
172
174
  sections.append(thinking_section)
@@ -214,7 +214,7 @@ class EntmlProtocol(ToolProtocol):
214
214
 
215
215
  # thinking 放在最后,超限截断时优先保留在 send_text 尾部
216
216
  thinking_section = build_entml_thinking_section(
217
- protocol_options, has_tools=bool(tool_descs)
217
+ protocol_options, has_tools=bool(tool_descs), history_text=history_text
218
218
  )
219
219
  if thinking_section:
220
220
  sections.append(thinking_section)
@@ -80,6 +80,16 @@ After completed tool turns appear in conversation history inside <tool> blocks (
80
80
  _THINKING_BEHAVIOR_AUTO_NO_TOOLS = """\
81
81
  You decide whether extended thinking helps for each reply. When the question has hidden complexity or when you are uncertain, open a <entml:thinking> block before continuing and strongly prefer to do so rather than guessing."""
82
82
 
83
+ _THINKING_BEHAVIOR_OFF_WITH_HISTORY_WITH_TOOLS = """\
84
+ Extended thinking is disabled for this reply. Do NOT output a <entml:thinking> block.
85
+
86
+ Past assistant turns in conversation history may include <entml:thinking>...</entml:thinking> blocks for context only. Do not imitate or continue those blocks. Reply with visible text and/or <entml:invoke> tool call(s) directly."""
87
+
88
+ _THINKING_BEHAVIOR_OFF_WITH_HISTORY_NO_TOOLS = """\
89
+ Extended thinking is disabled for this reply. Do NOT output a <entml:thinking> block.
90
+
91
+ Past assistant turns in conversation history may include <entml:thinking>...</entml:thinking> blocks for context only. Do not imitate or continue those blocks. Reply with visible text directly."""
92
+
83
93
 
84
94
  def normalize_thinking_level(level: Any) -> Optional[str]:
85
95
  """将 echotools 思考挡位归一化为 none | low | medium | high | xhigh | max | auto。"""
@@ -181,14 +191,34 @@ def _format_thinking_behavior(injection_mode: str, *, has_tools: bool) -> str:
181
191
  return f"<thinking_behavior>\n{body}\n</thinking_behavior>"
182
192
 
183
193
 
194
+ def _format_forced_no_thinking_behavior(*, has_tools: bool) -> str:
195
+ body = (
196
+ _THINKING_BEHAVIOR_OFF_WITH_HISTORY_WITH_TOOLS
197
+ if has_tools
198
+ else _THINKING_BEHAVIOR_OFF_WITH_HISTORY_NO_TOOLS
199
+ )
200
+ return f"<thinking_behavior>\n{body}\n</thinking_behavior>"
201
+
202
+
184
203
  def build_entml_thinking_section(
185
204
  protocol_options: Optional[Dict[str, Any]] = None,
186
205
  *,
187
206
  has_tools: bool = True,
207
+ history_text: str = "",
188
208
  ) -> str:
189
- """按思考挡位构建注入块:thinking_mode + max_thinking_length + thinking_behavior。"""
209
+ """按思考挡位构建注入块:thinking_mode + max_thinking_length + thinking_behavior。
210
+
211
+ 思考关闭时:默认不注入;若 history 含 ``<entml:thinking>`` 则仅注入强制不思考的
212
+ ``<thinking_behavior>``(不含 ``<entml:thinking_mode>``)。
213
+ """
214
+ from echotools.exec.fncall.protocols.entml_think.hist import (
215
+ history_text_contains_entml_thinking,
216
+ )
217
+
190
218
  resolved = resolve_thinking_injection(protocol_options)
191
219
  if resolved is None:
220
+ if history_text_contains_entml_thinking(history_text):
221
+ return _format_forced_no_thinking_behavior(has_tools=has_tools)
192
222
  return ""
193
223
 
194
224
  injection_mode, max_length = resolved
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
3
4
  from typing import Any, Dict, List, Mapping, Optional
4
5
 
5
6
  REASONING_KEYS = ("reasoning", "reasoning_content", "reasoning_details")
@@ -13,6 +14,17 @@ _THINKING_BLOCK_TYPES = frozenset({"thinking", "reasoning", "redacted_thinking"}
13
14
 
14
15
  _THINKING_BLOCK_OPEN = "<entml:thinking>"
15
16
  _THINKING_BLOCK_CLOSE = "</entml:thinking>"
17
+ _THINKING_OPEN_DETECT_RE = re.compile(
18
+ r"<entml:thinking\b",
19
+ re.IGNORECASE,
20
+ )
21
+
22
+
23
+ def history_text_contains_entml_thinking(history_text: str) -> bool:
24
+ """conversation_history 正文是否含 ``<entml:thinking>`` 历史思考块。"""
25
+ if not history_text or not history_text.strip():
26
+ return False
27
+ return _THINKING_OPEN_DETECT_RE.search(history_text) is not None
16
28
 
17
29
 
18
30
  def format_entml_thinking_history_block(text: str) -> str:
@@ -25,6 +25,14 @@ _PLAIN_THINKING_OPEN_LINE_RE = re.compile(
25
25
  r"(?:^|\n)\s*<thinking\s*>\s*(?:\n|$)",
26
26
  re.IGNORECASE | re.MULTILINE,
27
27
  )
28
+ _ENTML_INVOKE_OPEN_RE = re.compile(
29
+ r"<entml:invoke\b",
30
+ re.IGNORECASE,
31
+ )
32
+ _ENTML_INVOKE_BLOCK_RE = re.compile(
33
+ r"<entml:invoke\b[^>]*>[\s\S]*?</entml:invoke>",
34
+ re.IGNORECASE,
35
+ )
28
36
 
29
37
 
30
38
  class HistoryMarkupDetectionResult:
@@ -65,6 +73,14 @@ def _paired_block_re(tag: str) -> re.Pattern[str]:
65
73
  )
66
74
 
67
75
 
76
+ def _paired_block_anywhere_re(tag: str) -> re.Pattern[str]:
77
+ """``<tool>\\n…\\n</tool>`` 即使前缀无换行(流式分片边界)也剥离。"""
78
+ return re.compile(
79
+ rf"<{tag}\s*>\s*\n[\s\S]*?\n\s*</{tag}\s*>",
80
+ re.IGNORECASE,
81
+ )
82
+
83
+
68
84
  def _orphan_close_re(tag: str) -> re.Pattern[str]:
69
85
  return re.compile(
70
86
  rf"(?:^|\n)([\s\S]*?)\n\s*</{tag}\s*>\s*(?:\n|$)",
@@ -79,19 +95,77 @@ def _orphan_open_re(tag: str) -> re.Pattern[str]:
79
95
  )
80
96
 
81
97
 
82
- def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
98
+ def _split_entml_invoke_protected(segment: str) -> List[Tuple[str, bool]]:
99
+ """按 ``<entml:invoke>`` 切分;invoke 块(含未闭合尾部)不参与伪 history 剥离。"""
100
+ if not segment:
101
+ return []
102
+ parts: List[Tuple[str, bool]] = []
103
+ i = 0
104
+ while i < len(segment):
105
+ open_m = _ENTML_INVOKE_OPEN_RE.search(segment, i)
106
+ if not open_m:
107
+ parts.append((segment[i:], False))
108
+ break
109
+ if open_m.start() > i:
110
+ parts.append((segment[i : open_m.start()], False))
111
+ block_m = _ENTML_INVOKE_BLOCK_RE.match(segment, open_m.start())
112
+ if block_m:
113
+ parts.append((block_m.group(0), True))
114
+ i = block_m.end()
115
+ else:
116
+ parts.append((segment[open_m.start() :], True))
117
+ break
118
+ return parts
119
+
120
+
121
+ def _open_before_entml_re(tag: str) -> re.Pattern[str]:
122
+ """``<tool>\\n…`` 未闭合但在 ``<entml:invoke`` 前 — 整段剥离。"""
123
+ return re.compile(
124
+ rf"<{tag}\s*>\s*\n[\s\S]*?(?=<entml:invoke\b)",
125
+ re.IGNORECASE,
126
+ )
127
+
128
+
129
+ def _orphan_open_to_eof_re(tag: str) -> re.Pattern[str]:
130
+ """未闭合伪块直到段末(下一段为 ``<entml:invoke>`` 时)。"""
131
+ return re.compile(
132
+ rf"<{tag}\s*>\s*\n[\s\S]*$",
133
+ re.IGNORECASE,
134
+ )
135
+
136
+
137
+ def _strip_fake_blocks_in_unprotected(
138
+ segment: str,
139
+ *,
140
+ followed_by_invoke: bool = False,
141
+ ) -> Tuple[str, bool]:
83
142
  if not segment:
84
143
  return segment, False
85
144
  found = False
86
145
  text = segment
87
146
  for tag in _FAKE_HISTORY_TAGS:
88
- pair_re = _paired_block_re(tag)
147
+ for pair_re in (_paired_block_re(tag), _paired_block_anywhere_re(tag)):
148
+ while True:
149
+ new_text, n = pair_re.subn("", text, count=1)
150
+ if n == 0:
151
+ break
152
+ found = True
153
+ text = new_text
154
+ before_entml = _open_before_entml_re(tag)
89
155
  while True:
90
- new_text, n = pair_re.subn("", text, count=1)
156
+ new_text, n = before_entml.subn("", text, count=1)
91
157
  if n == 0:
92
158
  break
93
159
  found = True
94
160
  text = new_text
161
+ if followed_by_invoke:
162
+ to_eof = _orphan_open_to_eof_re(tag)
163
+ while True:
164
+ new_text, n = to_eof.subn("", text, count=1)
165
+ if n == 0:
166
+ break
167
+ found = True
168
+ text = new_text
95
169
  close_re = _orphan_close_re(tag)
96
170
  while True:
97
171
  new_text, n = close_re.subn("\n", text, count=1)
@@ -117,6 +191,28 @@ def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
117
191
  return text, found
118
192
 
119
193
 
194
+ def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
195
+ if not segment:
196
+ return segment, False
197
+ parts = _split_entml_invoke_protected(segment)
198
+ if len(parts) == 1 and parts[0][1]:
199
+ return segment, False
200
+ out: List[str] = []
201
+ found = False
202
+ for idx, (chunk, protected) in enumerate(parts):
203
+ if protected:
204
+ out.append(chunk)
205
+ continue
206
+ next_is_invoke = idx + 1 < len(parts) and parts[idx + 1][1]
207
+ cleaned, hit = _strip_fake_blocks_in_unprotected(
208
+ chunk,
209
+ followed_by_invoke=next_is_invoke,
210
+ )
211
+ out.append(cleaned)
212
+ found = found or hit
213
+ return "".join(out), found
214
+
215
+
120
216
  def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
121
217
  """剥离块级伪 ``<assistant>`` / ``<tool>``;``<entml:thinking>`` 区域原样保留。"""
122
218
  if not content:
@@ -153,12 +249,24 @@ def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
153
249
  def strip_fake_history_markup_for_display(content: str) -> Tuple[str, bool]:
154
250
  """流式 ``partial_text`` 用:完整块剥离 + 截断行尾未收齐的伪标签。"""
155
251
  cleaned, found = strip_fake_history_markup(content)
252
+ invoke_m = _ENTML_INVOKE_OPEN_RE.search(cleaned)
253
+ if invoke_m and "</entml:invoke>" not in cleaned[invoke_m.start() :]:
254
+ return cleaned, found
156
255
  tail = re.search(
157
- r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*)$",
256
+ r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*(?:<(?:assistant|tool)\b[^\n]*)?)$",
158
257
  cleaned,
159
258
  re.IGNORECASE | re.MULTILINE,
160
259
  )
161
260
  if tail:
162
261
  cleaned = cleaned[: tail.start()]
163
262
  found = True
263
+ else:
264
+ glued = re.search(
265
+ r"</thinking>\s*<(?:assistant|tool)\b[^\n]*$",
266
+ cleaned,
267
+ re.IGNORECASE,
268
+ )
269
+ if glued:
270
+ cleaned = cleaned[: glued.start()]
271
+ found = True
164
272
  return cleaned, found
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echotools
3
- Version: 2.3.67
3
+ Version: 2.3.70
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Home-page: https://github.com/nichengfuben/echotools
6
6
  Author: nichengfuben
@@ -199,6 +199,9 @@ src/tests/test_errors_common.py
199
199
  src/tests/test_events.py
200
200
  src/tests/test_events_event.py
201
201
  src/tests/test_facade.py
202
+ src/tests/test_fake_history_markup_batch.py
203
+ src/tests/test_fake_history_markup_invoke_preserve.py
204
+ src/tests/test_fake_history_markup_stream.py
202
205
  src/tests/test_fault_thinking_close_batch.py
203
206
  src/tests/test_fault_thinking_close_stream.py
204
207
  src/tests/test_fncall.py
@@ -227,5 +230,6 @@ src/tests/test_usage.py
227
230
  src/tests/test_web.py
228
231
  src/tests/test_web_broker.py
229
232
  src/tests/fixtures/__init__.py
233
+ src/tests/fixtures/simulated_fake_history_markup_responses.py
230
234
  src/tests/fixtures/simulated_fault_thinking_responses.py
231
235
  src/tests/fixtures/simulated_llm_tool_responses.py