echotools 2.3.94__tar.gz → 2.3.97__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.94 → echotools-2.3.97}/PKG-INFO +1 -1
- {echotools-2.3.94 → echotools-2.3.97}/pyproject.toml +1 -1
- echotools-2.3.97/src/echotools/exec/fncall/protocols/entml_fake_structure_markup.py +198 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/parse.py +12 -4
- echotools-2.3.97/src/echotools/exec/fncall/protocols/entml_tool_result_comment.py +70 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/history_markup.py +9 -2
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools.egg-info/SOURCES.txt +3 -0
- echotools-2.3.97/src/tests/test_entml_fake_structure_markup.py +211 -0
- {echotools-2.3.94 → echotools-2.3.97}/README.md +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/setup.cfg +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/compat.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/stream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/stream_delta.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/stream_feed.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/stream_final.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/behavior_blocks.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_schema.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_stream/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_stream/body.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_stream/snap.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/blocks.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/filter.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/hold.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools/version.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/bench_import.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/conftest.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_completion.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_config.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_console.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_batch_stream_comprehensive.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_invoke_known_tool_gate.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_errors.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_events.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_facade.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fake_history_markup_batch.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fake_history_markup_stream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fault_thinking_close_batch.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fault_thinking_close_stream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_history_markup_filter.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_ids.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_imports.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_io.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_keys.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_logger.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_merge.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_stream_partial_json_parity.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_usage.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_web.py +0 -0
- {echotools-2.3.94 → echotools-2.3.97}/src/tests/test_web_broker.py +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""剥离模型误生成的 entml 顶层结构标签与 Tool Result ID 注释。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
|
|
8
|
+
from echotools.exec.fncall.protocols.entml_tool_result_comment import (
|
|
9
|
+
leading_partial_tool_result_id_comment_len,
|
|
10
|
+
strip_complete_tool_result_id_comments,
|
|
11
|
+
trailing_partial_tool_result_id_comment_len,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Step 1:带 id 的 result 整块(含内部正文)剥离。
|
|
15
|
+
_ENTML_RESULT_ID_BLOCK_RE = re.compile(
|
|
16
|
+
r"<entml:result\b[^>]*\bid\s*=[^>]*>[\s\S]*?</entml:result\s*>",
|
|
17
|
+
re.IGNORECASE,
|
|
18
|
+
)
|
|
19
|
+
_ENTML_RESULT_ID_OPEN_INNER_RE = re.compile(
|
|
20
|
+
r"<entml:result\b[^>]*\bid\s*=",
|
|
21
|
+
re.IGNORECASE,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Step 2:仅剥离开/闭标签(``>`` 闭合即移除,不删标签间/后正文)。
|
|
25
|
+
_TAG_ONLY_OPEN_RES = (
|
|
26
|
+
re.compile(r"<entml:funtions_results\b[^>]*>", re.IGNORECASE),
|
|
27
|
+
re.compile(r"<entml:conversation_history\b[^>]*>", re.IGNORECASE),
|
|
28
|
+
re.compile(r"<entml:calls\b[^>]*>", re.IGNORECASE),
|
|
29
|
+
re.compile(r"<function_calling_behavior\b[^>]*>", re.IGNORECASE),
|
|
30
|
+
re.compile(r"<thinking_behavior\b[^>]*>", re.IGNORECASE),
|
|
31
|
+
re.compile(r"<entml:result\b[^>]*>", re.IGNORECASE),
|
|
32
|
+
)
|
|
33
|
+
_TAG_ONLY_CLOSE_RES = (
|
|
34
|
+
re.compile(r"</entml:funtions_results\s*>", re.IGNORECASE),
|
|
35
|
+
re.compile(r"</entml:conversation_history\s*>", re.IGNORECASE),
|
|
36
|
+
re.compile(r"</entml:calls\s*>", re.IGNORECASE),
|
|
37
|
+
re.compile(r"</function_calling_behavior\s*>", re.IGNORECASE),
|
|
38
|
+
re.compile(r"</thinking_behavior\s*>", re.IGNORECASE),
|
|
39
|
+
re.compile(r"</entml:result\s*>", re.IGNORECASE),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
_FAKE_ENTML_TAG_PREFIXES: Tuple[str, ...] = (
|
|
43
|
+
"<entml:result",
|
|
44
|
+
"</entml:result",
|
|
45
|
+
"<entml:funtions_results",
|
|
46
|
+
"</entml:funtions_results",
|
|
47
|
+
"<entml:conversation_history",
|
|
48
|
+
"</entml:conversation_history",
|
|
49
|
+
"<entml:calls",
|
|
50
|
+
"</entml:calls",
|
|
51
|
+
"<function_calling_behavior",
|
|
52
|
+
"</function_calling_behavior",
|
|
53
|
+
"<thinking_behavior",
|
|
54
|
+
"</thinking_behavior",
|
|
55
|
+
"</entml:invoke",
|
|
56
|
+
"</entml:parameter",
|
|
57
|
+
"</entml:thinking",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# 模型误输出的残缺/单行 ``</entml:…``(无完整闭标签名或未收齐 ``>``)。
|
|
61
|
+
_ORPHAN_ENTML_CLOSE_LINE_RE = re.compile(
|
|
62
|
+
r"(?m)^\s*(?:●\s*)?</entml:?[a-z0-9_-]*\s*$",
|
|
63
|
+
re.IGNORECASE,
|
|
64
|
+
)
|
|
65
|
+
_TRAILING_INCOMPLETE_ENTML_CLOSE_RE = re.compile(
|
|
66
|
+
r"</entml:?[a-z0-9_-]*\s*$",
|
|
67
|
+
re.IGNORECASE,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _strip_fake_entml_result_id_blocks(text: str) -> Tuple[str, bool]:
|
|
72
|
+
text, n = _ENTML_RESULT_ID_BLOCK_RE.subn("", text)
|
|
73
|
+
return text, n > 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _strip_tag_only_markup(text: str) -> Tuple[str, bool]:
|
|
77
|
+
found = False
|
|
78
|
+
for pattern in _TAG_ONLY_OPEN_RES + _TAG_ONLY_CLOSE_RES:
|
|
79
|
+
text, n = pattern.subn("", text)
|
|
80
|
+
if n:
|
|
81
|
+
found = True
|
|
82
|
+
return text, found
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _strip_orphan_entml_close_leaks(text: str) -> Tuple[str, bool]:
|
|
86
|
+
text, n1 = _ORPHAN_ENTML_CLOSE_LINE_RE.subn("", text)
|
|
87
|
+
text, n2 = _TRAILING_INCOMPLETE_ENTML_CLOSE_RE.subn("", text)
|
|
88
|
+
return text, (n1 + n2) > 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _could_be_fake_entml_tag_prefix(fragment: str) -> bool:
|
|
92
|
+
frag = fragment.lower()
|
|
93
|
+
if not frag.startswith("<"):
|
|
94
|
+
return False
|
|
95
|
+
if ">" in frag:
|
|
96
|
+
return False
|
|
97
|
+
for prefix in _FAKE_ENTML_TAG_PREFIXES:
|
|
98
|
+
pl = prefix.lower()
|
|
99
|
+
if pl.startswith(frag) or frag.startswith(pl):
|
|
100
|
+
return True
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def trailing_partial_fake_entml_structure_len(text: str) -> int:
|
|
105
|
+
"""尾部未收齐至 ``>`` 的伪 entml 结构标签应 hold 的字节数。"""
|
|
106
|
+
hold = trailing_partial_tool_result_id_comment_len(text)
|
|
107
|
+
if hold:
|
|
108
|
+
return hold
|
|
109
|
+
lt = text.rfind("<")
|
|
110
|
+
if lt < 0:
|
|
111
|
+
return 0
|
|
112
|
+
suffix = text[lt:]
|
|
113
|
+
if ">" in suffix:
|
|
114
|
+
return 0
|
|
115
|
+
if _could_be_fake_entml_tag_prefix(suffix):
|
|
116
|
+
return len(suffix)
|
|
117
|
+
return 0
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def leading_partial_fake_entml_structure_len(text: str) -> int:
|
|
121
|
+
"""开头未收齐至 ``>`` 的伪 entml 结构标签应 hold 的字节数。"""
|
|
122
|
+
hold = leading_partial_tool_result_id_comment_len(text)
|
|
123
|
+
if hold:
|
|
124
|
+
return hold
|
|
125
|
+
stripped = text.lstrip()
|
|
126
|
+
if not stripped.startswith("<"):
|
|
127
|
+
return 0
|
|
128
|
+
if ">" in stripped:
|
|
129
|
+
return 0
|
|
130
|
+
if _could_be_fake_entml_tag_prefix(stripped):
|
|
131
|
+
return len(text)
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _truncate_unclosed_fake_result_id_tail(text: str) -> Tuple[str, bool]:
|
|
136
|
+
"""流式/batch:``<entml:result id=...>`` 未闭合至 ``</entml:result>`` 前均不可见。"""
|
|
137
|
+
found = False
|
|
138
|
+
search_from = 0
|
|
139
|
+
while True:
|
|
140
|
+
open_m = _ENTML_RESULT_ID_OPEN_INNER_RE.search(text[search_from:])
|
|
141
|
+
if not open_m:
|
|
142
|
+
break
|
|
143
|
+
abs_start = search_from + open_m.start()
|
|
144
|
+
gt = text.find(">", abs_start)
|
|
145
|
+
if gt < 0:
|
|
146
|
+
text = text[:abs_start]
|
|
147
|
+
found = True
|
|
148
|
+
break
|
|
149
|
+
abs_end = gt + 1
|
|
150
|
+
tail = text[abs_end:]
|
|
151
|
+
close_m = re.search(r"</entml:result\s*>", tail, re.IGNORECASE)
|
|
152
|
+
if close_m:
|
|
153
|
+
search_from = abs_end + close_m.end()
|
|
154
|
+
continue
|
|
155
|
+
text = text[:abs_start]
|
|
156
|
+
found = True
|
|
157
|
+
break
|
|
158
|
+
return text, found
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def strip_fake_entml_structure_markup(content: str) -> Tuple[str, bool]:
|
|
162
|
+
"""batch:id result 整块 → 仅标签 → Tool Result ID 注释。"""
|
|
163
|
+
if not content:
|
|
164
|
+
return content, False
|
|
165
|
+
found = False
|
|
166
|
+
text = strip_complete_tool_result_id_comments(content)
|
|
167
|
+
if text != content:
|
|
168
|
+
found = True
|
|
169
|
+
text, hit = _strip_fake_entml_result_id_blocks(text)
|
|
170
|
+
found = found or hit
|
|
171
|
+
text, hit = _truncate_unclosed_fake_result_id_tail(text)
|
|
172
|
+
found = found or hit
|
|
173
|
+
text, hit = _strip_tag_only_markup(text)
|
|
174
|
+
found = found or hit
|
|
175
|
+
text, hit = _strip_orphan_entml_close_leaks(text)
|
|
176
|
+
found = found or hit
|
|
177
|
+
text = re.sub(r"\n{3,}", "\n\n", text)
|
|
178
|
+
return text, found
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def strip_fake_entml_structure_markup_for_display(content: str) -> Tuple[str, bool]:
|
|
182
|
+
"""流式 ``partial_text``:hold 未收齐标签 + 截断未闭合 id result + 剥离。"""
|
|
183
|
+
if not content:
|
|
184
|
+
return content, False
|
|
185
|
+
found = False
|
|
186
|
+
tail_hold = trailing_partial_fake_entml_structure_len(content)
|
|
187
|
+
text = content[:-tail_hold] if tail_hold else content
|
|
188
|
+
if tail_hold:
|
|
189
|
+
found = True
|
|
190
|
+
text, hit = _truncate_unclosed_fake_result_id_tail(text)
|
|
191
|
+
found = found or hit
|
|
192
|
+
text, hit = strip_fake_entml_structure_markup(text)
|
|
193
|
+
found = found or hit
|
|
194
|
+
tail_hold2 = trailing_partial_fake_entml_structure_len(text)
|
|
195
|
+
if tail_hold2:
|
|
196
|
+
text = text[:-tail_hold2]
|
|
197
|
+
found = True
|
|
198
|
+
return text, found
|
{echotools-2.3.94 → echotools-2.3.97}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
@@ -3,10 +3,11 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import List, Optional, Tuple
|
|
5
5
|
|
|
6
|
-
from echotools.exec.fncall.protocols.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
from echotools.exec.fncall.protocols.entml_fake_structure_markup import (
|
|
7
|
+
leading_partial_fake_entml_structure_len,
|
|
8
|
+
strip_fake_entml_structure_markup,
|
|
9
|
+
strip_fake_entml_structure_markup_for_display,
|
|
10
|
+
trailing_partial_fake_entml_structure_len,
|
|
10
11
|
)
|
|
11
12
|
|
|
12
13
|
THINKING_BLOCK_RE = re.compile(
|
|
@@ -67,6 +68,9 @@ def leading_entml_tag_holdback_len(
|
|
|
67
68
|
"""buffer 开头是 entml/thinking/invoke 标签真前缀时,应 hold 的字节数。"""
|
|
68
69
|
if not text:
|
|
69
70
|
return 0
|
|
71
|
+
comment_hold = leading_partial_fake_entml_structure_len(text)
|
|
72
|
+
if comment_hold:
|
|
73
|
+
return comment_hold
|
|
70
74
|
prefixes: List[str] = list(_LEADING_HOLD_PREFIXES)
|
|
71
75
|
if thinking_enabled:
|
|
72
76
|
prefixes.append(_PLAIN_THINKING_OPEN_PREFIX)
|
|
@@ -110,6 +114,9 @@ def trailing_entml_tag_holdback_len(text: str) -> int:
|
|
|
110
114
|
"""buffer 尾部是 entml/thinking 标签真前缀时,应 hold 的字节数。"""
|
|
111
115
|
if not text:
|
|
112
116
|
return 0
|
|
117
|
+
comment_hold = trailing_partial_fake_entml_structure_len(text)
|
|
118
|
+
if comment_hold:
|
|
119
|
+
return comment_hold
|
|
113
120
|
invoke_idx = text.rfind("<entml:invoke")
|
|
114
121
|
if invoke_idx >= 0:
|
|
115
122
|
tail_from_invoke = text[invoke_idx:]
|
|
@@ -171,6 +178,7 @@ def clean_stream_partial_visible(
|
|
|
171
178
|
tail_hold = trailing_entml_tag_holdback_len(text)
|
|
172
179
|
if tail_hold:
|
|
173
180
|
text = text[:-tail_hold]
|
|
181
|
+
text = strip_fake_entml_structure_markup_for_display(text)[0]
|
|
174
182
|
if has_calls:
|
|
175
183
|
text = text.rstrip()
|
|
176
184
|
if not text.strip():
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""剥离模型误生成的 ``<!-- Tool Result ID:… -->``(仅 history 注入合法)。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
# 与 format_tool_result_id_comment() 输出格式一致;允许空白变化。
|
|
8
|
+
_TOOL_RESULT_ID_COMMENT_RE = re.compile(
|
|
9
|
+
r"<!--\s*Tool\s+Result\s+ID\s*:\s*.*?-->",
|
|
10
|
+
re.IGNORECASE | re.DOTALL,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
_MARKER_LOWER = "<!-- tool result id:"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _could_be_tool_result_id_comment_open(fragment: str) -> bool:
|
|
17
|
+
"""``fragment`` 以 ``<!--`` 开头且可能长成 Tool Result ID 注释(未闭合)。"""
|
|
18
|
+
frag = fragment.strip().lower()
|
|
19
|
+
if not frag.startswith("<!--"):
|
|
20
|
+
return False
|
|
21
|
+
if "-->" in frag:
|
|
22
|
+
return False
|
|
23
|
+
return _MARKER_LOWER.startswith(frag) or frag.startswith(_MARKER_LOWER)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def strip_complete_tool_result_id_comments(text: str) -> str:
|
|
27
|
+
"""移除已完整闭合的 Tool Result ID HTML 注释。"""
|
|
28
|
+
if not text:
|
|
29
|
+
return ""
|
|
30
|
+
return _TOOL_RESULT_ID_COMMENT_RE.sub("", text)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def trailing_partial_tool_result_id_comment_len(text: str) -> int:
|
|
34
|
+
"""尾部未闭合的 Tool Result ID 注释应 hold 的字节数(含 ``<!--`` 起)。"""
|
|
35
|
+
if not text:
|
|
36
|
+
return 0
|
|
37
|
+
idx = text.rfind("<!--")
|
|
38
|
+
if idx < 0:
|
|
39
|
+
return 0
|
|
40
|
+
suffix = text[idx:]
|
|
41
|
+
if "-->" in suffix:
|
|
42
|
+
return 0
|
|
43
|
+
if _could_be_tool_result_id_comment_open(suffix):
|
|
44
|
+
return len(suffix)
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def leading_partial_tool_result_id_comment_len(text: str) -> int:
|
|
49
|
+
"""开头未闭合的 Tool Result ID 注释应 hold 的字节数。"""
|
|
50
|
+
if not text:
|
|
51
|
+
return 0
|
|
52
|
+
stripped = text.lstrip()
|
|
53
|
+
if not stripped.startswith("<!--"):
|
|
54
|
+
return 0
|
|
55
|
+
if "-->" in stripped:
|
|
56
|
+
return 0
|
|
57
|
+
if _could_be_tool_result_id_comment_open(stripped):
|
|
58
|
+
return len(text)
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def strip_tool_result_id_comments_for_display(text: str) -> str:
|
|
63
|
+
"""流式可见区:去掉完整注释;截断尾部未闭合的伪注释。"""
|
|
64
|
+
if not text:
|
|
65
|
+
return ""
|
|
66
|
+
cleaned = strip_complete_tool_result_id_comments(text)
|
|
67
|
+
tail_hold = trailing_partial_tool_result_id_comment_len(cleaned)
|
|
68
|
+
if tail_hold:
|
|
69
|
+
cleaned = cleaned[:-tail_hold]
|
|
70
|
+
return cleaned
|
|
@@ -5,6 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
import re
|
|
6
6
|
from typing import Any, Dict, List, Tuple
|
|
7
7
|
|
|
8
|
+
from echotools.exec.fncall.protocols.entml_fake_structure_markup import (
|
|
9
|
+
strip_fake_entml_structure_markup,
|
|
10
|
+
strip_fake_entml_structure_markup_for_display,
|
|
11
|
+
)
|
|
8
12
|
from echotools.exec.fncall.shared.normalization import normalize_content
|
|
9
13
|
|
|
10
14
|
_FAKE_HISTORY_TAGS = ("assistant", "tool")
|
|
@@ -266,7 +270,9 @@ def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
|
|
|
266
270
|
parts.append(content[open_m.start() :])
|
|
267
271
|
break
|
|
268
272
|
|
|
269
|
-
|
|
273
|
+
merged = "".join(parts)
|
|
274
|
+
merged, hit = strip_fake_entml_structure_markup(merged)
|
|
275
|
+
return merged, found or hit
|
|
270
276
|
|
|
271
277
|
|
|
272
278
|
def _truncate_display_fake_tail(cleaned: str) -> Tuple[str, bool]:
|
|
@@ -323,4 +329,5 @@ def strip_fake_history_markup_for_display(content: str) -> Tuple[str, bool]:
|
|
|
323
329
|
):
|
|
324
330
|
return cleaned, found
|
|
325
331
|
cleaned2, hit = _truncate_display_fake_tail(cleaned)
|
|
326
|
-
|
|
332
|
+
cleaned3, hit2 = strip_fake_entml_structure_markup_for_display(cleaned2)
|
|
333
|
+
return cleaned3, found or hit or hit2
|
|
@@ -59,9 +59,11 @@ src/echotools/exec/fncall/prompt/prompt_helpers.py
|
|
|
59
59
|
src/echotools/exec/fncall/prompt/templates.py
|
|
60
60
|
src/echotools/exec/fncall/protocols/__init__.py
|
|
61
61
|
src/echotools/exec/fncall/protocols/entml.py
|
|
62
|
+
src/echotools/exec/fncall/protocols/entml_fake_structure_markup.py
|
|
62
63
|
src/echotools/exec/fncall/protocols/entml_invoke.py
|
|
63
64
|
src/echotools/exec/fncall/protocols/entml_patterns.py
|
|
64
65
|
src/echotools/exec/fncall/protocols/entml_schema.py
|
|
66
|
+
src/echotools/exec/fncall/protocols/entml_tool_result_comment.py
|
|
65
67
|
src/echotools/exec/fncall/protocols/entml_stream/__init__.py
|
|
66
68
|
src/echotools/exec/fncall/protocols/entml_stream/body.py
|
|
67
69
|
src/echotools/exec/fncall/protocols/entml_stream/snap.py
|
|
@@ -193,6 +195,7 @@ src/tests/test_dispatch_race.py
|
|
|
193
195
|
src/tests/test_entml_agent_output_formats.py
|
|
194
196
|
src/tests/test_entml_bare_invoke_format.py
|
|
195
197
|
src/tests/test_entml_batch_stream_comprehensive.py
|
|
198
|
+
src/tests/test_entml_fake_structure_markup.py
|
|
196
199
|
src/tests/test_entml_invoke_known_tool_gate.py
|
|
197
200
|
src/tests/test_entml_parser_comprehensive.py
|
|
198
201
|
src/tests/test_entml_protocol.py
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from echotools.exec.fncall import get_protocol
|
|
6
|
+
from echotools.exec.fncall.parsers.stream import FncallStreamParser
|
|
7
|
+
from echotools.exec.fncall.protocols.entml_fake_structure_markup import (
|
|
8
|
+
strip_fake_entml_structure_markup,
|
|
9
|
+
strip_fake_entml_structure_markup_for_display,
|
|
10
|
+
)
|
|
11
|
+
from echotools.exec.fncall.protocols.entml_tool_result_comment import (
|
|
12
|
+
trailing_partial_tool_result_id_comment_len,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
READ_TOOL = {
|
|
16
|
+
"type": "function",
|
|
17
|
+
"function": {
|
|
18
|
+
"name": "Read",
|
|
19
|
+
"parameters": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {"path": {"type": "string"}},
|
|
22
|
+
"required": ["path"],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize(
|
|
29
|
+
"raw,expect_sub,expect_absent,expect_present",
|
|
30
|
+
[
|
|
31
|
+
(
|
|
32
|
+
'前言\n<!-- Tool Result ID:toolu_f1a2b3c4 -->\n后缀',
|
|
33
|
+
"前言",
|
|
34
|
+
"Tool Result ID",
|
|
35
|
+
None,
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
'可见\n<entml:result id="toolu_46bd973b2dbd48e681b54714">\n{"ok":1}\n</entml:result>\n尾',
|
|
39
|
+
"可见",
|
|
40
|
+
'{"ok":1}',
|
|
41
|
+
"尾",
|
|
42
|
+
),
|
|
43
|
+
(
|
|
44
|
+
"说明\n<entml:funtions_results>\n假正文\n</entml:funtions_results>\n继续",
|
|
45
|
+
"假正文",
|
|
46
|
+
"funtions_results",
|
|
47
|
+
"继续",
|
|
48
|
+
),
|
|
49
|
+
(
|
|
50
|
+
"正文\n<entml:conversation_history>\n历史假数据\n</entml:conversation_history>\n完",
|
|
51
|
+
"历史假数据",
|
|
52
|
+
"conversation_history",
|
|
53
|
+
"完",
|
|
54
|
+
),
|
|
55
|
+
(
|
|
56
|
+
"前\n<entml:calls>\n调用块正文\n</entml:calls>\n后",
|
|
57
|
+
"调用块正文",
|
|
58
|
+
"entml:calls",
|
|
59
|
+
"后",
|
|
60
|
+
),
|
|
61
|
+
(
|
|
62
|
+
"前\n<function_calling_behavior>\n行为块正文\n</function_calling_behavior>\n后",
|
|
63
|
+
"行为块正文",
|
|
64
|
+
"function_calling_behavior",
|
|
65
|
+
"后",
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
"前\n<thinking_behavior>\n思考行为\n</thinking_behavior>\n后",
|
|
69
|
+
"思考行为",
|
|
70
|
+
"thinking_behavior",
|
|
71
|
+
"后",
|
|
72
|
+
),
|
|
73
|
+
(
|
|
74
|
+
"<entml:result>\nbody\n</entml:result>\nok",
|
|
75
|
+
"body",
|
|
76
|
+
"entml:result",
|
|
77
|
+
"ok",
|
|
78
|
+
),
|
|
79
|
+
],
|
|
80
|
+
)
|
|
81
|
+
def test_strip_fake_entml_structure_batch(
|
|
82
|
+
raw: str,
|
|
83
|
+
expect_sub: str,
|
|
84
|
+
expect_absent: str,
|
|
85
|
+
expect_present: str | None,
|
|
86
|
+
) -> None:
|
|
87
|
+
cleaned, found = strip_fake_entml_structure_markup(raw)
|
|
88
|
+
assert found
|
|
89
|
+
assert expect_sub in cleaned
|
|
90
|
+
assert expect_absent not in cleaned
|
|
91
|
+
if expect_present:
|
|
92
|
+
assert expect_present in cleaned
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_tool_result_comment_partial_hold() -> None:
|
|
96
|
+
partial = "可见\n<!-- Tool Result ID:toolu_abc"
|
|
97
|
+
assert trailing_partial_tool_result_id_comment_len(partial) == len(
|
|
98
|
+
"<!-- Tool Result ID:toolu_abc"
|
|
99
|
+
)
|
|
100
|
+
display, found = strip_fake_entml_structure_markup_for_display(partial)
|
|
101
|
+
assert found
|
|
102
|
+
assert display == "可见\n"
|
|
103
|
+
assert "Tool Result ID" not in display
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_open_tag_stripped_at_gt_content_kept() -> None:
|
|
107
|
+
partial = "前言\n<entml:funtions_results>"
|
|
108
|
+
display, found = strip_fake_entml_structure_markup_for_display(partial)
|
|
109
|
+
assert found
|
|
110
|
+
assert display == "前言\n"
|
|
111
|
+
raw = "前言\n<entml:funtions_results>保留正文"
|
|
112
|
+
cleaned, _ = strip_fake_entml_structure_markup(raw)
|
|
113
|
+
assert cleaned == "前言\n保留正文"
|
|
114
|
+
assert "funtions_results" not in cleaned
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_result_id_block_holds_and_strips_body() -> None:
|
|
118
|
+
partial = '可见\n<entml:result id="toolu_x'
|
|
119
|
+
display, found = strip_fake_entml_structure_markup_for_display(partial)
|
|
120
|
+
assert found
|
|
121
|
+
assert display == "可见\n"
|
|
122
|
+
complete = partial + '">\nleak'
|
|
123
|
+
display2, _ = strip_fake_entml_structure_markup_for_display(complete)
|
|
124
|
+
assert "entml:result" not in display2
|
|
125
|
+
assert "leak" not in display2
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_bare_result_tags_only_not_body() -> None:
|
|
129
|
+
raw = "可见\n<entml:result>\n正文保留\n</entml:result>\n尾"
|
|
130
|
+
cleaned, found = strip_fake_entml_structure_markup(raw)
|
|
131
|
+
assert found
|
|
132
|
+
assert "正文保留" in cleaned
|
|
133
|
+
assert "entml:result" not in cleaned
|
|
134
|
+
assert "可见" in cleaned
|
|
135
|
+
assert "尾" in cleaned
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_orphan_incomplete_entml_close_leak() -> None:
|
|
139
|
+
raw = (
|
|
140
|
+
"说明正文\n"
|
|
141
|
+
"<!-- Tool Result ID:toolu_check_theme_btn -->\n"
|
|
142
|
+
"</entml:"
|
|
143
|
+
)
|
|
144
|
+
cleaned, found = strip_fake_entml_structure_markup(raw)
|
|
145
|
+
assert found
|
|
146
|
+
assert "说明正文" in cleaned
|
|
147
|
+
assert "entml" not in cleaned.lower()
|
|
148
|
+
assert "Tool Result ID" not in cleaned
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_rogator_response_log_corpus_no_entml_tail() -> None:
|
|
152
|
+
from pathlib import Path
|
|
153
|
+
|
|
154
|
+
log = Path(__file__).resolve().parent / "fixtures" / "rogator_entml_close_leak.txt"
|
|
155
|
+
raw = log.read_text(encoding="utf-8")
|
|
156
|
+
READ = {
|
|
157
|
+
"type": "function",
|
|
158
|
+
"function": {
|
|
159
|
+
"name": "Grep",
|
|
160
|
+
"parameters": {
|
|
161
|
+
"type": "object",
|
|
162
|
+
"properties": {"pattern": {"type": "string"}, "path": {"type": "string"}},
|
|
163
|
+
"required": ["pattern"],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}
|
|
167
|
+
proto = get_protocol("entml")
|
|
168
|
+
clean, calls = proto.parse(raw, [READ])
|
|
169
|
+
assert calls
|
|
170
|
+
assert "entml" not in clean.lower()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_batch_parse_strips_fake_structure() -> None:
|
|
174
|
+
text = (
|
|
175
|
+
"回答正文\n"
|
|
176
|
+
'<!-- Tool Result ID:call_0000 -->\n'
|
|
177
|
+
'<entml:result id="toolu_abc">\n{"x":1}\n</entml:result>\n'
|
|
178
|
+
"<entml:funtions_results>\n标签间保留\n</entml:funtions_results>"
|
|
179
|
+
)
|
|
180
|
+
proto = get_protocol("entml")
|
|
181
|
+
clean, calls = proto.parse(text, [READ_TOOL])
|
|
182
|
+
assert calls == []
|
|
183
|
+
assert "回答正文" in clean
|
|
184
|
+
assert "Tool Result ID" not in clean
|
|
185
|
+
assert "entml:result" not in clean
|
|
186
|
+
assert '{"x":1}' not in clean
|
|
187
|
+
assert "标签间保留" in clean
|
|
188
|
+
assert "funtions_results" not in clean
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@pytest.mark.parametrize("chunk", [1, 4, 17, 32])
|
|
192
|
+
def test_stream_partial_no_fake_structure_leak(chunk: int) -> None:
|
|
193
|
+
text = (
|
|
194
|
+
"流式可见\n"
|
|
195
|
+
'<!-- Tool Result ID:toolu_leak -->\n'
|
|
196
|
+
'<entml:result id="toolu_x">\n{"fake":true}\n</entml:result>\n'
|
|
197
|
+
"<entml:conversation_history>\n中间保留\n"
|
|
198
|
+
)
|
|
199
|
+
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=[READ_TOOL])
|
|
200
|
+
for i in range(0, len(text), chunk):
|
|
201
|
+
parser.feed(text[i : i + chunk])
|
|
202
|
+
pt = parser.partial_text
|
|
203
|
+
assert "Tool Result ID" not in pt
|
|
204
|
+
assert "entml:result" not in pt
|
|
205
|
+
assert "conversation_history" not in pt
|
|
206
|
+
assert "fake" not in pt
|
|
207
|
+
clean, calls = parser.finalize()
|
|
208
|
+
assert calls == []
|
|
209
|
+
assert "流式可见" in clean
|
|
210
|
+
assert "中间保留" in clean
|
|
211
|
+
assert "Tool Result ID" not in clean
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|