echotools 2.3.87__tar.gz → 2.3.88__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 (244) hide show
  1. {echotools-2.3.87 → echotools-2.3.88}/PKG-INFO +1 -1
  2. {echotools-2.3.87 → echotools-2.3.88}/pyproject.toml +1 -1
  3. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_invoke.py +6 -3
  4. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_patterns.py +81 -5
  5. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_stream/body.py +4 -31
  6. {echotools-2.3.87 → echotools-2.3.88}/src/echotools.egg-info/PKG-INFO +1 -1
  7. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_protocol.py +104 -0
  8. {echotools-2.3.87 → echotools-2.3.88}/README.md +0 -0
  9. {echotools-2.3.87 → echotools-2.3.88}/setup.cfg +0 -0
  10. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/__init__.py +0 -0
  11. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/cache/__init__.py +0 -0
  12. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/cache/list_cache.py +0 -0
  13. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/cache/memory_cache.py +0 -0
  14. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/__init__.py +0 -0
  15. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/base.py +0 -0
  16. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/center.py +0 -0
  17. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/center_notify.py +0 -0
  18. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/center_template.py +0 -0
  19. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/loader.py +0 -0
  20. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/config/merge.py +0 -0
  21. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/errors/__init__.py +0 -0
  22. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/errors/base.py +0 -0
  23. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/errors/classify.py +0 -0
  24. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/errors/common.py +0 -0
  25. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/errors/http.py +0 -0
  26. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/ids/__init__.py +0 -0
  27. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/ids/generator.py +0 -0
  28. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/io/__init__.py +0 -0
  29. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/io/io_utils.py +0 -0
  30. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/io/printstream.py +0 -0
  31. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/logger/__init__.py +0 -0
  32. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/logger/manager.py +0 -0
  33. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/retry/__init__.py +0 -0
  34. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/base/retry/retry.py +0 -0
  35. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/compat.py +0 -0
  36. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/__init__.py +0 -0
  37. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/candidate.py +0 -0
  38. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/dispatcher.py +0 -0
  39. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
  40. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/race.py +0 -0
  41. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/selector.py +0 -0
  42. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/dispatch/usage.py +0 -0
  43. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/__init__.py +0 -0
  44. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
  45. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/stream.py +0 -0
  46. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/stream_delta.py +0 -0
  47. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/stream_feed.py +0 -0
  48. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/stream_final.py +0 -0
  49. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
  50. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
  51. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
  52. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/prompt/history.py +0 -0
  53. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
  54. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/prompt/inject.py +0 -0
  55. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
  56. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/prompt/templates.py +0 -0
  57. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
  58. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml.py +0 -0
  59. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_schema.py +0 -0
  60. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_stream/__init__.py +0 -0
  61. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_stream/snap.py +0 -0
  62. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
  63. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/blocks.py +0 -0
  64. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
  65. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/filter.py +0 -0
  66. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
  67. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/hold.py +0 -0
  68. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/protocols/entml_think/parse.py +0 -0
  69. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/registry.py +0 -0
  70. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/__init__.py +0 -0
  71. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/coercion.py +0 -0
  72. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/history_markup.py +0 -0
  73. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
  74. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/normalization.py +0 -0
  75. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
  76. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
  77. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/keys.py +0 -0
  78. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/lifecycle/__init__.py +0 -0
  79. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/lifecycle/manager.py +0 -0
  80. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/lifecycle/updater.py +0 -0
  81. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
  82. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/process/__init__.py +0 -0
  83. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/process/port.py +0 -0
  84. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/protocol/__init__.py +0 -0
  85. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/protocol/base.py +0 -0
  86. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/__init__.py +0 -0
  87. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/conpty.py +0 -0
  88. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/local/__init__.py +0 -0
  89. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/local/core.py +0 -0
  90. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/local/proc.py +0 -0
  91. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/local/unix.py +0 -0
  92. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/local/win.py +0 -0
  93. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/sanitize.py +0 -0
  94. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/session/__init__.py +0 -0
  95. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/session/base.py +0 -0
  96. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/session/client.py +0 -0
  97. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/ssh.py +0 -0
  98. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/exec/terminal/tmux.py +0 -0
  99. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/__init__.py +0 -0
  100. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/charmap.py +0 -0
  101. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/spinner.py +0 -0
  102. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/ui.py +0 -0
  103. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
  104. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
  105. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
  106. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
  107. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
  108. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
  109. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/ui_console.py +0 -0
  110. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/ui_io.py +0 -0
  111. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/ui_log.py +0 -0
  112. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/ui_platform.py +0 -0
  113. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/ui_text.py +0 -0
  114. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uicore/ui_types.py +0 -0
  115. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
  116. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
  117. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
  118. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
  119. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uilayout/ui_table.py +0 -0
  120. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
  121. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
  122. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
  123. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
  124. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
  125. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
  126. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/events/__init__.py +0 -0
  127. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/events/bus.py +0 -0
  128. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/events/event.py +0 -0
  129. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/spinner/__init__.py +0 -0
  130. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/spinner/spinner.py +0 -0
  131. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/spinner/spinner_color.py +0 -0
  132. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/tracing/__init__.py +0 -0
  133. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/tracing/context.py +0 -0
  134. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/tracing/span.py +0 -0
  135. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/tracing/tracer.py +0 -0
  136. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/translate.py +0 -0
  137. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/watcher/__init__.py +0 -0
  138. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/watcher/file_watcher.py +0 -0
  139. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/__init__.py +0 -0
  140. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/application.py +0 -0
  141. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/broker.py +0 -0
  142. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/app.js +0 -0
  143. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/filezone.js +0 -0
  144. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
  145. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
  146. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
  147. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
  148. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/textinput.js +0 -0
  149. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
  150. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
  151. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
  152. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
  153. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/inputbox.css +0 -0
  154. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/inputbox.js +0 -0
  155. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/style.css +0 -0
  156. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/input_box/voicegif.js +0 -0
  157. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/middleware.py +0 -0
  158. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/stats.py +0 -0
  159. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/media/web/utils.py +0 -0
  160. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/files/__init__.py +0 -0
  161. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/files/file_util.py +0 -0
  162. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/network/__init__.py +0 -0
  163. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/network/http_utils.py +0 -0
  164. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/plugin/__init__.py +0 -0
  165. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/plugin/base.py +0 -0
  166. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/plugin/discovery.py +0 -0
  167. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/plugin/registry.py +0 -0
  168. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/plugin/registry_ops.py +0 -0
  169. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/proxy/__init__.py +0 -0
  170. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/proxy/manager.py +0 -0
  171. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/runtime/__init__.py +0 -0
  172. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/runtime/collector.py +0 -0
  173. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/scheduler/__init__.py +0 -0
  174. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/scheduler/scheduler.py +0 -0
  175. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/sdk/__init__.py +0 -0
  176. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/plat/sdk/facade.py +0 -0
  177. {echotools-2.3.87 → echotools-2.3.88}/src/echotools/version.py +0 -0
  178. {echotools-2.3.87 → echotools-2.3.88}/src/echotools.egg-info/SOURCES.txt +0 -0
  179. {echotools-2.3.87 → echotools-2.3.88}/src/echotools.egg-info/dependency_links.txt +0 -0
  180. {echotools-2.3.87 → echotools-2.3.88}/src/echotools.egg-info/requires.txt +0 -0
  181. {echotools-2.3.87 → echotools-2.3.88}/src/echotools.egg-info/top_level.txt +0 -0
  182. {echotools-2.3.87 → echotools-2.3.88}/src/tests/bench_import.py +0 -0
  183. {echotools-2.3.87 → echotools-2.3.88}/src/tests/conftest.py +0 -0
  184. {echotools-2.3.87 → echotools-2.3.88}/src/tests/fixtures/__init__.py +0 -0
  185. {echotools-2.3.87 → echotools-2.3.88}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
  186. {echotools-2.3.87 → echotools-2.3.88}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
  187. {echotools-2.3.87 → echotools-2.3.88}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
  188. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_adversarial_stream_splits.py +0 -0
  189. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_cache_retry.py +0 -0
  190. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_completion.py +0 -0
  191. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_config.py +0 -0
  192. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_config_extended.py +0 -0
  193. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_console.py +0 -0
  194. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_coverage_core.py +0 -0
  195. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_dispatch.py +0 -0
  196. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_dispatch_extended.py +0 -0
  197. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_dispatch_race.py +0 -0
  198. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_agent_output_formats.py +0 -0
  199. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_bare_invoke_format.py +0 -0
  200. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_batch_stream_comprehensive.py +0 -0
  201. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_parser_comprehensive.py +0 -0
  202. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_stream_json.py +0 -0
  203. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
  204. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_thinking_history.py +0 -0
  205. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_thinking_parse.py +0 -0
  206. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_tool_parse_leak.py +0 -0
  207. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_tool_pipeline.py +0 -0
  208. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_entml_tools.py +0 -0
  209. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_errors.py +0 -0
  210. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_errors_common.py +0 -0
  211. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_events.py +0 -0
  212. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_events_event.py +0 -0
  213. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_facade.py +0 -0
  214. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fake_history_markup_batch.py +0 -0
  215. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
  216. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
  217. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fake_history_markup_stream.py +0 -0
  218. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fault_thinking_close_batch.py +0 -0
  219. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fault_thinking_close_stream.py +0 -0
  220. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fncall.py +0 -0
  221. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_fncall_stream_thinking.py +0 -0
  222. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_history_markup_filter.py +0 -0
  223. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_ids.py +0 -0
  224. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_imports.py +0 -0
  225. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_io.py +0 -0
  226. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_keys.py +0 -0
  227. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_lifecycle.py +0 -0
  228. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_logger.py +0 -0
  229. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_merge.py +0 -0
  230. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_model_branch_matrix.py +0 -0
  231. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_plugin.py +0 -0
  232. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_printstream.py +0 -0
  233. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_protocols.py +0 -0
  234. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_proxy.py +0 -0
  235. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_proxy_selector.py +0 -0
  236. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_runtime.py +0 -0
  237. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_scheduler.py +0 -0
  238. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_simulated_llm_tool_parse.py +0 -0
  239. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_stream_partial_json_parity.py +0 -0
  240. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_stream_realtime_parse.py +0 -0
  241. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_tracing.py +0 -0
  242. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_usage.py +0 -0
  243. {echotools-2.3.87 → echotools-2.3.88}/src/tests/test_web.py +0 -0
  244. {echotools-2.3.87 → echotools-2.3.88}/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.88
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.88"
8
8
  description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -11,6 +11,7 @@ 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,
@@ -25,8 +26,9 @@ def _parse_direct_child_tags(
25
26
  args: Dict[str, Any],
26
27
  func_props: Dict[str, Dict[str, Any]],
27
28
  ) -> None:
28
- """模型用 ``<key>value</key>`` 代替 ``<parameter name=\"key\">``。"""
29
- for match in INVOKE_DIRECT_CHILD_RE.finditer(body):
29
+ """模型用 ``<key>value</key>`` 代替 ``<parameter name=\"key\">``(仅 structural gap)。"""
30
+ gap_text = invoke_structural_gap_text(body)
31
+ for match in INVOKE_DIRECT_CHILD_RE.finditer(gap_text):
30
32
  key = normalize_entml_name(match.group(1))
31
33
  if not key or key.lower() in INVOKE_DIRECT_CHILD_SKIP or key in args:
32
34
  continue
@@ -42,7 +44,8 @@ def _parse_bare_invoke_children(
42
44
  args: Dict[str, Any],
43
45
  func_props: Dict[str, Dict[str, Any]],
44
46
  ) -> None:
45
- for match in BARE_INVOKE_CHILD_RE.finditer(body):
47
+ gap_text = invoke_structural_gap_text(body)
48
+ for match in BARE_INVOKE_CHILD_RE.finditer(gap_text):
46
49
  key = normalize_entml_name(match.group(1))
47
50
  if not key or key in args:
48
51
  continue
@@ -15,17 +15,22 @@ INVOKE_RE = re.compile(
15
15
  r"<entml:invoke\b([^>]*)>([\s\S]*?)</entml:invoke>",
16
16
  re.DOTALL,
17
17
  )
18
+ # invoke 内直接子元素:<pattern>v</pattern>、<-n>true</-n>(标签名即参数名)
19
+ _INVOKE_DIRECT_TAG_NAME = r"(?:-\w+|[a-zA-Z_][\w.-]*)"
18
20
  # 允许 name / type 等属性任意顺序;单/双引号均可。
19
- # 闭合标签后必须是下一 parameter / invoke 结束 / parameters 结束。
21
+ # 闭合标签后必须是 invoke 内合法兄弟节点(parameter / entml 子标签 / 直接子元素 / invoke 结束)。
20
22
  # 批量解析允许参数块在 body 末尾($);流式未闭合 invoke 不允许 $,避免 chunk 落在假闭合上误判。
21
23
  # 模型偶发省略 entml: 前缀:<parameter name="...">...</parameter>
22
24
  _BARE_PARAM_OPEN_TAG = r"<parameter\b(?![s>])"
23
25
  PARAM_OPEN_PATTERN = rf"(?:<entml:parameter\b|{_BARE_PARAM_OPEN_TAG})"
24
26
  PARAM_CLOSE_ENTML = "</entml:parameter>"
25
27
  PARAM_CLOSE_BARE = "</parameter>"
26
- # 闭合标签后必须是 invoke 内合法兄弟节点(含裸 description/timeout 等)。
27
28
  _INVOKE_SIBLING_OPEN = r"<entml:(?!parameter\b|parameters\b|invoke\b)\w+\b"
28
- _PARAM_CLOSE_FOLLOWERS = rf"(?:{PARAM_OPEN_PATTERN}|{_INVOKE_SIBLING_OPEN}|</entml:invoke>|</entml:parameters>|{re.escape(PARAM_CLOSE_BARE)})"
29
+ _INVOKE_DIRECT_CHILD_OPEN = rf"<(?!entml:)({_INVOKE_DIRECT_TAG_NAME})\b"
30
+ _PARAM_CLOSE_FOLLOWERS = (
31
+ rf"(?:{PARAM_OPEN_PATTERN}|{_INVOKE_SIBLING_OPEN}|{_INVOKE_DIRECT_CHILD_OPEN}"
32
+ rf"|</entml:invoke>|</entml:parameters>|{re.escape(PARAM_CLOSE_BARE)})"
33
+ )
29
34
  _PARAM_CLOSE_LOOKAHEAD = rf"(?=\s*{_PARAM_CLOSE_FOLLOWERS})"
30
35
  _PARAM_CLOSE_LOOKAHEAD_EOL = rf"(?=\s*(?:{_PARAM_CLOSE_FOLLOWERS}|$))"
31
36
  PARAM_RE = re.compile(
@@ -60,8 +65,7 @@ BARE_INVOKE_CHILD_OPEN_RE = re.compile(
60
65
  r"<entml:(description|timeout)>([\s\S]*)",
61
66
  re.DOTALL | re.IGNORECASE,
62
67
  )
63
- # invoke 内直接子元素:<pattern>v</pattern>、<-n>true</-n>(标签名即参数名)
64
- _INVOKE_DIRECT_TAG_NAME = r"(?:-\w+|[a-zA-Z_][\w.-]*)"
68
+ # invoke 内直接子元素(完整/未闭合 regex 见下)
65
69
  INVOKE_DIRECT_CHILD_RE = re.compile(
66
70
  rf"<(?!entml:)({_INVOKE_DIRECT_TAG_NAME})>([\s\S]*?)</\1>",
67
71
  re.DOTALL,
@@ -243,6 +247,7 @@ _FOLLOWER_PREFIXES = (
243
247
  "</parameter",
244
248
  "<entml:description",
245
249
  "<entml:timeout",
250
+ "<",
246
251
  )
247
252
 
248
253
 
@@ -270,6 +275,8 @@ def _parameter_close_follower_ok(after: str, *, allow_end: bool) -> bool:
270
275
 
271
276
 
272
277
  _PARAM_OPEN_TAG_RE = re.compile(rf"{PARAM_OPEN_PATTERN}([^>]*)>", re.IGNORECASE)
278
+
279
+
273
280
  def synthetic_close_invoke_body(inner: str) -> str:
274
281
  """为 force_close / invoke 已闭合但未闭合的 parameter 补齐结构闭合标签。"""
275
282
  if not inner:
@@ -316,6 +323,75 @@ def find_valid_parameter_close(body: str, search_from: int = 0, *, allow_end: bo
316
323
  return -1
317
324
 
318
325
 
326
+ def parameter_value_spans(body: str) -> List[Tuple[int, int]]:
327
+ """``<entml:parameter>`` 值区间(含未闭合 parameter 的 growing tail)。"""
328
+ if not body:
329
+ return []
330
+ spans: List[Tuple[int, int]] = []
331
+ i = 0
332
+ while i < len(body):
333
+ match = _PARAM_OPEN_TAG_RE.search(body, i)
334
+ if not match:
335
+ break
336
+ val_start = match.end()
337
+ close = find_valid_parameter_close(body, val_start, allow_end=True)
338
+ if close < 0:
339
+ spans.append((val_start, len(body)))
340
+ break
341
+ spans.append((val_start, close))
342
+ i = close + parameter_close_at(body, close)
343
+ return spans
344
+
345
+
346
+ def parameter_block_spans(body: str) -> List[Tuple[int, int]]:
347
+ """完整 ``<entml:parameter …>…</entml:parameter>`` 块(含开闭标签)。"""
348
+ if not body:
349
+ return []
350
+ spans: List[Tuple[int, int]] = []
351
+ i = 0
352
+ while i < len(body):
353
+ match = _PARAM_OPEN_TAG_RE.search(body, i)
354
+ if not match:
355
+ break
356
+ block_start = match.start()
357
+ val_start = match.end()
358
+ close = find_valid_parameter_close(body, val_start, allow_end=True)
359
+ if close < 0:
360
+ spans.append((block_start, len(body)))
361
+ break
362
+ block_end = close + parameter_close_at(body, close)
363
+ spans.append((block_start, block_end))
364
+ i = block_end
365
+ return spans
366
+
367
+
368
+ def invoke_structural_gaps(body: str) -> List[Tuple[int, int]]:
369
+ """invoke 体内可承载备用参数语法的区间(parameter 块之外)。"""
370
+ if not body:
371
+ return []
372
+ blocks = parameter_block_spans(body)
373
+ if not blocks:
374
+ return [(0, len(body))]
375
+ gaps: List[Tuple[int, int]] = []
376
+ pos = 0
377
+ for start, end in blocks:
378
+ if pos < start:
379
+ gaps.append((pos, start))
380
+ pos = end
381
+ if pos < len(body):
382
+ gaps.append((pos, len(body)))
383
+ return gaps
384
+
385
+
386
+ def invoke_structural_gap_text(body: str) -> str:
387
+ """parameter 块视为不透明 payload 后,invoke 体剩余可解析文本。"""
388
+ return "".join(body[s:e] for s, e in invoke_structural_gaps(body))
389
+
390
+
391
+ def inside_parameter_value(pos: int, spans: List[Tuple[int, int]]) -> bool:
392
+ return any(start <= pos < end for start, end in spans)
393
+
394
+
319
395
  def extract_attr_value(attrs: str, attr_name: str = "name") -> Optional[str]:
320
396
  """从标签属性串中提取 name/type 等(支持单双引号)。"""
321
397
  if not attrs:
@@ -16,6 +16,7 @@ from echotools.exec.fncall.protocols.entml_patterns import (
16
16
  extract_attr_value,
17
17
  extract_parameter_type_attr,
18
18
  find_valid_parameter_close,
19
+ invoke_structural_gap_text,
19
20
  normalize_entml_name,
20
21
  parameter_close_at,
21
22
  split_mangled_json_param_tail,
@@ -206,46 +207,18 @@ def _parse_bare_invoke_entries(body: str) -> List[Tuple[str, str, bool, Optional
206
207
  return entries
207
208
 
208
209
 
209
- def _parameter_value_spans(body: str) -> List[Tuple[int, int]]:
210
- """``<entml:parameter>`` 值区间(含流式未闭合 parameter 的 growing tail)。"""
211
- if not body:
212
- return []
213
- spans: List[Tuple[int, int]] = []
214
- i = 0
215
- while i < len(body):
216
- match = _PARAM_OPEN_RE.search(body, i)
217
- if not match:
218
- break
219
- val_start = match.end()
220
- close = find_valid_parameter_close(body, val_start, allow_end=True)
221
- if close < 0:
222
- spans.append((val_start, len(body)))
223
- break
224
- spans.append((val_start, close))
225
- i = close + parameter_close_at(body, close)
226
- return spans
227
-
228
-
229
- def _inside_parameter_value(pos: int, spans: List[Tuple[int, int]]) -> bool:
230
- return any(start <= pos < end for start, end in spans)
231
-
232
-
233
210
  def _parse_direct_child_entries(body: str) -> List[Tuple[str, str, bool, Optional[str]]]:
234
211
  """返回 [(key, value, is_complete, type_hint), ...](直接子元素标签)。"""
235
212
  if not body:
236
213
  return []
237
- param_spans = _parameter_value_spans(body)
214
+ gap_text = invoke_structural_gap_text(body)
238
215
  tagged: List[Tuple[int, str, str, bool, Optional[str]]] = []
239
- for match in INVOKE_DIRECT_CHILD_RE.finditer(body):
240
- if _inside_parameter_value(match.start(), param_spans):
241
- continue
216
+ for match in INVOKE_DIRECT_CHILD_RE.finditer(gap_text):
242
217
  key = normalize_entml_name(match.group(1))
243
218
  if not key or key.lower() in INVOKE_DIRECT_CHILD_SKIP:
244
219
  continue
245
220
  tagged.append((match.start(), key, (match.group(2) or "").strip(), True, None))
246
- for match in INVOKE_DIRECT_CHILD_OPEN_RE.finditer(body):
247
- if _inside_parameter_value(match.start(), param_spans):
248
- continue
221
+ for match in INVOKE_DIRECT_CHILD_OPEN_RE.finditer(gap_text):
249
222
  key = normalize_entml_name(match.group(1))
250
223
  if not key or key.lower() in INVOKE_DIRECT_CHILD_SKIP:
251
224
  continue
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: echotools
3
- Version: 2.3.87
3
+ Version: 2.3.88
4
4
  Summary: 通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链
5
5
  Home-page: https://github.com/nichengfuben/echotools
6
6
  Author: nichengfuben
@@ -6,6 +6,11 @@ import pytest
6
6
 
7
7
  from echotools.exec.fncall import get_protocol, inject_fncall
8
8
  from echotools.exec.fncall.protocols.entml_invoke import parse_entml_tool_calls
9
+ from echotools.exec.fncall.protocols.entml_patterns import (
10
+ invoke_structural_gap_text,
11
+ invoke_structural_gaps,
12
+ parameter_block_spans,
13
+ )
9
14
  from echotools.exec.fncall.protocols.entml_think.core import (
10
15
  build_entml_thinking_section,
11
16
  default_max_thinking_length_for_level,
@@ -1700,3 +1705,102 @@ def test_write_path_content_hybrid_tool_block() -> None:
1700
1705
  assert "Agent WebUI" in args["content"]
1701
1706
  assert "Phase 1" in args["content"]
1702
1707
 
1708
+
1709
+ def test_invoke_structural_gaps_exclude_parameter_blocks() -> None:
1710
+ body = (
1711
+ "pre\n"
1712
+ '<entml:parameter name="content"><span>x</span></entml:parameter>\n'
1713
+ "<path>src/a.py</path>\n"
1714
+ "post"
1715
+ )
1716
+ blocks = parameter_block_spans(body)
1717
+ assert len(blocks) == 1
1718
+ assert body[blocks[0][0] : blocks[0][1]].startswith("<entml:parameter")
1719
+ gaps = invoke_structural_gaps(body)
1720
+ gap_text = invoke_structural_gap_text(body)
1721
+ assert "<span>" not in gap_text
1722
+ assert "<path>src/a.py</path>" in gap_text
1723
+ assert "pre" in gap_text
1724
+ assert "post" in gap_text
1725
+ assert sum(e - s for s, e in gaps) == len(gap_text)
1726
+
1727
+
1728
+ def test_write_parameter_payload_opaque_to_alternate_syntax() -> None:
1729
+ """parameter 块内任意类 XML/HTML 标签不得被 invoke 备用语法解析为参数。"""
1730
+ schema_index = _build_param_schema_index(WRITE_TOOLS)
1731
+ sample = (
1732
+ '<entml:invoke name="Write">\n'
1733
+ '<entml:parameter name="path">src/select.js</entml:parameter>\n'
1734
+ '<entml:parameter name="content">'
1735
+ "app.component('core-collapsible', {\n"
1736
+ " template: [\n"
1737
+ " '<span class=\"collapsible-arrow\"></span>',\n"
1738
+ " '<span>{{ title }}</span>',\n"
1739
+ " '<slot></slot>',\n"
1740
+ " '<div v-if=\"open\"></div>',\n"
1741
+ " ].join('\\n')\n"
1742
+ "});\n"
1743
+ "</entml:parameter>\n"
1744
+ "</entml:invoke>"
1745
+ )
1746
+ calls = parse_entml_tool_calls(sample, WRITE_TOOLS, schema_index)
1747
+ assert len(calls) == 1
1748
+ args = json.loads(calls[0]["function"]["arguments"])
1749
+ assert set(args.keys()) == {"file_path", "content"}
1750
+ assert "<slot></slot>" in args["content"]
1751
+ assert "{{ title }}" in args["content"]
1752
+
1753
+
1754
+ def test_invoke_mixed_parameter_and_direct_child_syntax() -> None:
1755
+ """structural gap:parameter 与直接子标签可并存,互不污染。"""
1756
+ tools = [
1757
+ {
1758
+ "type": "function",
1759
+ "function": {
1760
+ "name": "Grep",
1761
+ "parameters": {
1762
+ "type": "object",
1763
+ "properties": {
1764
+ "pattern": {"type": "string"},
1765
+ "path": {"type": "string"},
1766
+ "-n": {"type": "boolean"},
1767
+ },
1768
+ "required": ["pattern"],
1769
+ },
1770
+ },
1771
+ }
1772
+ ]
1773
+ schema_index = _build_param_schema_index(tools)
1774
+ sample = (
1775
+ '<entml:invoke name="Grep">\n'
1776
+ '<entml:parameter name="pattern">foo|bar</entml:parameter>\n'
1777
+ "<path>src/main.py</path>\n"
1778
+ "<-n>true</-n>\n"
1779
+ "</entml:invoke>"
1780
+ )
1781
+ calls = parse_entml_tool_calls(sample, tools, schema_index)
1782
+ args = json.loads(calls[0]["function"]["arguments"])
1783
+ assert args == {"pattern": "foo|bar", "path": "src/main.py", "-n": True}
1784
+
1785
+
1786
+ def test_write_parameter_payload_corpus_req_1785323083() -> None:
1787
+ """回归 req-1785323083:parameter payload 内 markup 不得泄漏为额外参数。"""
1788
+ from pathlib import Path
1789
+
1790
+ sample_path = Path(
1791
+ r"X:/Project/Public/Qwen/logs/responses/req-1785323083-f6847d18c1f9.txt"
1792
+ )
1793
+ if not sample_path.is_file():
1794
+ pytest.skip("corpus file not available")
1795
+ text = sample_path.read_text(encoding="utf-8")
1796
+ proto = get_protocol("entml")
1797
+ _, batch_calls = proto.parse(text, WRITE_TOOLS)
1798
+ write_calls = [c for c in batch_calls if c["function"]["name"] == "Write"]
1799
+ assert len(write_calls) == 1
1800
+ args = json.loads(write_calls[0]["function"]["arguments"])
1801
+ assert "span" not in args
1802
+ assert "slot" not in args
1803
+ assert args["file_path"].endswith("select.js")
1804
+ assert "core-select" in args["content"]
1805
+ assert "<slot></slot>" in args["content"]
1806
+
File without changes
File without changes