echotools 2.3.48__tar.gz → 2.3.49__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.48 → echotools-2.3.49}/PKG-INFO +1 -1
- {echotools-2.3.48 → echotools-2.3.49}/pyproject.toml +1 -1
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/parse.py +72 -49
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_fncall_stream_thinking.py +44 -11
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_stream_realtime_parse.py +4 -5
- {echotools-2.3.48 → echotools-2.3.49}/README.md +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/docs/README.md +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/setup.cfg +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/compat.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/parsers/stream.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools/version.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools.egg-info/SOURCES.txt +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/bench_import.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/conftest.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_completion.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_config.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_console.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_errors.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_events.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_facade.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_ids.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_imports.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_io.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_keys.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_logger.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_merge.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_usage.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_web.py +0 -0
- {echotools-2.3.48 → echotools-2.3.49}/src/tests/test_web_broker.py +0 -0
{echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
@@ -15,6 +15,7 @@ THINKING_BLOCK_RE = re.compile(
|
|
|
15
15
|
|
|
16
16
|
_THINKING_OPEN_PREFIX = "<entml:thinking"
|
|
17
17
|
_THINKING_CLOSE = "</entml:thinking>"
|
|
18
|
+
_FAULT_THINKING_CLOSE = "</thinking>"
|
|
18
19
|
# 与 thinking 共享 `<entml:` 前缀的其它标签:歧义 holdback 应交由工具流式状态机处理
|
|
19
20
|
_AMBIGUOUS_ENTML_PREFIXES = (
|
|
20
21
|
"<entml:invoke",
|
|
@@ -142,10 +143,13 @@ class EntmlThinkingStreamFilter:
|
|
|
142
143
|
self._in_block = False
|
|
143
144
|
# 当前 thinking 块是否已输出过任何内容(用于首片去前导空白)
|
|
144
145
|
self._thinking_started = False
|
|
146
|
+
# 见到 ``</thinking>`` 后暂存,仅当后续出现 invoke 才视为闭合;否则当纯文本
|
|
147
|
+
self._fault_watch = False
|
|
148
|
+
self._fault_buffer = ""
|
|
145
149
|
|
|
146
150
|
def in_open_thinking(self) -> bool:
|
|
147
|
-
"""思考块已开始且尚未收到
|
|
148
|
-
if self._in_block:
|
|
151
|
+
"""思考块已开始且尚未收到 ``</entml:thinking>``(含开/闭标签分片 hold)。"""
|
|
152
|
+
if self._in_block or self._fault_watch:
|
|
149
153
|
return True
|
|
150
154
|
open_at = self._pending.find(_THINKING_OPEN_PREFIX)
|
|
151
155
|
if open_at >= 0:
|
|
@@ -157,47 +161,47 @@ class EntmlThinkingStreamFilter:
|
|
|
157
161
|
return True
|
|
158
162
|
return False
|
|
159
163
|
|
|
160
|
-
def
|
|
161
|
-
"""thinking
|
|
162
|
-
|
|
163
|
-
if tool_at < 0:
|
|
164
|
+
def _try_resolve_fault_watch_on_invoke(self) -> bool:
|
|
165
|
+
"""``</thinking>`` 之后若出现完整 invoke 开标签,则在该处结束 thinking。"""
|
|
166
|
+
if not self._fault_watch or not self._fault_buffer:
|
|
164
167
|
return False
|
|
165
|
-
|
|
166
|
-
if
|
|
168
|
+
invoke_at = find_complete_entml_invoke_open(self._fault_buffer)
|
|
169
|
+
if invoke_at < len(_FAULT_THINKING_CLOSE):
|
|
167
170
|
return False
|
|
168
|
-
if tool_at > 0:
|
|
169
|
-
emitted = self._emit_thinking_piece(self._pending[:tool_at])
|
|
170
|
-
if emitted:
|
|
171
|
-
out.append(("thinking", emitted))
|
|
172
171
|
self._in_block = False
|
|
172
|
+
self._fault_watch = False
|
|
173
173
|
self._thinking_started = False
|
|
174
|
-
self._pending = self.
|
|
174
|
+
self._pending = self._fault_buffer[len(_FAULT_THINKING_CLOSE) :]
|
|
175
|
+
self._fault_buffer = ""
|
|
175
176
|
return True
|
|
176
177
|
|
|
177
|
-
def
|
|
178
|
-
"""
|
|
179
|
-
|
|
180
|
-
if invoke_at < 0:
|
|
178
|
+
def _try_resolve_fault_watch_on_entml_close(self, out: List[Tuple[str, str]]) -> bool:
|
|
179
|
+
"""未见 invoke 时出现 ``</entml:thinking>`` → ``</thinking>`` 仅为思考内纯文本。"""
|
|
180
|
+
if not self._fault_watch:
|
|
181
181
|
return False
|
|
182
|
-
close_at = self.
|
|
183
|
-
if close_at
|
|
182
|
+
close_at = self._fault_buffer.find(_THINKING_CLOSE)
|
|
183
|
+
if close_at < 0:
|
|
184
184
|
return False
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
out.append(("thinking", emitted))
|
|
185
|
+
emitted = self._emit_thinking_piece(self._fault_buffer[:close_at])
|
|
186
|
+
if emitted:
|
|
187
|
+
out.append(("thinking", emitted))
|
|
189
188
|
self._in_block = False
|
|
189
|
+
self._fault_watch = False
|
|
190
190
|
self._thinking_started = False
|
|
191
|
-
self._pending = self.
|
|
191
|
+
self._pending = self._fault_buffer[close_at + len(_THINKING_CLOSE) :]
|
|
192
|
+
self._fault_buffer = ""
|
|
192
193
|
return True
|
|
193
194
|
|
|
194
|
-
def
|
|
195
|
-
"""
|
|
196
|
-
if self.
|
|
195
|
+
def _feed_fault_watch(self, out: List[Tuple[str, str]]) -> bool:
|
|
196
|
+
"""处理 ``</thinking>`` 容错观察期;返回 True 表示可继续主循环。"""
|
|
197
|
+
if self._try_resolve_fault_watch_on_invoke():
|
|
197
198
|
return True
|
|
198
|
-
if self.
|
|
199
|
+
if self._try_resolve_fault_watch_on_entml_close(out):
|
|
199
200
|
return True
|
|
201
|
+
return False
|
|
200
202
|
|
|
203
|
+
def _feed_in_block(self, out: List[Tuple[str, str]]) -> bool:
|
|
204
|
+
"""处理块内 pending;返回 False 表示应退出 feed 主循环。"""
|
|
201
205
|
close_at = self._pending.find(_THINKING_CLOSE)
|
|
202
206
|
if close_at >= 0:
|
|
203
207
|
piece = self._pending[:close_at]
|
|
@@ -205,10 +209,23 @@ class EntmlThinkingStreamFilter:
|
|
|
205
209
|
if emitted:
|
|
206
210
|
out.append(("thinking", emitted))
|
|
207
211
|
self._in_block = False
|
|
212
|
+
self._fault_watch = False
|
|
213
|
+
self._fault_buffer = ""
|
|
208
214
|
self._thinking_started = False
|
|
209
215
|
self._pending = self._pending[close_at + len(_THINKING_CLOSE) :]
|
|
210
216
|
return True
|
|
211
217
|
|
|
218
|
+
fault_at = self._pending.find(_FAULT_THINKING_CLOSE)
|
|
219
|
+
if fault_at >= 0:
|
|
220
|
+
before = self._pending[:fault_at]
|
|
221
|
+
emitted = self._emit_thinking_piece(before)
|
|
222
|
+
if emitted:
|
|
223
|
+
out.append(("thinking", emitted))
|
|
224
|
+
self._fault_watch = True
|
|
225
|
+
self._fault_buffer = self._pending[fault_at:]
|
|
226
|
+
self._pending = ""
|
|
227
|
+
return False
|
|
228
|
+
|
|
212
229
|
safe, tool_hold = _hold_ambiguous_tool_markup(self._pending)
|
|
213
230
|
if tool_hold:
|
|
214
231
|
if safe:
|
|
@@ -219,6 +236,8 @@ class EntmlThinkingStreamFilter:
|
|
|
219
236
|
return False
|
|
220
237
|
|
|
221
238
|
safe, hold = _hold_prefix(self._pending, _THINKING_CLOSE)
|
|
239
|
+
if not hold:
|
|
240
|
+
safe, hold = _hold_prefix(safe, _FAULT_THINKING_CLOSE)
|
|
222
241
|
if safe:
|
|
223
242
|
emitted = self._emit_thinking_piece(safe)
|
|
224
243
|
if emitted:
|
|
@@ -257,6 +276,12 @@ class EntmlThinkingStreamFilter:
|
|
|
257
276
|
out: List[Tuple[str, str]] = []
|
|
258
277
|
|
|
259
278
|
while self._pending:
|
|
279
|
+
if self._fault_watch:
|
|
280
|
+
self._fault_buffer += self._pending
|
|
281
|
+
self._pending = ""
|
|
282
|
+
if self._feed_fault_watch(out):
|
|
283
|
+
continue
|
|
284
|
+
break
|
|
260
285
|
if self._in_block:
|
|
261
286
|
if not self._feed_in_block(out):
|
|
262
287
|
break
|
|
@@ -279,39 +304,37 @@ class EntmlThinkingStreamFilter:
|
|
|
279
304
|
|
|
280
305
|
def finalize(self) -> List[Tuple[str, str]]:
|
|
281
306
|
out: List[Tuple[str, str]] = []
|
|
307
|
+
if self._fault_watch:
|
|
308
|
+
if self._try_resolve_fault_watch_on_invoke():
|
|
309
|
+
pass
|
|
310
|
+
elif self._try_resolve_fault_watch_on_entml_close(out):
|
|
311
|
+
pass
|
|
312
|
+
else:
|
|
313
|
+
emitted = self._emit_thinking_piece(self._fault_buffer)
|
|
314
|
+
if emitted:
|
|
315
|
+
out.append(("thinking", emitted))
|
|
316
|
+
self._fault_buffer = ""
|
|
317
|
+
self._fault_watch = False
|
|
318
|
+
self._in_block = False
|
|
319
|
+
self._thinking_started = False
|
|
282
320
|
if self._in_block:
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
emitted = self._emit_thinking_piece(self._pending)
|
|
287
|
-
if emitted:
|
|
288
|
-
out.append(("thinking", emitted))
|
|
289
|
-
self._pending = ""
|
|
290
|
-
self._in_block = False
|
|
291
|
-
self._thinking_started = False
|
|
292
|
-
return out
|
|
293
|
-
# invoke 已切出,继续处理 _pending 中的正文
|
|
294
|
-
while self._pending:
|
|
295
|
-
if self._in_block:
|
|
296
|
-
if not self._feed_in_block(out):
|
|
297
|
-
break
|
|
298
|
-
continue
|
|
299
|
-
if not self._feed_before_block(out):
|
|
300
|
-
break
|
|
321
|
+
emitted = self._emit_thinking_piece(self._pending)
|
|
322
|
+
if emitted:
|
|
323
|
+
out.append(("thinking", emitted))
|
|
301
324
|
self._pending = ""
|
|
302
325
|
self._in_block = False
|
|
303
326
|
self._thinking_started = False
|
|
304
|
-
|
|
305
|
-
elif self._pending:
|
|
327
|
+
if self._pending:
|
|
306
328
|
content, thinking = split_entml_thinking(self._pending)
|
|
307
329
|
if thinking:
|
|
308
330
|
out.append(("thinking", thinking))
|
|
309
331
|
if content:
|
|
310
332
|
out.append(("content", content))
|
|
311
333
|
elif self._pending and not thinking:
|
|
312
|
-
# 仅 hold 了半截开标签:原样吐出,避免吞字
|
|
313
334
|
out.append(("content", self._pending))
|
|
314
335
|
self._pending = ""
|
|
315
336
|
self._in_block = False
|
|
337
|
+
self._fault_watch = False
|
|
338
|
+
self._fault_buffer = ""
|
|
316
339
|
self._thinking_started = False
|
|
317
340
|
return out
|
|
@@ -53,19 +53,16 @@ def test_split_entml_thinking_ignores_unclosed_block() -> None:
|
|
|
53
53
|
assert INVOKE in content
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def
|
|
57
|
-
"""
|
|
56
|
+
def test_invoke_inside_unclosed_thinking_not_parsed() -> None:
|
|
57
|
+
"""未闭合 thinking 内的 entml 标签仅作思考文本,不解析为 tool call。"""
|
|
58
58
|
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
59
59
|
stream = f"<entml:thinking>\nplan {INVOKE}\n"
|
|
60
60
|
for i in range(0, len(stream), 7):
|
|
61
61
|
parser.feed(stream[i : i + 7])
|
|
62
|
-
assert parser.has_calls
|
|
63
|
-
assert "
|
|
64
|
-
assert INVOKE not in parser.partial_thinking
|
|
62
|
+
assert not parser.has_calls
|
|
63
|
+
assert INVOKE in parser.partial_thinking or "entml:invoke" in parser.partial_thinking
|
|
65
64
|
clean, calls = parser.finalize()
|
|
66
|
-
assert len(calls) ==
|
|
67
|
-
assert calls[0]["function"]["name"] == "get_weather"
|
|
68
|
-
assert INVOKE not in clean
|
|
65
|
+
assert len(calls) == 0
|
|
69
66
|
|
|
70
67
|
|
|
71
68
|
def test_invoke_after_thinking_close_is_parsed() -> None:
|
|
@@ -98,16 +95,52 @@ def test_no_thinking_invoke_still_works() -> None:
|
|
|
98
95
|
assert "Sure" in clean
|
|
99
96
|
|
|
100
97
|
|
|
101
|
-
def
|
|
98
|
+
def test_stream_thinking_invoke_inside_stays_thinking_until_close() -> None:
|
|
102
99
|
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
103
100
|
parser.feed("<entml:thinking>\n")
|
|
104
101
|
parser.feed("line one\n")
|
|
105
102
|
assert "line one" in parser.partial_thinking
|
|
106
103
|
assert not parser.has_calls
|
|
107
104
|
parser.feed(f"mention {INVOKE}\n")
|
|
108
|
-
assert
|
|
109
|
-
assert parser.
|
|
105
|
+
assert not parser.has_calls
|
|
106
|
+
assert "entml:invoke" in parser.partial_thinking
|
|
110
107
|
parser.feed("</entml:thinking>\nanswer")
|
|
111
108
|
clean, calls = parser.finalize()
|
|
109
|
+
assert len(calls) == 0
|
|
110
|
+
assert "answer" in clean
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_fault_thinking_close_then_invoke() -> None:
|
|
114
|
+
"""``</thinking>`` 后若出现 invoke,则在该处结束思考并解析工具。"""
|
|
115
|
+
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
116
|
+
text = f"<entml:thinking>\nplan\n</thinking>\n{INVOKE}"
|
|
117
|
+
for i in range(0, len(text), 5):
|
|
118
|
+
parser.feed(text[i : i + 5])
|
|
119
|
+
clean, calls = parser.finalize()
|
|
120
|
+
assert "plan" in parser.partial_thinking
|
|
112
121
|
assert len(calls) == 1
|
|
122
|
+
assert calls[0]["function"]["name"] == "get_weather"
|
|
123
|
+
assert INVOKE not in clean
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def test_fault_thinking_close_without_invoke_is_plain_text() -> None:
|
|
127
|
+
"""``</thinking>`` 后若无 invoke 直到 ``</entml:thinking>``,则视为思考内纯文本。"""
|
|
128
|
+
parser = FncallStreamParser(protocol=get_protocol("entml"), tools=TOOLS)
|
|
129
|
+
text = "<entml:thinking>\nplan\n</thinking>\nmore\n</entml:thinking>\nanswer"
|
|
130
|
+
parser.feed(text)
|
|
131
|
+
clean, calls = parser.finalize()
|
|
132
|
+
assert len(calls) == 0
|
|
133
|
+
assert "plan" in parser.partial_thinking
|
|
134
|
+
assert "</thinking>" in parser.partial_thinking or "more" in parser.partial_thinking
|
|
113
135
|
assert "answer" in clean
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_fault_thinking_close_stays_open_while_waiting_for_invoke() -> None:
|
|
139
|
+
filt = EntmlThinkingStreamFilter()
|
|
140
|
+
filt.feed("<entml:thinking>\nplan\n</thinking>\nstill ")
|
|
141
|
+
assert filt.in_open_thinking()
|
|
142
|
+
filt.feed("waiting\n")
|
|
143
|
+
assert filt.in_open_thinking()
|
|
144
|
+
filt.feed(INVOKE)
|
|
145
|
+
kinds = [k for k, _ in filt.feed("")]
|
|
146
|
+
assert "content" in kinds or filt.in_open_thinking() is False
|
|
@@ -112,7 +112,7 @@ def test_char_by_char_ready_matches_final_for_all_tool_cases() -> None:
|
|
|
112
112
|
assert _args(incremental) == _args(final) == case.expect_args, case.id
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
def
|
|
115
|
+
def test_unclosed_thinking_invoke_not_parsed_until_close() -> None:
|
|
116
116
|
proto = get_protocol("entml")
|
|
117
117
|
tools = TOOLS
|
|
118
118
|
invoke = (
|
|
@@ -124,11 +124,10 @@ def test_unclosed_thinking_invoke_parsed_once_inside_block() -> None:
|
|
|
124
124
|
ready = []
|
|
125
125
|
for ch in f"<entml:thinking>\nplan {invoke}\n":
|
|
126
126
|
ready.extend(parser.feed(ch))
|
|
127
|
-
assert
|
|
127
|
+
assert ready == []
|
|
128
128
|
for ch in f"</entml:thinking>\n可见\n{invoke}":
|
|
129
129
|
ready.extend(parser.feed(ch))
|
|
130
130
|
ready.extend(parser.get_ready_tool_calls())
|
|
131
131
|
_, final = parser.finalize()
|
|
132
|
-
|
|
133
|
-
assert
|
|
134
|
-
assert len(final) == 2
|
|
132
|
+
assert _names(final) == ["get_weather"]
|
|
133
|
+
assert len(final) == 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_stream_json.py
RENAMED
|
File without changes
|
{echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/__init__.py
RENAMED
|
File without changes
|
{echotools-2.3.48 → echotools-2.3.49}/src/echotools/exec/fncall/protocols/entml_think/core.py
RENAMED
|
File without changes
|
{echotools-2.3.48 → echotools-2.3.49}/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.48 → echotools-2.3.49}/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
|