echotools 2.3.52__tar.gz → 2.3.53__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.52 → echotools-2.3.53}/PKG-INFO +1 -1
- {echotools-2.3.52 → echotools-2.3.53}/pyproject.toml +1 -1
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/parsers/stream.py +21 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml.py +6 -1
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_patterns.py +62 -2
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/parse.py +34 -4
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_fncall_stream_thinking.py +31 -0
- {echotools-2.3.52 → echotools-2.3.53}/README.md +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/docs/README.md +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/setup.cfg +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/compat.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools/version.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools.egg-info/SOURCES.txt +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/bench_import.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/conftest.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_completion.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_config.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_console.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_errors.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_events.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_facade.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_ids.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_imports.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_io.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_keys.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_logger.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_merge.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_usage.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_web.py +0 -0
- {echotools-2.3.52 → echotools-2.3.53}/src/tests/test_web_broker.py +0 -0
|
@@ -349,6 +349,26 @@ class FncallStreamParser:
|
|
|
349
349
|
body = self._fncall_buf[body_start:]
|
|
350
350
|
self._json_stream_encoder._emitted = build_streaming_json_snapshot(body)
|
|
351
351
|
|
|
352
|
+
def _reclassify_orphan_thinking_close_in_text(self) -> None:
|
|
353
|
+
"""将误进 visible 的「无开标签 + 闭标签」思考正文移回 thinking。"""
|
|
354
|
+
if self._thinking_filter is not None and self._thinking_filter.in_open_thinking():
|
|
355
|
+
return
|
|
356
|
+
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
357
|
+
_THINKING_CLOSE,
|
|
358
|
+
_THINKING_OPEN_PREFIX,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
visible = "".join(self._text_parts)
|
|
362
|
+
if not visible or _THINKING_OPEN_PREFIX in visible:
|
|
363
|
+
return
|
|
364
|
+
close_at = visible.find(_THINKING_CLOSE)
|
|
365
|
+
if close_at < 0:
|
|
366
|
+
return
|
|
367
|
+
thinking_body = visible[:close_at]
|
|
368
|
+
after = visible[close_at + len(_THINKING_CLOSE) :].lstrip("\n")
|
|
369
|
+
self._thinking_parts.append(thinking_body)
|
|
370
|
+
self._text_parts = [after] if after else []
|
|
371
|
+
|
|
352
372
|
def feed(self, chunk: str) -> List[Dict[str, Any]]:
|
|
353
373
|
"""喂入新的流式文本块。
|
|
354
374
|
|
|
@@ -370,6 +390,7 @@ class FncallStreamParser:
|
|
|
370
390
|
ready = self.get_ready_tool_calls()
|
|
371
391
|
if ready and self._json_stream_encoder is not None:
|
|
372
392
|
self._sync_json_stream_encoder_emitted()
|
|
393
|
+
self._reclassify_orphan_thinking_close_in_text()
|
|
373
394
|
return ready
|
|
374
395
|
|
|
375
396
|
def _poll_streaming_tool_input_delta(self) -> Optional[Tuple[str, str]]:
|
|
@@ -20,6 +20,7 @@ from echotools.exec.fncall.protocols.entml_invoke import (
|
|
|
20
20
|
)
|
|
21
21
|
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
22
22
|
BLOCK_RE,
|
|
23
|
+
entml_invoke_open_may_be_streaming,
|
|
23
24
|
extract_attr_value,
|
|
24
25
|
normalize_entml_name,
|
|
25
26
|
strip_legacy_function_calls_wrapper,
|
|
@@ -225,7 +226,11 @@ class EntmlProtocol(ToolProtocol):
|
|
|
225
226
|
if close < 0 and (hold is None or legacy_pos < hold):
|
|
226
227
|
hold = legacy_pos
|
|
227
228
|
invoke_pos = buffer.find(self._TRIGGER_PREFIX)
|
|
228
|
-
if
|
|
229
|
+
if (
|
|
230
|
+
invoke_pos >= 0
|
|
231
|
+
and entml_invoke_open_may_be_streaming(buffer, invoke_pos)
|
|
232
|
+
and (hold is None or invoke_pos < hold)
|
|
233
|
+
):
|
|
229
234
|
hold = invoke_pos
|
|
230
235
|
return hold
|
|
231
236
|
|
|
@@ -60,6 +60,65 @@ _FENCE_ONLY_LINE_RE = re.compile(
|
|
|
60
60
|
r"^\s*```(?:xml|entml|text)?\s*$",
|
|
61
61
|
re.IGNORECASE | re.MULTILINE,
|
|
62
62
|
)
|
|
63
|
+
_INVOKE_OPEN_PREFIX = "<entml:invoke"
|
|
64
|
+
_PLACEHOLDER_INVOKE_NAMES = frozenset({"$FUNCTION_NAME", "$FUNCTION_NAME2"})
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def is_placeholder_invoke_name(name: str) -> bool:
|
|
68
|
+
"""提示词占位符(如 ``$FUNCTION_NAME``)不算真实工具调用。"""
|
|
69
|
+
n = (name or "").strip()
|
|
70
|
+
return not n or "$" in n
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def entml_invoke_open_is_actionable(buffer: str, pos: int) -> bool:
|
|
74
|
+
"""``pos`` 处 ``<entml:invoke`` 是否为已闭合且含真实 name 的工具开标签。"""
|
|
75
|
+
if pos < 0 or not buffer.startswith(_INVOKE_OPEN_PREFIX, pos):
|
|
76
|
+
return False
|
|
77
|
+
gt = buffer.find(">", pos + len(_INVOKE_OPEN_PREFIX))
|
|
78
|
+
if gt < 0:
|
|
79
|
+
return False
|
|
80
|
+
attrs = buffer[pos + len(_INVOKE_OPEN_PREFIX) : gt]
|
|
81
|
+
name = extract_attr_value(attrs, "name")
|
|
82
|
+
if not name:
|
|
83
|
+
return False
|
|
84
|
+
name = normalize_entml_name(name)
|
|
85
|
+
return bool(name) and not is_placeholder_invoke_name(name)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def entml_invoke_open_may_be_streaming(buffer: str, pos: int) -> bool:
|
|
89
|
+
"""``pos`` 处 ``<entml:invoke`` 是否仍可能长成真实工具开标签(非 prose 提及)。"""
|
|
90
|
+
if pos < 0 or not buffer.startswith(_INVOKE_OPEN_PREFIX, pos):
|
|
91
|
+
return False
|
|
92
|
+
if entml_invoke_open_is_actionable(buffer, pos):
|
|
93
|
+
return True
|
|
94
|
+
gt = buffer.find(">", pos + len(_INVOKE_OPEN_PREFIX))
|
|
95
|
+
if gt < 0:
|
|
96
|
+
return True
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _strip_orphan_invoke_tags(content: str) -> str:
|
|
101
|
+
"""仅剥离带真实 name 的 invoke 孤儿标签;保留 prose 中的 ``<entml:invoke>`` 提及。"""
|
|
102
|
+
pattern = re.compile(r"</?entml:invoke\b[^>]*/?>", re.DOTALL)
|
|
103
|
+
|
|
104
|
+
def repl(match: re.Match[str]) -> str:
|
|
105
|
+
tag = match.group(0)
|
|
106
|
+
if tag.startswith("</"):
|
|
107
|
+
return ""
|
|
108
|
+
if entml_invoke_open_is_actionable(content, match.start()):
|
|
109
|
+
return ""
|
|
110
|
+
return tag
|
|
111
|
+
|
|
112
|
+
return pattern.sub(repl, content)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _strip_orphan_non_invoke_tool_tags(content: str) -> str:
|
|
116
|
+
return re.sub(
|
|
117
|
+
r"</?entml:(?:function_calls|parameter|parameters)\b[^>]*/?>",
|
|
118
|
+
"",
|
|
119
|
+
content,
|
|
120
|
+
flags=re.DOTALL,
|
|
121
|
+
)
|
|
63
122
|
|
|
64
123
|
|
|
65
124
|
def extract_attr_value(attrs: str, attr_name: str = "name") -> Optional[str]:
|
|
@@ -117,12 +176,13 @@ def strip_legacy_function_calls_wrapper(text: str) -> str:
|
|
|
117
176
|
|
|
118
177
|
|
|
119
178
|
def strip_tool_entml_residue(content: str) -> str:
|
|
120
|
-
"""剥离工具相关 entml 标签残留,保留 thinking
|
|
179
|
+
"""剥离工具相关 entml 标签残留,保留 thinking 与非工具 prose 提及。"""
|
|
121
180
|
if not content:
|
|
122
181
|
return content
|
|
123
182
|
cleaned = _TOOL_WRAPPER_PAIR_RE.sub("", content)
|
|
124
183
|
cleaned = BLOCK_RE.sub("", cleaned)
|
|
125
|
-
cleaned =
|
|
184
|
+
cleaned = _strip_orphan_invoke_tags(cleaned)
|
|
185
|
+
cleaned = _strip_orphan_non_invoke_tool_tags(cleaned)
|
|
126
186
|
cleaned = _EMPTY_FENCE_RE.sub("", cleaned)
|
|
127
187
|
cleaned = _FENCE_ONLY_LINE_RE.sub("", cleaned)
|
|
128
188
|
# 折叠因剥离产生的多余空行
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
@@ -5,6 +5,7 @@ from typing import List, Optional, Tuple
|
|
|
5
5
|
|
|
6
6
|
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
7
7
|
BLOCK_RE,
|
|
8
|
+
entml_invoke_open_may_be_streaming,
|
|
8
9
|
extract_attr_value,
|
|
9
10
|
normalize_entml_name,
|
|
10
11
|
)
|
|
@@ -116,12 +117,22 @@ def find_ambiguous_entml_tool_prefix(buffer: str) -> int:
|
|
|
116
117
|
|
|
117
118
|
|
|
118
119
|
def _hold_ambiguous_tool_markup(buffer: str) -> Tuple[str, str]:
|
|
119
|
-
"""thinking
|
|
120
|
+
"""thinking 块内:hold 可能长成真实工具调用的 markup(非 prose ``<entml:invoke>`` 提及)。"""
|
|
120
121
|
if not buffer:
|
|
121
122
|
return "", ""
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
hold_from: Optional[int] = None
|
|
124
|
+
invoke_pos = buffer.find(_INVOKE_PREFIX)
|
|
125
|
+
if invoke_pos >= 0 and entml_invoke_open_may_be_streaming(buffer, invoke_pos):
|
|
126
|
+
if hold_from is None or invoke_pos < hold_from:
|
|
127
|
+
hold_from = invoke_pos
|
|
128
|
+
for prefix in _AMBIGUOUS_ENTML_PREFIXES:
|
|
129
|
+
if prefix == _INVOKE_PREFIX:
|
|
130
|
+
continue
|
|
131
|
+
pos = buffer.find(prefix)
|
|
132
|
+
if pos >= 0 and (hold_from is None or pos < hold_from):
|
|
133
|
+
hold_from = pos
|
|
134
|
+
if hold_from is not None:
|
|
135
|
+
return buffer[:hold_from], buffer[hold_from:]
|
|
125
136
|
lt = buffer.rfind("<")
|
|
126
137
|
if lt < 0:
|
|
127
138
|
return buffer, ""
|
|
@@ -233,6 +244,22 @@ class EntmlThinkingStreamFilter:
|
|
|
233
244
|
self._fault_buffer = ""
|
|
234
245
|
self._thinking_started = False
|
|
235
246
|
|
|
247
|
+
def _resolve_orphan_thinking_close(self, out: List[Tuple[str, str]]) -> bool:
|
|
248
|
+
"""仅有 ``</entml:thinking>`` 闭标签、无开标签时,将闭标签前 pending 视为 thinking。"""
|
|
249
|
+
close_at = self._pending.find(_THINKING_CLOSE)
|
|
250
|
+
if close_at < 0:
|
|
251
|
+
return False
|
|
252
|
+
if self._pending.find(_THINKING_OPEN_PREFIX) >= 0:
|
|
253
|
+
return False
|
|
254
|
+
before = self._pending[:close_at]
|
|
255
|
+
if before:
|
|
256
|
+
emitted = self._emit_thinking_piece(before)
|
|
257
|
+
if emitted:
|
|
258
|
+
out.append(("thinking", emitted))
|
|
259
|
+
self._pending = self._pending[close_at + len(_THINKING_CLOSE) :]
|
|
260
|
+
self._thinking_started = False
|
|
261
|
+
return True
|
|
262
|
+
|
|
236
263
|
def in_open_thinking(self) -> bool:
|
|
237
264
|
"""思考块已开始且尚未收到 ``</entml:thinking>``(含开/闭标签分片 hold)。"""
|
|
238
265
|
if self._in_block or self._fault_watch:
|
|
@@ -378,6 +405,9 @@ class EntmlThinkingStreamFilter:
|
|
|
378
405
|
|
|
379
406
|
def _feed_before_block(self, out: List[Tuple[str, str]]) -> bool:
|
|
380
407
|
"""开标签之前或尚未进入块;返回 False 表示应退出 feed 主循环。"""
|
|
408
|
+
if self._resolve_orphan_thinking_close(out):
|
|
409
|
+
return True
|
|
410
|
+
|
|
381
411
|
open_at = self._pending.find(_THINKING_OPEN_PREFIX)
|
|
382
412
|
if open_at < 0:
|
|
383
413
|
safe, self._pending = _hold_prefix(self._pending, _THINKING_OPEN_PREFIX)
|
|
@@ -170,6 +170,37 @@ def test_fault_thinking_close_without_invoke_is_plain_text() -> None:
|
|
|
170
170
|
assert "answer" in clean
|
|
171
171
|
|
|
172
172
|
|
|
173
|
+
def test_orphan_thinking_close_without_open_reclassified_from_visible() -> None:
|
|
174
|
+
"""漏写 ``<entml:thinking>`` 仅有闭标签时,闭标签前正文应进 thinking 而非 visible。"""
|
|
175
|
+
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
176
|
+
text = "plan step one\nplan step two\n</entml:thinking>\nvisible reply\n"
|
|
177
|
+
for i in range(0, len(text), 9):
|
|
178
|
+
parser.feed(text[i : i + 9])
|
|
179
|
+
assert "plan step one" in parser.partial_thinking
|
|
180
|
+
assert "visible reply" in parser.partial_text
|
|
181
|
+
assert "</entml:thinking>" not in parser.partial_text
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_prose_invoke_mention_preserved_in_visible_text() -> None:
|
|
185
|
+
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
186
|
+
text = "当我使用`<entml:invoke>`格式时,参数要简单。\n"
|
|
187
|
+
parser.feed(text)
|
|
188
|
+
clean, calls = parser.finalize()
|
|
189
|
+
assert not calls
|
|
190
|
+
assert "<entml:invoke>" in parser.partial_text
|
|
191
|
+
assert "<entml:invoke>" in clean
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_prose_invoke_mention_preserved_in_thinking_stream() -> None:
|
|
195
|
+
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
196
|
+
text = "<entml:thinking>\n当我使用`<entml:invoke>`格式时\n</entml:thinking>\n"
|
|
197
|
+
for i in range(0, len(text), 4):
|
|
198
|
+
parser.feed(text[i : i + 4])
|
|
199
|
+
_, calls = parser.finalize()
|
|
200
|
+
assert not calls
|
|
201
|
+
assert "<entml:invoke>" in parser.partial_thinking
|
|
202
|
+
|
|
203
|
+
|
|
173
204
|
def test_stream_delta_not_duplicated_after_invoke_in_thinking() -> None:
|
|
174
205
|
"""thinking 内 invoke ready 后,下一 chunk 不应重发整段 partial_json。"""
|
|
175
206
|
import json
|
|
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.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_stream_json.py
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/__init__.py
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/core.py
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/exec/fncall/protocols/entml_think/hist.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
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/console/uicore/mixins/uiinteract.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
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/filezone.js
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/motionkit.js
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/sendbutton.js
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/sortablelist.css
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/sortablelist.js
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/textinput.js
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/components/voiceinput.js
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/fonts/inter-500.woff2
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/fonts/inter-600.woff2
RENAMED
|
File without changes
|
{echotools-2.3.52 → echotools-2.3.53}/src/echotools/media/web/input_box/fonts/inter400.woff2
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
|
|
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
|