echotools 2.3.43__tar.gz → 2.3.45__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.43 → echotools-2.3.45}/PKG-INFO +1 -1
- {echotools-2.3.43 → echotools-2.3.45}/pyproject.toml +2 -1
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/parsers/stream.py +64 -42
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/prompt/inject.py +3 -1
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml.py +36 -12
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_invoke.py +13 -4
- echotools-2.3.45/src/echotools/exec/fncall/protocols/entml_patterns.py +113 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_think/parse.py +15 -2
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/shared/coercion.py +5 -3
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools.egg-info/SOURCES.txt +8 -1
- echotools-2.3.45/src/tests/fixtures/__init__.py +0 -0
- echotools-2.3.45/src/tests/fixtures/simulated_llm_tool_responses.py +450 -0
- echotools-2.3.45/src/tests/test_adversarial_stream_splits.py +145 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_entml_protocol.py +3 -1
- echotools-2.3.45/src/tests/test_entml_tool_parse_leak.py +256 -0
- echotools-2.3.45/src/tests/test_entml_tool_pipeline.py +813 -0
- echotools-2.3.45/src/tests/test_simulated_llm_tool_parse.py +166 -0
- echotools-2.3.45/src/tests/test_stream_realtime_parse.py +137 -0
- echotools-2.3.43/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -51
- {echotools-2.3.43 → echotools-2.3.45}/README.md +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/docs/README.md +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/setup.cfg +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/compat.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_tools.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_values.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/fonts/tailwind.css +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools/version.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/bench_import.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/conftest.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_completion.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_config.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_console.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_errors.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_events.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_facade.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_ids.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_imports.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_io.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_keys.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_logger.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_merge.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_usage.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_web.py +0 -0
- {echotools-2.3.43 → echotools-2.3.45}/src/tests/test_web_broker.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "echotools"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.45"
|
|
8
8
|
description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
|
|
9
9
|
readme = "docs/README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -103,6 +103,7 @@ ignore_errors = true
|
|
|
103
103
|
|
|
104
104
|
[tool.pytest.ini_options]
|
|
105
105
|
testpaths = ["src/tests"]
|
|
106
|
+
pythonpath = ["src", "src/tests"]
|
|
106
107
|
asyncio_mode = "auto"
|
|
107
108
|
addopts = ["--strict-markers", "--tb=short"]
|
|
108
109
|
markers = [
|
|
@@ -176,37 +176,40 @@ class FncallStreamParser:
|
|
|
176
176
|
combined = self._waiting_tail + chunk
|
|
177
177
|
self._waiting_tail = ""
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
self._thinking_filter.in_open_thinking()
|
|
185
|
-
or has_unclosed_entml_thinking(combined)
|
|
186
|
-
):
|
|
187
|
-
self._feed_waiting_thinking_plain(combined)
|
|
188
|
-
return
|
|
179
|
+
# 已进入 thinking 块时,块内不做 invoke 检测。
|
|
180
|
+
if self._thinking_filter is not None and self._thinking_filter.in_open_thinking():
|
|
181
|
+
self._feed_waiting_thinking_plain(combined)
|
|
182
|
+
return
|
|
189
183
|
|
|
190
184
|
trigger_tags = self._protocol.get_trigger_tags()
|
|
191
185
|
found, pos = self._protocol.detect_start(combined)
|
|
192
186
|
|
|
193
|
-
if
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
self._emit_text(
|
|
197
|
-
self.
|
|
187
|
+
if found:
|
|
188
|
+
if pos > 0:
|
|
189
|
+
# 工具标签前的正文仍可能含 thinking,走过滤器。
|
|
190
|
+
self._emit_text(combined[:pos])
|
|
191
|
+
self._fncall_buf = combined[pos:]
|
|
192
|
+
self._detected = True
|
|
193
|
+
self._state = self.IN_FUNCTION_CALLS
|
|
194
|
+
if self._is_call_closed():
|
|
195
|
+
self._state = self.DONE
|
|
198
196
|
return
|
|
199
197
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
self.
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
# 无完整工具开标签时,再处理未闭合 thinking / 思考开标签 holdback。
|
|
199
|
+
# 必须放在 detect_start 之后:否则 `<entml:function_calls>\n<en` 会被
|
|
200
|
+
# 尾部 `<en` 误判为 thinking 前缀,导致 invoke 内容被截断。
|
|
201
|
+
if self._thinking_filter is not None:
|
|
202
|
+
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
203
|
+
has_unclosed_entml_thinking,
|
|
204
|
+
)
|
|
205
|
+
if has_unclosed_entml_thinking(combined):
|
|
206
|
+
self._feed_waiting_thinking_plain(combined)
|
|
207
|
+
return
|
|
206
208
|
|
|
207
|
-
|
|
208
|
-
if
|
|
209
|
-
self.
|
|
209
|
+
safe, remain = self._split_safe_text(combined, trigger_tags)
|
|
210
|
+
if safe:
|
|
211
|
+
self._emit_text(safe)
|
|
212
|
+
self._waiting_tail = remain
|
|
210
213
|
|
|
211
214
|
def _is_call_closed(self) -> bool:
|
|
212
215
|
"""检测 fncall 缓冲区中是否包含结束标记。"""
|
|
@@ -218,12 +221,15 @@ class FncallStreamParser:
|
|
|
218
221
|
return True
|
|
219
222
|
return False
|
|
220
223
|
|
|
221
|
-
def feed(self, chunk: str) ->
|
|
222
|
-
"""
|
|
224
|
+
def feed(self, chunk: str) -> List[Dict[str, Any]]:
|
|
225
|
+
"""喂入新的流式文本块,返回本轮新完成的 tool_calls(可空列表)。
|
|
226
|
+
|
|
227
|
+
DONE 或 finalize 后调用静默忽略并返回 ``[]``。
|
|
228
|
+
"""
|
|
223
229
|
if not chunk or self._state == self.DONE:
|
|
224
|
-
return
|
|
230
|
+
return []
|
|
225
231
|
if self._finalized_result is not None:
|
|
226
|
-
return
|
|
232
|
+
return []
|
|
227
233
|
|
|
228
234
|
self._raw_buf += chunk
|
|
229
235
|
|
|
@@ -234,6 +240,12 @@ class FncallStreamParser:
|
|
|
234
240
|
if self._is_call_closed():
|
|
235
241
|
self._state = self.DONE
|
|
236
242
|
|
|
243
|
+
return self.get_ready_tool_calls()
|
|
244
|
+
|
|
245
|
+
def _assembly_for_tool_parse(self) -> str:
|
|
246
|
+
"""可供工具解析的已缓冲文本(不含 thinking 过滤器内部 pending)。"""
|
|
247
|
+
return "".join(self._text_parts) + self._waiting_tail + self._fncall_buf
|
|
248
|
+
|
|
237
249
|
def finalize(self) -> Tuple[str, List[Dict[str, Any]]]:
|
|
238
250
|
"""结束流式解析,返回 (清理后文本, tool_calls 列表)。幂等。"""
|
|
239
251
|
if self._finalized_result is not None:
|
|
@@ -253,12 +265,18 @@ class FncallStreamParser:
|
|
|
253
265
|
else:
|
|
254
266
|
self._text_parts.append(part)
|
|
255
267
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
268
|
+
# 统一从「可见正文 + fncall 缓冲」解析,避免漏检;thinking 已在过滤器中剥离
|
|
269
|
+
assembled = "".join(self._text_parts) + self._fncall_buf
|
|
270
|
+
clean_text, tool_calls = self._protocol.parse(assembled, self._tools)
|
|
271
|
+
|
|
272
|
+
clean_fn = getattr(self._protocol, "clean_tool_tags", None)
|
|
273
|
+
if callable(clean_fn):
|
|
274
|
+
clean_text = clean_fn(clean_text)
|
|
275
|
+
elif getattr(self._protocol, "id", None) == "entml":
|
|
276
|
+
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
277
|
+
strip_tool_entml_residue,
|
|
278
|
+
)
|
|
279
|
+
clean_text = strip_tool_entml_residue(clean_text)
|
|
262
280
|
|
|
263
281
|
# 兜底:剥离残留 <entml:thinking>(holdback 边界/分片异常时)
|
|
264
282
|
if self._thinking_filter is not None and clean_text:
|
|
@@ -298,16 +316,20 @@ class FncallStreamParser:
|
|
|
298
316
|
def get_ready_tool_calls(self) -> List[Dict[str, Any]]:
|
|
299
317
|
"""返回新完成的 tool_calls(增量,每次调用只返回上次调用之后新增的)。
|
|
300
318
|
|
|
301
|
-
|
|
319
|
+
流式阶段可在每次 ``feed`` 后调用,无需等待整个 buffer 结束。
|
|
302
320
|
"""
|
|
303
|
-
if self.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
321
|
+
if self._finalized_result is not None:
|
|
322
|
+
_, all_calls = self._finalized_result
|
|
323
|
+
else:
|
|
324
|
+
try:
|
|
325
|
+
_, all_calls = self._protocol.parse(
|
|
326
|
+
self._assembly_for_tool_parse(),
|
|
327
|
+
self._tools,
|
|
328
|
+
)
|
|
329
|
+
except Exception:
|
|
330
|
+
return []
|
|
309
331
|
if len(all_calls) <= self._emitted_invoke_count:
|
|
310
332
|
return []
|
|
311
|
-
new_calls = all_calls[self._emitted_invoke_count:]
|
|
333
|
+
new_calls = all_calls[self._emitted_invoke_count :]
|
|
312
334
|
self._emitted_invoke_count = len(all_calls)
|
|
313
335
|
return new_calls
|
|
@@ -22,7 +22,9 @@ from echotools.exec.fncall.protocols.entml import (
|
|
|
22
22
|
format_entml_conversation_history,
|
|
23
23
|
format_entml_current_user_message,
|
|
24
24
|
)
|
|
25
|
-
from echotools.exec.fncall.protocols.entml_think.core import
|
|
25
|
+
from echotools.exec.fncall.protocols.entml_think.core import (
|
|
26
|
+
build_entml_thinking_section,
|
|
27
|
+
)
|
|
26
28
|
from echotools.exec.fncall.protocols.entml_think.hist import (
|
|
27
29
|
apply_thinking_history_policy,
|
|
28
30
|
parse_include_thinking_in_history,
|
|
@@ -18,8 +18,13 @@ from echotools.exec.fncall.prompt.templates import (
|
|
|
18
18
|
from echotools.exec.fncall.protocols.entml_invoke import (
|
|
19
19
|
parse_entml_tool_calls,
|
|
20
20
|
)
|
|
21
|
-
from echotools.exec.fncall.protocols.entml_patterns import
|
|
22
|
-
|
|
21
|
+
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
22
|
+
BLOCK_RE,
|
|
23
|
+
strip_tool_entml_residue,
|
|
24
|
+
)
|
|
25
|
+
from echotools.exec.fncall.protocols.entml_think.core import (
|
|
26
|
+
build_entml_thinking_section,
|
|
27
|
+
)
|
|
23
28
|
from echotools.exec.fncall.protocols.entml_tools import format_entml_tool_descs
|
|
24
29
|
from echotools.exec.fncall.shared.coercion import _build_param_schema_index
|
|
25
30
|
from echotools.exec.fncall.shared.normalization import (
|
|
@@ -131,10 +136,19 @@ class EntmlProtocol(ToolProtocol):
|
|
|
131
136
|
|
|
132
137
|
_TRIGGER = "<entml:invoke>"
|
|
133
138
|
_TRIGGER_PREFIX = "<entml:invoke"
|
|
139
|
+
_WRAPPER_PREFIX = "<entml:function_calls"
|
|
140
|
+
_THINKING_PREFIX = "<entml:thinking"
|
|
134
141
|
|
|
135
142
|
def get_trigger_tags(self) -> List[str]:
|
|
136
|
-
#
|
|
137
|
-
return [
|
|
143
|
+
# invoke / function_calls / thinking 前缀一并 holdback,避免 `<e` 歧义被提前吐出
|
|
144
|
+
return [
|
|
145
|
+
self._TRIGGER,
|
|
146
|
+
self._TRIGGER_PREFIX,
|
|
147
|
+
self._WRAPPER_PREFIX,
|
|
148
|
+
f"{self._WRAPPER_PREFIX}>",
|
|
149
|
+
self._THINKING_PREFIX,
|
|
150
|
+
f"{self._THINKING_PREFIX}>",
|
|
151
|
+
]
|
|
138
152
|
|
|
139
153
|
def get_stream_end_tags(self) -> List[str]:
|
|
140
154
|
"""新格式无外层 wrapper,不自动关闭流,由 finalize() 统一解析。"""
|
|
@@ -187,14 +201,18 @@ class EntmlProtocol(ToolProtocol):
|
|
|
187
201
|
return "\n\n".join(sections)
|
|
188
202
|
|
|
189
203
|
def detect_start(self, buffer: str) -> Tuple[bool, int]:
|
|
190
|
-
|
|
191
|
-
|
|
204
|
+
candidates: List[int] = []
|
|
205
|
+
for prefix in (self._WRAPPER_PREFIX, self._TRIGGER_PREFIX):
|
|
206
|
+
pos = buffer.find(prefix)
|
|
207
|
+
if pos < 0:
|
|
208
|
+
continue
|
|
209
|
+
close = buffer.find(">", pos + len(prefix))
|
|
210
|
+
if close < 0:
|
|
211
|
+
continue
|
|
212
|
+
candidates.append(pos)
|
|
213
|
+
if not candidates:
|
|
192
214
|
return (False, -1)
|
|
193
|
-
|
|
194
|
-
close = buffer.find(">", pos + len(self._TRIGGER_PREFIX))
|
|
195
|
-
if close < 0:
|
|
196
|
-
return (False, -1)
|
|
197
|
-
return (True, pos)
|
|
215
|
+
return (True, min(candidates))
|
|
198
216
|
|
|
199
217
|
def parse(
|
|
200
218
|
self,
|
|
@@ -205,7 +223,9 @@ class EntmlProtocol(ToolProtocol):
|
|
|
205
223
|
tool_calls = parse_entml_tool_calls(text, tools, schema_index)
|
|
206
224
|
clean = text
|
|
207
225
|
if tool_calls:
|
|
208
|
-
clean = BLOCK_RE.sub("", text)
|
|
226
|
+
clean = BLOCK_RE.sub("", text)
|
|
227
|
+
# 无论是否解析成功,都剥离工具相关残留,避免标签泄露;thinking 保留给后续 split。
|
|
228
|
+
clean = strip_tool_entml_residue(clean)
|
|
209
229
|
return (clean, normalize_tool_calls(tool_calls, tools))
|
|
210
230
|
|
|
211
231
|
def parse_fragment(
|
|
@@ -219,6 +239,10 @@ class EntmlProtocol(ToolProtocol):
|
|
|
219
239
|
def clean_tags(self, content: str) -> str:
|
|
220
240
|
return strip_entml_from_content(content)
|
|
221
241
|
|
|
242
|
+
def clean_tool_tags(self, content: str) -> str:
|
|
243
|
+
"""仅剥离工具相关标签残留,保留 thinking。"""
|
|
244
|
+
return strip_tool_entml_residue(content)
|
|
245
|
+
|
|
222
246
|
def format_assistant_tool_calls(
|
|
223
247
|
self,
|
|
224
248
|
tool_calls: List[Dict[str, Any]],
|
|
@@ -7,7 +7,9 @@ from .entml_patterns import (
|
|
|
7
7
|
INVOKE_RE,
|
|
8
8
|
PARAM_RE,
|
|
9
9
|
PARAMETERS_RE,
|
|
10
|
+
extract_attr_value,
|
|
10
11
|
extract_parameter_type_attr,
|
|
12
|
+
normalize_entml_name,
|
|
11
13
|
parse_sub_tags,
|
|
12
14
|
)
|
|
13
15
|
from .entml_values import coerce_entml_arguments, coerce_entml_parameter_value
|
|
@@ -36,9 +38,12 @@ def parse_invoke_args(
|
|
|
36
38
|
|
|
37
39
|
args: Dict[str, Any] = {}
|
|
38
40
|
for param_m in PARAM_RE.finditer(body):
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
attrs = param_m.group(1) or ""
|
|
42
|
+
pname = extract_attr_value(attrs, "name")
|
|
43
|
+
if not pname:
|
|
44
|
+
continue
|
|
45
|
+
pname = normalize_entml_name(pname)
|
|
46
|
+
pval = (param_m.group(2) or "").strip()
|
|
42
47
|
type_hint = extract_parameter_type_attr(attrs)
|
|
43
48
|
pschema = func_props.get(pname) or {}
|
|
44
49
|
args[pname] = coerce_entml_parameter_value(
|
|
@@ -57,7 +62,11 @@ def parse_entml_tool_calls(
|
|
|
57
62
|
) -> List[Dict[str, Any]]:
|
|
58
63
|
tool_calls: List[Dict[str, Any]] = []
|
|
59
64
|
for invoke_m in INVOKE_RE.finditer(text):
|
|
60
|
-
|
|
65
|
+
attrs = invoke_m.group(1) or ""
|
|
66
|
+
name = extract_attr_value(attrs, "name")
|
|
67
|
+
if not name:
|
|
68
|
+
continue
|
|
69
|
+
name = normalize_entml_name(name)
|
|
61
70
|
args = parse_invoke_args(invoke_m.group(2), name, schema_index)
|
|
62
71
|
arguments = json.dumps(args, ensure_ascii=False)
|
|
63
72
|
tool_calls.append(
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
|
|
6
|
+
from .entml_values import coerce_entml_parameter_value
|
|
7
|
+
|
|
8
|
+
BLOCK_RE = re.compile(
|
|
9
|
+
r"<entml:invoke\b[^>]*>[\s\S]*?</entml:invoke>",
|
|
10
|
+
re.DOTALL,
|
|
11
|
+
)
|
|
12
|
+
# 允许 name 前后有其它属性;单/双引号均可。
|
|
13
|
+
INVOKE_RE = re.compile(
|
|
14
|
+
r"<entml:invoke\b([^>]*)>([\s\S]*?)</entml:invoke>",
|
|
15
|
+
re.DOTALL,
|
|
16
|
+
)
|
|
17
|
+
# 允许 name / type 等属性任意顺序;单/双引号均可。
|
|
18
|
+
PARAM_RE = re.compile(
|
|
19
|
+
r"<entml:parameter\b([^>]*)>([\s\S]*?)</entml:parameter>",
|
|
20
|
+
re.DOTALL,
|
|
21
|
+
)
|
|
22
|
+
_ATTR_NAME_RE = re.compile(
|
|
23
|
+
r"""\bname\s*=\s*(?P<q>["'])(?P<v>.*?)(?P=q)""",
|
|
24
|
+
re.DOTALL,
|
|
25
|
+
)
|
|
26
|
+
_PARAM_TYPE_ATTR_RE = re.compile(
|
|
27
|
+
r"""\btype\s*=\s*(?P<q>["'])(?P<v>.*?)(?P=q)"""
|
|
28
|
+
)
|
|
29
|
+
PARAMETERS_RE = re.compile(
|
|
30
|
+
r"<entml:parameters>([\s\S]*?)</entml:parameters>",
|
|
31
|
+
re.DOTALL,
|
|
32
|
+
)
|
|
33
|
+
SUB_TAG_RE = re.compile(
|
|
34
|
+
r"<([^>]+)>([\s\S]*?)</\1>",
|
|
35
|
+
re.DOTALL,
|
|
36
|
+
)
|
|
37
|
+
# 工具相关外壳 / 残留(不含 thinking,留给 split_entml_thinking)。
|
|
38
|
+
_TOOL_WRAPPER_PAIR_RE = re.compile(
|
|
39
|
+
r"<entml:function_calls\b[^>]*>[\s\S]*?</entml:function_calls>",
|
|
40
|
+
re.DOTALL,
|
|
41
|
+
)
|
|
42
|
+
_TOOL_ORPHAN_TAG_RE = re.compile(
|
|
43
|
+
r"</?entml:(?:function_calls|invoke|parameter|parameters)\b[^>]*/?>",
|
|
44
|
+
re.DOTALL,
|
|
45
|
+
)
|
|
46
|
+
_EMPTY_FENCE_RE = re.compile(
|
|
47
|
+
r"```(?:xml|entml|text)?\s*```",
|
|
48
|
+
re.IGNORECASE,
|
|
49
|
+
)
|
|
50
|
+
_FENCE_ONLY_LINE_RE = re.compile(
|
|
51
|
+
r"^\s*```(?:xml|entml|text)?\s*$",
|
|
52
|
+
re.IGNORECASE | re.MULTILINE,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def extract_attr_value(attrs: str, attr_name: str = "name") -> Optional[str]:
|
|
57
|
+
"""从标签属性串中提取 name/type 等(支持单双引号)。"""
|
|
58
|
+
if not attrs:
|
|
59
|
+
return None
|
|
60
|
+
if attr_name == "name":
|
|
61
|
+
match = _ATTR_NAME_RE.search(attrs)
|
|
62
|
+
elif attr_name == "type":
|
|
63
|
+
match = _PARAM_TYPE_ATTR_RE.search(attrs)
|
|
64
|
+
else:
|
|
65
|
+
pattern = re.compile(
|
|
66
|
+
rf"""\b{re.escape(attr_name)}\s*=\s*(?P<q>["'])(?P<v>.*?)(?P=q)"""
|
|
67
|
+
)
|
|
68
|
+
match = pattern.search(attrs)
|
|
69
|
+
if not match:
|
|
70
|
+
return None
|
|
71
|
+
return match.group("v").strip()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def normalize_entml_name(name: str) -> str:
|
|
75
|
+
"""还原 markdown 转义下划线等常见名称噪声。"""
|
|
76
|
+
if not name:
|
|
77
|
+
return ""
|
|
78
|
+
return name.replace("\\_", "_").replace("\\-", "-").strip()
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def extract_parameter_type_attr(attrs: str) -> Optional[str]:
|
|
82
|
+
"""从 parameter 开标签属性中提取 type=\"...\"。"""
|
|
83
|
+
return extract_attr_value(attrs or "", "type")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def parse_sub_tags(
|
|
87
|
+
content: str,
|
|
88
|
+
schema_index: Optional[Dict[str, Any]] = None,
|
|
89
|
+
func_name: str = "",
|
|
90
|
+
) -> Dict[str, Any]:
|
|
91
|
+
"""解析 <entml:parameters> 内的子标签,返回参数字典。"""
|
|
92
|
+
args: Dict[str, Any] = {}
|
|
93
|
+
for m in SUB_TAG_RE.finditer(content):
|
|
94
|
+
pname = m.group(1).strip()
|
|
95
|
+
pval = m.group(2).strip()
|
|
96
|
+
pschema = schema_index.get(func_name, {}).get(pname, {}) if schema_index else {}
|
|
97
|
+
args[pname] = coerce_entml_parameter_value(pval, pschema or None)
|
|
98
|
+
return args
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def strip_tool_entml_residue(content: str) -> str:
|
|
102
|
+
"""剥离工具相关 entml 标签残留,保留 thinking 等非工具标签。"""
|
|
103
|
+
if not content:
|
|
104
|
+
return content
|
|
105
|
+
cleaned = _TOOL_WRAPPER_PAIR_RE.sub("", content)
|
|
106
|
+
cleaned = BLOCK_RE.sub("", cleaned)
|
|
107
|
+
cleaned = _TOOL_ORPHAN_TAG_RE.sub("", cleaned)
|
|
108
|
+
cleaned = _EMPTY_FENCE_RE.sub("", cleaned)
|
|
109
|
+
cleaned = _FENCE_ONLY_LINE_RE.sub("", cleaned)
|
|
110
|
+
# 折叠因剥离产生的多余空行
|
|
111
|
+
cleaned = re.sub(r"[ \t]+\n", "\n", cleaned)
|
|
112
|
+
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
|
|
113
|
+
return cleaned.strip()
|
{echotools-2.3.43 → echotools-2.3.45}/src/echotools/exec/fncall/protocols/entml_think/parse.py
RENAMED
|
@@ -10,6 +10,13 @@ THINKING_BLOCK_RE = re.compile(
|
|
|
10
10
|
|
|
11
11
|
_THINKING_OPEN_PREFIX = "<entml:thinking"
|
|
12
12
|
_THINKING_CLOSE = "</entml:thinking>"
|
|
13
|
+
# 与 thinking 共享 `<entml:` 前缀的其它标签:歧义 holdback 应交由工具流式状态机处理
|
|
14
|
+
_AMBIGUOUS_ENTML_PREFIXES = (
|
|
15
|
+
"<entml:invoke",
|
|
16
|
+
"<entml:function_calls",
|
|
17
|
+
"<entml:parameter",
|
|
18
|
+
"<entml:parameters",
|
|
19
|
+
)
|
|
13
20
|
|
|
14
21
|
|
|
15
22
|
def has_unclosed_entml_thinking(text: str) -> bool:
|
|
@@ -32,8 +39,14 @@ def has_unclosed_entml_thinking(text: str) -> bool:
|
|
|
32
39
|
check_len = min(len(text), max_keep)
|
|
33
40
|
for length in range(check_len, 0, -1):
|
|
34
41
|
suffix = text[-length:]
|
|
35
|
-
if
|
|
36
|
-
|
|
42
|
+
if not (
|
|
43
|
+
_THINKING_OPEN_PREFIX.startswith(suffix) and suffix != _THINKING_OPEN_PREFIX
|
|
44
|
+
):
|
|
45
|
+
continue
|
|
46
|
+
# `<e` / `<entml:` 等对 invoke 同样是真前缀 → 不在此抢占
|
|
47
|
+
if any(other.startswith(suffix) for other in _AMBIGUOUS_ENTML_PREFIXES):
|
|
48
|
+
continue
|
|
49
|
+
return True
|
|
37
50
|
return False
|
|
38
51
|
|
|
39
52
|
|
|
@@ -257,9 +257,11 @@ def _coerce_param_value(raw: str, schema: Dict[str, Any]) -> Any:
|
|
|
257
257
|
return parsed
|
|
258
258
|
|
|
259
259
|
if effective_type == "string":
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
# string 字段保留原文;仅当整段是 JSON 字符串字面量 ("...") 时才解包
|
|
261
|
+
if len(stripped) >= 2 and stripped[0] == '"' and stripped[-1] == '"':
|
|
262
|
+
if isinstance(parsed, str):
|
|
263
|
+
return parsed
|
|
264
|
+
return stripped
|
|
263
265
|
|
|
264
266
|
if effective_type == "integer":
|
|
265
267
|
return _coerce_to_integer(stripped, parsed)
|
|
@@ -148,6 +148,7 @@ src/echotools/plat/sdk/__init__.py
|
|
|
148
148
|
src/echotools/plat/sdk/facade.py
|
|
149
149
|
src/tests/bench_import.py
|
|
150
150
|
src/tests/conftest.py
|
|
151
|
+
src/tests/test_adversarial_stream_splits.py
|
|
151
152
|
src/tests/test_cache_retry.py
|
|
152
153
|
src/tests/test_completion.py
|
|
153
154
|
src/tests/test_config.py
|
|
@@ -160,6 +161,8 @@ src/tests/test_dispatch_race.py
|
|
|
160
161
|
src/tests/test_entml_protocol.py
|
|
161
162
|
src/tests/test_entml_thinking_history.py
|
|
162
163
|
src/tests/test_entml_thinking_parse.py
|
|
164
|
+
src/tests/test_entml_tool_parse_leak.py
|
|
165
|
+
src/tests/test_entml_tool_pipeline.py
|
|
163
166
|
src/tests/test_entml_tools.py
|
|
164
167
|
src/tests/test_errors.py
|
|
165
168
|
src/tests/test_errors_common.py
|
|
@@ -182,7 +185,11 @@ src/tests/test_proxy.py
|
|
|
182
185
|
src/tests/test_proxy_selector.py
|
|
183
186
|
src/tests/test_runtime.py
|
|
184
187
|
src/tests/test_scheduler.py
|
|
188
|
+
src/tests/test_simulated_llm_tool_parse.py
|
|
189
|
+
src/tests/test_stream_realtime_parse.py
|
|
185
190
|
src/tests/test_tracing.py
|
|
186
191
|
src/tests/test_usage.py
|
|
187
192
|
src/tests/test_web.py
|
|
188
|
-
src/tests/test_web_broker.py
|
|
193
|
+
src/tests/test_web_broker.py
|
|
194
|
+
src/tests/fixtures/__init__.py
|
|
195
|
+
src/tests/fixtures/simulated_llm_tool_responses.py
|
|
File without changes
|