echotools 2.3.87__tar.gz → 2.3.89__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 (245) hide show
  1. {echotools-2.3.87 → echotools-2.3.89}/PKG-INFO +1 -1
  2. {echotools-2.3.87 → echotools-2.3.89}/pyproject.toml +1 -1
  3. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/stream.py +5 -0
  4. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/stream_delta.py +5 -5
  5. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/stream_feed.py +5 -5
  6. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/stream_final.py +27 -6
  7. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml.py +43 -26
  8. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_invoke.py +11 -4
  9. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_patterns.py +190 -30
  10. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_stream/body.py +18 -33
  11. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/hold.py +17 -23
  12. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/parse.py +29 -13
  13. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/protocol/base.py +5 -1
  14. {echotools-2.3.87 → echotools-2.3.89}/src/echotools.egg-info/PKG-INFO +1 -1
  15. {echotools-2.3.87 → echotools-2.3.89}/src/echotools.egg-info/SOURCES.txt +1 -0
  16. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_bare_invoke_format.py +30 -9
  17. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_batch_stream_comprehensive.py +2 -1
  18. echotools-2.3.89/src/tests/test_entml_invoke_known_tool_gate.py +220 -0
  19. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_protocol.py +104 -0
  20. {echotools-2.3.87 → echotools-2.3.89}/README.md +0 -0
  21. {echotools-2.3.87 → echotools-2.3.89}/setup.cfg +0 -0
  22. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/__init__.py +0 -0
  23. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/cache/__init__.py +0 -0
  24. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/cache/list_cache.py +0 -0
  25. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/cache/memory_cache.py +0 -0
  26. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/__init__.py +0 -0
  27. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/base.py +0 -0
  28. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/center.py +0 -0
  29. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/center_notify.py +0 -0
  30. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/center_template.py +0 -0
  31. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/loader.py +0 -0
  32. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/config/merge.py +0 -0
  33. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/errors/__init__.py +0 -0
  34. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/errors/base.py +0 -0
  35. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/errors/classify.py +0 -0
  36. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/errors/common.py +0 -0
  37. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/errors/http.py +0 -0
  38. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/ids/__init__.py +0 -0
  39. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/ids/generator.py +0 -0
  40. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/io/__init__.py +0 -0
  41. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/io/io_utils.py +0 -0
  42. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/io/printstream.py +0 -0
  43. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/logger/__init__.py +0 -0
  44. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/logger/manager.py +0 -0
  45. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/retry/__init__.py +0 -0
  46. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/base/retry/retry.py +0 -0
  47. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/compat.py +0 -0
  48. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/__init__.py +0 -0
  49. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/candidate.py +0 -0
  50. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/dispatcher.py +0 -0
  51. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
  52. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/race.py +0 -0
  53. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/selector.py +0 -0
  54. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/dispatch/usage.py +0 -0
  55. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/__init__.py +0 -0
  56. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
  57. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
  58. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
  59. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
  60. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/prompt/history.py +0 -0
  61. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
  62. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/prompt/inject.py +0 -0
  63. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
  64. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/prompt/templates.py +0 -0
  65. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
  66. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_schema.py +0 -0
  67. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_stream/__init__.py +0 -0
  68. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_stream/snap.py +0 -0
  69. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
  70. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/blocks.py +0 -0
  71. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
  72. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/filter.py +0 -0
  73. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
  74. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/registry.py +0 -0
  75. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/__init__.py +0 -0
  76. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/coercion.py +0 -0
  77. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/history_markup.py +0 -0
  78. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
  79. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/normalization.py +0 -0
  80. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
  81. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
  82. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/keys.py +0 -0
  83. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/lifecycle/__init__.py +0 -0
  84. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/lifecycle/manager.py +0 -0
  85. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/lifecycle/updater.py +0 -0
  86. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
  87. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/process/__init__.py +0 -0
  88. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/process/port.py +0 -0
  89. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/protocol/__init__.py +0 -0
  90. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/__init__.py +0 -0
  91. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/conpty.py +0 -0
  92. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/local/__init__.py +0 -0
  93. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/local/core.py +0 -0
  94. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/local/proc.py +0 -0
  95. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/local/unix.py +0 -0
  96. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/local/win.py +0 -0
  97. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/sanitize.py +0 -0
  98. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/session/__init__.py +0 -0
  99. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/session/base.py +0 -0
  100. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/session/client.py +0 -0
  101. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/ssh.py +0 -0
  102. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/exec/terminal/tmux.py +0 -0
  103. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/__init__.py +0 -0
  104. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/charmap.py +0 -0
  105. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/spinner.py +0 -0
  106. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/ui.py +0 -0
  107. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
  108. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
  109. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
  110. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
  111. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
  112. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
  113. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/ui_console.py +0 -0
  114. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/ui_io.py +0 -0
  115. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/ui_log.py +0 -0
  116. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/ui_platform.py +0 -0
  117. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/ui_text.py +0 -0
  118. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uicore/ui_types.py +0 -0
  119. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
  120. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
  121. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
  122. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
  123. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uilayout/ui_table.py +0 -0
  124. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
  125. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
  126. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
  127. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
  128. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
  129. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
  130. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/events/__init__.py +0 -0
  131. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/events/bus.py +0 -0
  132. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/events/event.py +0 -0
  133. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/spinner/__init__.py +0 -0
  134. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/spinner/spinner.py +0 -0
  135. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/spinner/spinner_color.py +0 -0
  136. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/tracing/__init__.py +0 -0
  137. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/tracing/context.py +0 -0
  138. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/tracing/span.py +0 -0
  139. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/tracing/tracer.py +0 -0
  140. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/translate.py +0 -0
  141. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/watcher/__init__.py +0 -0
  142. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/watcher/file_watcher.py +0 -0
  143. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/__init__.py +0 -0
  144. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/application.py +0 -0
  145. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/broker.py +0 -0
  146. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/app.js +0 -0
  147. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/filezone.js +0 -0
  148. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
  149. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
  150. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
  151. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
  152. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/textinput.js +0 -0
  153. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
  154. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
  155. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
  156. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
  157. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/inputbox.css +0 -0
  158. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/inputbox.js +0 -0
  159. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/style.css +0 -0
  160. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/input_box/voicegif.js +0 -0
  161. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/middleware.py +0 -0
  162. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/stats.py +0 -0
  163. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/media/web/utils.py +0 -0
  164. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/files/__init__.py +0 -0
  165. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/files/file_util.py +0 -0
  166. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/network/__init__.py +0 -0
  167. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/network/http_utils.py +0 -0
  168. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/plugin/__init__.py +0 -0
  169. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/plugin/base.py +0 -0
  170. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/plugin/discovery.py +0 -0
  171. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/plugin/registry.py +0 -0
  172. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/plugin/registry_ops.py +0 -0
  173. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/proxy/__init__.py +0 -0
  174. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/proxy/manager.py +0 -0
  175. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/runtime/__init__.py +0 -0
  176. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/runtime/collector.py +0 -0
  177. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/scheduler/__init__.py +0 -0
  178. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/scheduler/scheduler.py +0 -0
  179. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/sdk/__init__.py +0 -0
  180. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/plat/sdk/facade.py +0 -0
  181. {echotools-2.3.87 → echotools-2.3.89}/src/echotools/version.py +0 -0
  182. {echotools-2.3.87 → echotools-2.3.89}/src/echotools.egg-info/dependency_links.txt +0 -0
  183. {echotools-2.3.87 → echotools-2.3.89}/src/echotools.egg-info/requires.txt +0 -0
  184. {echotools-2.3.87 → echotools-2.3.89}/src/echotools.egg-info/top_level.txt +0 -0
  185. {echotools-2.3.87 → echotools-2.3.89}/src/tests/bench_import.py +0 -0
  186. {echotools-2.3.87 → echotools-2.3.89}/src/tests/conftest.py +0 -0
  187. {echotools-2.3.87 → echotools-2.3.89}/src/tests/fixtures/__init__.py +0 -0
  188. {echotools-2.3.87 → echotools-2.3.89}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
  189. {echotools-2.3.87 → echotools-2.3.89}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
  190. {echotools-2.3.87 → echotools-2.3.89}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
  191. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_adversarial_stream_splits.py +0 -0
  192. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_cache_retry.py +0 -0
  193. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_completion.py +0 -0
  194. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_config.py +0 -0
  195. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_config_extended.py +0 -0
  196. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_console.py +0 -0
  197. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_coverage_core.py +0 -0
  198. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_dispatch.py +0 -0
  199. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_dispatch_extended.py +0 -0
  200. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_dispatch_race.py +0 -0
  201. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_agent_output_formats.py +0 -0
  202. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_parser_comprehensive.py +0 -0
  203. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_stream_json.py +0 -0
  204. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
  205. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_thinking_history.py +0 -0
  206. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_thinking_parse.py +0 -0
  207. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_tool_parse_leak.py +0 -0
  208. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_tool_pipeline.py +0 -0
  209. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_entml_tools.py +0 -0
  210. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_errors.py +0 -0
  211. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_errors_common.py +0 -0
  212. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_events.py +0 -0
  213. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_events_event.py +0 -0
  214. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_facade.py +0 -0
  215. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fake_history_markup_batch.py +0 -0
  216. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
  217. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
  218. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fake_history_markup_stream.py +0 -0
  219. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fault_thinking_close_batch.py +0 -0
  220. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fault_thinking_close_stream.py +0 -0
  221. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fncall.py +0 -0
  222. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_fncall_stream_thinking.py +0 -0
  223. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_history_markup_filter.py +0 -0
  224. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_ids.py +0 -0
  225. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_imports.py +0 -0
  226. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_io.py +0 -0
  227. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_keys.py +0 -0
  228. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_lifecycle.py +0 -0
  229. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_logger.py +0 -0
  230. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_merge.py +0 -0
  231. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_model_branch_matrix.py +0 -0
  232. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_plugin.py +0 -0
  233. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_printstream.py +0 -0
  234. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_protocols.py +0 -0
  235. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_proxy.py +0 -0
  236. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_proxy_selector.py +0 -0
  237. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_runtime.py +0 -0
  238. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_scheduler.py +0 -0
  239. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_simulated_llm_tool_parse.py +0 -0
  240. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_stream_partial_json_parity.py +0 -0
  241. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_stream_realtime_parse.py +0 -0
  242. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_tracing.py +0 -0
  243. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_usage.py +0 -0
  244. {echotools-2.3.87 → echotools-2.3.89}/src/tests/test_web.py +0 -0
  245. {echotools-2.3.87 → echotools-2.3.89}/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.87
3
+ Version: 2.3.89
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.87"
7
+ version = "2.3.89"
8
8
  description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -100,6 +100,11 @@ class FncallStreamParser(StreamFeedMixin, StreamDeltaMixin, StreamFinalMixin):
100
100
  self._schema_index = _build_param_schema_index(self._tools)
101
101
  return self._schema_index
102
102
 
103
+ def _get_known_tool_names(self):
104
+ from echotools.exec.fncall.protocols.entml_patterns import resolve_known_tool_names
105
+
106
+ return resolve_known_tool_names(self._tools, self._get_schema_index())
107
+
103
108
  def _emit_text(self, text: str) -> None:
104
109
  """将文本路由给思考过滤器(若启用)或直接追加到 _text_parts。"""
105
110
  if not text:
@@ -26,7 +26,7 @@ class StreamDeltaMixin:
26
26
 
27
27
  complete = list(INVOKE_RE.finditer(self._fncall_buf))
28
28
  complete_n = len(complete)
29
- parsed = split_invoke_open(self._fncall_buf)
29
+ parsed = split_invoke_open(self._fncall_buf, known_names=self._get_known_tool_names())
30
30
  if parsed is None:
31
31
  return max(0, complete_n - 1)
32
32
  _name, body_start = parsed
@@ -77,7 +77,7 @@ class StreamDeltaMixin:
77
77
  build_streaming_json_snapshot,
78
78
  split_invoke_open,
79
79
  )
80
- parsed = split_invoke_open(self._fncall_buf)
80
+ parsed = split_invoke_open(self._fncall_buf, known_names=self._get_known_tool_names())
81
81
  if not parsed:
82
82
  self._json_stream_encoder.reset()
83
83
  return
@@ -125,7 +125,7 @@ class StreamDeltaMixin:
125
125
  split_invoke_open,
126
126
  )
127
127
 
128
- parsed = split_invoke_open(self._fncall_buf)
128
+ parsed = split_invoke_open(self._fncall_buf, known_names=self._get_known_tool_names())
129
129
  if not parsed:
130
130
  self._json_stream_encoder.reset()
131
131
  return
@@ -172,7 +172,7 @@ class StreamDeltaMixin:
172
172
  split_invoke_open,
173
173
  )
174
174
 
175
- parsed = split_invoke_open(self._fncall_buf)
175
+ parsed = split_invoke_open(self._fncall_buf, known_names=self._get_known_tool_names())
176
176
  if not parsed:
177
177
  return None
178
178
  name, body_start = parsed
@@ -241,7 +241,7 @@ class StreamDeltaMixin:
241
241
  split_invoke_open,
242
242
  )
243
243
 
244
- parsed = split_invoke_open(self._fncall_buf)
244
+ parsed = split_invoke_open(self._fncall_buf, known_names=self._get_known_tool_names())
245
245
  if not parsed:
246
246
  return None
247
247
  name, body_start = parsed
@@ -37,7 +37,7 @@ class StreamFeedMixin:
37
37
  """True 表示已消费 text(含 holdback)。"""
38
38
  hold_from_fn = getattr(self._protocol, "find_fncall_hold_from", None)
39
39
  if hold_from_fn is not None:
40
- hold_from = hold_from_fn(text)
40
+ hold_from = hold_from_fn(text, tools=self._tools)
41
41
  if hold_from is not None:
42
42
  if hold_from > 0:
43
43
  self._append_content_text(text[:hold_from])
@@ -52,7 +52,7 @@ class StreamFeedMixin:
52
52
  def _hold_or_split_emit(self, text: str, trigger_tags) -> None:
53
53
  hold_from_fn = getattr(self._protocol, "find_fncall_hold_from", None)
54
54
  if hold_from_fn is not None:
55
- hold_from = hold_from_fn(text)
55
+ hold_from = hold_from_fn(text, tools=self._tools)
56
56
  if hold_from is not None:
57
57
  if hold_from > 0:
58
58
  self._emit_text(text[:hold_from])
@@ -81,7 +81,7 @@ class StreamFeedMixin:
81
81
  self._fncall_buf += text
82
82
  return
83
83
  trigger_tags = self._protocol.get_trigger_tags()
84
- found, pos = self._protocol.detect_start(text)
84
+ found, pos = self._protocol.detect_start(text, tools=self._tools)
85
85
  if found:
86
86
  if pos > 0:
87
87
  self._append_content_text(text[:pos])
@@ -90,7 +90,7 @@ class StreamFeedMixin:
90
90
  self._hold_or_split_content(text, trigger_tags)
91
91
  return
92
92
  trigger_tags = self._protocol.get_trigger_tags()
93
- found, pos = self._protocol.detect_start(text)
93
+ found, pos = self._protocol.detect_start(text, tools=self._tools)
94
94
  if not found:
95
95
  self._hold_or_split_content(text, trigger_tags)
96
96
  return
@@ -138,7 +138,7 @@ class StreamFeedMixin:
138
138
  self._feed_waiting_thinking_plain(combined)
139
139
  return
140
140
  trigger_tags = self._protocol.get_trigger_tags()
141
- found, pos = self._protocol.detect_start(combined)
141
+ found, pos = self._protocol.detect_start(combined, tools=self._tools)
142
142
  if found:
143
143
  if self._thinking_filter is not None:
144
144
  from echotools.exec.fncall.protocols.entml_think.parse import (
@@ -67,10 +67,18 @@ class StreamFinalMixin:
67
67
 
68
68
  lower = raw.lower()
69
69
  cut = len(raw)
70
- for marker in ("<entml:invoke", "<entml:function_calls"):
71
- pos = lower.find(marker)
72
- if pos >= 0:
73
- cut = min(cut, pos)
70
+ from echotools.exec.fncall.protocols.entml_patterns import (
71
+ find_actionable_entml_invoke_open,
72
+ )
73
+
74
+ invoke_pos = find_actionable_entml_invoke_open(
75
+ raw, known_names=self._get_known_tool_names()
76
+ )
77
+ if invoke_pos >= 0:
78
+ cut = min(cut, invoke_pos)
79
+ legacy_pos = lower.find("<entml:function_calls")
80
+ if legacy_pos >= 0:
81
+ cut = min(cut, legacy_pos)
74
82
  segment = raw[:cut]
75
83
 
76
84
  buf = stream_safe_visible_prefix(
@@ -128,12 +136,25 @@ class StreamFinalMixin:
128
136
  )
129
137
  clean_fn = getattr(self._protocol, "clean_tool_tags", None)
130
138
  if callable(clean_fn):
131
- clean_text = clean_fn(clean_text)
139
+ import inspect
140
+
141
+ sig = inspect.signature(clean_fn)
142
+ if "tools" in sig.parameters:
143
+ clean_text = clean_fn(clean_text, tools=self._tools)
144
+ else:
145
+ clean_text = clean_fn(clean_text)
132
146
  elif getattr(self._protocol, "id", None) == "entml":
133
147
  from echotools.exec.fncall.protocols.entml_patterns import (
148
+ resolve_known_tool_names,
134
149
  strip_tool_entml_residue,
135
150
  )
136
- clean_text = strip_tool_entml_residue(clean_text)
151
+
152
+ clean_text = strip_tool_entml_residue(
153
+ clean_text,
154
+ known_names=resolve_known_tool_names(
155
+ self._tools, self._get_schema_index()
156
+ ),
157
+ )
137
158
  display_text = self._finalize_display_text(clean_text)
138
159
  self._text_parts = [display_text] if display_text else []
139
160
  self._finalized_result = (display_text, tool_calls)
@@ -25,7 +25,9 @@ from echotools.exec.fncall.protocols.entml_think.blocks import (
25
25
  from echotools.exec.fncall.protocols.entml_patterns import (
26
26
  entml_invoke_open_may_be_streaming,
27
27
  extract_attr_value,
28
+ find_actionable_entml_invoke_open,
28
29
  normalize_entml_name,
30
+ resolve_known_tool_names,
29
31
  strip_actionable_entml_invoke_blocks,
30
32
  strip_legacy_function_calls_wrapper,
31
33
  strip_tool_entml_residue,
@@ -215,16 +217,28 @@ class EntmlProtocol(ToolProtocol):
215
217
 
216
218
  return "\n\n".join(sections)
217
219
 
218
- def detect_start(self, buffer: str) -> Tuple[bool, int]:
219
- """invoke 开标签(含 name 且闭合 ``>``)稳定后才视为工具流开始。"""
220
- invoke_pos = self._find_complete_invoke_open(buffer)
220
+ def detect_start(
221
+ self,
222
+ buffer: str,
223
+ tools: Optional[List[Dict[str, Any]]] = None,
224
+ ) -> Tuple[bool, int]:
225
+ """invoke 开标签(闭合 ``>`` 且 name 在 tools 内)稳定后才视为工具流开始。"""
226
+ schema_index = _build_param_schema_index(tools) if tools else None
227
+ known = resolve_known_tool_names(tools, schema_index)
228
+ invoke_pos = find_actionable_entml_invoke_open(buffer, known_names=known)
221
229
  if invoke_pos < 0:
222
230
  return (False, -1)
223
231
  return (True, invoke_pos)
224
232
 
225
- def find_fncall_hold_from(self, buffer: str) -> Optional[int]:
233
+ def find_fncall_hold_from(
234
+ self,
235
+ buffer: str,
236
+ tools: Optional[List[Dict[str, Any]]] = None,
237
+ ) -> Optional[int]:
226
238
  """invoke 未稳定前 hold 前缀;legacy 外壳开标签未闭合时同样 hold。"""
227
- if self._find_complete_invoke_open(buffer) >= 0:
239
+ schema_index = _build_param_schema_index(tools) if tools else None
240
+ known = resolve_known_tool_names(tools, schema_index)
241
+ if find_actionable_entml_invoke_open(buffer, known_names=known) >= 0:
228
242
  return None
229
243
  hold: Optional[int] = None
230
244
  legacy_pos = buffer.find(self._LEGACY_WRAPPER_PREFIX)
@@ -235,28 +249,23 @@ class EntmlProtocol(ToolProtocol):
235
249
  invoke_pos = buffer.find(self._TRIGGER_PREFIX)
236
250
  if (
237
251
  invoke_pos >= 0
238
- and entml_invoke_open_may_be_streaming(buffer, invoke_pos)
252
+ and entml_invoke_open_may_be_streaming(
253
+ buffer, invoke_pos, known_names=known
254
+ )
239
255
  and (hold is None or invoke_pos < hold)
240
256
  ):
241
257
  hold = invoke_pos
242
258
  return hold
243
259
 
244
- def _find_complete_invoke_open(self, buffer: str) -> int:
245
- """返回首个含 name 且已闭合 ``>`` 的 ``<entml:invoke`` 起始下标;否则 -1。"""
246
- search_from = 0
247
- prefix_len = len(self._TRIGGER_PREFIX)
248
- while True:
249
- pos = buffer.find(self._TRIGGER_PREFIX, search_from)
250
- if pos < 0:
251
- return -1
252
- close = buffer.find(">", pos + prefix_len)
253
- if close < 0:
254
- return -1
255
- attrs = buffer[pos + prefix_len : close]
256
- name = extract_attr_value(attrs, "name")
257
- if name and normalize_entml_name(name):
258
- return pos
259
- search_from = close + 1
260
+ def _find_complete_invoke_open(
261
+ self,
262
+ buffer: str,
263
+ tools: Optional[List[Dict[str, Any]]] = None,
264
+ ) -> int:
265
+ """返回首个可解析 ``<entml:invoke`` 起始下标;否则 ``-1``。"""
266
+ schema_index = _build_param_schema_index(tools) if tools else None
267
+ known = resolve_known_tool_names(tools, schema_index)
268
+ return find_actionable_entml_invoke_open(buffer, known_names=known)
260
269
 
261
270
  def parse(
262
271
  self,
@@ -282,6 +291,8 @@ class EntmlProtocol(ToolProtocol):
282
291
  )
283
292
  if unclosed_open_at >= 0:
284
293
  parse_text = text[:unclosed_open_at]
294
+ schema_index = _build_param_schema_index(tools) if tools else None
295
+ known = resolve_known_tool_names(tools, schema_index)
285
296
  tool_calls = parse_entml_tool_calls(parse_text, tools, schema_index)
286
297
  tool_block_calls: List[Dict[str, Any]] = []
287
298
  if include_tool_blocks:
@@ -297,8 +308,8 @@ class EntmlProtocol(ToolProtocol):
297
308
  # 须在移除 invoke 之前剥离伪 history,且须晚于已解析的 ``<tool>`` 块剥离。
298
309
  clean, _ = strip_fake_history_markup(clean)
299
310
  if tool_calls:
300
- clean = strip_actionable_entml_invoke_blocks(clean)
301
- clean = strip_tool_entml_residue(clean)
311
+ clean = strip_actionable_entml_invoke_blocks(clean, known_names=known)
312
+ clean = strip_tool_entml_residue(clean, known_names=known)
302
313
  return (clean, normalize_tool_calls(tool_calls, tools))
303
314
 
304
315
  def parse_fragment(
@@ -313,9 +324,15 @@ class EntmlProtocol(ToolProtocol):
313
324
  """user 消息:仅去掉 ``entml:`` 前缀,不做其它剥离。"""
314
325
  return strip_entml_from_content(content)
315
326
 
316
- def clean_tool_tags(self, content: str) -> str:
327
+ def clean_tool_tags(
328
+ self,
329
+ content: str,
330
+ tools: Optional[List[Dict[str, Any]]] = None,
331
+ ) -> str:
317
332
  """仅剥离工具相关标签残留,保留 thinking;并移除伪 history 块。"""
318
- cleaned = strip_tool_entml_residue(content)
333
+ schema_index = _build_param_schema_index(tools) if tools else None
334
+ known = resolve_known_tool_names(tools, schema_index)
335
+ cleaned = strip_tool_entml_residue(content, known_names=known)
319
336
  cleaned, _ = strip_fake_history_markup(cleaned)
320
337
  return cleaned
321
338
 
@@ -11,9 +11,11 @@ from .entml_patterns import (
11
11
  PARAMETERS_RE,
12
12
  extract_attr_value,
13
13
  extract_parameter_type_attr,
14
+ invoke_structural_gap_text,
14
15
  iter_actionable_entml_invoke_blocks,
15
16
  normalize_entml_name,
16
17
  parse_sub_tags,
18
+ resolve_known_tool_names,
17
19
  split_mangled_json_param_tail,
18
20
  synthetic_close_invoke_body,
19
21
  )
@@ -25,8 +27,9 @@ def _parse_direct_child_tags(
25
27
  args: Dict[str, Any],
26
28
  func_props: Dict[str, Dict[str, Any]],
27
29
  ) -> None:
28
- """模型用 ``<key>value</key>`` 代替 ``<parameter name=\"key\">``。"""
29
- for match in INVOKE_DIRECT_CHILD_RE.finditer(body):
30
+ """模型用 ``<key>value</key>`` 代替 ``<parameter name=\"key\">``(仅 structural gap)。"""
31
+ gap_text = invoke_structural_gap_text(body)
32
+ for match in INVOKE_DIRECT_CHILD_RE.finditer(gap_text):
30
33
  key = normalize_entml_name(match.group(1))
31
34
  if not key or key.lower() in INVOKE_DIRECT_CHILD_SKIP or key in args:
32
35
  continue
@@ -42,7 +45,8 @@ def _parse_bare_invoke_children(
42
45
  args: Dict[str, Any],
43
46
  func_props: Dict[str, Dict[str, Any]],
44
47
  ) -> None:
45
- for match in BARE_INVOKE_CHILD_RE.finditer(body):
48
+ gap_text = invoke_structural_gap_text(body)
49
+ for match in BARE_INVOKE_CHILD_RE.finditer(gap_text):
46
50
  key = normalize_entml_name(match.group(1))
47
51
  if not key or key in args:
48
52
  continue
@@ -140,7 +144,10 @@ def parse_entml_tool_calls(
140
144
  schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]],
141
145
  ) -> List[Dict[str, Any]]:
142
146
  tool_calls: List[Dict[str, Any]] = []
143
- for _start, _end, attrs, body in iter_actionable_entml_invoke_blocks(text):
147
+ known = resolve_known_tool_names(tools, schema_index)
148
+ for _start, _end, attrs, body in iter_actionable_entml_invoke_blocks(
149
+ text, known_names=known
150
+ ):
144
151
  name = extract_attr_value(attrs, "name")
145
152
  if not name:
146
153
  continue