echotools 2.3.100__tar.gz → 2.3.103__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 (273) hide show
  1. {echotools-2.3.100 → echotools-2.3.103}/PKG-INFO +1 -1
  2. {echotools-2.3.100 → echotools-2.3.103}/pyproject.toml +1 -1
  3. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/xml_parser.py +1 -1
  4. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/behavior_blocks.py +1 -1
  5. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/history.py +1 -1
  6. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml.py +241 -0
  7. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/__init__.py +93 -0
  8. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/invoke.py +197 -0
  9. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/params.py +219 -0
  10. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/regex.py +107 -0
  11. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/strip.py +42 -0
  12. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_schema/__init__.py +26 -0
  13. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_schema/mangled.py +201 -0
  14. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_stream/body.py +1 -1
  15. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_stream/snap.py +28 -26
  16. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/blocks.py +2 -2
  17. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/parse.py +1 -1
  18. echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/__init__.py +56 -0
  19. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_tool_blocks.py → echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/blocks.py +4 -3
  20. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_fake_structure_markup.py → echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/fakemarkup.py +1 -1
  21. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_invoke.py → echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/invoke.py +14 -10
  22. echotools-2.3.103/src/echotools/exec/fncall/shared/entml_format.py +169 -0
  23. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/shared/history_markup.py +1 -1
  24. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/shared/loop_detect.py +39 -0
  25. echotools-2.3.103/src/echotools/media/console/__init__.py +89 -0
  26. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_text.py +59 -32
  27. echotools-2.3.103/src/echotools/media/console/uilayout/ui_bridge.py +205 -0
  28. echotools-2.3.103/src/echotools/media/console/uiwidgets/ui_richcli.py +110 -0
  29. echotools-2.3.103/src/echotools/media/console/uiwidgets/ui_themes.py +243 -0
  30. {echotools-2.3.100 → echotools-2.3.103}/src/echotools.egg-info/PKG-INFO +1 -1
  31. {echotools-2.3.100 → echotools-2.3.103}/src/echotools.egg-info/SOURCES.txt +22 -10
  32. echotools-2.3.103/src/tests/_check_parity.py +75 -0
  33. echotools-2.3.103/src/tests/fixtures/entml_golden.py +104 -0
  34. echotools-2.3.103/src/tests/fixtures/entml_golden_update.py +24 -0
  35. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_bare_invoke_format.py +1 -1
  36. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_batch_stream_comprehensive.py +13 -2
  37. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_fake_structure_markup.py +2 -2
  38. echotools-2.3.103/src/tests/test_entml_golden.py +44 -0
  39. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_protocol.py +7 -4
  40. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_tool_pipeline.py +1 -1
  41. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_printstream.py +3 -3
  42. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_stream_partial_json_parity.py +11 -5
  43. echotools-2.3.103/src/tests/test_ui_bridge.py +62 -0
  44. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml.py +0 -408
  45. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -553
  46. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_schema.py +0 -493
  47. echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -460
  48. echotools-2.3.100/src/echotools/exec/fncall/shared/xml_helpers.py +0 -56
  49. echotools-2.3.100/src/echotools/media/console/__init__.py +0 -38
  50. echotools-2.3.100/src/tests/_check_parity.py +0 -77
  51. {echotools-2.3.100 → echotools-2.3.103}/README.md +0 -0
  52. {echotools-2.3.100 → echotools-2.3.103}/setup.cfg +0 -0
  53. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/__init__.py +0 -0
  54. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/cache/__init__.py +0 -0
  55. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/cache/list_cache.py +0 -0
  56. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/cache/memory_cache.py +0 -0
  57. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/__init__.py +0 -0
  58. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/base.py +0 -0
  59. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/center.py +0 -0
  60. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/center_notify.py +0 -0
  61. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/center_template.py +0 -0
  62. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/loader.py +0 -0
  63. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/config/merge.py +0 -0
  64. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/errors/__init__.py +0 -0
  65. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/errors/base.py +0 -0
  66. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/errors/classify.py +0 -0
  67. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/errors/common.py +0 -0
  68. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/errors/http.py +0 -0
  69. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/ids/__init__.py +0 -0
  70. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/ids/generator.py +0 -0
  71. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/io/__init__.py +0 -0
  72. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/io/io_utils.py +0 -0
  73. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/io/printstream.py +0 -0
  74. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/logger/__init__.py +0 -0
  75. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/logger/manager.py +0 -0
  76. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/retry/__init__.py +0 -0
  77. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/base/retry/retry.py +0 -0
  78. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/compat.py +0 -0
  79. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/__init__.py +0 -0
  80. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/candidate.py +0 -0
  81. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/dispatcher.py +0 -0
  82. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
  83. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/race.py +0 -0
  84. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/selector.py +0 -0
  85. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/dispatch/usage.py +0 -0
  86. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/__init__.py +0 -0
  87. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
  88. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream.py +0 -0
  89. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream_delta.py +0 -0
  90. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream_feed.py +0 -0
  91. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream_final.py +0 -0
  92. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
  93. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
  94. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
  95. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/inject.py +0 -0
  96. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
  97. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/templates.py +0 -0
  98. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
  99. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_stream/__init__.py +0 -0
  100. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
  101. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
  102. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/filter.py +0 -0
  103. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
  104. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/hold.py +0 -0
  105. /echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_tool_result_comment.py → /echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/comment.py +0 -0
  106. /echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_tools.py → /echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/tools.py +0 -0
  107. /echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_values.py → /echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/values.py +0 -0
  108. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/registry.py +0 -0
  109. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/shared/__init__.py +0 -0
  110. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/shared/coercion.py +0 -0
  111. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/shared/normalization.py +0 -0
  112. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
  113. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/keys.py +0 -0
  114. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/lifecycle/__init__.py +0 -0
  115. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/lifecycle/manager.py +0 -0
  116. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/lifecycle/updater.py +0 -0
  117. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
  118. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/process/__init__.py +0 -0
  119. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/process/port.py +0 -0
  120. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/protocol/__init__.py +0 -0
  121. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/protocol/base.py +0 -0
  122. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/__init__.py +0 -0
  123. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/conpty.py +0 -0
  124. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/local/__init__.py +0 -0
  125. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/local/core.py +0 -0
  126. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/local/proc.py +0 -0
  127. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/local/unix.py +0 -0
  128. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/local/win.py +0 -0
  129. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/sanitize.py +0 -0
  130. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/session/__init__.py +0 -0
  131. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/session/base.py +0 -0
  132. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/session/client.py +0 -0
  133. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/ssh.py +0 -0
  134. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/exec/terminal/tmux.py +0 -0
  135. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/charmap.py +0 -0
  136. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/spinner.py +0 -0
  137. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/ui.py +0 -0
  138. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
  139. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
  140. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
  141. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
  142. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
  143. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
  144. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_console.py +0 -0
  145. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_io.py +0 -0
  146. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_log.py +0 -0
  147. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_platform.py +0 -0
  148. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_types.py +0 -0
  149. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
  150. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
  151. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
  152. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
  153. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_table.py +0 -0
  154. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
  155. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
  156. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
  157. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
  158. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
  159. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
  160. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/events/__init__.py +0 -0
  161. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/events/bus.py +0 -0
  162. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/events/event.py +0 -0
  163. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/spinner/__init__.py +0 -0
  164. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/spinner/spinner.py +0 -0
  165. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/spinner/spinner_color.py +0 -0
  166. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/tracing/__init__.py +0 -0
  167. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/tracing/context.py +0 -0
  168. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/tracing/span.py +0 -0
  169. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/tracing/tracer.py +0 -0
  170. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/translate.py +0 -0
  171. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/watcher/__init__.py +0 -0
  172. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/watcher/file_watcher.py +0 -0
  173. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/__init__.py +0 -0
  174. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/application.py +0 -0
  175. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/broker.py +0 -0
  176. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/app.js +0 -0
  177. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/filezone.js +0 -0
  178. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
  179. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
  180. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
  181. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
  182. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/textinput.js +0 -0
  183. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
  184. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
  185. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
  186. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
  187. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/inputbox.css +0 -0
  188. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/inputbox.js +0 -0
  189. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/style.css +0 -0
  190. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/input_box/voicegif.js +0 -0
  191. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/middleware.py +0 -0
  192. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/stats.py +0 -0
  193. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/media/web/utils.py +0 -0
  194. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/files/__init__.py +0 -0
  195. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/files/file_util.py +0 -0
  196. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/network/__init__.py +0 -0
  197. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/network/http_utils.py +0 -0
  198. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/plugin/__init__.py +0 -0
  199. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/plugin/base.py +0 -0
  200. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/plugin/discovery.py +0 -0
  201. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/plugin/registry.py +0 -0
  202. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/plugin/registry_ops.py +0 -0
  203. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/proxy/__init__.py +0 -0
  204. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/proxy/manager.py +0 -0
  205. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/runtime/__init__.py +0 -0
  206. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/runtime/collector.py +0 -0
  207. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/scheduler/__init__.py +0 -0
  208. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/scheduler/scheduler.py +0 -0
  209. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/sdk/__init__.py +0 -0
  210. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/plat/sdk/facade.py +0 -0
  211. {echotools-2.3.100 → echotools-2.3.103}/src/echotools/version.py +0 -0
  212. {echotools-2.3.100 → echotools-2.3.103}/src/echotools.egg-info/dependency_links.txt +0 -0
  213. {echotools-2.3.100 → echotools-2.3.103}/src/echotools.egg-info/requires.txt +0 -0
  214. {echotools-2.3.100 → echotools-2.3.103}/src/echotools.egg-info/top_level.txt +0 -0
  215. {echotools-2.3.100 → echotools-2.3.103}/src/tests/bench_import.py +0 -0
  216. {echotools-2.3.100 → echotools-2.3.103}/src/tests/conftest.py +0 -0
  217. {echotools-2.3.100 → echotools-2.3.103}/src/tests/fixtures/__init__.py +0 -0
  218. {echotools-2.3.100 → echotools-2.3.103}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
  219. {echotools-2.3.100 → echotools-2.3.103}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
  220. {echotools-2.3.100 → echotools-2.3.103}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
  221. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_adversarial_stream_splits.py +0 -0
  222. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_cache_retry.py +0 -0
  223. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_completion.py +0 -0
  224. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_config.py +0 -0
  225. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_config_extended.py +0 -0
  226. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_console.py +0 -0
  227. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_coverage_core.py +0 -0
  228. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_dispatch.py +0 -0
  229. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_dispatch_extended.py +0 -0
  230. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_dispatch_race.py +0 -0
  231. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_agent_output_formats.py +0 -0
  232. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_invoke_known_tool_gate.py +0 -0
  233. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_parser_comprehensive.py +0 -0
  234. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_stream_json.py +0 -0
  235. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
  236. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_thinking_history.py +0 -0
  237. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_thinking_parse.py +0 -0
  238. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_tool_parse_leak.py +0 -0
  239. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_entml_tools.py +0 -0
  240. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_errors.py +0 -0
  241. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_errors_common.py +0 -0
  242. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_events.py +0 -0
  243. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_events_event.py +0 -0
  244. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_facade.py +0 -0
  245. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fake_history_markup_batch.py +0 -0
  246. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
  247. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
  248. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fake_history_markup_stream.py +0 -0
  249. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fault_thinking_close_batch.py +0 -0
  250. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fault_thinking_close_stream.py +0 -0
  251. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fncall.py +0 -0
  252. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_fncall_stream_thinking.py +0 -0
  253. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_history_markup_filter.py +0 -0
  254. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_ids.py +0 -0
  255. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_imports.py +0 -0
  256. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_io.py +0 -0
  257. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_keys.py +0 -0
  258. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_lifecycle.py +0 -0
  259. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_logger.py +0 -0
  260. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_merge.py +0 -0
  261. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_model_branch_matrix.py +0 -0
  262. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_plugin.py +0 -0
  263. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_protocols.py +0 -0
  264. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_proxy.py +0 -0
  265. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_proxy_selector.py +0 -0
  266. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_runtime.py +0 -0
  267. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_scheduler.py +0 -0
  268. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_simulated_llm_tool_parse.py +0 -0
  269. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_stream_realtime_parse.py +0 -0
  270. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_tracing.py +0 -0
  271. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_usage.py +0 -0
  272. {echotools-2.3.100 → echotools-2.3.103}/src/tests/test_web.py +0 -0
  273. {echotools-2.3.100 → echotools-2.3.103}/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.100
3
+ Version: 2.3.103
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.100"
7
+ version = "2.3.103"
8
8
  description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -17,7 +17,7 @@ from echotools.exec.fncall.shared.coercion import (
17
17
  _coerce_param_value,
18
18
  )
19
19
  from echotools.exec.fncall.shared.normalization import normalize_tool_calls
20
- from echotools.exec.fncall.shared.xml_helpers import (
20
+ from echotools.exec.fncall.shared.loop_detect import (
21
21
  _PROVIDER_BLOCK_RE,
22
22
  _PROVIDER_INVOKE_RE,
23
23
  _PROVIDER_PARAM_RE,
@@ -40,7 +40,7 @@ You think before answering even when the request appears trivial. When a request
40
40
 
41
41
  Depth scales with the request. Routine requests receive a short block. Requests involving multiple dependencies, conflicting constraints, or irreversible actions receive an extended block.
42
42
 
43
- Your default reply language to the user is Simplified Chinese. You switch languages only when the user explicitly requests a different language. The final line of every thinking block states the language of the reply that follows.
43
+ Your default reply language to the user is Simplified Chinese. You switch languages only when the user explicitly requests a different language.
44
44
 
45
45
  IMPORTANT: The thinking block is the first structural element of your output, and a tool invocation never appears inside it."""
46
46
 
@@ -83,7 +83,7 @@ def _clean_entml_tags(content: str) -> str:
83
83
  # ---------------------------------------------------------------------------
84
84
 
85
85
 
86
- from echotools.exec.fncall.protocols.entml_invoke import format_entml_parameter_value
86
+ from echotools.exec.fncall.protocols.entml_tool.invoke import format_entml_parameter_value
87
87
 
88
88
 
89
89
  def _render_parameter_value(v: Any) -> str:
@@ -0,0 +1,241 @@
1
+ """Entropy ML (entml) 协议实现。
2
+
3
+ - 工具调用:<entml:invoke> / <entml:parameter>(legacy ``function_calls`` 外壳仅解析兼容)
4
+ - 对话历史:<entml:conversation_history>
5
+ - 当前用户消息:<current_user_message>
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, Dict, List, Optional, Tuple
11
+
12
+ from echotools.exec.fncall.protocols.entml_patterns import (
13
+ entml_invoke_open_may_be_streaming,
14
+ find_actionable_entml_invoke_open,
15
+ resolve_known_tool_names,
16
+ strip_actionable_entml_invoke_blocks,
17
+ strip_legacy_function_calls_wrapper,
18
+ strip_tool_entml_residue,
19
+ )
20
+ from echotools.exec.fncall.protocols.entml_schema import format_entml_tool_descs
21
+ from echotools.exec.fncall.protocols.entml_tool.fakemarkup import strip_orphan_entml_close_tags
22
+ from echotools.exec.fncall.protocols.entml_tool.invoke import (
23
+ format_entml_tool_calls,
24
+ parse_entml_tool_calls,
25
+ )
26
+ from echotools.exec.fncall.shared.coercion import _build_param_schema_index
27
+ from echotools.exec.fncall.shared.entml_format import (
28
+ _parse_tool_call_args,
29
+ _render_tool_call_line,
30
+ _render_tool_history_block,
31
+ build_entml_render_prompt,
32
+ format_entml_conversation_history,
33
+ format_entml_current_user_message,
34
+ format_entml_functions_results,
35
+ format_tool_result_id_comment,
36
+ strip_entml_from_content,
37
+ )
38
+ from echotools.exec.fncall.shared.history_markup import strip_fake_history_markup
39
+ from echotools.exec.fncall.shared.normalization import normalize_tool_calls
40
+ from echotools.exec.protocol.base import ToolProtocol
41
+
42
+ __all__ = [
43
+ "EntmlProtocol",
44
+ "format_entml_conversation_history",
45
+ "format_entml_current_user_message",
46
+ "format_entml_functions_results",
47
+ "format_tool_result_id_comment",
48
+ "strip_entml_from_content",
49
+ ]
50
+
51
+
52
+ class EntmlProtocol(ToolProtocol):
53
+ """Entropy ML (entml) 格式工具调用协议适配器。"""
54
+
55
+ @property
56
+ def id(self) -> str:
57
+ return "entml"
58
+
59
+ _TRIGGER = "<entml:invoke>"
60
+ _TRIGGER_PREFIX = "<entml:invoke"
61
+ _LEGACY_WRAPPER_PREFIX = "<entml:function_calls"
62
+ _THINKING_PREFIX = "<entml:thinking"
63
+
64
+ def get_trigger_tags(self) -> List[str]:
65
+ return [
66
+ self._TRIGGER,
67
+ self._TRIGGER_PREFIX,
68
+ self._THINKING_PREFIX,
69
+ f"{self._THINKING_PREFIX}>",
70
+ ]
71
+
72
+ def normalize_stream_buffer(self, buffer: str) -> str:
73
+ return strip_legacy_function_calls_wrapper(buffer)
74
+
75
+ def get_stream_end_tags(self) -> List[str]:
76
+ return []
77
+
78
+ @staticmethod
79
+ def format_tool_descs(tools: List[Dict[str, Any]]) -> str:
80
+ return format_entml_tool_descs(tools)
81
+
82
+ def render_prompt(
83
+ self,
84
+ tool_descs: str,
85
+ lang: str,
86
+ user_system_prompt: str = "",
87
+ history_text: str = "",
88
+ loop_warning: str = "",
89
+ history_markup_warning: str = "",
90
+ current_user_message: Optional[str] = None,
91
+ protocol_options: Optional[Dict[str, Any]] = None,
92
+ history_has_tool_calls: bool = False,
93
+ functions_results_text: str = "",
94
+ ) -> str:
95
+ _ = (lang, history_has_tool_calls)
96
+ return build_entml_render_prompt(
97
+ tool_descs=tool_descs,
98
+ user_system_prompt=user_system_prompt,
99
+ history_text=history_text,
100
+ loop_warning=loop_warning,
101
+ history_markup_warning=history_markup_warning,
102
+ current_user_message=current_user_message,
103
+ protocol_options=protocol_options,
104
+ functions_results_text=functions_results_text,
105
+ )
106
+
107
+ def detect_start(
108
+ self,
109
+ buffer: str,
110
+ tools: Optional[List[Dict[str, Any]]] = None,
111
+ ) -> Tuple[bool, int]:
112
+ schema_index = _build_param_schema_index(tools) if tools else None
113
+ known = resolve_known_tool_names(tools, schema_index)
114
+ invoke_pos = find_actionable_entml_invoke_open(buffer, known_names=known)
115
+ if invoke_pos < 0:
116
+ return (False, -1)
117
+ return (True, invoke_pos)
118
+
119
+ def find_fncall_hold_from(
120
+ self,
121
+ buffer: str,
122
+ tools: Optional[List[Dict[str, Any]]] = None,
123
+ ) -> Optional[int]:
124
+ schema_index = _build_param_schema_index(tools) if tools else None
125
+ known = resolve_known_tool_names(tools, schema_index)
126
+ if find_actionable_entml_invoke_open(buffer, known_names=known) >= 0:
127
+ return None
128
+ hold: Optional[int] = None
129
+ legacy_pos = buffer.find(self._LEGACY_WRAPPER_PREFIX)
130
+ if legacy_pos >= 0:
131
+ close = buffer.find(">", legacy_pos)
132
+ if close < 0 and (hold is None or legacy_pos < hold):
133
+ hold = legacy_pos
134
+ invoke_pos = buffer.find(self._TRIGGER_PREFIX)
135
+ if (
136
+ invoke_pos >= 0
137
+ and entml_invoke_open_may_be_streaming(
138
+ buffer, invoke_pos, known_names=known
139
+ )
140
+ and (hold is None or invoke_pos < hold)
141
+ ):
142
+ hold = invoke_pos
143
+ return hold
144
+
145
+ def _find_complete_invoke_open(
146
+ self,
147
+ buffer: str,
148
+ tools: Optional[List[Dict[str, Any]]] = None,
149
+ ) -> int:
150
+ schema_index = _build_param_schema_index(tools) if tools else None
151
+ known = resolve_known_tool_names(tools, schema_index)
152
+ return find_actionable_entml_invoke_open(buffer, known_names=known)
153
+
154
+ def parse(
155
+ self,
156
+ text: str,
157
+ tools: Optional[List[Dict[str, Any]]] = None,
158
+ *,
159
+ include_tool_blocks: bool = True,
160
+ thinking_enabled: bool = True,
161
+ ) -> Tuple[str, List[Dict[str, Any]]]:
162
+ _ = include_tool_blocks
163
+ from echotools.exec.fncall.protocols.entml_think.parse import (
164
+ _find_earliest_thinking_open,
165
+ has_unclosed_entml_thinking,
166
+ split_entml_thinking,
167
+ )
168
+
169
+ schema_index = _build_param_schema_index(tools) if tools else None
170
+ parse_text, _thinking = split_entml_thinking(text, thinking_enabled=True)
171
+ unclosed_open_at = -1
172
+ if has_unclosed_entml_thinking(text, thinking_enabled=True):
173
+ unclosed_open_at, _ = _find_earliest_thinking_open(
174
+ text, thinking_enabled=True
175
+ )
176
+ if unclosed_open_at >= 0:
177
+ parse_text = text[:unclosed_open_at]
178
+ known = resolve_known_tool_names(tools, schema_index)
179
+ tool_calls = parse_entml_tool_calls(parse_text, tools, schema_index)
180
+ clean = text[:unclosed_open_at] if unclosed_open_at >= 0 else text
181
+ if thinking_enabled:
182
+ visible, _ = split_entml_thinking(clean, thinking_enabled=True)
183
+ clean = visible
184
+ clean, _ = strip_fake_history_markup(clean)
185
+ if tool_calls:
186
+ clean = strip_actionable_entml_invoke_blocks(clean, known_names=known)
187
+ clean = strip_tool_entml_residue(clean, known_names=known)
188
+ clean, _ = strip_orphan_entml_close_tags(clean)
189
+ return (clean, normalize_tool_calls(tool_calls, tools))
190
+
191
+ def parse_fragment(
192
+ self,
193
+ fragment: str,
194
+ tools: Optional[List[Dict[str, Any]]] = None,
195
+ ) -> List[Dict[str, Any]]:
196
+ _, tool_calls = self.parse(fragment, tools)
197
+ return tool_calls
198
+
199
+ def clean_tags(self, content: str) -> str:
200
+ return strip_entml_from_content(content)
201
+
202
+ def clean_tool_tags(
203
+ self,
204
+ content: str,
205
+ tools: Optional[List[Dict[str, Any]]] = None,
206
+ ) -> str:
207
+ schema_index = _build_param_schema_index(tools) if tools else None
208
+ known = resolve_known_tool_names(tools, schema_index)
209
+ cleaned = strip_tool_entml_residue(content, known_names=known)
210
+ cleaned, _ = strip_fake_history_markup(cleaned)
211
+ return cleaned
212
+
213
+ def format_assistant_tool_calls(
214
+ self,
215
+ tool_calls: List[Dict[str, Any]],
216
+ ) -> str:
217
+ lines: List[str] = []
218
+ for tc in tool_calls:
219
+ name, args = _parse_tool_call_args(tc)
220
+ lines.append(_render_tool_call_line(name, args))
221
+ return "\n".join(lines)
222
+
223
+ def format_assistant_tool_turn_block(
224
+ self,
225
+ tool_calls: List[Dict[str, Any]],
226
+ tid_to_result: Dict[str, Dict[str, Any]],
227
+ ) -> str:
228
+ _ = tid_to_result
229
+ lines: List[str] = []
230
+ for tc in tool_calls:
231
+ invoke = format_entml_tool_calls([tc])
232
+ if invoke:
233
+ lines.append(invoke)
234
+ tid = tc.get("id") or ""
235
+ comment = format_tool_result_id_comment(tid)
236
+ if comment:
237
+ lines.append(comment)
238
+ return "\n".join(lines)
239
+
240
+ def supports_streaming(self) -> bool:
241
+ return True
@@ -0,0 +1,93 @@
1
+ """Regex patterns and helpers for entml invoke/parameter parsing."""
2
+ from __future__ import annotations
3
+
4
+ from ..entml_schema.mangled import (
5
+ mangled_json_param_tail_in_progress,
6
+ split_mangled_json_param_tail,
7
+ )
8
+ from .invoke import (
9
+ entml_invoke_open_is_actionable,
10
+ entml_invoke_open_may_be_streaming,
11
+ find_actionable_entml_invoke_open,
12
+ is_placeholder_invoke_name,
13
+ iter_actionable_entml_invoke_blocks,
14
+ resolve_known_tool_names,
15
+ )
16
+ from .params import (
17
+ extract_attr_value,
18
+ extract_parameter_type_attr,
19
+ find_valid_parameter_close,
20
+ inside_parameter_value,
21
+ invoke_structural_gap_text,
22
+ invoke_structural_gaps,
23
+ normalize_entml_name,
24
+ parameter_block_spans,
25
+ parameter_close_at,
26
+ parameter_value_spans,
27
+ parse_sub_tags,
28
+ strip_actionable_entml_invoke_blocks,
29
+ synthetic_close_invoke_body,
30
+ )
31
+ from .regex import (
32
+ BARE_INVOKE_CHILD_OPEN_RE,
33
+ BARE_INVOKE_CHILD_RE,
34
+ BLOCK_RE,
35
+ INVOKE_DIRECT_CHILD_OPEN_RE,
36
+ INVOKE_DIRECT_CHILD_RE,
37
+ INVOKE_DIRECT_CHILD_SKIP,
38
+ INVOKE_RE,
39
+ PARAM_CLOSE_BARE,
40
+ PARAM_CLOSE_ENTML,
41
+ PARAM_OPEN_PATTERN,
42
+ PARAM_RE,
43
+ PARAMETERS_RE,
44
+ SUB_TAG_RE,
45
+ _INVOKE_CLOSE,
46
+ _INVOKE_OPEN_PREFIX,
47
+ _PARAM_CLOSE_VALID_RE,
48
+ _PARAM_CLOSE_VALID_STREAM_RE,
49
+ )
50
+ from .strip import strip_legacy_function_calls_wrapper, strip_tool_entml_residue
51
+
52
+ __all__ = [
53
+ "BARE_INVOKE_CHILD_OPEN_RE",
54
+ "BARE_INVOKE_CHILD_RE",
55
+ "BLOCK_RE",
56
+ "INVOKE_DIRECT_CHILD_OPEN_RE",
57
+ "INVOKE_DIRECT_CHILD_RE",
58
+ "INVOKE_DIRECT_CHILD_SKIP",
59
+ "INVOKE_RE",
60
+ "PARAM_CLOSE_BARE",
61
+ "PARAM_CLOSE_ENTML",
62
+ "PARAM_OPEN_PATTERN",
63
+ "PARAM_RE",
64
+ "PARAMETERS_RE",
65
+ "SUB_TAG_RE",
66
+ "_INVOKE_CLOSE",
67
+ "_INVOKE_OPEN_PREFIX",
68
+ "_PARAM_CLOSE_VALID_RE",
69
+ "_PARAM_CLOSE_VALID_STREAM_RE",
70
+ "entml_invoke_open_is_actionable",
71
+ "entml_invoke_open_may_be_streaming",
72
+ "extract_attr_value",
73
+ "extract_parameter_type_attr",
74
+ "find_actionable_entml_invoke_open",
75
+ "find_valid_parameter_close",
76
+ "inside_parameter_value",
77
+ "invoke_structural_gap_text",
78
+ "invoke_structural_gaps",
79
+ "is_placeholder_invoke_name",
80
+ "iter_actionable_entml_invoke_blocks",
81
+ "mangled_json_param_tail_in_progress",
82
+ "normalize_entml_name",
83
+ "parameter_block_spans",
84
+ "parameter_close_at",
85
+ "parameter_value_spans",
86
+ "parse_sub_tags",
87
+ "resolve_known_tool_names",
88
+ "split_mangled_json_param_tail",
89
+ "strip_actionable_entml_invoke_blocks",
90
+ "strip_legacy_function_calls_wrapper",
91
+ "strip_tool_entml_residue",
92
+ "synthetic_close_invoke_body",
93
+ ]
@@ -0,0 +1,197 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Any, Dict, Iterator, List, Optional, Set, Tuple
5
+
6
+ from .params import extract_attr_value, normalize_entml_name
7
+ from .regex import _INVOKE_CLOSE, _INVOKE_OPEN_PREFIX
8
+
9
+
10
+ def is_placeholder_invoke_name(name: str) -> bool:
11
+ """提示词占位符(如 ``$FUNCTION_NAME``)不算真实工具调用。"""
12
+ n = (name or "").strip()
13
+ return not n or "$" in n
14
+
15
+
16
+ def resolve_known_tool_names(
17
+ tools: Optional[List[Dict[str, Any]]] = None,
18
+ schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None,
19
+ ) -> Optional[Set[str]]:
20
+ """从 tools / schema_index 解析已知工具名;无约束时返回 ``None``。"""
21
+ if schema_index:
22
+ return {normalize_entml_name(str(k)) for k in schema_index.keys()}
23
+ names: Set[str] = set()
24
+ for tool in tools or []:
25
+ fn = tool.get("function") or {}
26
+ name = fn.get("name")
27
+ if name:
28
+ names.add(normalize_entml_name(str(name)))
29
+ return names if names else None
30
+
31
+
32
+ def _name_is_known(name: str, known_names: Optional[Set[str]]) -> bool:
33
+ if known_names is None:
34
+ return True
35
+ if not known_names:
36
+ return False
37
+ return normalize_entml_name(name) in known_names
38
+
39
+
40
+ def entml_invoke_open_is_actionable(
41
+ buffer: str,
42
+ pos: int,
43
+ *,
44
+ known_names: Optional[Set[str]] = None,
45
+ ) -> bool:
46
+ """``pos`` 处 ``<entml:invoke`` 是否为已闭合、含真实 name 且(若提供)在已知工具表内的开标签。"""
47
+ if pos < 0 or not buffer.startswith(_INVOKE_OPEN_PREFIX, pos):
48
+ return False
49
+ gt = buffer.find(">", pos + len(_INVOKE_OPEN_PREFIX))
50
+ if gt < 0:
51
+ return False
52
+ attrs = buffer[pos + len(_INVOKE_OPEN_PREFIX) : gt]
53
+ name = extract_attr_value(attrs, "name")
54
+ if not name:
55
+ return False
56
+ name = normalize_entml_name(name)
57
+ if not name or is_placeholder_invoke_name(name):
58
+ return False
59
+ return _name_is_known(name, known_names)
60
+
61
+
62
+ def entml_invoke_open_may_be_streaming(
63
+ buffer: str,
64
+ pos: int,
65
+ *,
66
+ known_names: Optional[Set[str]] = None,
67
+ ) -> bool:
68
+ """``pos`` 处 ``<entml:invoke`` 是否仍可能长成可解析工具开标签(非 prose 提及)。"""
69
+ if pos < 0 or not buffer.startswith(_INVOKE_OPEN_PREFIX, pos):
70
+ return False
71
+ gt = buffer.find(">", pos + len(_INVOKE_OPEN_PREFIX))
72
+ if gt < 0:
73
+ return True
74
+ return entml_invoke_open_is_actionable(buffer, pos, known_names=known_names)
75
+
76
+
77
+ def find_actionable_entml_invoke_open(
78
+ buffer: str,
79
+ *,
80
+ known_names: Optional[Set[str]] = None,
81
+ ) -> int:
82
+ """返回首个可解析 ``<entml:invoke`` 起始下标;否则 ``-1``。"""
83
+ if not buffer:
84
+ return -1
85
+ search_from = 0
86
+ prefix_len = len(_INVOKE_OPEN_PREFIX)
87
+ while search_from < len(buffer):
88
+ pos = buffer.find(_INVOKE_OPEN_PREFIX, search_from)
89
+ if pos < 0:
90
+ return -1
91
+ if entml_invoke_open_is_actionable(buffer, pos, known_names=known_names):
92
+ return pos
93
+ gt = buffer.find(">", pos + prefix_len)
94
+ search_from = (gt + 1) if gt >= 0 else (pos + prefix_len)
95
+ return -1
96
+
97
+
98
+ def _actionable_invoke_open_before_close(
99
+ content: str,
100
+ close_pos: int,
101
+ *,
102
+ known_names: Optional[Set[str]] = None,
103
+ ) -> bool:
104
+ """``close_pos`` 处的 ``</entml:invoke>`` 是否闭合带真实 name 的开标签。"""
105
+ search = close_pos - 1
106
+ while search >= 0:
107
+ pos = content.rfind(_INVOKE_OPEN_PREFIX, 0, search + 1)
108
+ if pos < 0:
109
+ return False
110
+ if entml_invoke_open_is_actionable(content, pos, known_names=known_names):
111
+ gt = content.find(">", pos)
112
+ if gt < 0 or gt >= close_pos:
113
+ search = pos - 1
114
+ continue
115
+ inner_close = content.find(_INVOKE_CLOSE, gt + 1, close_pos)
116
+ if inner_close < 0:
117
+ return True
118
+ search = pos - 1
119
+ continue
120
+ search = pos - 1
121
+ return False
122
+
123
+
124
+ def _invoke_close_should_strip(
125
+ content: str,
126
+ close_pos: int,
127
+ *,
128
+ known_names: Optional[Set[str]] = None,
129
+ ) -> bool:
130
+ """是否应剥离 ``</entml:invoke>``(孤儿噪声或真实工具块残留)。"""
131
+ if _actionable_invoke_open_before_close(content, close_pos, known_names=known_names):
132
+ return True
133
+ open_pos = content.rfind(_INVOKE_OPEN_PREFIX, 0, close_pos)
134
+ if open_pos < 0:
135
+ return True
136
+ return entml_invoke_open_is_actionable(content, open_pos, known_names=known_names)
137
+
138
+
139
+ def iter_actionable_entml_invoke_blocks(
140
+ text: str,
141
+ *,
142
+ known_names: Optional[Set[str]] = None,
143
+ ) -> Iterator[Tuple[int, int, str, str]]:
144
+ """迭代含真实 ``name`` 的完整 ``<entml:invoke>…</entml:invoke>`` 块。"""
145
+ if not text:
146
+ return
147
+ search_from = 0
148
+ prefix_len = len(_INVOKE_OPEN_PREFIX)
149
+ close_len = len(_INVOKE_CLOSE)
150
+ while search_from < len(text):
151
+ pos = text.find(_INVOKE_OPEN_PREFIX, search_from)
152
+ if pos < 0:
153
+ break
154
+ if not entml_invoke_open_is_actionable(text, pos, known_names=known_names):
155
+ gt = text.find(">", pos + prefix_len)
156
+ search_from = (gt + 1) if gt >= 0 else (pos + prefix_len)
157
+ continue
158
+ gt = text.find(">", pos + prefix_len)
159
+ if gt < 0:
160
+ break
161
+ close = text.find(_INVOKE_CLOSE, gt + 1)
162
+ if close < 0:
163
+ break
164
+ attrs = text[pos + prefix_len : gt]
165
+ body = text[gt + 1 : close]
166
+ yield pos, close + close_len, attrs, body
167
+ search_from = close + close_len
168
+
169
+
170
+ def _strip_orphan_invoke_tags(
171
+ content: str,
172
+ *,
173
+ known_names: Optional[Set[str]] = None,
174
+ ) -> str:
175
+ """仅剥离带真实 name 的 invoke 孤儿标签;保留 prose 中的 ``<entml:invoke>`` 提及。"""
176
+ pattern = re.compile(r"</?entml:invoke\b[^>]*/?>", re.DOTALL)
177
+
178
+ def repl(match: re.Match[str]) -> str:
179
+ tag = match.group(0)
180
+ if tag.startswith("</"):
181
+ if _invoke_close_should_strip(content, match.start(), known_names=known_names):
182
+ return ""
183
+ return tag
184
+ if entml_invoke_open_is_actionable(content, match.start(), known_names=known_names):
185
+ return ""
186
+ return tag
187
+
188
+ return pattern.sub(repl, content)
189
+
190
+
191
+ def _strip_orphan_non_invoke_tool_tags(content: str) -> str:
192
+ return re.sub(
193
+ r"</?entml:(?:function_calls|parameter|parameters)\b[^>]*/?>",
194
+ "",
195
+ content,
196
+ flags=re.DOTALL,
197
+ )