echotools 2.3.99__tar.gz → 2.3.100__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.99 → echotools-2.3.100}/PKG-INFO +1 -1
- {echotools-2.3.99 → echotools-2.3.100}/pyproject.toml +8 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/stream.py +4 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/stream_feed.py +0 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/stream_final.py +8 -11
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml.py +2 -6
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_invoke.py +5 -1
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_patterns.py +7 -5
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_schema.py +166 -65
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_stream/snap.py +36 -40
- echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_stream_json.py +460 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/filter.py +2 -11
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/parse.py +2 -3
- echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_tool_blocks.py +362 -0
- echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_tools.py +156 -0
- echotools-2.3.100/src/echotools/exec/fncall/protocols/entml_values.py +134 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools.egg-info/SOURCES.txt +5 -0
- echotools-2.3.100/src/tests/_check_parity.py +77 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/fixtures/simulated_fault_thinking_responses.py +2 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_bare_invoke_format.py +0 -1
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_batch_stream_comprehensive.py +1 -1
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_invoke_known_tool_gate.py +1 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_protocol.py +1 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_tool_pipeline.py +2 -2
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_stream_partial_json_parity.py +113 -2
- {echotools-2.3.99 → echotools-2.3.100}/README.md +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/setup.cfg +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/compat.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/stream_delta.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/parsers/xml_parser.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/behavior_blocks.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/history.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_fake_structure_markup.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_stream/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_stream/body.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/blocks.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_think/hold.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_tool_result_comment.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/history_markup.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/loop_detect.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/shared/xml_helpers.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/ui_text.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools/version.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/bench_import.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/conftest.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_completion.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_config.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_console.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_fake_structure_markup.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_errors.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_events.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_facade.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fake_history_markup_batch.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fake_history_markup_stream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fault_thinking_close_batch.py +5 -5
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fault_thinking_close_stream.py +4 -4
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_history_markup_filter.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_ids.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_imports.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_io.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_keys.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_logger.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_merge.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_printstream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_usage.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/src/tests/test_web.py +0 -0
- {echotools-2.3.99 → echotools-2.3.100}/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.100"
|
|
8
8
|
description = "通用基础设施 SDK:配置、日志、事件、调度、插件、协议、调用链"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -76,7 +76,7 @@ ignore = ["E501", "E402"]
|
|
|
76
76
|
known-first-party = ["echotools"]
|
|
77
77
|
|
|
78
78
|
[tool.mypy]
|
|
79
|
-
python_version = "3.
|
|
79
|
+
python_version = "3.10"
|
|
80
80
|
warn_return_any = true
|
|
81
81
|
warn_unused_ignores = true
|
|
82
82
|
disallow_untyped_defs = false
|
|
@@ -91,6 +91,8 @@ disable_error_code = [
|
|
|
91
91
|
"arg-type",
|
|
92
92
|
"attr-defined",
|
|
93
93
|
"var-annotated",
|
|
94
|
+
"has-type",
|
|
95
|
+
"misc",
|
|
94
96
|
]
|
|
95
97
|
|
|
96
98
|
[[tool.mypy.overrides]]
|
|
@@ -101,6 +103,10 @@ ignore_errors = true
|
|
|
101
103
|
module = "echotools.media.console.*"
|
|
102
104
|
ignore_errors = true
|
|
103
105
|
|
|
106
|
+
[[tool.mypy.overrides]]
|
|
107
|
+
module = "echotools.exec.fncall.parsers.*"
|
|
108
|
+
disable_error_code = ["has-type", "misc", "return-value"]
|
|
109
|
+
|
|
104
110
|
[tool.pytest.ini_options]
|
|
105
111
|
testpaths = ["src/tests"]
|
|
106
112
|
pythonpath = ["src", "src/tests"]
|
|
@@ -23,8 +23,8 @@ def _make_thinking_filter(protocol: ToolProtocol, *, thinking_enabled: bool = Tr
|
|
|
23
23
|
return None
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
from echotools.exec.fncall.parsers.stream_feed import StreamFeedMixin
|
|
27
26
|
from echotools.exec.fncall.parsers.stream_delta import StreamDeltaMixin
|
|
27
|
+
from echotools.exec.fncall.parsers.stream_feed import StreamFeedMixin
|
|
28
28
|
from echotools.exec.fncall.parsers.stream_final import StreamFinalMixin
|
|
29
29
|
|
|
30
30
|
|
|
@@ -101,7 +101,9 @@ class FncallStreamParser(StreamFeedMixin, StreamDeltaMixin, StreamFinalMixin):
|
|
|
101
101
|
return self._schema_index
|
|
102
102
|
|
|
103
103
|
def _get_known_tool_names(self):
|
|
104
|
-
from echotools.exec.fncall.protocols.entml_patterns import
|
|
104
|
+
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
105
|
+
resolve_known_tool_names,
|
|
106
|
+
)
|
|
105
107
|
|
|
106
108
|
return resolve_known_tool_names(self._tools, self._get_schema_index())
|
|
107
109
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""FncallStreamParser finalize / partial display helpers."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from typing import Any, Dict, List,
|
|
4
|
+
from typing import Any, Dict, List, Tuple
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class StreamFinalMixin:
|
|
@@ -13,11 +13,8 @@ class StreamFinalMixin:
|
|
|
13
13
|
"""与 batch ``parse`` 一致:未闭合 thinking 之后的正文不参与可见/剥离。"""
|
|
14
14
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
15
15
|
clean_stream_partial_visible,
|
|
16
|
-
stream_safe_visible_prefix,
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
20
16
|
split_entml_thinking,
|
|
17
|
+
stream_safe_visible_prefix,
|
|
21
18
|
)
|
|
22
19
|
|
|
23
20
|
buf = stream_safe_visible_prefix(
|
|
@@ -34,12 +31,12 @@ class StreamFinalMixin:
|
|
|
34
31
|
|
|
35
32
|
def _stream_display_text(self) -> str:
|
|
36
33
|
"""流式可见正文:在完整 raw 缓冲上剥离伪 history(保留 thinking 保护区)。"""
|
|
37
|
-
from echotools.exec.fncall.shared.history_markup import (
|
|
38
|
-
strip_fake_history_markup_for_display,
|
|
39
|
-
)
|
|
40
34
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
41
35
|
split_entml_thinking,
|
|
42
36
|
)
|
|
37
|
+
from echotools.exec.fncall.shared.history_markup import (
|
|
38
|
+
strip_fake_history_markup_for_display,
|
|
39
|
+
)
|
|
43
40
|
|
|
44
41
|
buf = self._stream_visible_buffer()
|
|
45
42
|
if not buf:
|
|
@@ -52,14 +49,14 @@ class StreamFinalMixin:
|
|
|
52
49
|
|
|
53
50
|
def _stream_partial_display_text(self) -> str:
|
|
54
51
|
"""流式 ``partial_text``:在 invoke 起点前截断 raw,保留尾部 holdback 空白。"""
|
|
55
|
-
from echotools.exec.fncall.shared.history_markup import (
|
|
56
|
-
strip_fake_history_markup_for_display,
|
|
57
|
-
)
|
|
58
52
|
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
59
53
|
clean_stream_partial_visible,
|
|
60
54
|
split_entml_thinking,
|
|
61
55
|
stream_safe_visible_prefix,
|
|
62
56
|
)
|
|
57
|
+
from echotools.exec.fncall.shared.history_markup import (
|
|
58
|
+
strip_fake_history_markup_for_display,
|
|
59
|
+
)
|
|
63
60
|
|
|
64
61
|
raw = self._raw_buf
|
|
65
62
|
if not raw:
|
|
@@ -14,7 +14,6 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
14
14
|
from echotools.exec.fncall.prompt.behavior_blocks import (
|
|
15
15
|
format_function_calling_behavior,
|
|
16
16
|
format_hard_constraint_restatement,
|
|
17
|
-
format_thinking_behavior,
|
|
18
17
|
)
|
|
19
18
|
from echotools.exec.fncall.prompt.templates import (
|
|
20
19
|
_HISTORY_CLARIFY_EN,
|
|
@@ -25,23 +24,20 @@ from echotools.exec.fncall.protocols.entml_invoke import (
|
|
|
25
24
|
)
|
|
26
25
|
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
27
26
|
entml_invoke_open_may_be_streaming,
|
|
28
|
-
extract_attr_value,
|
|
29
27
|
find_actionable_entml_invoke_open,
|
|
30
|
-
normalize_entml_name,
|
|
31
28
|
resolve_known_tool_names,
|
|
32
29
|
strip_actionable_entml_invoke_blocks,
|
|
33
30
|
strip_legacy_function_calls_wrapper,
|
|
34
31
|
strip_tool_entml_residue,
|
|
35
32
|
)
|
|
33
|
+
from echotools.exec.fncall.protocols.entml_schema import format_entml_tool_descs
|
|
36
34
|
from echotools.exec.fncall.protocols.entml_think.core import (
|
|
37
35
|
build_entml_thinking_behavior_section,
|
|
38
36
|
build_entml_thinking_meta_section,
|
|
39
37
|
)
|
|
40
|
-
from echotools.exec.fncall.protocols.entml_schema import format_entml_tool_descs
|
|
41
38
|
from echotools.exec.fncall.shared.coercion import _build_param_schema_index
|
|
42
39
|
from echotools.exec.fncall.shared.history_markup import strip_fake_history_markup
|
|
43
40
|
from echotools.exec.fncall.shared.normalization import (
|
|
44
|
-
normalize_content,
|
|
45
41
|
normalize_tool_calls,
|
|
46
42
|
)
|
|
47
43
|
from echotools.exec.protocol.base import ToolProtocol
|
|
@@ -139,7 +135,7 @@ def format_entml_functions_results(
|
|
|
139
135
|
if not lines:
|
|
140
136
|
return ""
|
|
141
137
|
return (
|
|
142
|
-
|
|
138
|
+
"<entml:funtions_results>\n"
|
|
143
139
|
+ "\n".join(lines)
|
|
144
140
|
+ "\n</entml:funtions_results>"
|
|
145
141
|
)
|
|
@@ -19,7 +19,11 @@ from .entml_patterns import (
|
|
|
19
19
|
split_mangled_json_param_tail,
|
|
20
20
|
synthetic_close_invoke_body,
|
|
21
21
|
)
|
|
22
|
-
from .entml_schema import
|
|
22
|
+
from .entml_schema import (
|
|
23
|
+
_coerce_entml_arg_value,
|
|
24
|
+
coerce_entml_arguments,
|
|
25
|
+
coerce_entml_parameter_value,
|
|
26
|
+
)
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
def _parse_direct_child_tags(
|
{echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_patterns.py
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
3
|
import re
|
|
5
4
|
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple
|
|
6
5
|
|
|
@@ -545,7 +544,10 @@ def strip_tool_entml_residue(
|
|
|
545
544
|
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
|
|
546
545
|
return cleaned.strip()
|
|
547
546
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
547
|
+
|
|
548
|
+
# 兼容旧 import 路径(body/invoke/stream 等仍从本模块取 mangled helpers)
|
|
549
|
+
import echotools.exec.fncall.protocols.entml_schema as _entml_schema
|
|
550
|
+
|
|
551
|
+
mangled_json_param_tail_in_progress = _entml_schema.mangled_json_param_tail_in_progress
|
|
552
|
+
split_mangled_json_param_tail = _entml_schema.split_mangled_json_param_tail
|
|
553
|
+
|
|
@@ -4,7 +4,10 @@ import json
|
|
|
4
4
|
import re
|
|
5
5
|
from typing import Any, Dict, List, Mapping, Optional, Tuple
|
|
6
6
|
|
|
7
|
-
from echotools.exec.fncall.shared.coercion import
|
|
7
|
+
from echotools.exec.fncall.shared.coercion import (
|
|
8
|
+
_coerce_param_value,
|
|
9
|
+
_resolve_effective_type,
|
|
10
|
+
)
|
|
8
11
|
|
|
9
12
|
_TYPE_HINT_TO_JSON_TYPE = {
|
|
10
13
|
"str": "string",
|
|
@@ -286,48 +289,19 @@ def format_entml_tool_descs(tools: List[Dict[str, Any]]) -> str:
|
|
|
286
289
|
)
|
|
287
290
|
return "\n\n".join(blocks)
|
|
288
291
|
|
|
289
|
-
# --- mangled param tail
|
|
292
|
+
# --- mangled param tail ---
|
|
293
|
+
#
|
|
294
|
+
# 模型偶发把 schema 尾缀 ``", "description": "...", "timeout": N}}`` 粘进标量 parameter。
|
|
295
|
+
# 通用规则:仅当「从候选起点到 EOS」整段都是该尾缀(可未写完)时才截断;
|
|
296
|
+
# 一旦后续偏离(例如内嵌 JSON 的 ``"method"``),一律视为正文。不依赖参数名黑名单。
|
|
290
297
|
|
|
291
298
|
_MANGLED_PARAM_JSON_TAIL_RE = re.compile(
|
|
292
299
|
r'"\s*,\s*"description"\s*:\s*"((?:[^"\\]|\\.)*)"\s*,\s*"timeout"\s*:\s*(\d+)\s*\}\}?\s*$',
|
|
293
300
|
re.DOTALL,
|
|
294
301
|
)
|
|
295
|
-
_MANGLED_PARAM_JSON_TAIL_START_RE = re.compile(
|
|
296
|
-
r'"\s*,\s*"description"\s*:\s*"',
|
|
297
|
-
re.DOTALL,
|
|
298
|
-
)
|
|
299
|
-
_MANGLED_PARAM_JSON_TAIL_EARLY_RE = re.compile(
|
|
300
|
-
r'"\s*,\s*"(?:description|timeout)\b',
|
|
301
|
-
re.DOTALL,
|
|
302
|
-
)
|
|
303
|
-
_MANGLED_PARAM_JSON_TAIL_IN_PROGRESS_RE = re.compile(
|
|
304
|
-
r'"\s*,\s*"(?:d|t)',
|
|
305
|
-
re.DOTALL,
|
|
306
|
-
)
|
|
307
|
-
_MANGLED_TAIL_SKIP_PARAM_NAMES = frozenset(
|
|
308
|
-
{
|
|
309
|
-
"content",
|
|
310
|
-
"contents",
|
|
311
|
-
"text",
|
|
312
|
-
"body",
|
|
313
|
-
"file_path",
|
|
314
|
-
"path",
|
|
315
|
-
"message",
|
|
316
|
-
"html",
|
|
317
|
-
"data",
|
|
318
|
-
"code",
|
|
319
|
-
}
|
|
320
|
-
)
|
|
321
302
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
r'"\s*,\s*"$',
|
|
325
|
-
re.DOTALL,
|
|
326
|
-
)
|
|
327
|
-
_MANGLED_PARAM_JSON_COMMA_AFTER_QUOTE_RE = re.compile(
|
|
328
|
-
r'"\s*,\s*$',
|
|
329
|
-
re.DOTALL,
|
|
330
|
-
)
|
|
303
|
+
_DESC_KEY = "description"
|
|
304
|
+
_TIMEOUT_KEY = "timeout"
|
|
331
305
|
|
|
332
306
|
|
|
333
307
|
def _param_value_is_json_container(value: str) -> bool:
|
|
@@ -335,11 +309,156 @@ def _param_value_is_json_container(value: str) -> bool:
|
|
|
335
309
|
return bool(stripped) and stripped[0] in "{["
|
|
336
310
|
|
|
337
311
|
|
|
312
|
+
def _match_schema_key_prefix(
|
|
313
|
+
text: str,
|
|
314
|
+
pos: int,
|
|
315
|
+
*,
|
|
316
|
+
allowed: Tuple[str, ...] = (_DESC_KEY, _TIMEOUT_KEY),
|
|
317
|
+
) -> Optional[Tuple[str, int]]:
|
|
318
|
+
"""匹配 schema 键。
|
|
319
|
+
|
|
320
|
+
返回 ``("<key>"|"partial", new_pos)``;已偏离则 ``None``。
|
|
321
|
+
"""
|
|
322
|
+
if pos >= len(text):
|
|
323
|
+
return ("partial", pos)
|
|
324
|
+
for key in allowed:
|
|
325
|
+
if text.startswith(key, pos):
|
|
326
|
+
return (key, pos + len(key))
|
|
327
|
+
remain = text[pos:]
|
|
328
|
+
for key in allowed:
|
|
329
|
+
if key.startswith(remain):
|
|
330
|
+
return ("partial", len(text))
|
|
331
|
+
return None
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _consume_json_string_body(text: str, pos: int) -> Tuple[int, bool]:
|
|
335
|
+
"""从 JSON 字符串内容起点消费(不含开引号)。返回 ``(new_pos, closed)``。"""
|
|
336
|
+
i = pos
|
|
337
|
+
while i < len(text):
|
|
338
|
+
ch = text[i]
|
|
339
|
+
if ch == "\\":
|
|
340
|
+
if i + 1 >= len(text):
|
|
341
|
+
return len(text), False
|
|
342
|
+
i += 2
|
|
343
|
+
continue
|
|
344
|
+
if ch == '"':
|
|
345
|
+
return i + 1, True
|
|
346
|
+
i += 1
|
|
347
|
+
return len(text), False
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _is_mangled_schema_tail_suffix(suffix: str) -> bool:
|
|
351
|
+
"""``suffix`` 是否整段为 mangled schema 尾缀(允许流式未写完)。
|
|
352
|
+
|
|
353
|
+
必须以 ``description``(或其真前缀)起头;禁止把文末单独的
|
|
354
|
+
``", "timeout": N`` 当成尾缀(避免截断正文 JSON)。
|
|
355
|
+
"""
|
|
356
|
+
if not suffix:
|
|
357
|
+
return False
|
|
358
|
+
m = re.match(r'^"\s*,\s*"', suffix)
|
|
359
|
+
if not m:
|
|
360
|
+
return False
|
|
361
|
+
pos = m.end()
|
|
362
|
+
# 首键只能是 description(或其真前缀),避免 ", \"t" 误伤 type/timeout 正文
|
|
363
|
+
key_match = _match_schema_key_prefix(suffix, pos, allowed=(_DESC_KEY,))
|
|
364
|
+
if key_match is None:
|
|
365
|
+
return False
|
|
366
|
+
key, pos = key_match
|
|
367
|
+
if key == "partial":
|
|
368
|
+
return pos == len(suffix)
|
|
369
|
+
if key != _DESC_KEY:
|
|
370
|
+
return False
|
|
371
|
+
if pos >= len(suffix):
|
|
372
|
+
return True
|
|
373
|
+
if suffix[pos] != '"':
|
|
374
|
+
return False
|
|
375
|
+
pos += 1
|
|
376
|
+
m_colon = re.match(r"\s*:\s*", suffix[pos:])
|
|
377
|
+
if not m_colon:
|
|
378
|
+
return bool(re.match(r"\s*$", suffix[pos:]))
|
|
379
|
+
pos += m_colon.end()
|
|
380
|
+
if pos >= len(suffix):
|
|
381
|
+
return True
|
|
382
|
+
if suffix[pos] != '"':
|
|
383
|
+
return False
|
|
384
|
+
pos += 1
|
|
385
|
+
pos, closed = _consume_json_string_body(suffix, pos)
|
|
386
|
+
if not closed:
|
|
387
|
+
return pos == len(suffix)
|
|
388
|
+
if pos == len(suffix):
|
|
389
|
+
# description 已闭合、timeout 尚未出现:流式未完成或残缺尾缀,仍视为 mangled
|
|
390
|
+
return True
|
|
391
|
+
m_rest = re.match(r"\s*,\s*", suffix[pos:])
|
|
392
|
+
if not m_rest:
|
|
393
|
+
return re.match(r"\s*\}{0,2}\s*$", suffix[pos:]) is not None
|
|
394
|
+
pos += m_rest.end()
|
|
395
|
+
if pos >= len(suffix):
|
|
396
|
+
return True
|
|
397
|
+
if suffix[pos] != '"':
|
|
398
|
+
return False
|
|
399
|
+
pos += 1
|
|
400
|
+
key2 = _match_schema_key_prefix(suffix, pos, allowed=(_TIMEOUT_KEY,))
|
|
401
|
+
if key2 is None:
|
|
402
|
+
return False
|
|
403
|
+
key2_name, pos = key2
|
|
404
|
+
if key2_name == "partial":
|
|
405
|
+
return pos == len(suffix)
|
|
406
|
+
if key2_name != _TIMEOUT_KEY:
|
|
407
|
+
return False
|
|
408
|
+
if pos >= len(suffix):
|
|
409
|
+
return True
|
|
410
|
+
if suffix[pos] != '"':
|
|
411
|
+
return False
|
|
412
|
+
pos += 1
|
|
413
|
+
m_colon2 = re.match(r"\s*:\s*", suffix[pos:])
|
|
414
|
+
if not m_colon2:
|
|
415
|
+
return bool(re.match(r"\s*$", suffix[pos:]))
|
|
416
|
+
pos += m_colon2.end()
|
|
417
|
+
m_num2 = re.match(r"\d*", suffix[pos:])
|
|
418
|
+
assert m_num2 is not None
|
|
419
|
+
pos += m_num2.end()
|
|
420
|
+
return re.match(r"\s*\}{0,2}\s*$", suffix[pos:]) is not None
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _ambiguous_comma_hold_end(value: str) -> int:
|
|
424
|
+
"""值以 ``",`` / ``", "`` 结尾且下一键未明时,返回应保留到的终点(含引号)。
|
|
425
|
+
|
|
426
|
+
流式在键名出现前无法区分 mangled 尾缀与正文 JSON 换键;先截到引号以保持
|
|
427
|
+
partial_json 单调,待后续字符证明不是 description 后再放出全文。
|
|
428
|
+
"""
|
|
429
|
+
m = re.search(r'"\s*,\s*"?$', value)
|
|
430
|
+
if not m:
|
|
431
|
+
return -1
|
|
432
|
+
return m.start() + 1
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _find_mangled_schema_tail_start(value: str) -> int:
|
|
436
|
+
"""返回 mangled 尾缀起点;无则 -1。
|
|
437
|
+
|
|
438
|
+
取最左合法起点,保证一旦出现 ``", "description"...`` 就截在 command 侧,
|
|
439
|
+
避免流式先发出更长前缀再无法回缩。
|
|
440
|
+
"""
|
|
441
|
+
starts = [m.start() for m in re.finditer(r'"\s*,\s*"', value)]
|
|
442
|
+
for start in starts:
|
|
443
|
+
if _is_mangled_schema_tail_suffix(value[start:]):
|
|
444
|
+
return start
|
|
445
|
+
hold = _ambiguous_comma_hold_end(value)
|
|
446
|
+
if hold >= 0:
|
|
447
|
+
# 把 hold 点前的引号视为尾缀起点(与合法 mangled 起点同形)
|
|
448
|
+
return hold - 1
|
|
449
|
+
return -1
|
|
450
|
+
|
|
451
|
+
|
|
338
452
|
def mangled_json_param_tail_in_progress(value: str) -> bool:
|
|
339
|
-
"""
|
|
453
|
+
"""值末尾正在形成 mangled schema 尾缀(尚未收齐)时抑制 partial_json 增长。"""
|
|
340
454
|
if not value or _param_value_is_json_container(value):
|
|
341
455
|
return False
|
|
342
|
-
|
|
456
|
+
start = _find_mangled_schema_tail_start(value)
|
|
457
|
+
if start < 0:
|
|
458
|
+
return False
|
|
459
|
+
if _MANGLED_PARAM_JSON_TAIL_RE.search(value[start:]):
|
|
460
|
+
return False
|
|
461
|
+
return True
|
|
343
462
|
|
|
344
463
|
|
|
345
464
|
def split_mangled_json_param_tail(
|
|
@@ -348,45 +467,27 @@ def split_mangled_json_param_tail(
|
|
|
348
467
|
param_name: str = "",
|
|
349
468
|
) -> Tuple[str, Dict[str, Any]]:
|
|
350
469
|
"""模型把 ``", "description": ..., "timeout": ...}}`` 误写入 parameter 值时的拆分。"""
|
|
470
|
+
_ = param_name
|
|
351
471
|
if not value:
|
|
352
472
|
return value, {}
|
|
353
|
-
if param_name in _MANGLED_TAIL_SKIP_PARAM_NAMES:
|
|
354
|
-
return value, {}
|
|
355
473
|
if _param_value_is_json_container(value):
|
|
356
474
|
try:
|
|
357
475
|
json.loads(value)
|
|
358
476
|
return value, {}
|
|
359
477
|
except json.JSONDecodeError:
|
|
360
|
-
# 合法 JSON 数组/对象(含 options.description 等)不得走标量 command 尾缀启发式。
|
|
361
478
|
return value, {}
|
|
479
|
+
|
|
362
480
|
match = _MANGLED_PARAM_JSON_TAIL_RE.search(value)
|
|
363
481
|
if match:
|
|
364
|
-
|
|
365
|
-
extra: Dict[str, Any] = {
|
|
482
|
+
return value[: match.start() + 1], {
|
|
366
483
|
"description": match.group(1),
|
|
367
484
|
"timeout": int(match.group(2)),
|
|
368
485
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if
|
|
372
|
-
return value
|
|
373
|
-
|
|
374
|
-
if early:
|
|
375
|
-
return value[: early.start() + 1], {}
|
|
376
|
-
inprog = _MANGLED_PARAM_JSON_TAIL_IN_PROGRESS_RE.search(value)
|
|
377
|
-
if inprog:
|
|
378
|
-
before = value[: inprog.start()]
|
|
379
|
-
matches = list(re.finditer(r'"\s*,\s*"', before))
|
|
380
|
-
if matches:
|
|
381
|
-
return value[: matches[-1].start() + 1], {}
|
|
382
|
-
return value[: inprog.start() + 1], {}
|
|
383
|
-
comma_quote_end = _MANGLED_PARAM_JSON_COMMA_QUOTE_END_RE.search(value)
|
|
384
|
-
if comma_quote_end:
|
|
385
|
-
return value[: comma_quote_end.start() + 1], {}
|
|
386
|
-
comma_after_quote = _MANGLED_PARAM_JSON_COMMA_AFTER_QUOTE_RE.search(value)
|
|
387
|
-
if comma_after_quote:
|
|
388
|
-
return value[: comma_after_quote.start() + 1], {}
|
|
389
|
-
return value, {}
|
|
486
|
+
|
|
487
|
+
start = _find_mangled_schema_tail_start(value)
|
|
488
|
+
if start < 0:
|
|
489
|
+
return value, {}
|
|
490
|
+
return value[: start + 1], {}
|
|
390
491
|
|
|
391
492
|
|
|
392
493
|
|
{echotools-2.3.99 → echotools-2.3.100}/src/echotools/exec/fncall/protocols/entml_stream/snap.py
RENAMED
|
@@ -15,6 +15,7 @@ from echotools.exec.fncall.protocols.entml_stream.body import (
|
|
|
15
15
|
_parse_parameter_entries,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
|
|
18
19
|
def _parameters_block_snapshot(
|
|
19
20
|
inner: str,
|
|
20
21
|
*,
|
|
@@ -36,7 +37,35 @@ def _parameters_block_snapshot(
|
|
|
36
37
|
return ""
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
def
|
|
40
|
+
def build_streaming_json_snapshot(
|
|
41
|
+
body: str,
|
|
42
|
+
*,
|
|
43
|
+
tool_name: str = "",
|
|
44
|
+
schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None,
|
|
45
|
+
force_close: bool = False,
|
|
46
|
+
) -> str:
|
|
47
|
+
"""构造当前应已发出的 partial_json 累积串(可未完成)。
|
|
48
|
+
|
|
49
|
+
invoke 闭合(或 force_close)时直接走 ``parse_invoke_args``,与批量解析一致。
|
|
50
|
+
未完成时仅输出合法 JSON 前缀,且不在 ``</entml:invoke>`` 前闭合最外层 ``}``。
|
|
51
|
+
"""
|
|
52
|
+
invoke_closed = _INVOKE_CLOSE in body
|
|
53
|
+
inner = body[: body.index(_INVOKE_CLOSE)] if invoke_closed else body
|
|
54
|
+
|
|
55
|
+
if invoke_closed or force_close:
|
|
56
|
+
return _final_invoke_arguments_json(
|
|
57
|
+
body,
|
|
58
|
+
tool_name=tool_name,
|
|
59
|
+
schema_index=schema_index,
|
|
60
|
+
force_close=force_close,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
params_snap = _parameters_block_snapshot(
|
|
64
|
+
inner, tool_name=tool_name, schema_index=schema_index
|
|
65
|
+
)
|
|
66
|
+
if params_snap is not None:
|
|
67
|
+
return params_snap
|
|
68
|
+
|
|
40
69
|
entries = _parse_parameter_entries(inner)
|
|
41
70
|
seen_keys = {key for key, *_rest in entries}
|
|
42
71
|
for bare in _parse_bare_invoke_entries(inner):
|
|
@@ -47,15 +76,9 @@ def _collect_streaming_entries(inner: str):
|
|
|
47
76
|
if direct[0] not in seen_keys:
|
|
48
77
|
entries.append(direct)
|
|
49
78
|
seen_keys.add(direct[0])
|
|
50
|
-
|
|
51
|
-
|
|
79
|
+
if not entries:
|
|
80
|
+
return ""
|
|
52
81
|
|
|
53
|
-
def _entries_to_json_prefix(
|
|
54
|
-
entries,
|
|
55
|
-
*,
|
|
56
|
-
tool_name: str,
|
|
57
|
-
schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]],
|
|
58
|
-
) -> str:
|
|
59
82
|
func_schema = (schema_index or {}).get(tool_name) or {}
|
|
60
83
|
parts: List[str] = ["{"]
|
|
61
84
|
for idx, (key, value, is_complete, type_hint) in enumerate(entries):
|
|
@@ -69,41 +92,14 @@ def _entries_to_json_prefix(
|
|
|
69
92
|
value, is_complete, param_type, pschema, type_hint
|
|
70
93
|
)
|
|
71
94
|
if fragment is None:
|
|
72
|
-
|
|
95
|
+
if idx == 0:
|
|
96
|
+
return ""
|
|
97
|
+
return "".join(parts)
|
|
73
98
|
parts.append(fragment)
|
|
74
99
|
if not is_complete:
|
|
75
100
|
return "".join(parts)
|
|
76
|
-
return "".join(parts)
|
|
77
|
-
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
body: str,
|
|
81
|
-
*,
|
|
82
|
-
tool_name: str = "",
|
|
83
|
-
schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None,
|
|
84
|
-
force_close: bool = False,
|
|
85
|
-
) -> str:
|
|
86
|
-
"""构造当前应已发出的 partial_json 累积串(可未完成)。"""
|
|
87
|
-
invoke_closed = _INVOKE_CLOSE in body
|
|
88
|
-
inner = body[: body.index(_INVOKE_CLOSE)] if invoke_closed else body
|
|
89
|
-
if invoke_closed or force_close:
|
|
90
|
-
return _final_invoke_arguments_json(
|
|
91
|
-
body,
|
|
92
|
-
tool_name=tool_name,
|
|
93
|
-
schema_index=schema_index,
|
|
94
|
-
force_close=force_close,
|
|
95
|
-
)
|
|
96
|
-
params_snap = _parameters_block_snapshot(
|
|
97
|
-
inner, tool_name=tool_name, schema_index=schema_index
|
|
98
|
-
)
|
|
99
|
-
if params_snap is not None:
|
|
100
|
-
return params_snap
|
|
101
|
-
entries = _collect_streaming_entries(inner)
|
|
102
|
-
if not entries:
|
|
103
|
-
return ""
|
|
104
|
-
return _entries_to_json_prefix(
|
|
105
|
-
entries, tool_name=tool_name, schema_index=schema_index
|
|
106
|
-
)
|
|
102
|
+
return "".join(parts)
|
|
107
103
|
|
|
108
104
|
|
|
109
105
|
def _parse_partial_parameter_body(body: str) -> Dict[str, Any]:
|