echotools 2.3.78__tar.gz → 2.3.80__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.
- {echotools-2.3.78 → echotools-2.3.80}/PKG-INFO +1 -1
- {echotools-2.3.78 → echotools-2.3.80}/pyproject.toml +1 -1
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/parsers/stream.py +19 -16
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml.py +18 -4
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_think/parse.py +50 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_tool_blocks.py +117 -15
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools.egg-info/SOURCES.txt +1 -0
- echotools-2.3.80/src/tests/test_entml_batch_stream_comprehensive.py +358 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_tool_parse_leak.py +6 -5
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_tool_pipeline.py +3 -3
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_simulated_llm_tool_parse.py +21 -18
- {echotools-2.3.78 → echotools-2.3.80}/README.md +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/docs/README.md +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/setup.cfg +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/compat.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/history_markup.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools/version.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/bench_import.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/conftest.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_completion.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_config.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_console.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_errors.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_events.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_facade.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fake_history_markup_batch.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fake_history_markup_stream.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fault_thinking_close_batch.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fault_thinking_close_stream.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_history_markup_filter.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_ids.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_imports.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_io.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_keys.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_logger.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_merge.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_stream_partial_json_parity.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_usage.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_web.py +0 -0
- {echotools-2.3.78 → echotools-2.3.80}/src/tests/test_web_broker.py +0 -0
|
@@ -615,23 +615,14 @@ class FncallStreamParser:
|
|
|
615
615
|
|
|
616
616
|
def _stream_visible_buffer(self) -> str:
|
|
617
617
|
"""与 batch ``parse`` 一致:未闭合 thinking 之后的正文不参与可见/剥离。"""
|
|
618
|
-
buf = self._raw_buf
|
|
619
|
-
if not buf:
|
|
620
|
-
return ""
|
|
621
618
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
622
|
-
|
|
623
|
-
has_unclosed_entml_thinking,
|
|
619
|
+
stream_safe_visible_prefix,
|
|
624
620
|
)
|
|
625
621
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
buf, thinking_enabled=self._thinking_enabled
|
|
631
|
-
)
|
|
632
|
-
if open_at >= 0:
|
|
633
|
-
return buf[:open_at]
|
|
634
|
-
return buf
|
|
622
|
+
return stream_safe_visible_prefix(
|
|
623
|
+
self._raw_buf,
|
|
624
|
+
thinking_enabled=self._thinking_enabled,
|
|
625
|
+
)
|
|
635
626
|
|
|
636
627
|
def _stream_display_text(self) -> str:
|
|
637
628
|
"""流式可见正文:在完整 raw 缓冲上剥离伪 history(保留 thinking 保护区)。"""
|
|
@@ -671,7 +662,11 @@ class FncallStreamParser:
|
|
|
671
662
|
self._text_parts.append(part)
|
|
672
663
|
|
|
673
664
|
# 与 batch parse 同路径(须在含 thinking 标签的 raw 缓冲上剥离伪 history)。
|
|
674
|
-
clean_text, tool_calls = self._protocol.parse(
|
|
665
|
+
clean_text, tool_calls = self._protocol.parse(
|
|
666
|
+
self._raw_buf,
|
|
667
|
+
self._tools,
|
|
668
|
+
thinking_enabled=self._thinking_enabled,
|
|
669
|
+
)
|
|
675
670
|
|
|
676
671
|
clean_fn = getattr(self._protocol, "clean_tool_tags", None)
|
|
677
672
|
if callable(clean_fn):
|
|
@@ -728,7 +723,14 @@ class FncallStreamParser:
|
|
|
728
723
|
if self._thinking_enabled and self._raw_buf:
|
|
729
724
|
lower = self._raw_buf.lower()
|
|
730
725
|
if "<entml:invoke" not in lower and "<entml:function_calls" not in lower:
|
|
731
|
-
|
|
726
|
+
display = self._stream_display_text()
|
|
727
|
+
if display:
|
|
728
|
+
from echotools.exec.fncall.shared.history_markup import (
|
|
729
|
+
strip_fake_history_markup_for_display,
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
cleaned, _ = strip_fake_history_markup_for_display(display)
|
|
733
|
+
return cleaned
|
|
732
734
|
return ""
|
|
733
735
|
|
|
734
736
|
@property
|
|
@@ -748,6 +750,7 @@ class FncallStreamParser:
|
|
|
748
750
|
parse_kwargs: Dict[str, Any] = {}
|
|
749
751
|
if getattr(self._protocol, "id", None) == "entml":
|
|
750
752
|
parse_kwargs["include_tool_blocks"] = False
|
|
753
|
+
parse_kwargs["thinking_enabled"] = self._thinking_enabled
|
|
751
754
|
_, all_calls = self._protocol.parse(
|
|
752
755
|
self._raw_buf, self._tools, **parse_kwargs
|
|
753
756
|
)
|
|
@@ -18,7 +18,10 @@ from echotools.exec.fncall.prompt.templates import (
|
|
|
18
18
|
from echotools.exec.fncall.protocols.entml_invoke import (
|
|
19
19
|
parse_entml_tool_calls,
|
|
20
20
|
)
|
|
21
|
-
from echotools.exec.fncall.protocols.entml_tool_blocks import
|
|
21
|
+
from echotools.exec.fncall.protocols.entml_tool_blocks import (
|
|
22
|
+
parse_tool_block_calls,
|
|
23
|
+
strip_tool_block_spans,
|
|
24
|
+
)
|
|
22
25
|
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
23
26
|
BLOCK_RE,
|
|
24
27
|
entml_invoke_open_may_be_streaming,
|
|
@@ -151,12 +154,13 @@ class EntmlProtocol(ToolProtocol):
|
|
|
151
154
|
_THINKING_PREFIX = "<entml:thinking"
|
|
152
155
|
|
|
153
156
|
def get_trigger_tags(self) -> List[str]:
|
|
154
|
-
# invoke / thinking 前缀 holdback,避免 `<e` 歧义被提前吐出
|
|
157
|
+
# invoke / thinking / 伪 history tool 前缀 holdback,避免 `<e` / `<t` 歧义被提前吐出
|
|
155
158
|
return [
|
|
156
159
|
self._TRIGGER,
|
|
157
160
|
self._TRIGGER_PREFIX,
|
|
158
161
|
self._THINKING_PREFIX,
|
|
159
162
|
f"{self._THINKING_PREFIX}>",
|
|
163
|
+
"<tool",
|
|
160
164
|
]
|
|
161
165
|
|
|
162
166
|
def normalize_stream_buffer(self, buffer: str) -> str:
|
|
@@ -271,6 +275,7 @@ class EntmlProtocol(ToolProtocol):
|
|
|
271
275
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
272
276
|
*,
|
|
273
277
|
include_tool_blocks: bool = True,
|
|
278
|
+
thinking_enabled: bool = True,
|
|
274
279
|
) -> Tuple[str, List[Dict[str, Any]]]:
|
|
275
280
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
276
281
|
_find_earliest_thinking_open,
|
|
@@ -279,6 +284,7 @@ class EntmlProtocol(ToolProtocol):
|
|
|
279
284
|
)
|
|
280
285
|
|
|
281
286
|
schema_index = _build_param_schema_index(tools) if tools else None
|
|
287
|
+
# 工具解析始终按 thinking 开启语义分流(fault ``</thinking>`` 等);可见正文再按 thinking_enabled 剥离。
|
|
282
288
|
parse_text, _thinking = split_entml_thinking(text, thinking_enabled=True)
|
|
283
289
|
unclosed_open_at = -1
|
|
284
290
|
if has_unclosed_entml_thinking(text, thinking_enabled=True):
|
|
@@ -288,10 +294,18 @@ class EntmlProtocol(ToolProtocol):
|
|
|
288
294
|
if unclosed_open_at >= 0:
|
|
289
295
|
parse_text = text[:unclosed_open_at]
|
|
290
296
|
tool_calls = parse_entml_tool_calls(parse_text, tools, schema_index)
|
|
297
|
+
tool_block_calls: List[Dict[str, Any]] = []
|
|
291
298
|
if include_tool_blocks:
|
|
292
|
-
|
|
299
|
+
tool_block_calls = parse_tool_block_calls(parse_text, tools, schema_index)
|
|
300
|
+
tool_calls.extend(tool_block_calls)
|
|
293
301
|
clean = text[:unclosed_open_at] if unclosed_open_at >= 0 else text
|
|
294
|
-
|
|
302
|
+
if thinking_enabled:
|
|
303
|
+
# fault ``</thinking>`` 须借后续 invoke 判定闭合;thinking 剥离必须在 invoke 移除之前。
|
|
304
|
+
visible, _ = split_entml_thinking(clean, thinking_enabled=True)
|
|
305
|
+
clean = visible
|
|
306
|
+
if tool_block_calls:
|
|
307
|
+
clean = strip_tool_block_spans(clean)
|
|
308
|
+
# 须在移除 invoke 之前剥离伪 history,且须晚于已解析的 ``<tool>`` 块剥离。
|
|
295
309
|
clean, _ = strip_fake_history_markup(clean)
|
|
296
310
|
if tool_calls:
|
|
297
311
|
clean = BLOCK_RE.sub("", clean)
|
{echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
@@ -26,6 +26,56 @@ _AMBIGUOUS_ENTML_PREFIXES = (
|
|
|
26
26
|
"<entml:parameters",
|
|
27
27
|
)
|
|
28
28
|
_INVOKE_PREFIX = "<entml:invoke"
|
|
29
|
+
_FUNCTION_CALLS_PREFIX = "<entml:function_calls"
|
|
30
|
+
_LEADING_HOLD_PREFIXES = _AMBIGUOUS_ENTML_PREFIXES + (
|
|
31
|
+
_THINKING_OPEN_PREFIX,
|
|
32
|
+
_FUNCTION_CALLS_PREFIX,
|
|
33
|
+
"<tool",
|
|
34
|
+
"<assistant",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def leading_entml_tag_holdback_len(
|
|
39
|
+
text: str,
|
|
40
|
+
*,
|
|
41
|
+
thinking_enabled: bool = True,
|
|
42
|
+
) -> int:
|
|
43
|
+
"""buffer 开头是 entml/thinking/invoke 标签真前缀时,应 hold 的字节数。"""
|
|
44
|
+
if not text:
|
|
45
|
+
return 0
|
|
46
|
+
prefixes: List[str] = list(_LEADING_HOLD_PREFIXES)
|
|
47
|
+
if thinking_enabled:
|
|
48
|
+
prefixes.append(_PLAIN_THINKING_OPEN_PREFIX)
|
|
49
|
+
for prefix in prefixes:
|
|
50
|
+
if text.startswith(prefix):
|
|
51
|
+
return 0
|
|
52
|
+
max_hold = max(len(prefix) - 1 for prefix in prefixes)
|
|
53
|
+
for length in range(min(len(text), max_hold), 0, -1):
|
|
54
|
+
head = text[:length]
|
|
55
|
+
if any(prefix.startswith(head) and head != prefix for prefix in prefixes):
|
|
56
|
+
return length
|
|
57
|
+
return 0
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def stream_safe_visible_prefix(
|
|
61
|
+
text: str,
|
|
62
|
+
*,
|
|
63
|
+
thinking_enabled: bool = True,
|
|
64
|
+
) -> str:
|
|
65
|
+
"""流式 UI 可安全展示的 prefix(不含未收齐的 entml/thinking 开标签)。"""
|
|
66
|
+
if not text:
|
|
67
|
+
return ""
|
|
68
|
+
hold = leading_entml_tag_holdback_len(text, thinking_enabled=thinking_enabled)
|
|
69
|
+
if hold >= len(text):
|
|
70
|
+
return ""
|
|
71
|
+
buf = text[hold:]
|
|
72
|
+
if has_unclosed_entml_thinking(buf, thinking_enabled=thinking_enabled):
|
|
73
|
+
open_at, _ = _find_earliest_thinking_open(
|
|
74
|
+
buf, thinking_enabled=thinking_enabled
|
|
75
|
+
)
|
|
76
|
+
if open_at >= 0:
|
|
77
|
+
return buf[:open_at]
|
|
78
|
+
return buf
|
|
29
79
|
|
|
30
80
|
|
|
31
81
|
def find_complete_entml_invoke_open(buffer: str) -> int:
|
{echotools-2.3.78 → echotools-2.3.80}/src/echotools/exec/fncall/protocols/entml_tool_blocks.py
RENAMED
|
@@ -4,11 +4,17 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import re
|
|
7
|
-
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
7
|
+
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple
|
|
8
8
|
|
|
9
9
|
from .entml_patterns import INVOKE_RE, normalize_entml_name
|
|
10
10
|
from .entml_values import coerce_entml_arguments
|
|
11
11
|
|
|
12
|
+
_TOOL_BLOCK_OPEN_RE = re.compile(r"<tool\s*>", re.IGNORECASE)
|
|
13
|
+
_TOOL_BLOCK_CLOSE_RE = re.compile(
|
|
14
|
+
r"(?:</tool\s*>|</system\b[^>]*>|</assistant\s*>)",
|
|
15
|
+
re.IGNORECASE,
|
|
16
|
+
)
|
|
17
|
+
# legacy 整段匹配(测试/简单场景)
|
|
12
18
|
_TOOL_BLOCK_RE = re.compile(
|
|
13
19
|
r"<tool\s*>\s*([\s\S]*?)\s*</tool\s*>",
|
|
14
20
|
re.IGNORECASE,
|
|
@@ -18,8 +24,9 @@ _INNER_TOOL_TAG_RE = re.compile(
|
|
|
18
24
|
re.IGNORECASE,
|
|
19
25
|
)
|
|
20
26
|
_BRACE_TOOL_HEAD_RE = re.compile(r"\{([A-Za-z][A-Za-z0-9_]*)\s*:\s*")
|
|
21
|
-
# 工具结果常见行号泄漏(Read
|
|
27
|
+
# 工具结果常见行号泄漏(Read 伪块)— 仅用于多块 brace 判定
|
|
22
28
|
_OUTPUT_LINE_RE = re.compile(r"^\s*\d+\s+\S", re.MULTILINE)
|
|
29
|
+
_SCALAR_ARG_KEYS = ("path", "command", "pattern", "query", "file_path")
|
|
23
30
|
|
|
24
31
|
|
|
25
32
|
def _known_tool_names(
|
|
@@ -53,25 +60,116 @@ def _args_from_parsed(name: str, parsed: Any) -> Dict[str, Any]:
|
|
|
53
60
|
return {"value": parsed}
|
|
54
61
|
|
|
55
62
|
|
|
63
|
+
def _args_from_scalar(
|
|
64
|
+
name: str,
|
|
65
|
+
scalar: str,
|
|
66
|
+
schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]],
|
|
67
|
+
) -> Dict[str, Any]:
|
|
68
|
+
"""``{Read: path/to/file}`` / ``{Bash: echo hi}`` 等标量行。"""
|
|
69
|
+
func_props = (schema_index or {}).get(name) or {}
|
|
70
|
+
if len(func_props) == 1:
|
|
71
|
+
key = next(iter(func_props))
|
|
72
|
+
return {key: scalar}
|
|
73
|
+
for key in _SCALAR_ARG_KEYS:
|
|
74
|
+
if key in func_props:
|
|
75
|
+
return {key: scalar}
|
|
76
|
+
return {"value": scalar}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _looks_like_file_path(value: str) -> bool:
|
|
80
|
+
text = (value or "").strip()
|
|
81
|
+
if not text:
|
|
82
|
+
return False
|
|
83
|
+
if "/" in text or "\\" in text:
|
|
84
|
+
return True
|
|
85
|
+
if len(text) >= 2 and text[1] == ":" and text[0].isalpha():
|
|
86
|
+
return True
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
|
|
56
90
|
def _extract_first_brace_tool_call(body: str) -> Optional[Tuple[str, Any]]:
|
|
57
|
-
"""从正文提取首个 ``{Tool: json}``(允许后面跟工具输出 tail)。"""
|
|
91
|
+
"""从正文提取首个 ``{Tool: json|scalar}``(允许后面跟工具输出 tail)。"""
|
|
58
92
|
match = _BRACE_TOOL_HEAD_RE.search(body)
|
|
59
93
|
if not match:
|
|
60
94
|
return None
|
|
61
95
|
name = normalize_entml_name(match.group(1))
|
|
62
96
|
rest = body[match.end() :].lstrip()
|
|
63
|
-
if not rest
|
|
97
|
+
if not rest:
|
|
64
98
|
return None
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
99
|
+
if rest[0] in "{[":
|
|
100
|
+
try:
|
|
101
|
+
parsed, end = json.JSONDecoder().raw_decode(rest)
|
|
102
|
+
except json.JSONDecodeError:
|
|
103
|
+
return None
|
|
104
|
+
after = rest[end:].lstrip()
|
|
105
|
+
if after.startswith("}"):
|
|
106
|
+
after = after[1:].lstrip()
|
|
107
|
+
# JSON + 行号 tail 为 history Read 伪块;标量 path + tail 仍解析(模型误格式真调用)
|
|
108
|
+
if after and _OUTPUT_LINE_RE.search(after):
|
|
109
|
+
return None
|
|
110
|
+
return name, parsed
|
|
111
|
+
close = rest.find("}")
|
|
112
|
+
if close < 0:
|
|
113
|
+
return None
|
|
114
|
+
scalar = rest[:close].strip()
|
|
115
|
+
if not scalar:
|
|
68
116
|
return None
|
|
69
|
-
after = rest[
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if
|
|
117
|
+
after = rest[close + 1 :]
|
|
118
|
+
has_output_tail = bool(_OUTPUT_LINE_RE.search(after))
|
|
119
|
+
# 标量行:仅 path 型 Read/Glob 或带 Read 行号 tail;``{Bash: echo hi}`` 视为 history
|
|
120
|
+
if not has_output_tail and not _looks_like_file_path(scalar):
|
|
73
121
|
return None
|
|
74
|
-
return name,
|
|
122
|
+
return name, scalar
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _iter_tool_block_spans(text: str) -> Iterator[Tuple[int, int, str]]:
|
|
126
|
+
"""yield ``(open_start, close_end, body)``。"""
|
|
127
|
+
if not text or "<tool" not in text.lower():
|
|
128
|
+
return
|
|
129
|
+
pos = 0
|
|
130
|
+
while True:
|
|
131
|
+
open_m = _TOOL_BLOCK_OPEN_RE.search(text, pos)
|
|
132
|
+
if not open_m:
|
|
133
|
+
break
|
|
134
|
+
body_start = open_m.end()
|
|
135
|
+
close_m = _TOOL_BLOCK_CLOSE_RE.search(text, body_start)
|
|
136
|
+
if close_m:
|
|
137
|
+
close_end = close_m.end()
|
|
138
|
+
close_tag = text[close_m.start() : close_m.end()].lower()
|
|
139
|
+
if close_tag.startswith("</system"):
|
|
140
|
+
after = text[close_end:]
|
|
141
|
+
footer = re.match(
|
|
142
|
+
r"[^\n<]*(?:</system\s*>)?",
|
|
143
|
+
after,
|
|
144
|
+
re.IGNORECASE,
|
|
145
|
+
)
|
|
146
|
+
if footer:
|
|
147
|
+
close_end += footer.end()
|
|
148
|
+
after = text[close_end:]
|
|
149
|
+
asst = re.search(r"^\s*</assistant\s*>", after, re.IGNORECASE | re.MULTILINE)
|
|
150
|
+
if asst:
|
|
151
|
+
close_end += asst.end()
|
|
152
|
+
yield open_m.start(), close_end, text[body_start : close_m.start()]
|
|
153
|
+
pos = close_end
|
|
154
|
+
else:
|
|
155
|
+
yield open_m.start(), len(text), text[body_start:]
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def strip_tool_block_spans(text: str) -> str:
|
|
160
|
+
"""移除 ``<tool>…`` 块(含误用的 ``</system>`` / ``</assistant>`` 闭合)。"""
|
|
161
|
+
if not text or "<tool" not in text.lower():
|
|
162
|
+
return text
|
|
163
|
+
parts: List[str] = []
|
|
164
|
+
last = 0
|
|
165
|
+
for open_start, close_end, _body in _iter_tool_block_spans(text):
|
|
166
|
+
parts.append(text[last:open_start])
|
|
167
|
+
last = close_end
|
|
168
|
+
parts.append(text[last:])
|
|
169
|
+
out = "".join(parts)
|
|
170
|
+
out = re.sub(r"^\s*<assistant\s*>\s*\n+", "", out, count=1, flags=re.IGNORECASE)
|
|
171
|
+
out = re.sub(r"\n{3,}", "\n\n", out)
|
|
172
|
+
return out.strip()
|
|
75
173
|
|
|
76
174
|
|
|
77
175
|
def _parse_inner_tag_calls(
|
|
@@ -114,7 +212,11 @@ def _parse_brace_calls(
|
|
|
114
212
|
name, value = parsed
|
|
115
213
|
if known and name not in known:
|
|
116
214
|
return []
|
|
117
|
-
|
|
215
|
+
if isinstance(value, str):
|
|
216
|
+
args = _args_from_scalar(name, value, schema_index)
|
|
217
|
+
else:
|
|
218
|
+
args = _args_from_parsed(name, value)
|
|
219
|
+
args = coerce_entml_arguments(args, name, schema_index)
|
|
118
220
|
return [(name, args)]
|
|
119
221
|
|
|
120
222
|
|
|
@@ -152,9 +254,9 @@ def parse_tool_block_calls(
|
|
|
152
254
|
|
|
153
255
|
allow_brace = not bool(INVOKE_RE.search(text))
|
|
154
256
|
tool_calls: List[Dict[str, Any]] = []
|
|
155
|
-
for
|
|
257
|
+
for _open, _close, body in _iter_tool_block_spans(text):
|
|
156
258
|
for name, args in parse_tool_block_body(
|
|
157
|
-
|
|
259
|
+
body,
|
|
158
260
|
tools=tools,
|
|
159
261
|
schema_index=schema_index,
|
|
160
262
|
allow_brace_format=allow_brace,
|
|
@@ -186,6 +186,7 @@ src/tests/test_dispatch_extended.py
|
|
|
186
186
|
src/tests/test_dispatch_race.py
|
|
187
187
|
src/tests/test_entml_agent_output_formats.py
|
|
188
188
|
src/tests/test_entml_bare_invoke_format.py
|
|
189
|
+
src/tests/test_entml_batch_stream_comprehensive.py
|
|
189
190
|
src/tests/test_entml_parser_comprehensive.py
|
|
190
191
|
src/tests/test_entml_protocol.py
|
|
191
192
|
src/tests/test_entml_stream_json.py
|