echotools 2.3.67__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.67 → echotools-2.3.71}/PKG-INFO +1 -1
- {echotools-2.3.67 → echotools-2.3.71}/pyproject.toml +1 -1
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/inject.py +3 -1
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml.py +3 -3
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/core.py +31 -1
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/hist.py +12 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/history_markup.py +138 -4
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/SOURCES.txt +5 -0
- echotools-2.3.71/src/tests/fixtures/simulated_fake_history_markup_responses.py +481 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_protocol.py +106 -0
- echotools-2.3.71/src/tests/test_fake_history_markup_batch.py +75 -0
- echotools-2.3.71/src/tests/test_fake_history_markup_invoke_preserve.py +130 -0
- echotools-2.3.71/src/tests/test_fake_history_markup_reply_protect.py +166 -0
- echotools-2.3.71/src/tests/test_fake_history_markup_stream.py +151 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_history_markup_filter.py +8 -0
- {echotools-2.3.67 → echotools-2.3.71}/README.md +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/docs/README.md +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/setup.cfg +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/compat.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/stream.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/parse.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools/version.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/bench_import.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/conftest.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/fixtures/simulated_fault_thinking_responses.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_completion.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_config.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_console.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_errors.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_events.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_facade.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fault_thinking_close_batch.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fault_thinking_close_stream.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_ids.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_imports.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_io.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_keys.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_logger.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_merge.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_stream_partial_json_parity.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_usage.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_web.py +0 -0
- {echotools-2.3.67 → echotools-2.3.71}/src/tests/test_web_broker.py +0 -0
|
@@ -166,7 +166,9 @@ def inject_fncall(
|
|
|
166
166
|
sections.append(format_entml_current_user_message(current_user_message))
|
|
167
167
|
# thinking 放在最后,超限截断时优先保留在 send_text 尾部
|
|
168
168
|
thinking_section = build_entml_thinking_section(
|
|
169
|
-
protocol_options,
|
|
169
|
+
protocol_options,
|
|
170
|
+
has_tools=False,
|
|
171
|
+
history_text=history_text,
|
|
170
172
|
)
|
|
171
173
|
if thinking_section:
|
|
172
174
|
sections.append(thinking_section)
|
|
@@ -214,7 +214,7 @@ class EntmlProtocol(ToolProtocol):
|
|
|
214
214
|
|
|
215
215
|
# thinking 放在最后,超限截断时优先保留在 send_text 尾部
|
|
216
216
|
thinking_section = build_entml_thinking_section(
|
|
217
|
-
protocol_options, has_tools=bool(tool_descs)
|
|
217
|
+
protocol_options, has_tools=bool(tool_descs), history_text=history_text
|
|
218
218
|
)
|
|
219
219
|
if thinking_section:
|
|
220
220
|
sections.append(thinking_section)
|
|
@@ -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(
|
{echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/core.py
RENAMED
|
@@ -80,6 +80,16 @@ After completed tool turns appear in conversation history inside <tool> blocks (
|
|
|
80
80
|
_THINKING_BEHAVIOR_AUTO_NO_TOOLS = """\
|
|
81
81
|
You decide whether extended thinking helps for each reply. When the question has hidden complexity or when you are uncertain, open a <entml:thinking> block before continuing and strongly prefer to do so rather than guessing."""
|
|
82
82
|
|
|
83
|
+
_THINKING_BEHAVIOR_OFF_WITH_HISTORY_WITH_TOOLS = """\
|
|
84
|
+
Extended thinking is disabled for this reply. Do NOT output a <entml:thinking> block.
|
|
85
|
+
|
|
86
|
+
Past assistant turns in conversation history may include <entml:thinking>...</entml:thinking> blocks for context only. Do not imitate or continue those blocks. Reply with visible text and/or <entml:invoke> tool call(s) directly."""
|
|
87
|
+
|
|
88
|
+
_THINKING_BEHAVIOR_OFF_WITH_HISTORY_NO_TOOLS = """\
|
|
89
|
+
Extended thinking is disabled for this reply. Do NOT output a <entml:thinking> block.
|
|
90
|
+
|
|
91
|
+
Past assistant turns in conversation history may include <entml:thinking>...</entml:thinking> blocks for context only. Do not imitate or continue those blocks. Reply with visible text directly."""
|
|
92
|
+
|
|
83
93
|
|
|
84
94
|
def normalize_thinking_level(level: Any) -> Optional[str]:
|
|
85
95
|
"""将 echotools 思考挡位归一化为 none | low | medium | high | xhigh | max | auto。"""
|
|
@@ -181,14 +191,34 @@ def _format_thinking_behavior(injection_mode: str, *, has_tools: bool) -> str:
|
|
|
181
191
|
return f"<thinking_behavior>\n{body}\n</thinking_behavior>"
|
|
182
192
|
|
|
183
193
|
|
|
194
|
+
def _format_forced_no_thinking_behavior(*, has_tools: bool) -> str:
|
|
195
|
+
body = (
|
|
196
|
+
_THINKING_BEHAVIOR_OFF_WITH_HISTORY_WITH_TOOLS
|
|
197
|
+
if has_tools
|
|
198
|
+
else _THINKING_BEHAVIOR_OFF_WITH_HISTORY_NO_TOOLS
|
|
199
|
+
)
|
|
200
|
+
return f"<thinking_behavior>\n{body}\n</thinking_behavior>"
|
|
201
|
+
|
|
202
|
+
|
|
184
203
|
def build_entml_thinking_section(
|
|
185
204
|
protocol_options: Optional[Dict[str, Any]] = None,
|
|
186
205
|
*,
|
|
187
206
|
has_tools: bool = True,
|
|
207
|
+
history_text: str = "",
|
|
188
208
|
) -> str:
|
|
189
|
-
"""按思考挡位构建注入块:thinking_mode + max_thinking_length + thinking_behavior。
|
|
209
|
+
"""按思考挡位构建注入块:thinking_mode + max_thinking_length + thinking_behavior。
|
|
210
|
+
|
|
211
|
+
思考关闭时:默认不注入;若 history 含 ``<entml:thinking>`` 则仅注入强制不思考的
|
|
212
|
+
``<thinking_behavior>``(不含 ``<entml:thinking_mode>``)。
|
|
213
|
+
"""
|
|
214
|
+
from echotools.exec.fncall.protocols.entml_think.hist import (
|
|
215
|
+
history_text_contains_entml_thinking,
|
|
216
|
+
)
|
|
217
|
+
|
|
190
218
|
resolved = resolve_thinking_injection(protocol_options)
|
|
191
219
|
if resolved is None:
|
|
220
|
+
if history_text_contains_entml_thinking(history_text):
|
|
221
|
+
return _format_forced_no_thinking_behavior(has_tools=has_tools)
|
|
192
222
|
return ""
|
|
193
223
|
|
|
194
224
|
injection_mode, max_length = resolved
|
{echotools-2.3.67 → echotools-2.3.71}/src/echotools/exec/fncall/protocols/entml_think/hist.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from typing import Any, Dict, List, Mapping, Optional
|
|
4
5
|
|
|
5
6
|
REASONING_KEYS = ("reasoning", "reasoning_content", "reasoning_details")
|
|
@@ -13,6 +14,17 @@ _THINKING_BLOCK_TYPES = frozenset({"thinking", "reasoning", "redacted_thinking"}
|
|
|
13
14
|
|
|
14
15
|
_THINKING_BLOCK_OPEN = "<entml:thinking>"
|
|
15
16
|
_THINKING_BLOCK_CLOSE = "</entml:thinking>"
|
|
17
|
+
_THINKING_OPEN_DETECT_RE = re.compile(
|
|
18
|
+
r"<entml:thinking\b",
|
|
19
|
+
re.IGNORECASE,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def history_text_contains_entml_thinking(history_text: str) -> bool:
|
|
24
|
+
"""conversation_history 正文是否含 ``<entml:thinking>`` 历史思考块。"""
|
|
25
|
+
if not history_text or not history_text.strip():
|
|
26
|
+
return False
|
|
27
|
+
return _THINKING_OPEN_DETECT_RE.search(history_text) is not None
|
|
16
28
|
|
|
17
29
|
|
|
18
30
|
def format_entml_thinking_history_block(text: str) -> str:
|
|
@@ -25,6 +25,23 @@ _PLAIN_THINKING_OPEN_LINE_RE = re.compile(
|
|
|
25
25
|
r"(?:^|\n)\s*<thinking\s*>\s*(?:\n|$)",
|
|
26
26
|
re.IGNORECASE | re.MULTILINE,
|
|
27
27
|
)
|
|
28
|
+
_ENTML_INVOKE_OPEN_RE = re.compile(
|
|
29
|
+
r"<entml:invoke\b",
|
|
30
|
+
re.IGNORECASE,
|
|
31
|
+
)
|
|
32
|
+
_ENTML_INVOKE_BLOCK_RE = re.compile(
|
|
33
|
+
r"<entml:invoke\b[^>]*>[\s\S]*?</entml:invoke>",
|
|
34
|
+
re.IGNORECASE,
|
|
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>"
|
|
28
45
|
|
|
29
46
|
|
|
30
47
|
class HistoryMarkupDetectionResult:
|
|
@@ -65,6 +82,14 @@ def _paired_block_re(tag: str) -> re.Pattern[str]:
|
|
|
65
82
|
)
|
|
66
83
|
|
|
67
84
|
|
|
85
|
+
def _paired_block_anywhere_re(tag: str) -> re.Pattern[str]:
|
|
86
|
+
"""``<tool>\\n…\\n</tool>`` 即使前缀无换行(流式分片边界)也剥离。"""
|
|
87
|
+
return re.compile(
|
|
88
|
+
rf"<{tag}\s*>\s*\n[\s\S]*?\n\s*</{tag}\s*>",
|
|
89
|
+
re.IGNORECASE,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
68
93
|
def _orphan_close_re(tag: str) -> re.Pattern[str]:
|
|
69
94
|
return re.compile(
|
|
70
95
|
rf"(?:^|\n)([\s\S]*?)\n\s*</{tag}\s*>\s*(?:\n|$)",
|
|
@@ -79,19 +104,83 @@ def _orphan_open_re(tag: str) -> re.Pattern[str]:
|
|
|
79
104
|
)
|
|
80
105
|
|
|
81
106
|
|
|
82
|
-
def
|
|
107
|
+
def _split_entml_invoke_protected(segment: str) -> List[Tuple[str, bool]]:
|
|
108
|
+
"""按 ``<entml:function_calls>`` / ``<entml:invoke>`` 切分;保护区不参与伪 history 剥离。"""
|
|
109
|
+
if not segment:
|
|
110
|
+
return []
|
|
111
|
+
parts: List[Tuple[str, bool]] = []
|
|
112
|
+
i = 0
|
|
113
|
+
while i < len(segment):
|
|
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:
|
|
118
|
+
parts.append((segment[i:], False))
|
|
119
|
+
break
|
|
120
|
+
open_m = min(candidates, key=lambda m: m.start())
|
|
121
|
+
if open_m.start() > i:
|
|
122
|
+
parts.append((segment[i : open_m.start()], False))
|
|
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())
|
|
127
|
+
if block_m:
|
|
128
|
+
parts.append((block_m.group(0), True))
|
|
129
|
+
i = block_m.end()
|
|
130
|
+
else:
|
|
131
|
+
parts.append((segment[open_m.start() :], True))
|
|
132
|
+
break
|
|
133
|
+
return parts
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _open_before_entml_re(tag: str) -> re.Pattern[str]:
|
|
137
|
+
"""``<tool>\\n…`` 未闭合但在 ``<entml:(function_calls|invoke)`` 前 — 整段剥离。"""
|
|
138
|
+
return re.compile(
|
|
139
|
+
rf"<{tag}\s*>\s*\n[\s\S]*?(?=<entml:(?:function_calls|invoke)\b)",
|
|
140
|
+
re.IGNORECASE,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _orphan_open_to_eof_re(tag: str) -> re.Pattern[str]:
|
|
145
|
+
"""未闭合伪块直到段末(下一段为 ``<entml:invoke>`` 时)。"""
|
|
146
|
+
return re.compile(
|
|
147
|
+
rf"<{tag}\s*>\s*\n[\s\S]*$",
|
|
148
|
+
re.IGNORECASE,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _strip_fake_blocks_in_unprotected(
|
|
153
|
+
segment: str,
|
|
154
|
+
*,
|
|
155
|
+
followed_by_invoke: bool = False,
|
|
156
|
+
) -> Tuple[str, bool]:
|
|
83
157
|
if not segment:
|
|
84
158
|
return segment, False
|
|
85
159
|
found = False
|
|
86
160
|
text = segment
|
|
87
161
|
for tag in _FAKE_HISTORY_TAGS:
|
|
88
|
-
pair_re
|
|
162
|
+
for pair_re in (_paired_block_re(tag), _paired_block_anywhere_re(tag)):
|
|
163
|
+
while True:
|
|
164
|
+
new_text, n = pair_re.subn("", text, count=1)
|
|
165
|
+
if n == 0:
|
|
166
|
+
break
|
|
167
|
+
found = True
|
|
168
|
+
text = new_text
|
|
169
|
+
before_entml = _open_before_entml_re(tag)
|
|
89
170
|
while True:
|
|
90
|
-
new_text, n =
|
|
171
|
+
new_text, n = before_entml.subn("", text, count=1)
|
|
91
172
|
if n == 0:
|
|
92
173
|
break
|
|
93
174
|
found = True
|
|
94
175
|
text = new_text
|
|
176
|
+
if followed_by_invoke:
|
|
177
|
+
to_eof = _orphan_open_to_eof_re(tag)
|
|
178
|
+
while True:
|
|
179
|
+
new_text, n = to_eof.subn("", text, count=1)
|
|
180
|
+
if n == 0:
|
|
181
|
+
break
|
|
182
|
+
found = True
|
|
183
|
+
text = new_text
|
|
95
184
|
close_re = _orphan_close_re(tag)
|
|
96
185
|
while True:
|
|
97
186
|
new_text, n = close_re.subn("\n", text, count=1)
|
|
@@ -117,6 +206,28 @@ def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
|
|
|
117
206
|
return text, found
|
|
118
207
|
|
|
119
208
|
|
|
209
|
+
def _strip_fake_blocks_in_segment(segment: str) -> Tuple[str, bool]:
|
|
210
|
+
if not segment:
|
|
211
|
+
return segment, False
|
|
212
|
+
parts = _split_entml_invoke_protected(segment)
|
|
213
|
+
if len(parts) == 1 and parts[0][1]:
|
|
214
|
+
return segment, False
|
|
215
|
+
out: List[str] = []
|
|
216
|
+
found = False
|
|
217
|
+
for idx, (chunk, protected) in enumerate(parts):
|
|
218
|
+
if protected:
|
|
219
|
+
out.append(chunk)
|
|
220
|
+
continue
|
|
221
|
+
next_is_invoke = idx + 1 < len(parts) and parts[idx + 1][1]
|
|
222
|
+
cleaned, hit = _strip_fake_blocks_in_unprotected(
|
|
223
|
+
chunk,
|
|
224
|
+
followed_by_invoke=next_is_invoke,
|
|
225
|
+
)
|
|
226
|
+
out.append(cleaned)
|
|
227
|
+
found = found or hit
|
|
228
|
+
return "".join(out), found
|
|
229
|
+
|
|
230
|
+
|
|
120
231
|
def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
|
|
121
232
|
"""剥离块级伪 ``<assistant>`` / ``<tool>``;``<entml:thinking>`` 区域原样保留。"""
|
|
122
233
|
if not content:
|
|
@@ -153,12 +264,35 @@ def strip_fake_history_markup(content: str) -> Tuple[str, bool]:
|
|
|
153
264
|
def strip_fake_history_markup_for_display(content: str) -> Tuple[str, bool]:
|
|
154
265
|
"""流式 ``partial_text`` 用:完整块剥离 + 截断行尾未收齐的伪标签。"""
|
|
155
266
|
cleaned, found = strip_fake_history_markup(content)
|
|
267
|
+
invoke_m = _ENTML_INVOKE_OPEN_RE.search(cleaned)
|
|
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
|
|
156
281
|
tail = re.search(
|
|
157
|
-
r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*)$",
|
|
282
|
+
r"(?:^|\n)\s*(?:</?(?:assistant|tool)\b[^\n]*|</thinking>\s*(?:<(?:assistant|tool)\b[^\n]*)?)$",
|
|
158
283
|
cleaned,
|
|
159
284
|
re.IGNORECASE | re.MULTILINE,
|
|
160
285
|
)
|
|
161
286
|
if tail:
|
|
162
287
|
cleaned = cleaned[: tail.start()]
|
|
163
288
|
found = True
|
|
289
|
+
else:
|
|
290
|
+
glued = re.search(
|
|
291
|
+
r"</thinking>\s*<(?:assistant|tool)\b[^\n]*$",
|
|
292
|
+
cleaned,
|
|
293
|
+
re.IGNORECASE,
|
|
294
|
+
)
|
|
295
|
+
if glued:
|
|
296
|
+
cleaned = cleaned[: glued.start()]
|
|
297
|
+
found = True
|
|
164
298
|
return cleaned, found
|
|
@@ -199,6 +199,10 @@ src/tests/test_errors_common.py
|
|
|
199
199
|
src/tests/test_events.py
|
|
200
200
|
src/tests/test_events_event.py
|
|
201
201
|
src/tests/test_facade.py
|
|
202
|
+
src/tests/test_fake_history_markup_batch.py
|
|
203
|
+
src/tests/test_fake_history_markup_invoke_preserve.py
|
|
204
|
+
src/tests/test_fake_history_markup_reply_protect.py
|
|
205
|
+
src/tests/test_fake_history_markup_stream.py
|
|
202
206
|
src/tests/test_fault_thinking_close_batch.py
|
|
203
207
|
src/tests/test_fault_thinking_close_stream.py
|
|
204
208
|
src/tests/test_fncall.py
|
|
@@ -227,5 +231,6 @@ src/tests/test_usage.py
|
|
|
227
231
|
src/tests/test_web.py
|
|
228
232
|
src/tests/test_web_broker.py
|
|
229
233
|
src/tests/fixtures/__init__.py
|
|
234
|
+
src/tests/fixtures/simulated_fake_history_markup_responses.py
|
|
230
235
|
src/tests/fixtures/simulated_fault_thinking_responses.py
|
|
231
236
|
src/tests/fixtures/simulated_llm_tool_responses.py
|