echotools 2.3.61__tar.gz → 2.3.62__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.61 → echotools-2.3.62}/PKG-INFO +1 -1
- {echotools-2.3.61 → echotools-2.3.62}/pyproject.toml +1 -1
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/parsers/stream.py +18 -6
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +2 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/core.py +10 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/parse.py +85 -22
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_thinking_parse.py +19 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_fncall_stream_thinking.py +32 -0
- {echotools-2.3.61 → echotools-2.3.62}/README.md +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/docs/README.md +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/setup.cfg +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/compat.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_invoke.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_stream_json.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools/version.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools.egg-info/SOURCES.txt +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/bench_import.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/conftest.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_completion.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_config.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_console.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_bare_invoke_format.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_protocol.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_tool_pipeline.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_errors.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_events.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_facade.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_ids.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_imports.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_io.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_keys.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_logger.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_merge.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_stream_partial_json_parity.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_usage.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_web.py +0 -0
- {echotools-2.3.61 → echotools-2.3.62}/src/tests/test_web_broker.py +0 -0
|
@@ -12,12 +12,12 @@ from echotools.exec.protocol.base import ToolProtocol
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
# 懒导入:仅 entml 协议需要
|
|
15
|
-
def _make_thinking_filter(protocol: ToolProtocol):
|
|
15
|
+
def _make_thinking_filter(protocol: ToolProtocol, *, thinking_enabled: bool = True):
|
|
16
16
|
if getattr(protocol, "id", None) == "entml":
|
|
17
17
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
18
18
|
EntmlThinkingStreamFilter,
|
|
19
19
|
)
|
|
20
|
-
return EntmlThinkingStreamFilter()
|
|
20
|
+
return EntmlThinkingStreamFilter(thinking_enabled=thinking_enabled)
|
|
21
21
|
return None
|
|
22
22
|
|
|
23
23
|
|
|
@@ -40,9 +40,13 @@ class FncallStreamParser:
|
|
|
40
40
|
self,
|
|
41
41
|
protocol: ToolProtocol,
|
|
42
42
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
43
|
+
protocol_options: Optional[Dict[str, Any]] = None,
|
|
43
44
|
) -> None:
|
|
44
45
|
self._protocol = protocol
|
|
45
46
|
self._tools = tools
|
|
47
|
+
from echotools.exec.fncall.protocols.entml_think.core import is_thinking_enabled
|
|
48
|
+
|
|
49
|
+
self._thinking_enabled = is_thinking_enabled(protocol_options)
|
|
46
50
|
self._raw_buf: str = ""
|
|
47
51
|
self._text_parts: List[str] = []
|
|
48
52
|
self._waiting_tail: str = ""
|
|
@@ -51,7 +55,9 @@ class FncallStreamParser:
|
|
|
51
55
|
self._state: str = self.WAITING_FOR_TAG
|
|
52
56
|
self._finalized_result: Optional[Tuple[str, List[Dict[str, Any]]]] = None
|
|
53
57
|
self._thinking_parts: List[str] = []
|
|
54
|
-
self._thinking_filter = _make_thinking_filter(
|
|
58
|
+
self._thinking_filter = _make_thinking_filter(
|
|
59
|
+
protocol, thinking_enabled=self._thinking_enabled
|
|
60
|
+
)
|
|
55
61
|
self._emitted_invoke_count: int = 0
|
|
56
62
|
self._json_stream_encoder = None
|
|
57
63
|
self._pending_stream_deltas: Deque[Tuple[str, str]] = deque()
|
|
@@ -298,7 +304,9 @@ class FncallStreamParser:
|
|
|
298
304
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
299
305
|
invoke_index_inside_unclosed_thinking,
|
|
300
306
|
)
|
|
301
|
-
if invoke_index_inside_unclosed_thinking(
|
|
307
|
+
if invoke_index_inside_unclosed_thinking(
|
|
308
|
+
combined, pos, thinking_enabled=self._thinking_enabled
|
|
309
|
+
):
|
|
302
310
|
self._feed_waiting_thinking_plain(combined)
|
|
303
311
|
return
|
|
304
312
|
if pos > 0:
|
|
@@ -314,7 +322,9 @@ class FncallStreamParser:
|
|
|
314
322
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
315
323
|
has_unclosed_entml_thinking,
|
|
316
324
|
)
|
|
317
|
-
if has_unclosed_entml_thinking(
|
|
325
|
+
if has_unclosed_entml_thinking(
|
|
326
|
+
combined, thinking_enabled=self._thinking_enabled
|
|
327
|
+
):
|
|
318
328
|
self._feed_waiting_thinking_plain(combined)
|
|
319
329
|
return
|
|
320
330
|
|
|
@@ -633,7 +643,9 @@ class FncallStreamParser:
|
|
|
633
643
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
634
644
|
split_entml_thinking,
|
|
635
645
|
)
|
|
636
|
-
clean_text, more_thinking = split_entml_thinking(
|
|
646
|
+
clean_text, more_thinking = split_entml_thinking(
|
|
647
|
+
clean_text, thinking_enabled=self._thinking_enabled
|
|
648
|
+
)
|
|
637
649
|
if more_thinking:
|
|
638
650
|
self._thinking_parts.append(more_thinking)
|
|
639
651
|
|
{echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/__init__.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from echotools.exec.fncall.protocols.entml_think.core import (
|
|
4
4
|
build_entml_thinking_section,
|
|
5
|
+
is_thinking_enabled,
|
|
5
6
|
parse_max_thinking_length,
|
|
6
7
|
)
|
|
7
8
|
from echotools.exec.fncall.protocols.entml_think.hist import (
|
|
@@ -24,6 +25,7 @@ __all__ = [
|
|
|
24
25
|
"extract_reasoning_text",
|
|
25
26
|
"format_entml_thinking_history_block",
|
|
26
27
|
"has_unclosed_entml_thinking",
|
|
28
|
+
"is_thinking_enabled",
|
|
27
29
|
"parse_include_thinking_in_history",
|
|
28
30
|
"parse_interleaved_history",
|
|
29
31
|
"parse_max_thinking_length",
|
{echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/core.py
RENAMED
|
@@ -159,6 +159,16 @@ def resolve_thinking_injection(
|
|
|
159
159
|
return injection_mode, max_length
|
|
160
160
|
|
|
161
161
|
|
|
162
|
+
def is_thinking_enabled(protocol_options: Optional[Dict[str, Any]] = None) -> bool:
|
|
163
|
+
"""是否开启思考模式(容错与 plain ``<thinking>`` 仅在此为 True 时生效)。
|
|
164
|
+
|
|
165
|
+
未传 ``protocol_options`` 时视为已开启(兼容旧调用);显式 ``off`` / ``none`` 时关闭。
|
|
166
|
+
"""
|
|
167
|
+
if protocol_options is None:
|
|
168
|
+
return True
|
|
169
|
+
return resolve_thinking_injection(protocol_options) is not None
|
|
170
|
+
|
|
171
|
+
|
|
162
172
|
def _uses_forced_thinking_behavior(injection_mode: str) -> bool:
|
|
163
173
|
return injection_mode != "auto"
|
|
164
174
|
|
{echotools-2.3.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
@@ -122,9 +122,18 @@ def _find_plain_thinking_open(text: str, start: int = 0) -> int:
|
|
|
122
122
|
return text.find(_PLAIN_THINKING_OPEN_PREFIX, start)
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
def _find_earliest_thinking_open(
|
|
125
|
+
def _find_earliest_thinking_open(
|
|
126
|
+
text: str,
|
|
127
|
+
start: int = 0,
|
|
128
|
+
*,
|
|
129
|
+
thinking_enabled: bool = True,
|
|
130
|
+
) -> Tuple[int, bool]:
|
|
126
131
|
"""返回 ``(开标签下标, 是否 plain)``;无则 ``(-1, False)``。"""
|
|
127
132
|
entml_at = text.find(_THINKING_OPEN_PREFIX, start)
|
|
133
|
+
if not thinking_enabled:
|
|
134
|
+
if entml_at < 0:
|
|
135
|
+
return -1, False
|
|
136
|
+
return entml_at, False
|
|
128
137
|
plain_at = _find_plain_thinking_open(text, start)
|
|
129
138
|
if entml_at < 0 and plain_at < 0:
|
|
130
139
|
return -1, False
|
|
@@ -142,8 +151,15 @@ def _find_thinking_close(
|
|
|
142
151
|
body_start: int,
|
|
143
152
|
*,
|
|
144
153
|
opened_plain: bool,
|
|
154
|
+
thinking_enabled: bool = True,
|
|
145
155
|
) -> Tuple[int, int]:
|
|
146
156
|
"""返回 ``(闭标签下标, 闭标签长度)``;未找到则 ``(-1, 0)``。"""
|
|
157
|
+
if not thinking_enabled:
|
|
158
|
+
close_at = text.find(_THINKING_CLOSE, body_start)
|
|
159
|
+
if close_at >= 0:
|
|
160
|
+
return close_at, len(_THINKING_CLOSE)
|
|
161
|
+
return -1, 0
|
|
162
|
+
|
|
147
163
|
if opened_plain:
|
|
148
164
|
entml_close = text.find(_THINKING_CLOSE, body_start)
|
|
149
165
|
fault_close = text.find(_FAULT_THINKING_CLOSE, body_start)
|
|
@@ -176,12 +192,18 @@ def _find_thinking_close(
|
|
|
176
192
|
return -1, 0
|
|
177
193
|
|
|
178
194
|
|
|
179
|
-
def _hold_thinking_open_prefixes(
|
|
195
|
+
def _hold_thinking_open_prefixes(
|
|
196
|
+
buffer: str,
|
|
197
|
+
*,
|
|
198
|
+
thinking_enabled: bool = True,
|
|
199
|
+
) -> Tuple[str, str]:
|
|
180
200
|
"""hold ``<entml:thinking`` 或 plain ``<thinking`` 的真前缀。"""
|
|
181
201
|
safe, hold = _hold_prefix(buffer, _THINKING_OPEN_PREFIX)
|
|
182
202
|
if hold:
|
|
183
203
|
return safe, hold
|
|
184
|
-
|
|
204
|
+
if thinking_enabled:
|
|
205
|
+
return _hold_prefix(safe, _PLAIN_THINKING_OPEN_PREFIX)
|
|
206
|
+
return safe, ""
|
|
185
207
|
|
|
186
208
|
|
|
187
209
|
def _hold_ambiguous_tool_markup(buffer: str) -> Tuple[str, str]:
|
|
@@ -211,13 +233,19 @@ def _hold_ambiguous_tool_markup(buffer: str) -> Tuple[str, str]:
|
|
|
211
233
|
return buffer, ""
|
|
212
234
|
|
|
213
235
|
|
|
214
|
-
def has_unclosed_entml_thinking(
|
|
236
|
+
def has_unclosed_entml_thinking(
|
|
237
|
+
text: str,
|
|
238
|
+
*,
|
|
239
|
+
thinking_enabled: bool = True,
|
|
240
|
+
) -> bool:
|
|
215
241
|
"""buffer 中是否存在尚未闭合的 thinking 块(含开标签未收齐)。"""
|
|
216
242
|
if not text:
|
|
217
243
|
return False
|
|
218
244
|
i = 0
|
|
219
245
|
while i < len(text):
|
|
220
|
-
open_at, opened_plain = _find_earliest_thinking_open(
|
|
246
|
+
open_at, opened_plain = _find_earliest_thinking_open(
|
|
247
|
+
text, i, thinking_enabled=thinking_enabled
|
|
248
|
+
)
|
|
221
249
|
if open_at < 0:
|
|
222
250
|
break
|
|
223
251
|
gt = text.find(">", open_at)
|
|
@@ -225,12 +253,17 @@ def has_unclosed_entml_thinking(text: str) -> bool:
|
|
|
225
253
|
return True
|
|
226
254
|
body_start = gt + 1
|
|
227
255
|
close_at, close_len = _find_thinking_close(
|
|
228
|
-
text,
|
|
256
|
+
text,
|
|
257
|
+
body_start,
|
|
258
|
+
opened_plain=opened_plain,
|
|
259
|
+
thinking_enabled=thinking_enabled,
|
|
229
260
|
)
|
|
230
261
|
if close_at < 0:
|
|
231
262
|
return True
|
|
232
263
|
i = close_at + close_len
|
|
233
|
-
max_keep =
|
|
264
|
+
max_keep = len(_THINKING_OPEN_PREFIX) - 1
|
|
265
|
+
if thinking_enabled:
|
|
266
|
+
max_keep = max(max_keep, len(_PLAIN_THINKING_OPEN_PREFIX) - 1)
|
|
234
267
|
check_len = min(len(text), max_keep)
|
|
235
268
|
for length in range(check_len, 0, -1):
|
|
236
269
|
suffix = text[-length:]
|
|
@@ -241,7 +274,7 @@ def has_unclosed_entml_thinking(text: str) -> bool:
|
|
|
241
274
|
if any(other.startswith(suffix) for other in _AMBIGUOUS_ENTML_PREFIXES):
|
|
242
275
|
continue
|
|
243
276
|
return True
|
|
244
|
-
if (
|
|
277
|
+
if thinking_enabled and (
|
|
245
278
|
_PLAIN_THINKING_OPEN_PREFIX.startswith(suffix)
|
|
246
279
|
and suffix != _PLAIN_THINKING_OPEN_PREFIX
|
|
247
280
|
):
|
|
@@ -249,11 +282,18 @@ def has_unclosed_entml_thinking(text: str) -> bool:
|
|
|
249
282
|
return False
|
|
250
283
|
|
|
251
284
|
|
|
252
|
-
def invoke_index_inside_unclosed_thinking(
|
|
285
|
+
def invoke_index_inside_unclosed_thinking(
|
|
286
|
+
text: str,
|
|
287
|
+
invoke_at: int,
|
|
288
|
+
*,
|
|
289
|
+
thinking_enabled: bool = True,
|
|
290
|
+
) -> bool:
|
|
253
291
|
"""``invoke_at`` 是否落在尚未闭合的 thinking 块内。"""
|
|
254
292
|
if invoke_at < 0:
|
|
255
293
|
return False
|
|
256
|
-
think_open, opened_plain = _find_earliest_thinking_open(
|
|
294
|
+
think_open, opened_plain = _find_earliest_thinking_open(
|
|
295
|
+
text, thinking_enabled=thinking_enabled
|
|
296
|
+
)
|
|
257
297
|
if think_open < 0 or invoke_at <= think_open:
|
|
258
298
|
return False
|
|
259
299
|
gt = text.find(">", think_open)
|
|
@@ -261,14 +301,21 @@ def invoke_index_inside_unclosed_thinking(text: str, invoke_at: int) -> bool:
|
|
|
261
301
|
return True
|
|
262
302
|
body_start = gt + 1
|
|
263
303
|
close_at, close_len = _find_thinking_close(
|
|
264
|
-
text,
|
|
304
|
+
text,
|
|
305
|
+
body_start,
|
|
306
|
+
opened_plain=opened_plain,
|
|
307
|
+
thinking_enabled=thinking_enabled,
|
|
265
308
|
)
|
|
266
309
|
if close_at < 0:
|
|
267
310
|
return True
|
|
268
311
|
return invoke_at < close_at + close_len
|
|
269
312
|
|
|
270
313
|
|
|
271
|
-
def split_entml_thinking(
|
|
314
|
+
def split_entml_thinking(
|
|
315
|
+
text: str,
|
|
316
|
+
*,
|
|
317
|
+
thinking_enabled: bool = True,
|
|
318
|
+
) -> Tuple[str, str]:
|
|
272
319
|
"""从文本中剥离 thinking 块,返回 (正文, 思考链拼接)。"""
|
|
273
320
|
if not text:
|
|
274
321
|
return "", ""
|
|
@@ -277,7 +324,9 @@ def split_entml_thinking(text: str) -> Tuple[str, str]:
|
|
|
277
324
|
clean_parts: List[str] = []
|
|
278
325
|
i = 0
|
|
279
326
|
while i < len(text):
|
|
280
|
-
open_at, opened_plain = _find_earliest_thinking_open(
|
|
327
|
+
open_at, opened_plain = _find_earliest_thinking_open(
|
|
328
|
+
text, i, thinking_enabled=thinking_enabled
|
|
329
|
+
)
|
|
281
330
|
if open_at < 0:
|
|
282
331
|
clean_parts.append(text[i:])
|
|
283
332
|
break
|
|
@@ -290,7 +339,10 @@ def split_entml_thinking(text: str) -> Tuple[str, str]:
|
|
|
290
339
|
|
|
291
340
|
body_start = gt + 1
|
|
292
341
|
close_at, close_len = _find_thinking_close(
|
|
293
|
-
text,
|
|
342
|
+
text,
|
|
343
|
+
body_start,
|
|
344
|
+
opened_plain=opened_plain,
|
|
345
|
+
thinking_enabled=thinking_enabled,
|
|
294
346
|
)
|
|
295
347
|
if close_at < 0:
|
|
296
348
|
clean_parts.append(text[open_at:])
|
|
@@ -336,7 +388,8 @@ class EntmlThinkingStreamFilter:
|
|
|
336
388
|
块内正文在收到时即增量输出 thinking,无需等闭合标签。
|
|
337
389
|
"""
|
|
338
390
|
|
|
339
|
-
def __init__(self) -> None:
|
|
391
|
+
def __init__(self, *, thinking_enabled: bool = True) -> None:
|
|
392
|
+
self._thinking_enabled = thinking_enabled
|
|
340
393
|
self._pending = ""
|
|
341
394
|
self._in_block = False
|
|
342
395
|
self._opened_plain = False
|
|
@@ -358,12 +411,16 @@ class EntmlThinkingStreamFilter:
|
|
|
358
411
|
"""思考块已开始且尚未闭合(含开/闭标签分片 hold)。"""
|
|
359
412
|
if self._in_block or self._fault_watch:
|
|
360
413
|
return True
|
|
361
|
-
open_at, _ = _find_earliest_thinking_open(
|
|
414
|
+
open_at, _ = _find_earliest_thinking_open(
|
|
415
|
+
self._pending, thinking_enabled=self._thinking_enabled
|
|
416
|
+
)
|
|
362
417
|
if open_at >= 0:
|
|
363
418
|
gt = self._pending.find(">", open_at)
|
|
364
419
|
if gt < 0:
|
|
365
420
|
return True
|
|
366
|
-
_, hold = _hold_thinking_open_prefixes(
|
|
421
|
+
_, hold = _hold_thinking_open_prefixes(
|
|
422
|
+
self._pending, thinking_enabled=self._thinking_enabled
|
|
423
|
+
)
|
|
367
424
|
if hold:
|
|
368
425
|
return True
|
|
369
426
|
return False
|
|
@@ -474,7 +531,7 @@ class EntmlThinkingStreamFilter:
|
|
|
474
531
|
return True
|
|
475
532
|
|
|
476
533
|
fault_at = self._pending.find(_FAULT_THINKING_CLOSE)
|
|
477
|
-
if fault_at >= 0:
|
|
534
|
+
if fault_at >= 0 and self._thinking_enabled:
|
|
478
535
|
before = self._pending[:fault_at]
|
|
479
536
|
if self._opened_plain:
|
|
480
537
|
emitted = self._emit_thinking_piece(before)
|
|
@@ -505,7 +562,7 @@ class EntmlThinkingStreamFilter:
|
|
|
505
562
|
return False
|
|
506
563
|
|
|
507
564
|
safe, hold = _hold_prefix(self._pending, _THINKING_CLOSE)
|
|
508
|
-
if not hold:
|
|
565
|
+
if not hold and self._thinking_enabled:
|
|
509
566
|
safe, hold = _hold_prefix(safe, _FAULT_THINKING_CLOSE)
|
|
510
567
|
if safe:
|
|
511
568
|
emitted = self._emit_thinking_piece(safe)
|
|
@@ -516,9 +573,13 @@ class EntmlThinkingStreamFilter:
|
|
|
516
573
|
|
|
517
574
|
def _feed_before_block(self, out: List[Tuple[str, str]]) -> bool:
|
|
518
575
|
"""开标签之前或尚未进入块;返回 False 表示应退出 feed 主循环。"""
|
|
519
|
-
open_at, opened_plain = _find_earliest_thinking_open(
|
|
576
|
+
open_at, opened_plain = _find_earliest_thinking_open(
|
|
577
|
+
self._pending, thinking_enabled=self._thinking_enabled
|
|
578
|
+
)
|
|
520
579
|
if open_at < 0:
|
|
521
|
-
safe, self._pending = _hold_thinking_open_prefixes(
|
|
580
|
+
safe, self._pending = _hold_thinking_open_prefixes(
|
|
581
|
+
self._pending, thinking_enabled=self._thinking_enabled
|
|
582
|
+
)
|
|
522
583
|
if safe:
|
|
523
584
|
out.append(("content", safe))
|
|
524
585
|
return False
|
|
@@ -598,7 +659,9 @@ class EntmlThinkingStreamFilter:
|
|
|
598
659
|
self._opened_plain = False
|
|
599
660
|
self._thinking_started = False
|
|
600
661
|
if self._pending:
|
|
601
|
-
content, thinking = split_entml_thinking(
|
|
662
|
+
content, thinking = split_entml_thinking(
|
|
663
|
+
self._pending, thinking_enabled=self._thinking_enabled
|
|
664
|
+
)
|
|
602
665
|
if thinking:
|
|
603
666
|
out.append(("thinking", thinking))
|
|
604
667
|
if content:
|
|
@@ -132,3 +132,22 @@ def test_plain_thinking_stream_fault_close() -> None:
|
|
|
132
132
|
assert thinking.strip() == "plan"
|
|
133
133
|
assert "hello" in content
|
|
134
134
|
assert not filt.in_open_thinking()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_is_thinking_enabled() -> None:
|
|
138
|
+
from echotools.exec.fncall.protocols.entml_think.core import is_thinking_enabled
|
|
139
|
+
|
|
140
|
+
assert is_thinking_enabled(None)
|
|
141
|
+
assert is_thinking_enabled({"thinking_mode": "on"})
|
|
142
|
+
assert is_thinking_enabled({"thinking_mode": "auto"})
|
|
143
|
+
assert not is_thinking_enabled({"thinking_mode": "off"})
|
|
144
|
+
assert not is_thinking_enabled({"thinking_level": "none"})
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_plain_thinking_requires_thinking_enabled_batch() -> None:
|
|
148
|
+
text = "<thinking>\nplan\n</thinking>\nanswer"
|
|
149
|
+
_, thinking_on = split_entml_thinking(text, thinking_enabled=True)
|
|
150
|
+
content_off, thinking_off = split_entml_thinking(text, thinking_enabled=False)
|
|
151
|
+
assert thinking_on == "plan"
|
|
152
|
+
assert thinking_off == ""
|
|
153
|
+
assert "<thinking>" in content_off
|
|
@@ -328,3 +328,35 @@ def test_entml_open_fault_close_then_invoke_still_works(chunk: int = 3) -> None:
|
|
|
328
328
|
clean, calls = parser.finalize()
|
|
329
329
|
assert len(calls) == 1
|
|
330
330
|
assert "plan" in parser.partial_thinking
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@pytest.mark.parametrize("chunk", [1, 5, 17])
|
|
334
|
+
def test_thinking_disabled_plain_open_stays_visible(chunk: int) -> None:
|
|
335
|
+
"""未开思考时 plain ``<thinking>`` 不得进入思考链。"""
|
|
336
|
+
parser = FncallStreamParser(
|
|
337
|
+
protocol=get_protocol("entml"),
|
|
338
|
+
tools=TOOLS,
|
|
339
|
+
protocol_options={"thinking_mode": "off"},
|
|
340
|
+
)
|
|
341
|
+
text = "<thinking>\nplan\n</thinking>\nvisible\n"
|
|
342
|
+
for i in range(0, len(text), chunk):
|
|
343
|
+
parser.feed(text[i : i + chunk])
|
|
344
|
+
clean, calls = parser.finalize()
|
|
345
|
+
assert not calls
|
|
346
|
+
assert not parser.partial_thinking
|
|
347
|
+
assert "<thinking>" in parser.partial_text or "plan" in parser.partial_text
|
|
348
|
+
assert "visible" in clean
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def test_thinking_disabled_fault_close_not_applied() -> None:
|
|
352
|
+
"""未开思考时 ``</thinking>`` 不得作为 entml 块的 fault 闭合。"""
|
|
353
|
+
parser = FncallStreamParser(
|
|
354
|
+
protocol=get_protocol("entml"),
|
|
355
|
+
tools=TOOLS,
|
|
356
|
+
protocol_options={"thinking_mode": "off"},
|
|
357
|
+
)
|
|
358
|
+
text = f"<entml:thinking>\nplan\n</thinking>\n{INVOKE}"
|
|
359
|
+
parser.feed(text)
|
|
360
|
+
clean, calls = parser.finalize()
|
|
361
|
+
assert len(calls) == 1
|
|
362
|
+
assert "plan" not in parser.partial_thinking or "</thinking>" in parser.partial_thinking
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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.61 → echotools-2.3.62}/src/echotools/exec/fncall/protocols/entml_stream_json.py
RENAMED
|
File without changes
|
{echotools-2.3.61 → echotools-2.3.62}/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
|