echotools 2.3.99__tar.gz → 2.3.103__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.103}/PKG-INFO +1 -1
- {echotools-2.3.99 → echotools-2.3.103}/pyproject.toml +8 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream.py +4 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream_feed.py +0 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream_final.py +8 -11
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/xml_parser.py +1 -1
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/behavior_blocks.py +1 -1
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/history.py +1 -1
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml.py +241 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/__init__.py +93 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/invoke.py +197 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/params.py +219 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/regex.py +107 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_patterns/strip.py +42 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_schema/__init__.py +26 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_schema/mangled.py +201 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_stream/body.py +1 -1
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_stream/snap.py +24 -26
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/blocks.py +2 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/filter.py +2 -11
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/parse.py +3 -4
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/__init__.py +56 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/blocks.py +363 -0
- echotools-2.3.99/src/echotools/exec/fncall/protocols/entml_fake_structure_markup.py → echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/fakemarkup.py +1 -1
- echotools-2.3.99/src/echotools/exec/fncall/protocols/entml_invoke.py → echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/invoke.py +18 -10
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/tools.py +156 -0
- echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/values.py +134 -0
- echotools-2.3.103/src/echotools/exec/fncall/shared/entml_format.py +169 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/shared/history_markup.py +1 -1
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/shared/loop_detect.py +39 -0
- echotools-2.3.103/src/echotools/media/console/__init__.py +89 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_text.py +59 -32
- echotools-2.3.103/src/echotools/media/console/uilayout/ui_bridge.py +205 -0
- echotools-2.3.103/src/echotools/media/console/uiwidgets/ui_richcli.py +110 -0
- echotools-2.3.103/src/echotools/media/console/uiwidgets/ui_themes.py +243 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools.egg-info/PKG-INFO +1 -1
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools.egg-info/SOURCES.txt +23 -6
- echotools-2.3.103/src/tests/_check_parity.py +75 -0
- echotools-2.3.103/src/tests/fixtures/entml_golden.py +104 -0
- echotools-2.3.103/src/tests/fixtures/entml_golden_update.py +24 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/fixtures/simulated_fault_thinking_responses.py +2 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_bare_invoke_format.py +1 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_batch_stream_comprehensive.py +14 -3
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_fake_structure_markup.py +2 -2
- echotools-2.3.103/src/tests/test_entml_golden.py +44 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_invoke_known_tool_gate.py +1 -2
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_protocol.py +8 -6
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_tool_pipeline.py +3 -3
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_printstream.py +3 -3
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_stream_partial_json_parity.py +122 -5
- echotools-2.3.103/src/tests/test_ui_bridge.py +62 -0
- echotools-2.3.99/src/echotools/exec/fncall/protocols/entml.py +0 -412
- echotools-2.3.99/src/echotools/exec/fncall/protocols/entml_patterns.py +0 -551
- echotools-2.3.99/src/echotools/exec/fncall/protocols/entml_schema.py +0 -392
- echotools-2.3.99/src/echotools/exec/fncall/shared/xml_helpers.py +0 -56
- echotools-2.3.99/src/echotools/media/console/__init__.py +0 -38
- {echotools-2.3.99 → echotools-2.3.103}/README.md +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/setup.cfg +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/cache/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/cache/list_cache.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/cache/memory_cache.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/center.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/center_notify.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/center_template.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/loader.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/config/merge.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/errors/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/errors/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/errors/classify.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/errors/common.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/errors/http.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/ids/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/ids/generator.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/io/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/io/io_utils.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/io/printstream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/logger/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/logger/manager.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/retry/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/base/retry/retry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/compat.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/candidate.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/dispatcher.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/proxy_selector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/race.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/selector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/dispatch/usage.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/stream_delta.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/parsers/xml_params.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/history_format.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/inject.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/prompt_helpers.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/prompt/templates.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_stream/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/core.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/hist.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/protocols/entml_think/hold.py +0 -0
- echotools-2.3.99/src/echotools/exec/fncall/protocols/entml_tool_result_comment.py → echotools-2.3.103/src/echotools/exec/fncall/protocols/entml_tool/comment.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/registry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/shared/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/shared/coercion.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/shared/normalization.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/fncall/shared/schema_render.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/keys.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/lifecycle/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/lifecycle/manager.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/lifecycle/updater.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/lifecycle/updater_pull.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/process/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/process/port.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/protocol/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/protocol/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/conpty.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/local/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/local/core.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/local/proc.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/local/unix.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/local/win.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/sanitize.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/session/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/session/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/session/client.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/ssh.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/exec/terminal/tmux.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/charmap.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/spinner.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/ui.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uibase.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uicmds.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uiinteract.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uioutput.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/mixins/uistream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_console.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_io.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_log.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_platform.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uicore/ui_types.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_countdown.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_layout.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_misc.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_panel.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_table.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uilayout/ui_tree.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_box.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_editor.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_progress.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_select.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/console/uiwidgets/ui_stream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/events/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/events/bus.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/events/event.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/spinner/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/spinner/spinner.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/spinner/spinner_color.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/tracing/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/tracing/context.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/tracing/span.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/tracing/tracer.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/translate.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/watcher/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/watcher/file_watcher.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/application.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/broker.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/app.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/filezone.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/motionkit.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/sendbutton.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/sortablelist.css +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/sortablelist.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/textinput.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/components/voiceinput.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/fonts/inter-500.woff2 +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/fonts/inter-600.woff2 +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/fonts/inter400.woff2 +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/inputbox.css +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/inputbox.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/style.css +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/input_box/voicegif.js +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/middleware.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/stats.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/media/web/utils.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/files/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/files/file_util.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/network/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/network/http_utils.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/plugin/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/plugin/base.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/plugin/discovery.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/plugin/registry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/plugin/registry_ops.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/proxy/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/proxy/manager.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/runtime/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/runtime/collector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/scheduler/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/scheduler/scheduler.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/sdk/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/plat/sdk/facade.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools/version.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools.egg-info/dependency_links.txt +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools.egg-info/requires.txt +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/echotools.egg-info/top_level.txt +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/bench_import.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/conftest.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/fixtures/__init__.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/fixtures/simulated_fake_history_markup_responses.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/fixtures/simulated_llm_tool_responses.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_adversarial_stream_splits.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_cache_retry.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_completion.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_config.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_config_extended.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_console.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_coverage_core.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_dispatch.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_dispatch_extended.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_dispatch_race.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_agent_output_formats.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_parser_comprehensive.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_stream_json.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_stream_json_exhaustive.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_thinking_history.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_thinking_parse.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_tool_parse_leak.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_entml_tools.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_errors.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_errors_common.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_events.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_events_event.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_facade.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fake_history_markup_batch.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fake_history_markup_invoke_preserve.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fake_history_markup_reply_protect.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fake_history_markup_stream.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fault_thinking_close_batch.py +5 -5
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fault_thinking_close_stream.py +4 -4
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fncall.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_fncall_stream_thinking.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_history_markup_filter.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_ids.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_imports.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_io.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_keys.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_lifecycle.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_logger.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_merge.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_model_branch_matrix.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_plugin.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_protocols.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_proxy.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_proxy_selector.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_runtime.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_scheduler.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_simulated_llm_tool_parse.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_stream_realtime_parse.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_tracing.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_usage.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/src/tests/test_web.py +0 -0
- {echotools-2.3.99 → echotools-2.3.103}/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.103"
|
|
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:
|
|
@@ -17,7 +17,7 @@ from echotools.exec.fncall.shared.coercion import (
|
|
|
17
17
|
_coerce_param_value,
|
|
18
18
|
)
|
|
19
19
|
from echotools.exec.fncall.shared.normalization import normalize_tool_calls
|
|
20
|
-
from echotools.exec.fncall.shared.
|
|
20
|
+
from echotools.exec.fncall.shared.loop_detect import (
|
|
21
21
|
_PROVIDER_BLOCK_RE,
|
|
22
22
|
_PROVIDER_INVOKE_RE,
|
|
23
23
|
_PROVIDER_PARAM_RE,
|
|
@@ -40,7 +40,7 @@ You think before answering even when the request appears trivial. When a request
|
|
|
40
40
|
|
|
41
41
|
Depth scales with the request. Routine requests receive a short block. Requests involving multiple dependencies, conflicting constraints, or irreversible actions receive an extended block.
|
|
42
42
|
|
|
43
|
-
Your default reply language to the user is Simplified Chinese. You switch languages only when the user explicitly requests a different language.
|
|
43
|
+
Your default reply language to the user is Simplified Chinese. You switch languages only when the user explicitly requests a different language.
|
|
44
44
|
|
|
45
45
|
IMPORTANT: The thinking block is the first structural element of your output, and a tool invocation never appears inside it."""
|
|
46
46
|
|
|
@@ -83,7 +83,7 @@ def _clean_entml_tags(content: str) -> str:
|
|
|
83
83
|
# ---------------------------------------------------------------------------
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
from echotools.exec.fncall.protocols.
|
|
86
|
+
from echotools.exec.fncall.protocols.entml_tool.invoke import format_entml_parameter_value
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def _render_parameter_value(v: Any) -> str:
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Entropy ML (entml) 协议实现。
|
|
2
|
+
|
|
3
|
+
- 工具调用:<entml:invoke> / <entml:parameter>(legacy ``function_calls`` 外壳仅解析兼容)
|
|
4
|
+
- 对话历史:<entml:conversation_history>
|
|
5
|
+
- 当前用户消息:<current_user_message>
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
11
|
+
|
|
12
|
+
from echotools.exec.fncall.protocols.entml_patterns import (
|
|
13
|
+
entml_invoke_open_may_be_streaming,
|
|
14
|
+
find_actionable_entml_invoke_open,
|
|
15
|
+
resolve_known_tool_names,
|
|
16
|
+
strip_actionable_entml_invoke_blocks,
|
|
17
|
+
strip_legacy_function_calls_wrapper,
|
|
18
|
+
strip_tool_entml_residue,
|
|
19
|
+
)
|
|
20
|
+
from echotools.exec.fncall.protocols.entml_schema import format_entml_tool_descs
|
|
21
|
+
from echotools.exec.fncall.protocols.entml_tool.fakemarkup import strip_orphan_entml_close_tags
|
|
22
|
+
from echotools.exec.fncall.protocols.entml_tool.invoke import (
|
|
23
|
+
format_entml_tool_calls,
|
|
24
|
+
parse_entml_tool_calls,
|
|
25
|
+
)
|
|
26
|
+
from echotools.exec.fncall.shared.coercion import _build_param_schema_index
|
|
27
|
+
from echotools.exec.fncall.shared.entml_format import (
|
|
28
|
+
_parse_tool_call_args,
|
|
29
|
+
_render_tool_call_line,
|
|
30
|
+
_render_tool_history_block,
|
|
31
|
+
build_entml_render_prompt,
|
|
32
|
+
format_entml_conversation_history,
|
|
33
|
+
format_entml_current_user_message,
|
|
34
|
+
format_entml_functions_results,
|
|
35
|
+
format_tool_result_id_comment,
|
|
36
|
+
strip_entml_from_content,
|
|
37
|
+
)
|
|
38
|
+
from echotools.exec.fncall.shared.history_markup import strip_fake_history_markup
|
|
39
|
+
from echotools.exec.fncall.shared.normalization import normalize_tool_calls
|
|
40
|
+
from echotools.exec.protocol.base import ToolProtocol
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"EntmlProtocol",
|
|
44
|
+
"format_entml_conversation_history",
|
|
45
|
+
"format_entml_current_user_message",
|
|
46
|
+
"format_entml_functions_results",
|
|
47
|
+
"format_tool_result_id_comment",
|
|
48
|
+
"strip_entml_from_content",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class EntmlProtocol(ToolProtocol):
|
|
53
|
+
"""Entropy ML (entml) 格式工具调用协议适配器。"""
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def id(self) -> str:
|
|
57
|
+
return "entml"
|
|
58
|
+
|
|
59
|
+
_TRIGGER = "<entml:invoke>"
|
|
60
|
+
_TRIGGER_PREFIX = "<entml:invoke"
|
|
61
|
+
_LEGACY_WRAPPER_PREFIX = "<entml:function_calls"
|
|
62
|
+
_THINKING_PREFIX = "<entml:thinking"
|
|
63
|
+
|
|
64
|
+
def get_trigger_tags(self) -> List[str]:
|
|
65
|
+
return [
|
|
66
|
+
self._TRIGGER,
|
|
67
|
+
self._TRIGGER_PREFIX,
|
|
68
|
+
self._THINKING_PREFIX,
|
|
69
|
+
f"{self._THINKING_PREFIX}>",
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
def normalize_stream_buffer(self, buffer: str) -> str:
|
|
73
|
+
return strip_legacy_function_calls_wrapper(buffer)
|
|
74
|
+
|
|
75
|
+
def get_stream_end_tags(self) -> List[str]:
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def format_tool_descs(tools: List[Dict[str, Any]]) -> str:
|
|
80
|
+
return format_entml_tool_descs(tools)
|
|
81
|
+
|
|
82
|
+
def render_prompt(
|
|
83
|
+
self,
|
|
84
|
+
tool_descs: str,
|
|
85
|
+
lang: str,
|
|
86
|
+
user_system_prompt: str = "",
|
|
87
|
+
history_text: str = "",
|
|
88
|
+
loop_warning: str = "",
|
|
89
|
+
history_markup_warning: str = "",
|
|
90
|
+
current_user_message: Optional[str] = None,
|
|
91
|
+
protocol_options: Optional[Dict[str, Any]] = None,
|
|
92
|
+
history_has_tool_calls: bool = False,
|
|
93
|
+
functions_results_text: str = "",
|
|
94
|
+
) -> str:
|
|
95
|
+
_ = (lang, history_has_tool_calls)
|
|
96
|
+
return build_entml_render_prompt(
|
|
97
|
+
tool_descs=tool_descs,
|
|
98
|
+
user_system_prompt=user_system_prompt,
|
|
99
|
+
history_text=history_text,
|
|
100
|
+
loop_warning=loop_warning,
|
|
101
|
+
history_markup_warning=history_markup_warning,
|
|
102
|
+
current_user_message=current_user_message,
|
|
103
|
+
protocol_options=protocol_options,
|
|
104
|
+
functions_results_text=functions_results_text,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def detect_start(
|
|
108
|
+
self,
|
|
109
|
+
buffer: str,
|
|
110
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
111
|
+
) -> Tuple[bool, int]:
|
|
112
|
+
schema_index = _build_param_schema_index(tools) if tools else None
|
|
113
|
+
known = resolve_known_tool_names(tools, schema_index)
|
|
114
|
+
invoke_pos = find_actionable_entml_invoke_open(buffer, known_names=known)
|
|
115
|
+
if invoke_pos < 0:
|
|
116
|
+
return (False, -1)
|
|
117
|
+
return (True, invoke_pos)
|
|
118
|
+
|
|
119
|
+
def find_fncall_hold_from(
|
|
120
|
+
self,
|
|
121
|
+
buffer: str,
|
|
122
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
123
|
+
) -> Optional[int]:
|
|
124
|
+
schema_index = _build_param_schema_index(tools) if tools else None
|
|
125
|
+
known = resolve_known_tool_names(tools, schema_index)
|
|
126
|
+
if find_actionable_entml_invoke_open(buffer, known_names=known) >= 0:
|
|
127
|
+
return None
|
|
128
|
+
hold: Optional[int] = None
|
|
129
|
+
legacy_pos = buffer.find(self._LEGACY_WRAPPER_PREFIX)
|
|
130
|
+
if legacy_pos >= 0:
|
|
131
|
+
close = buffer.find(">", legacy_pos)
|
|
132
|
+
if close < 0 and (hold is None or legacy_pos < hold):
|
|
133
|
+
hold = legacy_pos
|
|
134
|
+
invoke_pos = buffer.find(self._TRIGGER_PREFIX)
|
|
135
|
+
if (
|
|
136
|
+
invoke_pos >= 0
|
|
137
|
+
and entml_invoke_open_may_be_streaming(
|
|
138
|
+
buffer, invoke_pos, known_names=known
|
|
139
|
+
)
|
|
140
|
+
and (hold is None or invoke_pos < hold)
|
|
141
|
+
):
|
|
142
|
+
hold = invoke_pos
|
|
143
|
+
return hold
|
|
144
|
+
|
|
145
|
+
def _find_complete_invoke_open(
|
|
146
|
+
self,
|
|
147
|
+
buffer: str,
|
|
148
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
149
|
+
) -> int:
|
|
150
|
+
schema_index = _build_param_schema_index(tools) if tools else None
|
|
151
|
+
known = resolve_known_tool_names(tools, schema_index)
|
|
152
|
+
return find_actionable_entml_invoke_open(buffer, known_names=known)
|
|
153
|
+
|
|
154
|
+
def parse(
|
|
155
|
+
self,
|
|
156
|
+
text: str,
|
|
157
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
158
|
+
*,
|
|
159
|
+
include_tool_blocks: bool = True,
|
|
160
|
+
thinking_enabled: bool = True,
|
|
161
|
+
) -> Tuple[str, List[Dict[str, Any]]]:
|
|
162
|
+
_ = include_tool_blocks
|
|
163
|
+
from echotools.exec.fncall.protocols.entml_think.parse import (
|
|
164
|
+
_find_earliest_thinking_open,
|
|
165
|
+
has_unclosed_entml_thinking,
|
|
166
|
+
split_entml_thinking,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
schema_index = _build_param_schema_index(tools) if tools else None
|
|
170
|
+
parse_text, _thinking = split_entml_thinking(text, thinking_enabled=True)
|
|
171
|
+
unclosed_open_at = -1
|
|
172
|
+
if has_unclosed_entml_thinking(text, thinking_enabled=True):
|
|
173
|
+
unclosed_open_at, _ = _find_earliest_thinking_open(
|
|
174
|
+
text, thinking_enabled=True
|
|
175
|
+
)
|
|
176
|
+
if unclosed_open_at >= 0:
|
|
177
|
+
parse_text = text[:unclosed_open_at]
|
|
178
|
+
known = resolve_known_tool_names(tools, schema_index)
|
|
179
|
+
tool_calls = parse_entml_tool_calls(parse_text, tools, schema_index)
|
|
180
|
+
clean = text[:unclosed_open_at] if unclosed_open_at >= 0 else text
|
|
181
|
+
if thinking_enabled:
|
|
182
|
+
visible, _ = split_entml_thinking(clean, thinking_enabled=True)
|
|
183
|
+
clean = visible
|
|
184
|
+
clean, _ = strip_fake_history_markup(clean)
|
|
185
|
+
if tool_calls:
|
|
186
|
+
clean = strip_actionable_entml_invoke_blocks(clean, known_names=known)
|
|
187
|
+
clean = strip_tool_entml_residue(clean, known_names=known)
|
|
188
|
+
clean, _ = strip_orphan_entml_close_tags(clean)
|
|
189
|
+
return (clean, normalize_tool_calls(tool_calls, tools))
|
|
190
|
+
|
|
191
|
+
def parse_fragment(
|
|
192
|
+
self,
|
|
193
|
+
fragment: str,
|
|
194
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
195
|
+
) -> List[Dict[str, Any]]:
|
|
196
|
+
_, tool_calls = self.parse(fragment, tools)
|
|
197
|
+
return tool_calls
|
|
198
|
+
|
|
199
|
+
def clean_tags(self, content: str) -> str:
|
|
200
|
+
return strip_entml_from_content(content)
|
|
201
|
+
|
|
202
|
+
def clean_tool_tags(
|
|
203
|
+
self,
|
|
204
|
+
content: str,
|
|
205
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
206
|
+
) -> str:
|
|
207
|
+
schema_index = _build_param_schema_index(tools) if tools else None
|
|
208
|
+
known = resolve_known_tool_names(tools, schema_index)
|
|
209
|
+
cleaned = strip_tool_entml_residue(content, known_names=known)
|
|
210
|
+
cleaned, _ = strip_fake_history_markup(cleaned)
|
|
211
|
+
return cleaned
|
|
212
|
+
|
|
213
|
+
def format_assistant_tool_calls(
|
|
214
|
+
self,
|
|
215
|
+
tool_calls: List[Dict[str, Any]],
|
|
216
|
+
) -> str:
|
|
217
|
+
lines: List[str] = []
|
|
218
|
+
for tc in tool_calls:
|
|
219
|
+
name, args = _parse_tool_call_args(tc)
|
|
220
|
+
lines.append(_render_tool_call_line(name, args))
|
|
221
|
+
return "\n".join(lines)
|
|
222
|
+
|
|
223
|
+
def format_assistant_tool_turn_block(
|
|
224
|
+
self,
|
|
225
|
+
tool_calls: List[Dict[str, Any]],
|
|
226
|
+
tid_to_result: Dict[str, Dict[str, Any]],
|
|
227
|
+
) -> str:
|
|
228
|
+
_ = tid_to_result
|
|
229
|
+
lines: List[str] = []
|
|
230
|
+
for tc in tool_calls:
|
|
231
|
+
invoke = format_entml_tool_calls([tc])
|
|
232
|
+
if invoke:
|
|
233
|
+
lines.append(invoke)
|
|
234
|
+
tid = tc.get("id") or ""
|
|
235
|
+
comment = format_tool_result_id_comment(tid)
|
|
236
|
+
if comment:
|
|
237
|
+
lines.append(comment)
|
|
238
|
+
return "\n".join(lines)
|
|
239
|
+
|
|
240
|
+
def supports_streaming(self) -> bool:
|
|
241
|
+
return True
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Regex patterns and helpers for entml invoke/parameter parsing."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ..entml_schema.mangled import (
|
|
5
|
+
mangled_json_param_tail_in_progress,
|
|
6
|
+
split_mangled_json_param_tail,
|
|
7
|
+
)
|
|
8
|
+
from .invoke import (
|
|
9
|
+
entml_invoke_open_is_actionable,
|
|
10
|
+
entml_invoke_open_may_be_streaming,
|
|
11
|
+
find_actionable_entml_invoke_open,
|
|
12
|
+
is_placeholder_invoke_name,
|
|
13
|
+
iter_actionable_entml_invoke_blocks,
|
|
14
|
+
resolve_known_tool_names,
|
|
15
|
+
)
|
|
16
|
+
from .params import (
|
|
17
|
+
extract_attr_value,
|
|
18
|
+
extract_parameter_type_attr,
|
|
19
|
+
find_valid_parameter_close,
|
|
20
|
+
inside_parameter_value,
|
|
21
|
+
invoke_structural_gap_text,
|
|
22
|
+
invoke_structural_gaps,
|
|
23
|
+
normalize_entml_name,
|
|
24
|
+
parameter_block_spans,
|
|
25
|
+
parameter_close_at,
|
|
26
|
+
parameter_value_spans,
|
|
27
|
+
parse_sub_tags,
|
|
28
|
+
strip_actionable_entml_invoke_blocks,
|
|
29
|
+
synthetic_close_invoke_body,
|
|
30
|
+
)
|
|
31
|
+
from .regex import (
|
|
32
|
+
BARE_INVOKE_CHILD_OPEN_RE,
|
|
33
|
+
BARE_INVOKE_CHILD_RE,
|
|
34
|
+
BLOCK_RE,
|
|
35
|
+
INVOKE_DIRECT_CHILD_OPEN_RE,
|
|
36
|
+
INVOKE_DIRECT_CHILD_RE,
|
|
37
|
+
INVOKE_DIRECT_CHILD_SKIP,
|
|
38
|
+
INVOKE_RE,
|
|
39
|
+
PARAM_CLOSE_BARE,
|
|
40
|
+
PARAM_CLOSE_ENTML,
|
|
41
|
+
PARAM_OPEN_PATTERN,
|
|
42
|
+
PARAM_RE,
|
|
43
|
+
PARAMETERS_RE,
|
|
44
|
+
SUB_TAG_RE,
|
|
45
|
+
_INVOKE_CLOSE,
|
|
46
|
+
_INVOKE_OPEN_PREFIX,
|
|
47
|
+
_PARAM_CLOSE_VALID_RE,
|
|
48
|
+
_PARAM_CLOSE_VALID_STREAM_RE,
|
|
49
|
+
)
|
|
50
|
+
from .strip import strip_legacy_function_calls_wrapper, strip_tool_entml_residue
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
"BARE_INVOKE_CHILD_OPEN_RE",
|
|
54
|
+
"BARE_INVOKE_CHILD_RE",
|
|
55
|
+
"BLOCK_RE",
|
|
56
|
+
"INVOKE_DIRECT_CHILD_OPEN_RE",
|
|
57
|
+
"INVOKE_DIRECT_CHILD_RE",
|
|
58
|
+
"INVOKE_DIRECT_CHILD_SKIP",
|
|
59
|
+
"INVOKE_RE",
|
|
60
|
+
"PARAM_CLOSE_BARE",
|
|
61
|
+
"PARAM_CLOSE_ENTML",
|
|
62
|
+
"PARAM_OPEN_PATTERN",
|
|
63
|
+
"PARAM_RE",
|
|
64
|
+
"PARAMETERS_RE",
|
|
65
|
+
"SUB_TAG_RE",
|
|
66
|
+
"_INVOKE_CLOSE",
|
|
67
|
+
"_INVOKE_OPEN_PREFIX",
|
|
68
|
+
"_PARAM_CLOSE_VALID_RE",
|
|
69
|
+
"_PARAM_CLOSE_VALID_STREAM_RE",
|
|
70
|
+
"entml_invoke_open_is_actionable",
|
|
71
|
+
"entml_invoke_open_may_be_streaming",
|
|
72
|
+
"extract_attr_value",
|
|
73
|
+
"extract_parameter_type_attr",
|
|
74
|
+
"find_actionable_entml_invoke_open",
|
|
75
|
+
"find_valid_parameter_close",
|
|
76
|
+
"inside_parameter_value",
|
|
77
|
+
"invoke_structural_gap_text",
|
|
78
|
+
"invoke_structural_gaps",
|
|
79
|
+
"is_placeholder_invoke_name",
|
|
80
|
+
"iter_actionable_entml_invoke_blocks",
|
|
81
|
+
"mangled_json_param_tail_in_progress",
|
|
82
|
+
"normalize_entml_name",
|
|
83
|
+
"parameter_block_spans",
|
|
84
|
+
"parameter_close_at",
|
|
85
|
+
"parameter_value_spans",
|
|
86
|
+
"parse_sub_tags",
|
|
87
|
+
"resolve_known_tool_names",
|
|
88
|
+
"split_mangled_json_param_tail",
|
|
89
|
+
"strip_actionable_entml_invoke_blocks",
|
|
90
|
+
"strip_legacy_function_calls_wrapper",
|
|
91
|
+
"strip_tool_entml_residue",
|
|
92
|
+
"synthetic_close_invoke_body",
|
|
93
|
+
]
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple
|
|
5
|
+
|
|
6
|
+
from .params import extract_attr_value, normalize_entml_name
|
|
7
|
+
from .regex import _INVOKE_CLOSE, _INVOKE_OPEN_PREFIX
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_placeholder_invoke_name(name: str) -> bool:
|
|
11
|
+
"""提示词占位符(如 ``$FUNCTION_NAME``)不算真实工具调用。"""
|
|
12
|
+
n = (name or "").strip()
|
|
13
|
+
return not n or "$" in n
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def resolve_known_tool_names(
|
|
17
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
18
|
+
schema_index: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None,
|
|
19
|
+
) -> Optional[Set[str]]:
|
|
20
|
+
"""从 tools / schema_index 解析已知工具名;无约束时返回 ``None``。"""
|
|
21
|
+
if schema_index:
|
|
22
|
+
return {normalize_entml_name(str(k)) for k in schema_index.keys()}
|
|
23
|
+
names: Set[str] = set()
|
|
24
|
+
for tool in tools or []:
|
|
25
|
+
fn = tool.get("function") or {}
|
|
26
|
+
name = fn.get("name")
|
|
27
|
+
if name:
|
|
28
|
+
names.add(normalize_entml_name(str(name)))
|
|
29
|
+
return names if names else None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _name_is_known(name: str, known_names: Optional[Set[str]]) -> bool:
|
|
33
|
+
if known_names is None:
|
|
34
|
+
return True
|
|
35
|
+
if not known_names:
|
|
36
|
+
return False
|
|
37
|
+
return normalize_entml_name(name) in known_names
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def entml_invoke_open_is_actionable(
|
|
41
|
+
buffer: str,
|
|
42
|
+
pos: int,
|
|
43
|
+
*,
|
|
44
|
+
known_names: Optional[Set[str]] = None,
|
|
45
|
+
) -> bool:
|
|
46
|
+
"""``pos`` 处 ``<entml:invoke`` 是否为已闭合、含真实 name 且(若提供)在已知工具表内的开标签。"""
|
|
47
|
+
if pos < 0 or not buffer.startswith(_INVOKE_OPEN_PREFIX, pos):
|
|
48
|
+
return False
|
|
49
|
+
gt = buffer.find(">", pos + len(_INVOKE_OPEN_PREFIX))
|
|
50
|
+
if gt < 0:
|
|
51
|
+
return False
|
|
52
|
+
attrs = buffer[pos + len(_INVOKE_OPEN_PREFIX) : gt]
|
|
53
|
+
name = extract_attr_value(attrs, "name")
|
|
54
|
+
if not name:
|
|
55
|
+
return False
|
|
56
|
+
name = normalize_entml_name(name)
|
|
57
|
+
if not name or is_placeholder_invoke_name(name):
|
|
58
|
+
return False
|
|
59
|
+
return _name_is_known(name, known_names)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def entml_invoke_open_may_be_streaming(
|
|
63
|
+
buffer: str,
|
|
64
|
+
pos: int,
|
|
65
|
+
*,
|
|
66
|
+
known_names: Optional[Set[str]] = None,
|
|
67
|
+
) -> bool:
|
|
68
|
+
"""``pos`` 处 ``<entml:invoke`` 是否仍可能长成可解析工具开标签(非 prose 提及)。"""
|
|
69
|
+
if pos < 0 or not buffer.startswith(_INVOKE_OPEN_PREFIX, pos):
|
|
70
|
+
return False
|
|
71
|
+
gt = buffer.find(">", pos + len(_INVOKE_OPEN_PREFIX))
|
|
72
|
+
if gt < 0:
|
|
73
|
+
return True
|
|
74
|
+
return entml_invoke_open_is_actionable(buffer, pos, known_names=known_names)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def find_actionable_entml_invoke_open(
|
|
78
|
+
buffer: str,
|
|
79
|
+
*,
|
|
80
|
+
known_names: Optional[Set[str]] = None,
|
|
81
|
+
) -> int:
|
|
82
|
+
"""返回首个可解析 ``<entml:invoke`` 起始下标;否则 ``-1``。"""
|
|
83
|
+
if not buffer:
|
|
84
|
+
return -1
|
|
85
|
+
search_from = 0
|
|
86
|
+
prefix_len = len(_INVOKE_OPEN_PREFIX)
|
|
87
|
+
while search_from < len(buffer):
|
|
88
|
+
pos = buffer.find(_INVOKE_OPEN_PREFIX, search_from)
|
|
89
|
+
if pos < 0:
|
|
90
|
+
return -1
|
|
91
|
+
if entml_invoke_open_is_actionable(buffer, pos, known_names=known_names):
|
|
92
|
+
return pos
|
|
93
|
+
gt = buffer.find(">", pos + prefix_len)
|
|
94
|
+
search_from = (gt + 1) if gt >= 0 else (pos + prefix_len)
|
|
95
|
+
return -1
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _actionable_invoke_open_before_close(
|
|
99
|
+
content: str,
|
|
100
|
+
close_pos: int,
|
|
101
|
+
*,
|
|
102
|
+
known_names: Optional[Set[str]] = None,
|
|
103
|
+
) -> bool:
|
|
104
|
+
"""``close_pos`` 处的 ``</entml:invoke>`` 是否闭合带真实 name 的开标签。"""
|
|
105
|
+
search = close_pos - 1
|
|
106
|
+
while search >= 0:
|
|
107
|
+
pos = content.rfind(_INVOKE_OPEN_PREFIX, 0, search + 1)
|
|
108
|
+
if pos < 0:
|
|
109
|
+
return False
|
|
110
|
+
if entml_invoke_open_is_actionable(content, pos, known_names=known_names):
|
|
111
|
+
gt = content.find(">", pos)
|
|
112
|
+
if gt < 0 or gt >= close_pos:
|
|
113
|
+
search = pos - 1
|
|
114
|
+
continue
|
|
115
|
+
inner_close = content.find(_INVOKE_CLOSE, gt + 1, close_pos)
|
|
116
|
+
if inner_close < 0:
|
|
117
|
+
return True
|
|
118
|
+
search = pos - 1
|
|
119
|
+
continue
|
|
120
|
+
search = pos - 1
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _invoke_close_should_strip(
|
|
125
|
+
content: str,
|
|
126
|
+
close_pos: int,
|
|
127
|
+
*,
|
|
128
|
+
known_names: Optional[Set[str]] = None,
|
|
129
|
+
) -> bool:
|
|
130
|
+
"""是否应剥离 ``</entml:invoke>``(孤儿噪声或真实工具块残留)。"""
|
|
131
|
+
if _actionable_invoke_open_before_close(content, close_pos, known_names=known_names):
|
|
132
|
+
return True
|
|
133
|
+
open_pos = content.rfind(_INVOKE_OPEN_PREFIX, 0, close_pos)
|
|
134
|
+
if open_pos < 0:
|
|
135
|
+
return True
|
|
136
|
+
return entml_invoke_open_is_actionable(content, open_pos, known_names=known_names)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def iter_actionable_entml_invoke_blocks(
|
|
140
|
+
text: str,
|
|
141
|
+
*,
|
|
142
|
+
known_names: Optional[Set[str]] = None,
|
|
143
|
+
) -> Iterator[Tuple[int, int, str, str]]:
|
|
144
|
+
"""迭代含真实 ``name`` 的完整 ``<entml:invoke>…</entml:invoke>`` 块。"""
|
|
145
|
+
if not text:
|
|
146
|
+
return
|
|
147
|
+
search_from = 0
|
|
148
|
+
prefix_len = len(_INVOKE_OPEN_PREFIX)
|
|
149
|
+
close_len = len(_INVOKE_CLOSE)
|
|
150
|
+
while search_from < len(text):
|
|
151
|
+
pos = text.find(_INVOKE_OPEN_PREFIX, search_from)
|
|
152
|
+
if pos < 0:
|
|
153
|
+
break
|
|
154
|
+
if not entml_invoke_open_is_actionable(text, pos, known_names=known_names):
|
|
155
|
+
gt = text.find(">", pos + prefix_len)
|
|
156
|
+
search_from = (gt + 1) if gt >= 0 else (pos + prefix_len)
|
|
157
|
+
continue
|
|
158
|
+
gt = text.find(">", pos + prefix_len)
|
|
159
|
+
if gt < 0:
|
|
160
|
+
break
|
|
161
|
+
close = text.find(_INVOKE_CLOSE, gt + 1)
|
|
162
|
+
if close < 0:
|
|
163
|
+
break
|
|
164
|
+
attrs = text[pos + prefix_len : gt]
|
|
165
|
+
body = text[gt + 1 : close]
|
|
166
|
+
yield pos, close + close_len, attrs, body
|
|
167
|
+
search_from = close + close_len
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _strip_orphan_invoke_tags(
|
|
171
|
+
content: str,
|
|
172
|
+
*,
|
|
173
|
+
known_names: Optional[Set[str]] = None,
|
|
174
|
+
) -> str:
|
|
175
|
+
"""仅剥离带真实 name 的 invoke 孤儿标签;保留 prose 中的 ``<entml:invoke>`` 提及。"""
|
|
176
|
+
pattern = re.compile(r"</?entml:invoke\b[^>]*/?>", re.DOTALL)
|
|
177
|
+
|
|
178
|
+
def repl(match: re.Match[str]) -> str:
|
|
179
|
+
tag = match.group(0)
|
|
180
|
+
if tag.startswith("</"):
|
|
181
|
+
if _invoke_close_should_strip(content, match.start(), known_names=known_names):
|
|
182
|
+
return ""
|
|
183
|
+
return tag
|
|
184
|
+
if entml_invoke_open_is_actionable(content, match.start(), known_names=known_names):
|
|
185
|
+
return ""
|
|
186
|
+
return tag
|
|
187
|
+
|
|
188
|
+
return pattern.sub(repl, content)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _strip_orphan_non_invoke_tool_tags(content: str) -> str:
|
|
192
|
+
return re.sub(
|
|
193
|
+
r"</?entml:(?:function_calls|parameter|parameters)\b[^>]*/?>",
|
|
194
|
+
"",
|
|
195
|
+
content,
|
|
196
|
+
flags=re.DOTALL,
|
|
197
|
+
)
|