echotools 2.3.70__tar.gz → 2.3.71__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.70 → echotools-2.3.71}/PKG-INFO +1 -1
- {echotools-2.3.70 → echotools-2.3.71}/pyproject.toml +1 -1
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml.py +2 -2
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/history_markup.py +34 -8
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools.egg-info/SOURCES.txt +1 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/fixtures/simulated_fake_history_markup_responses.py +76 -2
- echotools-2.3.71/src/tests/test_fake_history_markup_reply_protect.py +166 -0
- {echotools-2.3.70 → echotools-2.3.71}/README.md +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/docs/README.md +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/setup.cfg +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/compat.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/stream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/parse.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools/version.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/bench_import.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/conftest.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_completion.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_config.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_console.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_errors.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_events.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_facade.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fake_history_markup_batch.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fake_history_markup_stream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fault_thinking_close_batch.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fault_thinking_close_stream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_history_markup_filter.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_ids.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_imports.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_io.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_keys.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_logger.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_merge.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_stream_partial_json_parity.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_usage.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_web.py +0 -0
- {echotools-2.3.70 → echotools-2.3.71}/src/tests/test_web_broker.py +0 -0
|
@@ -287,11 +287,11 @@ class EntmlProtocol(ToolProtocol):
|
|
|
287
287
|
parse_text, _ = strip_fake_history_markup(parse_text)
|
|
288
288
|
tool_calls = parse_entml_tool_calls(parse_text, tools, schema_index)
|
|
289
289
|
clean = text[:unclosed_open_at] if unclosed_open_at >= 0 else text
|
|
290
|
+
# 须在移除 invoke 之前剥离伪 history,否则未闭合 <tool> 会把 invoke 之后的可见回复误删。
|
|
291
|
+
clean, _ = strip_fake_history_markup(clean)
|
|
290
292
|
if tool_calls:
|
|
291
293
|
clean = BLOCK_RE.sub("", clean)
|
|
292
|
-
# 无论是否解析成功,都剥离工具相关残留,避免标签泄露;thinking 保留给后续 split。
|
|
293
294
|
clean = strip_tool_entml_residue(clean)
|
|
294
|
-
clean, _ = strip_fake_history_markup(clean)
|
|
295
295
|
return (clean, normalize_tool_calls(tool_calls, tools))
|
|
296
296
|
|
|
297
297
|
def parse_fragment(
|
|
@@ -33,6 +33,15 @@ _ENTML_INVOKE_BLOCK_RE = re.compile(
|
|
|
33
33
|
r"<entml:invoke\b[^>]*>[\s\S]*?</entml:invoke>",
|
|
34
34
|
re.IGNORECASE,
|
|
35
35
|
)
|
|
36
|
+
_ENTML_FUNCTION_CALLS_OPEN_RE = re.compile(
|
|
37
|
+
r"<entml:function_calls\b",
|
|
38
|
+
re.IGNORECASE,
|
|
39
|
+
)
|
|
40
|
+
_ENTML_FUNCTION_CALLS_BLOCK_RE = re.compile(
|
|
41
|
+
r"<entml:function_calls\b[^>]*>[\s\S]*?</entml:function_calls>",
|
|
42
|
+
re.IGNORECASE,
|
|
43
|
+
)
|
|
44
|
+
_ENTML_FUNCTION_CALLS_CLOSE = "</entml:function_calls>"
|
|
36
45
|
|
|
37
46
|
|
|
38
47
|
class HistoryMarkupDetectionResult:
|
|
@@ -96,19 +105,25 @@ def _orphan_open_re(tag: str) -> re.Pattern[str]:
|
|
|
96
105
|
|
|
97
106
|
|
|
98
107
|
def _split_entml_invoke_protected(segment: str) -> List[Tuple[str, bool]]:
|
|
99
|
-
"""按 ``<entml:
|
|
108
|
+
"""按 ``<entml:function_calls>`` / ``<entml:invoke>`` 切分;保护区不参与伪 history 剥离。"""
|
|
100
109
|
if not segment:
|
|
101
110
|
return []
|
|
102
111
|
parts: List[Tuple[str, bool]] = []
|
|
103
112
|
i = 0
|
|
104
113
|
while i < len(segment):
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
fc_m = _ENTML_FUNCTION_CALLS_OPEN_RE.search(segment, i)
|
|
115
|
+
inv_m = _ENTML_INVOKE_OPEN_RE.search(segment, i)
|
|
116
|
+
candidates = [m for m in (fc_m, inv_m) if m]
|
|
117
|
+
if not candidates:
|
|
107
118
|
parts.append((segment[i:], False))
|
|
108
119
|
break
|
|
120
|
+
open_m = min(candidates, key=lambda m: m.start())
|
|
109
121
|
if open_m.start() > i:
|
|
110
122
|
parts.append((segment[i : open_m.start()], False))
|
|
111
|
-
|
|
123
|
+
if open_m is fc_m:
|
|
124
|
+
block_m = _ENTML_FUNCTION_CALLS_BLOCK_RE.match(segment, open_m.start())
|
|
125
|
+
else:
|
|
126
|
+
block_m = _ENTML_INVOKE_BLOCK_RE.match(segment, open_m.start())
|
|
112
127
|
if block_m:
|
|
113
128
|
parts.append((block_m.group(0), True))
|
|
114
129
|
i = block_m.end()
|
|
@@ -119,9 +134,9 @@ def _split_entml_invoke_protected(segment: str) -> List[Tuple[str, bool]]:
|
|
|
119
134
|
|
|
120
135
|
|
|
121
136
|
def _open_before_entml_re(tag: str) -> re.Pattern[str]:
|
|
122
|
-
"""``<tool>\\n…`` 未闭合但在 ``<entml:invoke`` 前 — 整段剥离。"""
|
|
137
|
+
"""``<tool>\\n…`` 未闭合但在 ``<entml:(function_calls|invoke)`` 前 — 整段剥离。"""
|
|
123
138
|
return re.compile(
|
|
124
|
-
rf"<{tag}\s*>\s*\n[\s\S]*?(?=<entml:invoke\b)",
|
|
139
|
+
rf"<{tag}\s*>\s*\n[\s\S]*?(?=<entml:(?:function_calls|invoke)\b)",
|
|
125
140
|
re.IGNORECASE,
|
|
126
141
|
)
|
|
127
142
|
|
|
@@ -250,8 +265,19 @@ def strip_fake_history_markup_for_display(content: str) -> Tuple[str, bool]:
|
|
|
250
265
|
"""流式 ``partial_text`` 用:完整块剥离 + 截断行尾未收齐的伪标签。"""
|
|
251
266
|
cleaned, found = strip_fake_history_markup(content)
|
|
252
267
|
invoke_m = _ENTML_INVOKE_OPEN_RE.search(cleaned)
|
|
253
|
-
|
|
254
|
-
|
|
268
|
+
fc_m = _ENTML_FUNCTION_CALLS_OPEN_RE.search(cleaned)
|
|
269
|
+
prot_m = None
|
|
270
|
+
if invoke_m and fc_m:
|
|
271
|
+
prot_m = invoke_m if invoke_m.start() <= fc_m.start() else fc_m
|
|
272
|
+
else:
|
|
273
|
+
prot_m = invoke_m or fc_m
|
|
274
|
+
if prot_m:
|
|
275
|
+
tail = cleaned[prot_m.start() :]
|
|
276
|
+
invoke_open = _ENTML_INVOKE_OPEN_RE.search(tail)
|
|
277
|
+
if invoke_open and "</entml:invoke>" not in tail[invoke_open.start() :]:
|
|
278
|
+
return cleaned, found
|
|
279
|
+
if _ENTML_FUNCTION_CALLS_OPEN_RE.search(tail) and _ENTML_FUNCTION_CALLS_CLOSE not in tail:
|
|
280
|
+
return cleaned, found
|
|
255
281
|
tail = re.search(
|
|
256
282
|
r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*(?:<(?:assistant|tool)\b[^\n]*)?)$",
|
|
257
283
|
cleaned,
|
|
@@ -201,6 +201,7 @@ src/tests/test_events_event.py
|
|
|
201
201
|
src/tests/test_facade.py
|
|
202
202
|
src/tests/test_fake_history_markup_batch.py
|
|
203
203
|
src/tests/test_fake_history_markup_invoke_preserve.py
|
|
204
|
+
src/tests/test_fake_history_markup_reply_protect.py
|
|
204
205
|
src/tests/test_fake_history_markup_stream.py
|
|
205
206
|
src/tests/test_fault_thinking_close_batch.py
|
|
206
207
|
src/tests/test_fault_thinking_close_stream.py
|
{echotools-2.3.70 → echotools-2.3.71}/src/tests/fixtures/simulated_fake_history_markup_responses.py
RENAMED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
from dataclasses import dataclass
|
|
8
|
+
from dataclasses import dataclass
|
|
9
9
|
from typing import Any, Dict, List, Optional, Tuple
|
|
10
10
|
|
|
11
|
-
from fixtures.simulated_llm_tool_responses import
|
|
11
|
+
from fixtures.simulated_llm_tool_responses import _TOOL_BY_NAME, TOOLS
|
|
12
12
|
|
|
13
13
|
READ_TOOL: Dict[str, Any] = {
|
|
14
14
|
"type": "function",
|
|
@@ -400,6 +400,80 @@ FAKE_HISTORY_MARKUP_CASES: Tuple[HistoryMarkupCase, ...] = (
|
|
|
400
400
|
expect_clean_excludes=("{Read: x}",),
|
|
401
401
|
worst_split=True,
|
|
402
402
|
),
|
|
403
|
+
HistoryMarkupCase(
|
|
404
|
+
id="unclosed_fake_tool_invoke_trailing_reply",
|
|
405
|
+
description="未闭合伪 <tool> + invoke + 可见尾句:batch/stream 均须保留尾句",
|
|
406
|
+
extra_tools=("Read", "Edit"),
|
|
407
|
+
response=(
|
|
408
|
+
"前言\n"
|
|
409
|
+
"<tool>\n"
|
|
410
|
+
'{Edit: {"path": "a.py", "old_string": "x", "new_string": "y"}}\n'
|
|
411
|
+
f"{REAL_READ_INVOKE}\n"
|
|
412
|
+
"尾句保留。"
|
|
413
|
+
),
|
|
414
|
+
expect_names=("Read",),
|
|
415
|
+
expect_args=({"path": "src/main.py"},),
|
|
416
|
+
expect_call_count=1,
|
|
417
|
+
expect_clean_contains=("前言", "尾句保留"),
|
|
418
|
+
expect_clean_excludes=("Edit", "<tool>"),
|
|
419
|
+
partial_leak_markers=("<tool>", "{Edit:", "entml:parameter"),
|
|
420
|
+
worst_split=True,
|
|
421
|
+
),
|
|
422
|
+
HistoryMarkupCase(
|
|
423
|
+
id="function_calls_wrap_trailing_reply",
|
|
424
|
+
description="function_calls 包裹 invoke + 尾部可见回复",
|
|
425
|
+
extra_tools=("Read",),
|
|
426
|
+
response=(
|
|
427
|
+
"说明。\n"
|
|
428
|
+
"<entml:function_calls>\n"
|
|
429
|
+
f"{REAL_READ_INVOKE}\n"
|
|
430
|
+
"</entml:function_calls>\n"
|
|
431
|
+
"后续说明。"
|
|
432
|
+
),
|
|
433
|
+
expect_names=("Read",),
|
|
434
|
+
expect_args=({"path": "src/main.py"},),
|
|
435
|
+
expect_call_count=1,
|
|
436
|
+
expect_clean_contains=("说明", "后续说明"),
|
|
437
|
+
expect_clean_excludes=("<tool>",),
|
|
438
|
+
partial_leak_markers=("<tool>", "{Edit:"),
|
|
439
|
+
),
|
|
440
|
+
HistoryMarkupCase(
|
|
441
|
+
id="invoke_then_fake_tool_then_reply",
|
|
442
|
+
description="真实 invoke 后误写伪 tool,须剥离伪块保留尾句",
|
|
443
|
+
extra_tools=("Read", "Edit"),
|
|
444
|
+
response=(
|
|
445
|
+
f"{REAL_READ_INVOKE}\n"
|
|
446
|
+
"<tool>\n"
|
|
447
|
+
'{Edit: {"path": "a.py"}}\n'
|
|
448
|
+
"</tool>\n"
|
|
449
|
+
"尾句。"
|
|
450
|
+
),
|
|
451
|
+
expect_names=("Read",),
|
|
452
|
+
expect_args=({"path": "src/main.py"},),
|
|
453
|
+
expect_call_count=1,
|
|
454
|
+
expect_clean_contains=("尾句",),
|
|
455
|
+
expect_clean_excludes=("Edit", "<tool>"),
|
|
456
|
+
),
|
|
457
|
+
HistoryMarkupCase(
|
|
458
|
+
id="fake_tool_inside_function_calls_with_invoke",
|
|
459
|
+
description="伪 <tool> 块内含 function_calls+invoke,不得误删 invoke",
|
|
460
|
+
extra_tools=("Read", "Edit"),
|
|
461
|
+
response=(
|
|
462
|
+
"<tool>\n"
|
|
463
|
+
'{Edit: {"path": "a.py"}}\n'
|
|
464
|
+
"<entml:function_calls>\n"
|
|
465
|
+
f"{REAL_READ_INVOKE}\n"
|
|
466
|
+
"</entml:function_calls>\n"
|
|
467
|
+
"</tool>\n"
|
|
468
|
+
"尾句。"
|
|
469
|
+
),
|
|
470
|
+
expect_names=("Read",),
|
|
471
|
+
expect_args=({"path": "src/main.py"},),
|
|
472
|
+
expect_call_count=1,
|
|
473
|
+
expect_clean_contains=("尾句",),
|
|
474
|
+
expect_clean_excludes=("Edit", "<tool>"),
|
|
475
|
+
worst_split=True,
|
|
476
|
+
),
|
|
403
477
|
)
|
|
404
478
|
|
|
405
479
|
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""batch parse 与 stream finalize 对 invoke / 可见回复的保护须一致。"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Dict, List
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from fixtures.simulated_fake_history_markup_responses import (
|
|
10
|
+
REAL_READ_INVOKE,
|
|
11
|
+
REAL_WEATHER_INVOKE,
|
|
12
|
+
HistoryMarkupCase,
|
|
13
|
+
iter_fake_history_markup_cases,
|
|
14
|
+
tools_for_markup_case,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from echotools.exec.fncall import get_protocol
|
|
18
|
+
from echotools.exec.fncall.parsers.stream import FncallStreamParser
|
|
19
|
+
from echotools.exec.fncall.shared.history_markup import strip_fake_history_markup
|
|
20
|
+
|
|
21
|
+
INVOKE_REPLY_CASES = [
|
|
22
|
+
c
|
|
23
|
+
for c in iter_fake_history_markup_cases()
|
|
24
|
+
if c.expect_names
|
|
25
|
+
or c.id
|
|
26
|
+
in (
|
|
27
|
+
"unclosed_fake_tool_invoke_trailing_reply",
|
|
28
|
+
"function_calls_wrap_trailing_reply",
|
|
29
|
+
"invoke_then_fake_tool_then_reply",
|
|
30
|
+
"fake_tool_inside_function_calls_with_invoke",
|
|
31
|
+
)
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
EXTRA_CASES = (
|
|
35
|
+
HistoryMarkupCase(
|
|
36
|
+
id="visible_reply_before_and_after_invoke",
|
|
37
|
+
description="invoke 前后均有可见说明",
|
|
38
|
+
extra_tools=("Read",),
|
|
39
|
+
response=(
|
|
40
|
+
"开头说明。\n"
|
|
41
|
+
f"{REAL_READ_INVOKE}\n"
|
|
42
|
+
"结尾说明。"
|
|
43
|
+
),
|
|
44
|
+
expect_names=("Read",),
|
|
45
|
+
expect_args=({"path": "src/main.py"},),
|
|
46
|
+
expect_call_count=1,
|
|
47
|
+
expect_clean_contains=("开头说明", "结尾说明"),
|
|
48
|
+
expect_clean_excludes=("<tool>",),
|
|
49
|
+
partial_leak_markers=("{Edit:", "\n<tool>\n"),
|
|
50
|
+
),
|
|
51
|
+
HistoryMarkupCase(
|
|
52
|
+
id="thinking_visible_fake_invoke_reply",
|
|
53
|
+
description="thinking + 可见区伪块 + invoke + 尾句",
|
|
54
|
+
extra_tools=("Read", "Edit"),
|
|
55
|
+
response=(
|
|
56
|
+
"<entml:thinking>\nplan\n</entml:thinking>\n"
|
|
57
|
+
"中间说明\n"
|
|
58
|
+
"<tool>\n{Edit: x}\n</tool>\n"
|
|
59
|
+
f"{REAL_READ_INVOKE}\n"
|
|
60
|
+
"最终回复"
|
|
61
|
+
),
|
|
62
|
+
expect_names=("Read",),
|
|
63
|
+
expect_args=({"path": "src/main.py"},),
|
|
64
|
+
expect_call_count=1,
|
|
65
|
+
expect_clean_contains=("中间说明", "最终回复"),
|
|
66
|
+
expect_clean_excludes=("Edit", "<tool>"),
|
|
67
|
+
),
|
|
68
|
+
HistoryMarkupCase(
|
|
69
|
+
id="weather_after_unclosed_fake_tool",
|
|
70
|
+
description="未闭合伪 tool 后直接 weather invoke + 尾句",
|
|
71
|
+
response=(
|
|
72
|
+
"参考\n<tool>\n{get_weather: x}\n"
|
|
73
|
+
f"{REAL_WEATHER_INVOKE}\n"
|
|
74
|
+
"确认完毕。"
|
|
75
|
+
),
|
|
76
|
+
expect_names=("get_weather",),
|
|
77
|
+
expect_args=({"city": "杭州", "unit": "c"},),
|
|
78
|
+
expect_call_count=1,
|
|
79
|
+
expect_clean_contains=("参考", "确认完毕"),
|
|
80
|
+
expect_clean_excludes=("{get_weather:", "<tool>"),
|
|
81
|
+
worst_split=True,
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
ALL_CASES = INVOKE_REPLY_CASES + list(EXTRA_CASES)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _names(calls: List[Dict[str, Any]]) -> List[str]:
|
|
89
|
+
return [c["function"]["name"] for c in calls]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _args(calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
93
|
+
return [json.loads(c["function"]["arguments"]) for c in calls]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _batch(case: HistoryMarkupCase) -> tuple[str, List[Dict[str, Any]]]:
|
|
97
|
+
return get_protocol("entml").parse(case.response, tools_for_markup_case(case))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _stream(case: HistoryMarkupCase, chunk: int) -> tuple[str, List[Dict[str, Any]]]:
|
|
101
|
+
parser = FncallStreamParser(
|
|
102
|
+
protocol=get_protocol("entml"),
|
|
103
|
+
tools=tools_for_markup_case(case),
|
|
104
|
+
)
|
|
105
|
+
text = case.response
|
|
106
|
+
for i in range(0, len(text), chunk):
|
|
107
|
+
parser.feed(text[i : i + chunk])
|
|
108
|
+
return parser.finalize()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _assert_case(case: HistoryMarkupCase, clean: str, calls: List[Dict[str, Any]], label: str) -> None:
|
|
112
|
+
assert len(calls) == case.expect_call_count, f"{case.id}/{label}: call count"
|
|
113
|
+
if case.expect_names:
|
|
114
|
+
assert _names(calls) == list(case.expect_names), f"{case.id}/{label}: names"
|
|
115
|
+
if case.expect_args:
|
|
116
|
+
assert _args(calls) == list(case.expect_args), f"{case.id}/{label}: args"
|
|
117
|
+
for needle in case.expect_clean_contains:
|
|
118
|
+
assert needle in clean, f"{case.id}/{label}: missing {needle!r}"
|
|
119
|
+
for bad in case.expect_clean_excludes:
|
|
120
|
+
assert bad not in clean, f"{case.id}/{label}: leaked {bad!r}"
|
|
121
|
+
assert "entml:invoke" not in clean, f"{case.id}/{label}: invoke tag in clean"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@pytest.mark.parametrize("case", ALL_CASES, ids=lambda c: c.id)
|
|
125
|
+
def test_batch_stream_parity(case: HistoryMarkupCase) -> None:
|
|
126
|
+
batch_clean, batch_calls = _batch(case)
|
|
127
|
+
stream_clean, stream_calls = _stream(case, 3)
|
|
128
|
+
_assert_case(case, batch_clean, batch_calls, "batch")
|
|
129
|
+
_assert_case(case, stream_clean, stream_calls, "stream")
|
|
130
|
+
for needle in case.expect_clean_contains:
|
|
131
|
+
assert needle in batch_clean and needle in stream_clean, case.id
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@pytest.mark.parametrize("case", ALL_CASES, ids=lambda c: c.id)
|
|
135
|
+
@pytest.mark.parametrize("chunk", [1, 8, 17, 64], ids=lambda n: f"c{n}")
|
|
136
|
+
def test_stream_chunk_sizes(case: HistoryMarkupCase, chunk: int) -> None:
|
|
137
|
+
clean, calls = _stream(case, chunk)
|
|
138
|
+
_assert_case(case, clean, calls, f"c{chunk}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize("case", ALL_CASES, ids=lambda c: c.id)
|
|
142
|
+
def test_strip_preserves_invoke_and_reply(case: HistoryMarkupCase) -> None:
|
|
143
|
+
if not case.expect_names:
|
|
144
|
+
return
|
|
145
|
+
stripped, _ = strip_fake_history_markup(case.response)
|
|
146
|
+
assert "entml:invoke" in stripped, case.id
|
|
147
|
+
for needle in case.expect_clean_contains:
|
|
148
|
+
if needle not in ("说明", "参考历史", "我再确认一次"):
|
|
149
|
+
assert needle in stripped or needle in case.response, case.id
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_function_calls_partial_display() -> None:
|
|
153
|
+
partial = (
|
|
154
|
+
"说明。\n<tool>\n{Edit: x}\n"
|
|
155
|
+
"<entml:function_calls>\n"
|
|
156
|
+
'<entml:invoke name="Read">\n'
|
|
157
|
+
'<entml:parameter name="path">a.py'
|
|
158
|
+
)
|
|
159
|
+
from echotools.exec.fncall.shared.history_markup import (
|
|
160
|
+
strip_fake_history_markup_for_display,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
display, _ = strip_fake_history_markup_for_display(partial)
|
|
164
|
+
assert "说明" in display
|
|
165
|
+
assert "<entml:invoke" in display
|
|
166
|
+
assert "entml:parameter" not in display or "<entml:invoke" in display
|
|
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
|
|
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
|
{echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_stream_json.py
RENAMED
|
File without changes
|
{echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/__init__.py
RENAMED
|
File without changes
|
{echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/core.py
RENAMED
|
File without changes
|
{echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/hist.py
RENAMED
|
File without changes
|
{echotools-2.3.70 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
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
|
|
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
|