echotools 2.3.67__tar.gz → 2.3.71__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 (237) hide show
  1. {echotools-2.3.67 → echotools-2.3.71}/PKG-INFO +1 -1
  2. {echotools-2.3.67 → echotools-2.3.71}/pyproject.toml +1 -1
  3. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/inject.py +3 -1
  4. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml.py +3 -3
  5. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/core.py +31 -1
  6. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/hist.py +12 -0
  7. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/history_markup.py +138 -4
  8. {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/PKG-INFO +1 -1
  9. {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/SOURCES.txt +5 -0
  10. echotools-2.3.71/src/tests/fixtures/simulated_fake_history_markup_responses.py +481 -0
  11. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_protocol.py +106 -0
  12. echotools-2.3.71/src/tests/test_fake_history_markup_batch.py +75 -0
  13. echotools-2.3.71/src/tests/test_fake_history_markup_invoke_preserve.py +130 -0
  14. echotools-2.3.71/src/tests/test_fake_history_markup_reply_protect.py +166 -0
  15. echotools-2.3.71/src/tests/test_fake_history_markup_stream.py +151 -0
  16. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_history_markup_filter.py +8 -0
  17. {echotools-2.3.67 → echotools-2.3.71}/README.md +0 -0
  18. {echotools-2.3.67 → echotools-2.3.71}/docs/README.md +0 -0
  19. {echotools-2.3.67 → echotools-2.3.71}/setup.cfg +0 -0
  20. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/__init__.py +0 -0
  21. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/cache/__init__.py +0 -0
  22. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/cache/list_cache.py +0 -0
  23. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/cache/memory_cache.py +0 -0
  24. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/__init__.py +0 -0
  25. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/base.py +0 -0
  26. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/center.py +0 -0
  27. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/center_notify.py +0 -0
  28. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/center_template.py +0 -0
  29. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/loader.py +0 -0
  30. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/merge.py +0 -0
  31. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/__init__.py +0 -0
  32. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/base.py +0 -0
  33. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/classify.py +0 -0
  34. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/common.py +0 -0
  35. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/http.py +0 -0
  36. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/ids/__init__.py +0 -0
  37. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/ids/generator.py +0 -0
  38. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/io/__init__.py +0 -0
  39. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/io/io_utils.py +0 -0
  40. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/io/printstream.py +0 -0
  41. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/logger/__init__.py +0 -0
  42. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/logger/manager.py +0 -0
  43. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/retry/__init__.py +0 -0
  44. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/retry/retry.py +0 -0
  45. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/compat.py +0 -0
  46. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/__init__.py +0 -0
  47. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/candidate.py +0 -0
  48. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/dispatcher.py +0 -0
  49. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
  50. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/race.py +0 -0
  51. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/selector.py +0 -0
  52. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/usage.py +0 -0
  53. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/__init__.py +0 -0
  54. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
  55. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/stream.py +0 -0
  56. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
  57. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
  58. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
  59. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/history.py +0 -0
  60. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
  61. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
  62. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/templates.py +0 -0
  63. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
  64. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
  65. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
  66. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
  67. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
  68. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/parse.py +0 -0
  69. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
  70. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
  71. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/registry.py +0 -0
  72. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/__init__.py +0 -0
  73. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/coercion.py +0 -0
  74. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
  75. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/normalization.py +0 -0
  76. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
  77. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
  78. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/keys.py +0 -0
  79. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/__init__.py +0 -0
  80. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/manager.py +0 -0
  81. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/updater.py +0 -0
  82. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
  83. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/process/__init__.py +0 -0
  84. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/process/port.py +0 -0
  85. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/protocol/__init__.py +0 -0
  86. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/protocol/base.py +0 -0
  87. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/__init__.py +0 -0
  88. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/conpty.py +0 -0
  89. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/__init__.py +0 -0
  90. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/core.py +0 -0
  91. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/proc.py +0 -0
  92. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/unix.py +0 -0
  93. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/win.py +0 -0
  94. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/sanitize.py +0 -0
  95. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/session/__init__.py +0 -0
  96. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/session/base.py +0 -0
  97. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/session/client.py +0 -0
  98. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/ssh.py +0 -0
  99. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/tmux.py +0 -0
  100. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/__init__.py +0 -0
  101. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/charmap.py +0 -0
  102. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/spinner.py +0 -0
  103. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/ui.py +0 -0
  104. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
  105. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
  106. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
  107. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
  108. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
  109. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
  110. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_console.py +0 -0
  111. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_io.py +0 -0
  112. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_log.py +0 -0
  113. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_platform.py +0 -0
  114. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_text.py +0 -0
  115. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_types.py +0 -0
  116. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
  117. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
  118. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
  119. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
  120. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_table.py +0 -0
  121. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
  122. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
  123. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
  124. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
  125. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
  126. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
  127. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/events/__init__.py +0 -0
  128. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/events/bus.py +0 -0
  129. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/events/event.py +0 -0
  130. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/spinner/__init__.py +0 -0
  131. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/spinner/spinner.py +0 -0
  132. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/spinner/spinner_color.py +0 -0
  133. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/__init__.py +0 -0
  134. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/context.py +0 -0
  135. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/span.py +0 -0
  136. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/tracer.py +0 -0
  137. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/translate.py +0 -0
  138. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/watcher/__init__.py +0 -0
  139. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/watcher/file_watcher.py +0 -0
  140. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/__init__.py +0 -0
  141. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/application.py +0 -0
  142. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/broker.py +0 -0
  143. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/app.js +0 -0
  144. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/filezone.js +0 -0
  145. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
  146. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
  147. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
  148. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
  149. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/textinput.js +0 -0
  150. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
  151. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
  152. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
  153. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
  154. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/inputbox.css +0 -0
  155. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/inputbox.js +0 -0
  156. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/style.css +0 -0
  157. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/voicegif.js +0 -0
  158. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/middleware.py +0 -0
  159. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/stats.py +0 -0
  160. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/utils.py +0 -0
  161. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/files/__init__.py +0 -0
  162. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/files/file_util.py +0 -0
  163. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/network/__init__.py +0 -0
  164. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/network/http_utils.py +0 -0
  165. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/__init__.py +0 -0
  166. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/base.py +0 -0
  167. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/discovery.py +0 -0
  168. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/registry.py +0 -0
  169. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/registry_ops.py +0 -0
  170. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/proxy/__init__.py +0 -0
  171. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/proxy/manager.py +0 -0
  172. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/runtime/__init__.py +0 -0
  173. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/runtime/collector.py +0 -0
  174. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/scheduler/__init__.py +0 -0
  175. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/scheduler/scheduler.py +0 -0
  176. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/sdk/__init__.py +0 -0
  177. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/sdk/facade.py +0 -0
  178. {echotools-2.3.67 → echotools-2.3.71}/src/echotools/version.py +0 -0
  179. {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/dependency_links.txt +0 -0
  180. {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/requires.txt +0 -0
  181. {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/top_level.txt +0 -0
  182. {echotools-2.3.67 → echotools-2.3.71}/src/tests/bench_import.py +0 -0
  183. {echotools-2.3.67 → echotools-2.3.71}/src/tests/conftest.py +0 -0
  184. {echotools-2.3.67 → echotools-2.3.71}/src/tests/fixtures/__init__.py +0 -0
  185. {echotools-2.3.67 → echotools-2.3.71}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
  186. {echotools-2.3.67 → echotools-2.3.71}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
  187. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_adversarial_stream_splits.py +0 -0
  188. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_cache_retry.py +0 -0
  189. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_completion.py +0 -0
  190. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_config.py +0 -0
  191. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_config_extended.py +0 -0
  192. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_console.py +0 -0
  193. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_coverage_core.py +0 -0
  194. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_dispatch.py +0 -0
  195. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_dispatch_extended.py +0 -0
  196. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_dispatch_race.py +0 -0
  197. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_agent_output_formats.py +0 -0
  198. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_bare_invoke_format.py +0 -0
  199. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_parser_comprehensive.py +0 -0
  200. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_stream_json.py +0 -0
  201. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
  202. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_thinking_history.py +0 -0
  203. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_thinking_parse.py +0 -0
  204. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_tool_parse_leak.py +0 -0
  205. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_tool_pipeline.py +0 -0
  206. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_tools.py +0 -0
  207. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_errors.py +0 -0
  208. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_errors_common.py +0 -0
  209. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_events.py +0 -0
  210. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_events_event.py +0 -0
  211. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_facade.py +0 -0
  212. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fault_thinking_close_batch.py +0 -0
  213. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fault_thinking_close_stream.py +0 -0
  214. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fncall.py +0 -0
  215. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fncall_stream_thinking.py +0 -0
  216. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_ids.py +0 -0
  217. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_imports.py +0 -0
  218. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_io.py +0 -0
  219. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_keys.py +0 -0
  220. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_lifecycle.py +0 -0
  221. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_logger.py +0 -0
  222. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_merge.py +0 -0
  223. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_model_branch_matrix.py +0 -0
  224. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_plugin.py +0 -0
  225. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_printstream.py +0 -0
  226. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_protocols.py +0 -0
  227. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_proxy.py +0 -0
  228. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_proxy_selector.py +0 -0
  229. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_runtime.py +0 -0
  230. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_scheduler.py +0 -0
  231. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_simulated_llm_tool_parse.py +0 -0
  232. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_stream_partial_json_parity.py +0 -0
  233. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_stream_realtime_parse.py +0 -0
  234. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_tracing.py +0 -0
  235. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_usage.py +0 -0
  236. {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_web.py +0 -0
  237. {echotools-2.3.67 → echotools-2.3.71}/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.71
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.71"
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)
@@ -287,11 +287,11 @@ class EntmlProtocol(ToolProtocol):
287
287
  parse_text, _ = strip_fake_history_markup(parse_text)
288
288
  tool_calls = parse_entml_tool_calls(parse_text, tools, schema_index)
289
289
  clean = text[:unclosed_open_at] if unclosed_open_at >= 0 else text
290
+ # 须在移除 invoke 之前剥离伪 history,否则未闭合 <tool> 会把 invoke 之后的可见回复误删。
291
+ clean, _ = strip_fake_history_markup(clean)
290
292
  if tool_calls:
291
293
  clean = BLOCK_RE.sub("", clean)
292
- # 无论是否解析成功,都剥离工具相关残留,避免标签泄露;thinking 保留给后续 split。
293
294
  clean = strip_tool_entml_residue(clean)
294
- clean, _ = strip_fake_history_markup(clean)
295
295
  return (clean, normalize_tool_calls(tool_calls, tools))
296
296
 
297
297
  def parse_fragment(
@@ -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,23 @@ _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
+ )
36
+ _ENTML_FUNCTION_CALLS_OPEN_RE = re.compile(
37
+ r"<entml:function_calls\b",
38
+ re.IGNORECASE,
39
+ )
40
+ _ENTML_FUNCTION_CALLS_BLOCK_RE = re.compile(
41
+ r"<entml:function_calls\b[^>]*>[\s\S]*?</entml:function_calls>",
42
+ re.IGNORECASE,
43
+ )
44
+ _ENTML_FUNCTION_CALLS_CLOSE = "</entml:function_calls>"
28
45
 
29
46
 
30
47
  class HistoryMarkupDetectionResult:
@@ -65,6 +82,14 @@ def _paired_block_re(tag: str) -> re.Pattern[str]:
65
82
  )
66
83
 
67
84
 
85
+ def _paired_block_anywhere_re(tag: str) -> re.Pattern[str]:
86
+ """``<tool>\\n…\\n</tool>`` 即使前缀无换行(流式分片边界)也剥离。"""
87
+ return re.compile(
88
+ rf"<{tag}\s*>\s*\n[\s\S]*?\n\s*</{tag}\s*>",
89
+ re.IGNORECASE,
90
+ )
91
+
92
+
68
93
  def _orphan_close_re(tag: str) -> re.Pattern[str]:
69
94
  return re.compile(
70
95
  rf"(?:^|\n)([\s\S]*?)\n\s*</{tag}\s*>\s*(?:\n|$)",
@@ -79,19 +104,83 @@ def _orphan_open_re(tag: str) -> re.Pattern[str]:
79
104
  )
80
105
 
81
106
 
82
- def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
107
+ def _split_entml_invoke_protected(segment: str) -> List[Tuple[str, bool]]:
108
+ """按 ``<entml:function_calls>`` / ``<entml:invoke>`` 切分;保护区不参与伪 history 剥离。"""
109
+ if not segment:
110
+ return []
111
+ parts: List[Tuple[str, bool]] = []
112
+ i = 0
113
+ while i < len(segment):
114
+ fc_m = _ENTML_FUNCTION_CALLS_OPEN_RE.search(segment, i)
115
+ inv_m = _ENTML_INVOKE_OPEN_RE.search(segment, i)
116
+ candidates = [m for m in (fc_m, inv_m) if m]
117
+ if not candidates:
118
+ parts.append((segment[i:], False))
119
+ break
120
+ open_m = min(candidates, key=lambda m: m.start())
121
+ if open_m.start() > i:
122
+ parts.append((segment[i : open_m.start()], False))
123
+ if open_m is fc_m:
124
+ block_m = _ENTML_FUNCTION_CALLS_BLOCK_RE.match(segment, open_m.start())
125
+ else:
126
+ block_m = _ENTML_INVOKE_BLOCK_RE.match(segment, open_m.start())
127
+ if block_m:
128
+ parts.append((block_m.group(0), True))
129
+ i = block_m.end()
130
+ else:
131
+ parts.append((segment[open_m.start() :], True))
132
+ break
133
+ return parts
134
+
135
+
136
+ def _open_before_entml_re(tag: str) -> re.Pattern[str]:
137
+ """``<tool>\\n…`` 未闭合但在 ``<entml:(function_calls|invoke)`` 前 — 整段剥离。"""
138
+ return re.compile(
139
+ rf"<{tag}\s*>\s*\n[\s\S]*?(?=<entml:(?:function_calls|invoke)\b)",
140
+ re.IGNORECASE,
141
+ )
142
+
143
+
144
+ def _orphan_open_to_eof_re(tag: str) -> re.Pattern[str]:
145
+ """未闭合伪块直到段末(下一段为 ``<entml:invoke>`` 时)。"""
146
+ return re.compile(
147
+ rf"<{tag}\s*>\s*\n[\s\S]*$",
148
+ re.IGNORECASE,
149
+ )
150
+
151
+
152
+ def _strip_fake_blocks_in_unprotected(
153
+ segment: str,
154
+ *,
155
+ followed_by_invoke: bool = False,
156
+ ) -> Tuple[str, bool]:
83
157
  if not segment:
84
158
  return segment, False
85
159
  found = False
86
160
  text = segment
87
161
  for tag in _FAKE_HISTORY_TAGS:
88
- pair_re = _paired_block_re(tag)
162
+ for pair_re in (_paired_block_re(tag), _paired_block_anywhere_re(tag)):
163
+ while True:
164
+ new_text, n = pair_re.subn("", text, count=1)
165
+ if n == 0:
166
+ break
167
+ found = True
168
+ text = new_text
169
+ before_entml = _open_before_entml_re(tag)
89
170
  while True:
90
- new_text, n = pair_re.subn("", text, count=1)
171
+ new_text, n = before_entml.subn("", text, count=1)
91
172
  if n == 0:
92
173
  break
93
174
  found = True
94
175
  text = new_text
176
+ if followed_by_invoke:
177
+ to_eof = _orphan_open_to_eof_re(tag)
178
+ while True:
179
+ new_text, n = to_eof.subn("", text, count=1)
180
+ if n == 0:
181
+ break
182
+ found = True
183
+ text = new_text
95
184
  close_re = _orphan_close_re(tag)
96
185
  while True:
97
186
  new_text, n = close_re.subn("\n", text, count=1)
@@ -117,6 +206,28 @@ def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
117
206
  return text, found
118
207
 
119
208
 
209
+ def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
210
+ if not segment:
211
+ return segment, False
212
+ parts = _split_entml_invoke_protected(segment)
213
+ if len(parts) == 1 and parts[0][1]:
214
+ return segment, False
215
+ out: List[str] = []
216
+ found = False
217
+ for idx, (chunk, protected) in enumerate(parts):
218
+ if protected:
219
+ out.append(chunk)
220
+ continue
221
+ next_is_invoke = idx + 1 < len(parts) and parts[idx + 1][1]
222
+ cleaned, hit = _strip_fake_blocks_in_unprotected(
223
+ chunk,
224
+ followed_by_invoke=next_is_invoke,
225
+ )
226
+ out.append(cleaned)
227
+ found = found or hit
228
+ return "".join(out), found
229
+
230
+
120
231
  def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
121
232
  """剥离块级伪 ``<assistant>`` / ``<tool>``;``<entml:thinking>`` 区域原样保留。"""
122
233
  if not content:
@@ -153,12 +264,35 @@ def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
153
264
  def strip_fake_history_markup_for_display(content: str) -> Tuple[str, bool]:
154
265
  """流式 ``partial_text`` 用:完整块剥离 + 截断行尾未收齐的伪标签。"""
155
266
  cleaned, found = strip_fake_history_markup(content)
267
+ invoke_m = _ENTML_INVOKE_OPEN_RE.search(cleaned)
268
+ fc_m = _ENTML_FUNCTION_CALLS_OPEN_RE.search(cleaned)
269
+ prot_m = None
270
+ if invoke_m and fc_m:
271
+ prot_m = invoke_m if invoke_m.start() <= fc_m.start() else fc_m
272
+ else:
273
+ prot_m = invoke_m or fc_m
274
+ if prot_m:
275
+ tail = cleaned[prot_m.start() :]
276
+ invoke_open = _ENTML_INVOKE_OPEN_RE.search(tail)
277
+ if invoke_open and "</entml:invoke>" not in tail[invoke_open.start() :]:
278
+ return cleaned, found
279
+ if _ENTML_FUNCTION_CALLS_OPEN_RE.search(tail) and _ENTML_FUNCTION_CALLS_CLOSE not in tail:
280
+ return cleaned, found
156
281
  tail = re.search(
157
- r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*)$",
282
+ r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*(?:<(?:assistant|tool)\b[^\n]*)?)$",
158
283
  cleaned,
159
284
  re.IGNORECASE | re.MULTILINE,
160
285
  )
161
286
  if tail:
162
287
  cleaned = cleaned[: tail.start()]
163
288
  found = True
289
+ else:
290
+ glued = re.search(
291
+ r"</thinking>\s*<(?:assistant|tool)\b[^\n]*$",
292
+ cleaned,
293
+ re.IGNORECASE,
294
+ )
295
+ if glued:
296
+ cleaned = cleaned[: glued.start()]
297
+ found = True
164
298
  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.71
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Home-page: https://github.com/nichengfuben/echotools
6
6
  Author: nichengfuben
@@ -199,6 +199,10 @@ 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_reply_protect.py
205
+ src/tests/test_fake_history_markup_stream.py
202
206
  src/tests/test_fault_thinking_close_batch.py
203
207
  src/tests/test_fault_thinking_close_stream.py
204
208
  src/tests/test_fncall.py
@@ -227,5 +231,6 @@ src/tests/test_usage.py
227
231
  src/tests/test_web.py
228
232
  src/tests/test_web_broker.py
229
233
  src/tests/fixtures/__init__.py
234
+ src/tests/fixtures/simulated_fake_history_markup_responses.py
230
235
  src/tests/fixtures/simulated_fault_thinking_responses.py
231
236
  src/tests/fixtures/simulated_llm_tool_responses.py