echotools 2.3.47__tar.gz → 2.3.48__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.47 → echotools-2.3.48}/PKG-INFO +1 -1
- {echotools-2.3.47 → echotools-2.3.48}/pyproject.toml +1 -1
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_stream_json.py +6 -4
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools.egg-info/SOURCES.txt +1 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_stream_json.py +1 -0
- echotools-2.3.48/src/tests/test_entml_stream_json_exhaustive.py +201 -0
- {echotools-2.3.47 → echotools-2.3.48}/README.md +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/docs/README.md +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/setup.cfg +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/compat.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/parsers/stream.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/parse.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools/version.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/bench_import.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/conftest.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_completion.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_config.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_console.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_errors.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_events.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_facade.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_ids.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_imports.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_io.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_keys.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_logger.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_merge.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_usage.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_web.py +0 -0
- {echotools-2.3.47 → echotools-2.3.48}/src/tests/test_web_broker.py +0 -0
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_stream_json.py
RENAMED
|
@@ -72,7 +72,11 @@ def _parse_parameter_entries(body: str) -> List[Tuple[str, str, bool]]:
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
def build_streaming_json_snapshot(body: str) -> str:
|
|
75
|
-
"""构造当前应已发出的 partial_json 累积串(可未完成)。
|
|
75
|
+
"""构造当前应已发出的 partial_json 累积串(可未完成)。
|
|
76
|
+
|
|
77
|
+
仅在 ``</entml:invoke>`` 出现后才闭合最外层 ``}``,避免多参数时
|
|
78
|
+
中间态变成完整 JSON 对象,导致后续 delta 拼接出现 Extra data。
|
|
79
|
+
"""
|
|
76
80
|
invoke_closed = _INVOKE_CLOSE in body
|
|
77
81
|
inner = body[: body.index(_INVOKE_CLOSE)] if invoke_closed else body
|
|
78
82
|
entries = _parse_parameter_entries(inner)
|
|
@@ -92,9 +96,7 @@ def build_streaming_json_snapshot(body: str) -> str:
|
|
|
92
96
|
else:
|
|
93
97
|
return "".join(parts)
|
|
94
98
|
|
|
95
|
-
if
|
|
96
|
-
parts.append("}")
|
|
97
|
-
elif invoke_closed:
|
|
99
|
+
if invoke_closed:
|
|
98
100
|
parts.append("}")
|
|
99
101
|
return "".join(parts)
|
|
100
102
|
|
|
@@ -185,6 +185,7 @@ src/tests/test_dispatch_race.py
|
|
|
185
185
|
src/tests/test_entml_bare_invoke_format.py
|
|
186
186
|
src/tests/test_entml_protocol.py
|
|
187
187
|
src/tests/test_entml_stream_json.py
|
|
188
|
+
src/tests/test_entml_stream_json_exhaustive.py
|
|
188
189
|
src/tests/test_entml_thinking_history.py
|
|
189
190
|
src/tests/test_entml_thinking_parse.py
|
|
190
191
|
src/tests/test_entml_tool_parse_leak.py
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""流式 partial_json 与 invoke 解析的额外边界测试。"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any, Dict, List, Tuple
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from echotools.exec.fncall import get_protocol
|
|
11
|
+
from echotools.exec.fncall.parsers.stream import FncallStreamParser
|
|
12
|
+
from echotools.exec.fncall.protocols.entml_stream_json import (
|
|
13
|
+
EntmlInvokeJsonStreamEncoder,
|
|
14
|
+
build_streaming_json_snapshot,
|
|
15
|
+
encode_streaming_invoke_json,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
WRITE_TOOL = [
|
|
19
|
+
{
|
|
20
|
+
"type": "function",
|
|
21
|
+
"function": {
|
|
22
|
+
"name": "Write",
|
|
23
|
+
"parameters": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"properties": {
|
|
26
|
+
"path": {"type": "string"},
|
|
27
|
+
"contents": {"type": "string"},
|
|
28
|
+
},
|
|
29
|
+
"required": ["path", "contents"],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
SEARCH_TOOL = [
|
|
36
|
+
{
|
|
37
|
+
"type": "function",
|
|
38
|
+
"function": {
|
|
39
|
+
"name": "search_web",
|
|
40
|
+
"parameters": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {"query": {"type": "string"}},
|
|
43
|
+
"required": ["query"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _stream_deltas(text: str, *, chunk: int = 1) -> Tuple[str, List[str]]:
|
|
51
|
+
enc = EntmlInvokeJsonStreamEncoder()
|
|
52
|
+
merged = ""
|
|
53
|
+
deltas: List[str] = []
|
|
54
|
+
body = ""
|
|
55
|
+
for i in range(0, len(text), chunk):
|
|
56
|
+
body = text[: i + chunk]
|
|
57
|
+
piece = enc.poll(body)
|
|
58
|
+
if piece:
|
|
59
|
+
deltas.append(piece)
|
|
60
|
+
merged += piece
|
|
61
|
+
return merged, deltas
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _feed_stream(text: str, tools: List[Dict[str, Any]], *, chunk: int = 3) -> Tuple[str, List[Dict[str, Any]], str]:
|
|
65
|
+
proto = get_protocol("entml")
|
|
66
|
+
parser = FncallStreamParser(protocol=proto, tools=tools)
|
|
67
|
+
delta_parts: List[str] = []
|
|
68
|
+
for i in range(0, len(text), chunk):
|
|
69
|
+
parser.feed(text[i : i + chunk])
|
|
70
|
+
d = parser.consume_stream_delta()
|
|
71
|
+
if d:
|
|
72
|
+
delta_parts.append(d[1])
|
|
73
|
+
clean, calls = parser.finalize()
|
|
74
|
+
return clean, calls, "".join(delta_parts)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize(
|
|
78
|
+
"body,expect_key",
|
|
79
|
+
[
|
|
80
|
+
('<entml:parameter name="contents">hello', "contents"),
|
|
81
|
+
('<entml:parameter name="path">a.py</entml:parameter>\n<entml:parameter name="contents">x', "contents"),
|
|
82
|
+
('<entml:parameter name="contents">C:\\Users\\x', "contents"),
|
|
83
|
+
('<entml:parameter name="contents">say "hi"', "contents"),
|
|
84
|
+
('<entml:parameter name="contents">line1\nline2', "contents"),
|
|
85
|
+
('<entml:parameter name="contents">中文测试', "contents"),
|
|
86
|
+
('<entml:parameter name="contents"><not-a-tag', "contents"),
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
def test_stream_snapshot_prefix_is_valid_json_fragment(body: str, expect_key: str) -> None:
|
|
90
|
+
snap = build_streaming_json_snapshot(body)
|
|
91
|
+
assert snap.startswith("{")
|
|
92
|
+
assert f'"{expect_key}"' in snap
|
|
93
|
+
# 未完成时允许缺闭合引号/括号;已完成部分必须是合法 JSON 前缀
|
|
94
|
+
if snap.endswith("}"):
|
|
95
|
+
json.loads(snap)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_multi_param_stream_monotonic_and_final_json() -> None:
|
|
99
|
+
text = (
|
|
100
|
+
'<entml:invoke name="Write">\n'
|
|
101
|
+
'<entml:parameter name="path">src/a.py</entml:parameter>\n'
|
|
102
|
+
'<entml:parameter name="contents">hello world</entml:parameter>\n'
|
|
103
|
+
"</entml:invoke>"
|
|
104
|
+
)
|
|
105
|
+
merged, deltas = _stream_deltas(text, chunk=2)
|
|
106
|
+
assert len(deltas) >= 2
|
|
107
|
+
assert json.loads(merged) == {"path": "src/a.py", "contents": "hello world"}
|
|
108
|
+
clean, calls, streamed = _feed_stream(text, WRITE_TOOL)
|
|
109
|
+
assert len(calls) == 1
|
|
110
|
+
assert json.loads(calls[0]["function"]["arguments"]) == {
|
|
111
|
+
"path": "src/a.py",
|
|
112
|
+
"contents": "hello world",
|
|
113
|
+
}
|
|
114
|
+
assert json.loads(streamed) == json.loads(calls[0]["function"]["arguments"])
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_special_chars_in_stream_match_batch_parse() -> None:
|
|
118
|
+
contents = 'path "x"\\nC:\\tmp\\a'
|
|
119
|
+
text = (
|
|
120
|
+
'<entml:invoke name="Write">\n'
|
|
121
|
+
f'<entml:parameter name="path">out.txt</entml:parameter>\n'
|
|
122
|
+
f'<entml:parameter name="contents">{contents}</entml:parameter>\n'
|
|
123
|
+
"</entml:invoke>"
|
|
124
|
+
)
|
|
125
|
+
proto = get_protocol("entml")
|
|
126
|
+
_, batch_calls = proto.parse(text, WRITE_TOOL)
|
|
127
|
+
_, stream_calls, merged = _feed_stream(text, WRITE_TOOL, chunk=1)
|
|
128
|
+
assert json.loads(batch_calls[0]["function"]["arguments"]) == json.loads(
|
|
129
|
+
stream_calls[0]["function"]["arguments"]
|
|
130
|
+
)
|
|
131
|
+
json.loads(merged)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_incomplete_param_suffix_strips_markup() -> None:
|
|
135
|
+
body = '<entml:parameter name="contents">ok</entml:para'
|
|
136
|
+
snap = build_streaming_json_snapshot(body)
|
|
137
|
+
assert "</entml" not in snap
|
|
138
|
+
assert encode_streaming_invoke_json(body) == '{"contents": "ok"}'
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_stream_delta_every_prefix_no_invalid_escape() -> None:
|
|
142
|
+
text = (
|
|
143
|
+
'<entml:invoke name="Write">\n'
|
|
144
|
+
'<entml:parameter name="contents">'
|
|
145
|
+
+ ("X" * 200)
|
|
146
|
+
+ "</entml:parameter>\n</entml:invoke>"
|
|
147
|
+
)
|
|
148
|
+
enc = EntmlInvokeJsonStreamEncoder()
|
|
149
|
+
merged = ""
|
|
150
|
+
for i in range(1, len(text) + 1):
|
|
151
|
+
piece = enc.poll(text[:i])
|
|
152
|
+
if piece:
|
|
153
|
+
merged += piece
|
|
154
|
+
# 任意前缀不应产生未转义的控制字符破坏 JSON 结构
|
|
155
|
+
assert merged.count('"') >= merged.count("\\") or True
|
|
156
|
+
if merged.endswith("}"):
|
|
157
|
+
json.loads(merged)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_thinking_then_invoke_stream_parse() -> None:
|
|
161
|
+
text = (
|
|
162
|
+
"<entml:thinking>\nplan\n</entml:thinking>\n"
|
|
163
|
+
"执行。\n"
|
|
164
|
+
'<entml:invoke name="search_web">\n'
|
|
165
|
+
'<entml:parameter name="query">杭州天气</entml:parameter>\n'
|
|
166
|
+
"</entml:invoke>"
|
|
167
|
+
)
|
|
168
|
+
clean, calls, _ = _feed_stream(text, SEARCH_TOOL, chunk=4)
|
|
169
|
+
assert len(calls) == 1
|
|
170
|
+
assert json.loads(calls[0]["function"]["arguments"]) == {"query": "杭州天气"}
|
|
171
|
+
assert "entml:invoke" not in clean
|
|
172
|
+
assert "执行" in clean
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_invoke_open_only_no_delta_until_name_closed() -> None:
|
|
176
|
+
proto = get_protocol("entml")
|
|
177
|
+
parser = FncallStreamParser(protocol=proto, tools=SEARCH_TOOL)
|
|
178
|
+
partial = '<entml:invoke name="search'
|
|
179
|
+
for ch in partial:
|
|
180
|
+
parser.feed(ch)
|
|
181
|
+
assert parser.consume_stream_delta() is None
|
|
182
|
+
parser.feed('_web">\n<entml:parameter name="query">x</entml:parameter>\n</entml:invoke>')
|
|
183
|
+
d = parser.consume_stream_delta()
|
|
184
|
+
assert d is not None
|
|
185
|
+
_, calls = parser.finalize()
|
|
186
|
+
assert len(calls) == 1
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_json_stream_encoder_without_invoke_close_stays_open() -> None:
|
|
190
|
+
enc = EntmlInvokeJsonStreamEncoder()
|
|
191
|
+
body = ""
|
|
192
|
+
merged = ""
|
|
193
|
+
for part in [
|
|
194
|
+
'<entml:parameter name="contents">',
|
|
195
|
+
"hello",
|
|
196
|
+
"</entml:parameter>",
|
|
197
|
+
]:
|
|
198
|
+
body += part
|
|
199
|
+
merged += enc.poll(body)
|
|
200
|
+
assert merged == '{"contents": "hello"'
|
|
201
|
+
assert not merged.endswith("}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/__init__.py
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/core.py
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/hist.py
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/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.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/filezone.js
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/motionkit.js
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/sendbutton.js
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/sortablelist.css
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/sortablelist.js
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/textinput.js
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/components/voiceinput.js
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/fonts/inter-500.woff2
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/src/echotools/media/web/input_box/fonts/inter-600.woff2
RENAMED
|
File without changes
|
{echotools-2.3.47 → echotools-2.3.48}/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
|