code-muse 0.0.1__py3-none-any.whl
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.
- code_muse/__init__.py +26 -0
- code_muse/__main__.py +10 -0
- code_muse/agents/__init__.py +31 -0
- code_muse/agents/_builder.py +214 -0
- code_muse/agents/_compaction.py +506 -0
- code_muse/agents/_diagnostics.py +171 -0
- code_muse/agents/_history.py +382 -0
- code_muse/agents/_key_listeners.py +148 -0
- code_muse/agents/_non_streaming_render.py +148 -0
- code_muse/agents/_runtime.py +596 -0
- code_muse/agents/agent_creator_agent.py +603 -0
- code_muse/agents/agent_helios.py +47 -0
- code_muse/agents/agent_manager.py +740 -0
- code_muse/agents/agent_muse.py +78 -0
- code_muse/agents/agent_planning.py +44 -0
- code_muse/agents/agent_qa_melpomene.py +207 -0
- code_muse/agents/base_agent.py +194 -0
- code_muse/agents/event_stream_handler.py +361 -0
- code_muse/agents/json_agent.py +201 -0
- code_muse/agents/prompt_v3.py +521 -0
- code_muse/agents/subagent_stream_handler.py +273 -0
- code_muse/callbacks.py +941 -0
- code_muse/chatgpt_codex_client.py +333 -0
- code_muse/claude_cache_client.py +853 -0
- code_muse/cli_runner/__init__.py +319 -0
- code_muse/cli_runner/args.py +63 -0
- code_muse/cli_runner/loop.py +510 -0
- code_muse/cli_runner/resume.py +72 -0
- code_muse/cli_runner/runner.py +161 -0
- code_muse/command_line/__init__.py +1 -0
- code_muse/command_line/add_model_menu.py +1331 -0
- code_muse/command_line/agent_menu.py +674 -0
- code_muse/command_line/attachments.py +397 -0
- code_muse/command_line/autosave_menu.py +709 -0
- code_muse/command_line/clipboard.py +528 -0
- code_muse/command_line/colors_menu.py +530 -0
- code_muse/command_line/command_handler.py +262 -0
- code_muse/command_line/command_registry.py +150 -0
- code_muse/command_line/config_commands.py +711 -0
- code_muse/command_line/core_commands.py +740 -0
- code_muse/command_line/diff_menu.py +865 -0
- code_muse/command_line/file_path_completion.py +73 -0
- code_muse/command_line/load_context_completion.py +57 -0
- code_muse/command_line/model_picker_completion.py +512 -0
- code_muse/command_line/model_settings_menu.py +983 -0
- code_muse/command_line/onboarding_slides.py +162 -0
- code_muse/command_line/onboarding_wizard.py +337 -0
- code_muse/command_line/pagination.py +41 -0
- code_muse/command_line/pin_command_completion.py +329 -0
- code_muse/command_line/prompt_toolkit_completion.py +886 -0
- code_muse/command_line/session_commands.py +304 -0
- code_muse/command_line/shell_passthrough.py +145 -0
- code_muse/command_line/skills_completion.py +158 -0
- code_muse/command_line/types.py +18 -0
- code_muse/command_line/uc_menu.py +908 -0
- code_muse/command_line/utils.py +105 -0
- code_muse/command_line/wiggum_state.py +77 -0
- code_muse/config.py +1138 -0
- code_muse/config_agent.py +168 -0
- code_muse/config_appearance.py +241 -0
- code_muse/config_model.py +357 -0
- code_muse/config_security.py +73 -0
- code_muse/error_logging.py +132 -0
- code_muse/evals/__init__.py +35 -0
- code_muse/evals/eval_helpers.py +81 -0
- code_muse/evals/eval_runner.py +299 -0
- code_muse/evals/sample_evals/__init__.py +1 -0
- code_muse/evals/sample_evals/eval_frugal_reads.py +59 -0
- code_muse/evals/sample_evals/eval_memory_planning.py +31 -0
- code_muse/evals/sample_evals/eval_shell_efficiency.py +39 -0
- code_muse/evals/sample_evals/eval_tool_masking.py +33 -0
- code_muse/fs_scan_cache/__init__.py +31 -0
- code_muse/fs_scan_cache/invalidation_hooks.py +89 -0
- code_muse/fs_scan_cache/scan_cache_core.cpython-314-darwin.so +0 -0
- code_muse/fs_scan_cache/scan_cache_core.pyx +203 -0
- code_muse/fs_scan_cache/tool_integration.py +309 -0
- code_muse/fs_scan_cache/ttl_policy.py +44 -0
- code_muse/gemini_code_assist.py +383 -0
- code_muse/gemini_model.py +838 -0
- code_muse/hook_engine/README.md +105 -0
- code_muse/hook_engine/__init__.py +21 -0
- code_muse/hook_engine/aliases.py +153 -0
- code_muse/hook_engine/engine.py +221 -0
- code_muse/hook_engine/executor.py +347 -0
- code_muse/hook_engine/matcher.py +154 -0
- code_muse/hook_engine/models.py +245 -0
- code_muse/hook_engine/registry.py +114 -0
- code_muse/hook_engine/trust.py +268 -0
- code_muse/hook_engine/validator.py +144 -0
- code_muse/http_utils.py +360 -0
- code_muse/keymap.py +128 -0
- code_muse/list_filtering.py +26 -0
- code_muse/main.py +10 -0
- code_muse/messaging/__init__.py +259 -0
- code_muse/messaging/bus.py +621 -0
- code_muse/messaging/commands.py +166 -0
- code_muse/messaging/markdown_patches.py +57 -0
- code_muse/messaging/message_queue.py +397 -0
- code_muse/messaging/messages.py +591 -0
- code_muse/messaging/queue_console.py +269 -0
- code_muse/messaging/renderers.py +308 -0
- code_muse/messaging/rich_renderer.py +1158 -0
- code_muse/messaging/shimmer.py +154 -0
- code_muse/messaging/spinner/__init__.py +87 -0
- code_muse/messaging/spinner/console_spinner.py +250 -0
- code_muse/messaging/spinner/spinner_base.py +82 -0
- code_muse/messaging/subagent_console.py +458 -0
- code_muse/model_factory.py +1203 -0
- code_muse/model_switching.py +59 -0
- code_muse/model_utils.py +156 -0
- code_muse/models.json +66 -0
- code_muse/models_cache/__init__.py +26 -0
- code_muse/models_cache/blocking_lru_cache.py +98 -0
- code_muse/models_cache/cache_writer.py +86 -0
- code_muse/models_cache/sha256_hash.cpython-314-darwin.so +0 -0
- code_muse/models_cache/sha256_hash.pyx +34 -0
- code_muse/models_cache/startup_integration.py +75 -0
- code_muse/models_dev_api.json +1 -0
- code_muse/models_dev_parser.py +590 -0
- code_muse/motion.py +126 -0
- code_muse/plugins/__init__.py +471 -0
- code_muse/plugins/agent_skills/__init__.py +32 -0
- code_muse/plugins/agent_skills/config.py +176 -0
- code_muse/plugins/agent_skills/discovery.py +309 -0
- code_muse/plugins/agent_skills/downloader.py +389 -0
- code_muse/plugins/agent_skills/installer.py +19 -0
- code_muse/plugins/agent_skills/metadata.py +293 -0
- code_muse/plugins/agent_skills/prompt_builder.py +66 -0
- code_muse/plugins/agent_skills/register_callbacks.py +298 -0
- code_muse/plugins/agent_skills/remote_catalog.py +320 -0
- code_muse/plugins/agent_skills/skill_catalog.py +254 -0
- code_muse/plugins/agent_skills/skills_install_menu.py +690 -0
- code_muse/plugins/agent_skills/skills_menu.py +791 -0
- code_muse/plugins/autonomous_memory/__init__.py +39 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-darwin.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/autonomous_memory/bm25_scorer.pyx +291 -0
- code_muse/plugins/autonomous_memory/consolidation.py +82 -0
- code_muse/plugins/autonomous_memory/extraction.py +382 -0
- code_muse/plugins/autonomous_memory/lease_lock.py +105 -0
- code_muse/plugins/autonomous_memory/memory_injection.py +59 -0
- code_muse/plugins/autonomous_memory/register_callbacks.py +268 -0
- code_muse/plugins/autonomous_memory/secret_scanner.py +62 -0
- code_muse/plugins/autonomous_memory/session_scanner.py +163 -0
- code_muse/plugins/aws_bedrock/__init__.py +14 -0
- code_muse/plugins/aws_bedrock/config.py +99 -0
- code_muse/plugins/aws_bedrock/register_callbacks.py +241 -0
- code_muse/plugins/aws_bedrock/utils.py +153 -0
- code_muse/plugins/azure_foundry/README.md +238 -0
- code_muse/plugins/azure_foundry/__init__.py +15 -0
- code_muse/plugins/azure_foundry/config.py +125 -0
- code_muse/plugins/azure_foundry/discovery.py +187 -0
- code_muse/plugins/azure_foundry/register_callbacks.py +495 -0
- code_muse/plugins/azure_foundry/token.py +180 -0
- code_muse/plugins/azure_foundry/utils.py +345 -0
- code_muse/plugins/build_filter/__init__.py +1 -0
- code_muse/plugins/build_filter/register_callbacks.py +201 -0
- code_muse/plugins/build_filter/strategies/__init__.py +1 -0
- code_muse/plugins/build_filter/strategies/build.py +397 -0
- code_muse/plugins/chatgpt_oauth/__init__.py +6 -0
- code_muse/plugins/chatgpt_oauth/config.py +52 -0
- code_muse/plugins/chatgpt_oauth/oauth_flow.py +338 -0
- code_muse/plugins/chatgpt_oauth/register_callbacks.py +172 -0
- code_muse/plugins/chatgpt_oauth/test_plugin.py +301 -0
- code_muse/plugins/chatgpt_oauth/utils.py +538 -0
- code_muse/plugins/checkpointing/__init__.py +29 -0
- code_muse/plugins/checkpointing/checkpoint_hook.py +51 -0
- code_muse/plugins/checkpointing/conversation_snapshots.py +117 -0
- code_muse/plugins/checkpointing/register_callbacks.py +51 -0
- code_muse/plugins/checkpointing/restore_command.py +263 -0
- code_muse/plugins/checkpointing/rewind_shortcut.py +88 -0
- code_muse/plugins/checkpointing/shadow_git.py +90 -0
- code_muse/plugins/claude_code_hooks/__init__.py +1 -0
- code_muse/plugins/claude_code_hooks/config.py +188 -0
- code_muse/plugins/claude_code_hooks/register_callbacks.py +208 -0
- code_muse/plugins/claude_code_oauth/README.md +167 -0
- code_muse/plugins/claude_code_oauth/SETUP.md +93 -0
- code_muse/plugins/claude_code_oauth/__init__.py +25 -0
- code_muse/plugins/claude_code_oauth/config.py +52 -0
- code_muse/plugins/claude_code_oauth/fast_mode.py +124 -0
- code_muse/plugins/claude_code_oauth/prompt_handler.py +63 -0
- code_muse/plugins/claude_code_oauth/register_callbacks.py +547 -0
- code_muse/plugins/claude_code_oauth/test_fast_mode.py +165 -0
- code_muse/plugins/claude_code_oauth/test_plugin.py +283 -0
- code_muse/plugins/claude_code_oauth/token_refresh_heartbeat.py +237 -0
- code_muse/plugins/claude_code_oauth/utils.py +664 -0
- code_muse/plugins/copilot_auth/__init__.py +11 -0
- code_muse/plugins/copilot_auth/config.py +91 -0
- code_muse/plugins/copilot_auth/reasoning_client.py +409 -0
- code_muse/plugins/copilot_auth/register_callbacks.py +461 -0
- code_muse/plugins/copilot_auth/utils.py +584 -0
- code_muse/plugins/custom_commands/__init__.py +14 -0
- code_muse/plugins/custom_commands/args_injection.py +82 -0
- code_muse/plugins/custom_commands/command_discovery.py +89 -0
- code_muse/plugins/custom_commands/command_toml_schema.py +71 -0
- code_muse/plugins/custom_commands/register_callbacks.py +176 -0
- code_muse/plugins/customizable_commands/__init__.py +0 -0
- code_muse/plugins/customizable_commands/register_callbacks.py +136 -0
- code_muse/plugins/destructive_command_guard/__init__.py +14 -0
- code_muse/plugins/destructive_command_guard/detector.py +375 -0
- code_muse/plugins/destructive_command_guard/register_callbacks.py +148 -0
- code_muse/plugins/example_custom_command/README.md +280 -0
- code_muse/plugins/example_custom_command/register_callbacks.py +51 -0
- code_muse/plugins/file_permission_handler/__init__.py +4 -0
- code_muse/plugins/file_permission_handler/register_callbacks.py +441 -0
- code_muse/plugins/filter_engine/__init__.py +30 -0
- code_muse/plugins/filter_engine/classifier.py +153 -0
- code_muse/plugins/filter_engine/content_detector.py +184 -0
- code_muse/plugins/filter_engine/dispatcher.py +244 -0
- code_muse/plugins/filter_engine/register_callbacks.py +188 -0
- code_muse/plugins/filter_engine/registry.py +279 -0
- code_muse/plugins/filter_engine/strategies/__init__.py +8 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/ast_compressor.pyx +348 -0
- code_muse/plugins/filter_engine/strategies/ast_parser.py +167 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/code.pyx +584 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/git.pyx +438 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_compressor.pyx +253 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/json_patterns.pyx +178 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/lint.pyx +626 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-darwin.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/plugins/filter_engine/strategies/test.pyx +431 -0
- code_muse/plugins/filter_engine/verbosity.py +63 -0
- code_muse/plugins/force_push_guard/__init__.py +5 -0
- code_muse/plugins/force_push_guard/detector.py +96 -0
- code_muse/plugins/force_push_guard/register_callbacks.py +144 -0
- code_muse/plugins/force_push_guard/test_detector.py +143 -0
- code_muse/plugins/frontend_emitter/__init__.py +25 -0
- code_muse/plugins/frontend_emitter/emitter.py +121 -0
- code_muse/plugins/frontend_emitter/register_callbacks.py +259 -0
- code_muse/plugins/gac/__init__.py +4 -0
- code_muse/plugins/gac/git_ops.py +136 -0
- code_muse/plugins/gac/prompt.py +191 -0
- code_muse/plugins/gac/register_callbacks.py +82 -0
- code_muse/plugins/hook_creator/__init__.py +1 -0
- code_muse/plugins/hook_creator/register_callbacks.py +34 -0
- code_muse/plugins/hook_manager/__init__.py +1 -0
- code_muse/plugins/hook_manager/config.py +289 -0
- code_muse/plugins/hook_manager/hooks_menu.py +563 -0
- code_muse/plugins/hook_manager/register_callbacks.py +227 -0
- code_muse/plugins/hook_monitor/register_callbacks.py +36 -0
- code_muse/plugins/mindpack/__init__.py +0 -0
- code_muse/plugins/mindpack/factory.py +930 -0
- code_muse/plugins/mindpack/judge.py +573 -0
- code_muse/plugins/mindpack/memory.py +100 -0
- code_muse/plugins/mindpack/mindpack_menu.py +1552 -0
- code_muse/plugins/mindpack/orchestration.py +605 -0
- code_muse/plugins/mindpack/register_callbacks.py +175 -0
- code_muse/plugins/mindpack/schemas.py +358 -0
- code_muse/plugins/mindpack/tools.py +387 -0
- code_muse/plugins/oauth_muse_html.py +226 -0
- code_muse/plugins/ollama_setup/__init__.py +5 -0
- code_muse/plugins/ollama_setup/completer.py +36 -0
- code_muse/plugins/ollama_setup/register_callbacks.py +410 -0
- code_muse/plugins/plan_command/__init__.py +0 -0
- code_muse/plugins/plan_command/register_callbacks.py +206 -0
- code_muse/plugins/plan_mode/__init__.py +37 -0
- code_muse/plugins/plan_mode/mode_cycling.py +40 -0
- code_muse/plugins/plan_mode/plan_generation.py +68 -0
- code_muse/plugins/plan_mode/plan_hooks.py +74 -0
- code_muse/plugins/plan_mode/plan_mode_tools.py +138 -0
- code_muse/plugins/plan_mode/register_callbacks.py +121 -0
- code_muse/plugins/plugin_trust/register_callbacks.py +140 -0
- code_muse/plugins/policy_engine/__init__.py +46 -0
- code_muse/plugins/policy_engine/approval_flow_integration.py +59 -0
- code_muse/plugins/policy_engine/policy_evaluator.py +75 -0
- code_muse/plugins/policy_engine/policy_file_discovery.py +90 -0
- code_muse/plugins/policy_engine/policy_toml_schema.py +115 -0
- code_muse/plugins/policy_engine/register_callbacks.py +112 -0
- code_muse/plugins/pop_command/__init__.py +1 -0
- code_muse/plugins/pop_command/register_callbacks.py +189 -0
- code_muse/plugins/prompt_newline/__init__.py +13 -0
- code_muse/plugins/prompt_newline/config.py +19 -0
- code_muse/plugins/prompt_newline/register_callbacks.py +159 -0
- code_muse/plugins/safety_status/__init__.py +0 -0
- code_muse/plugins/safety_status/register_callbacks.py +113 -0
- code_muse/plugins/semantic_compression/__init__.py +6 -0
- code_muse/plugins/semantic_compression/compressor.py +295 -0
- code_muse/plugins/semantic_compression/config.py +123 -0
- code_muse/plugins/semantic_compression/register_callbacks.py +320 -0
- code_muse/plugins/shell_minimizer/__init__.py +50 -0
- code_muse/plugins/shell_minimizer/builtin_filters.toml +393 -0
- code_muse/plugins/shell_minimizer/pipeline.py +556 -0
- code_muse/plugins/shell_minimizer/primitives.py +482 -0
- code_muse/plugins/shell_minimizer/register_callbacks.py +276 -0
- code_muse/plugins/shell_safety/__init__.py +6 -0
- code_muse/plugins/shell_safety/agent_shell_safety.py +69 -0
- code_muse/plugins/shell_safety/command_cache.py +149 -0
- code_muse/plugins/shell_safety/register_callbacks.py +202 -0
- code_muse/plugins/synthetic_status/__init__.py +1 -0
- code_muse/plugins/synthetic_status/register_callbacks.py +128 -0
- code_muse/plugins/synthetic_status/status_api.py +145 -0
- code_muse/plugins/token_caching/__init__.py +21 -0
- code_muse/plugins/token_caching/cache_hit_tracking.py +128 -0
- code_muse/plugins/token_caching/cacheable_prefix_detection.py +28 -0
- code_muse/plugins/token_caching/register_callbacks.py +54 -0
- code_muse/plugins/token_caching/stats_display.py +35 -0
- code_muse/plugins/token_tracking/__init__.py +26 -0
- code_muse/plugins/token_tracking/database.py +381 -0
- code_muse/plugins/token_tracking/edit_analyzer.py +97 -0
- code_muse/plugins/token_tracking/record.py +55 -0
- code_muse/plugins/token_tracking/register_callbacks.py +277 -0
- code_muse/plugins/token_tracking/reports.py +329 -0
- code_muse/plugins/universal_constructor/__init__.py +13 -0
- code_muse/plugins/universal_constructor/models.py +136 -0
- code_muse/plugins/universal_constructor/register_callbacks.py +47 -0
- code_muse/plugins/universal_constructor/registry.py +390 -0
- code_muse/plugins/universal_constructor/runner.py +474 -0
- code_muse/plugins/universal_constructor/safety.py +440 -0
- code_muse/plugins/universal_constructor/sandbox.py +584 -0
- code_muse/provider_identity.py +105 -0
- code_muse/pydantic_patches.py +410 -0
- code_muse/reopenable_async_client.py +233 -0
- code_muse/round_robin_model.py +151 -0
- code_muse/secret_storage.py +74 -0
- code_muse/security/__init__.py +1 -0
- code_muse/security/redaction.cpython-314-darwin.so +0 -0
- code_muse/security/redaction.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/security/redaction.pyx +135 -0
- code_muse/session_storage.py +565 -0
- code_muse/status_display.py +261 -0
- code_muse/stream_parser/__init__.py +76 -0
- code_muse/stream_parser/assistant_text_parser.py +90 -0
- code_muse/stream_parser/citation_parser.py +76 -0
- code_muse/stream_parser/inline_hidden_tag_parser.py +236 -0
- code_muse/stream_parser/proposed_plan_parser.py +158 -0
- code_muse/stream_parser/stream_text_chunk.py +23 -0
- code_muse/stream_parser/stream_text_parser.py +27 -0
- code_muse/stream_parser/tagged_line_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/tagged_line_parser.pyx +251 -0
- code_muse/stream_parser/utf8_stream_parser.cpython-314-darwin.so +0 -0
- code_muse/stream_parser/utf8_stream_parser.pyx +206 -0
- code_muse/summarization_agent.py +308 -0
- code_muse/terminal_utils.cpython-314-darwin.so +0 -0
- code_muse/terminal_utils.cpython-314-x86_64-linux-gnu.so +0 -0
- code_muse/terminal_utils.pyx +483 -0
- code_muse/tools/__init__.py +459 -0
- code_muse/tools/agent_tools.py +613 -0
- code_muse/tools/ask_user_question/__init__.py +26 -0
- code_muse/tools/ask_user_question/constants.py +73 -0
- code_muse/tools/ask_user_question/demo_tui.py +55 -0
- code_muse/tools/ask_user_question/handler.py +232 -0
- code_muse/tools/ask_user_question/models.py +302 -0
- code_muse/tools/ask_user_question/registration.py +37 -0
- code_muse/tools/ask_user_question/renderers.py +336 -0
- code_muse/tools/ask_user_question/terminal_ui.py +327 -0
- code_muse/tools/ask_user_question/theme.py +156 -0
- code_muse/tools/ask_user_question/tui_loop.py +422 -0
- code_muse/tools/background_jobs.py +99 -0
- code_muse/tools/browser/__init__.py +37 -0
- code_muse/tools/browser/browser_control.py +289 -0
- code_muse/tools/browser/browser_interactions.py +545 -0
- code_muse/tools/browser/browser_locators.py +640 -0
- code_muse/tools/browser/browser_manager.py +376 -0
- code_muse/tools/browser/browser_navigation.py +251 -0
- code_muse/tools/browser/browser_screenshot.py +180 -0
- code_muse/tools/browser/browser_scripts.py +462 -0
- code_muse/tools/browser/browser_workflows.py +222 -0
- code_muse/tools/chrome_cdp/__init__.py +1070 -0
- code_muse/tools/chrome_cdp/register_callbacks.py +61 -0
- code_muse/tools/command_runner.py +1401 -0
- code_muse/tools/common.py +1407 -0
- code_muse/tools/display.py +87 -0
- code_muse/tools/file_modifications.py +1099 -0
- code_muse/tools/file_operations.py +860 -0
- code_muse/tools/image_tools.py +185 -0
- code_muse/tools/meetin_proxy/__init__.py +243 -0
- code_muse/tools/meetin_proxy/capture_addon.py +82 -0
- code_muse/tools/meetin_proxy/proxy_manager.py +326 -0
- code_muse/tools/meetin_proxy/register_callbacks.py +45 -0
- code_muse/tools/path_policy.py +219 -0
- code_muse/tools/skills_tools.py +586 -0
- code_muse/tools/subagent_context.py +158 -0
- code_muse/tools/tools_content.py +50 -0
- code_muse/tools/universal_constructor.py +965 -0
- code_muse/uvx_detection.py +241 -0
- code_muse/version_checker.py +86 -0
- code_muse-0.0.1.data/data/code_muse/models.json +66 -0
- code_muse-0.0.1.data/data/code_muse/models_dev_api.json +1 -0
- code_muse-0.0.1.dist-info/METADATA +845 -0
- code_muse-0.0.1.dist-info/RECORD +394 -0
- code_muse-0.0.1.dist-info/WHEEL +4 -0
- code_muse-0.0.1.dist-info/entry_points.txt +2 -0
- code_muse-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.live import Live
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.spinner import Spinner
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
11
|
+
# Registers the "radar" spinner in Rich's SPINNERS dictionary
|
|
12
|
+
import code_muse.messaging.spinner # noqa: F401
|
|
13
|
+
|
|
14
|
+
# Global variable to track current token per second rate
|
|
15
|
+
CURRENT_TOKEN_RATE = 0.0
|
|
16
|
+
# FREE-THREADED: _TOKEN_RATE_LOCK guards sync-only rate updates from display threads.
|
|
17
|
+
_TOKEN_RATE_LOCK = threading.Lock()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StatusDisplay:
|
|
21
|
+
"""
|
|
22
|
+
Displays real-time status information during model execution,
|
|
23
|
+
including token per second rate and rotating loading messages.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, console: Console):
|
|
27
|
+
self.console = console
|
|
28
|
+
self.token_count = 0
|
|
29
|
+
self.start_time = None
|
|
30
|
+
self.last_update_time = None
|
|
31
|
+
self.last_token_count = 0
|
|
32
|
+
self.current_rate = 0
|
|
33
|
+
self.is_active = False
|
|
34
|
+
self.task = None
|
|
35
|
+
self.live = None
|
|
36
|
+
self.loading_messages = [
|
|
37
|
+
"Fetching...",
|
|
38
|
+
"Sniffing around...",
|
|
39
|
+
"Wagging tail...",
|
|
40
|
+
"Pawsing for a moment...",
|
|
41
|
+
"Chasing tail...",
|
|
42
|
+
"Digging up results...",
|
|
43
|
+
"Barking at the data...",
|
|
44
|
+
"Rolling over...",
|
|
45
|
+
"Panting with excitement...",
|
|
46
|
+
"Chewing on it...",
|
|
47
|
+
"Prancing along...",
|
|
48
|
+
"Howling at the code...",
|
|
49
|
+
"Snuggling up to the task...",
|
|
50
|
+
"Bounding through data...",
|
|
51
|
+
"Puppy pondering...",
|
|
52
|
+
]
|
|
53
|
+
self.current_message_index = 0
|
|
54
|
+
self.spinner = Spinner("radar", text="")
|
|
55
|
+
|
|
56
|
+
def _calculate_rate(self) -> float:
|
|
57
|
+
"""Calculate the current token rate"""
|
|
58
|
+
current_time = time.time()
|
|
59
|
+
if self.last_update_time:
|
|
60
|
+
time_diff = current_time - self.last_update_time
|
|
61
|
+
token_diff = self.token_count - self.last_token_count
|
|
62
|
+
if time_diff > 0:
|
|
63
|
+
rate = token_diff / time_diff
|
|
64
|
+
# Smooth the rate calculation with the current rate
|
|
65
|
+
if self.current_rate > 0:
|
|
66
|
+
self.current_rate = (self.current_rate * 0.7) + (rate * 0.3)
|
|
67
|
+
else:
|
|
68
|
+
self.current_rate = rate
|
|
69
|
+
|
|
70
|
+
# Only ensure rate is not negative
|
|
71
|
+
self.current_rate = max(0, self.current_rate)
|
|
72
|
+
|
|
73
|
+
# Update the global rate for other components to access
|
|
74
|
+
global CURRENT_TOKEN_RATE
|
|
75
|
+
with _TOKEN_RATE_LOCK:
|
|
76
|
+
CURRENT_TOKEN_RATE = self.current_rate
|
|
77
|
+
|
|
78
|
+
self.last_update_time = current_time
|
|
79
|
+
self.last_token_count = self.token_count
|
|
80
|
+
return self.current_rate
|
|
81
|
+
|
|
82
|
+
def update_rate_from_sse(
|
|
83
|
+
self, completion_tokens: int, completion_time: float
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Update the token rate directly using SSE time_info data
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
completion_tokens: Number of tokens in the completion (from SSE stream)
|
|
89
|
+
completion_time: Time taken for completion in seconds (from SSE stream)
|
|
90
|
+
"""
|
|
91
|
+
if completion_time > 0:
|
|
92
|
+
# Using the direct t/s formula: tokens / time
|
|
93
|
+
rate = completion_tokens / completion_time
|
|
94
|
+
|
|
95
|
+
# Use a lighter smoothing for this more accurate data
|
|
96
|
+
if self.current_rate > 0:
|
|
97
|
+
self.current_rate = (self.current_rate * 0.3) + (
|
|
98
|
+
rate * 0.7
|
|
99
|
+
) # Weight SSE data more heavily
|
|
100
|
+
else:
|
|
101
|
+
self.current_rate = rate
|
|
102
|
+
|
|
103
|
+
# Update the global rate
|
|
104
|
+
global CURRENT_TOKEN_RATE
|
|
105
|
+
with _TOKEN_RATE_LOCK:
|
|
106
|
+
CURRENT_TOKEN_RATE = self.current_rate
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def get_current_rate() -> float:
|
|
110
|
+
"""Get the current token rate for use in other components"""
|
|
111
|
+
global CURRENT_TOKEN_RATE
|
|
112
|
+
with _TOKEN_RATE_LOCK:
|
|
113
|
+
return CURRENT_TOKEN_RATE
|
|
114
|
+
|
|
115
|
+
def update_token_count(self, tokens: int) -> None:
|
|
116
|
+
"""Update the token count and recalculate the rate"""
|
|
117
|
+
# Reset timing if this is the first update of a new task
|
|
118
|
+
if self.start_time is None:
|
|
119
|
+
self.start_time = time.time()
|
|
120
|
+
self.last_update_time = self.start_time
|
|
121
|
+
# Reset token counters for new task
|
|
122
|
+
self.last_token_count = 0
|
|
123
|
+
self.current_rate = 0.0
|
|
124
|
+
# Set initial token count
|
|
125
|
+
self.token_count = tokens if tokens >= 0 else 0
|
|
126
|
+
return # Don't calculate rate on first initialization
|
|
127
|
+
|
|
128
|
+
# Allow for incremental updates (common for streaming) or absolute updates
|
|
129
|
+
if tokens > self.token_count or tokens < 0:
|
|
130
|
+
# Incremental update or reset
|
|
131
|
+
self.token_count = tokens if tokens >= 0 else 0
|
|
132
|
+
else:
|
|
133
|
+
# If tokens <= current count but > 0, treat as incremental
|
|
134
|
+
# This handles simulated token streaming
|
|
135
|
+
self.token_count += tokens
|
|
136
|
+
|
|
137
|
+
self._calculate_rate()
|
|
138
|
+
|
|
139
|
+
def _get_status_panel(self) -> Panel:
|
|
140
|
+
"""Generate a status panel with current rate and animated message"""
|
|
141
|
+
rate_text = (
|
|
142
|
+
f"{self.current_rate:.1f} t/s" if self.current_rate > 0 else "Warming up..."
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Update spinner
|
|
146
|
+
self.spinner.update()
|
|
147
|
+
|
|
148
|
+
# Rotate through loading messages every few updates
|
|
149
|
+
if int(time.time() * 2) % 4 == 0:
|
|
150
|
+
self.current_message_index = (self.current_message_index + 1) % len(
|
|
151
|
+
self.loading_messages
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Create a highly visible status message
|
|
155
|
+
status_text = Text.assemble(
|
|
156
|
+
Text(f"⏳ {rate_text} ", style="bold green"),
|
|
157
|
+
str(self.spinner),
|
|
158
|
+
Text(
|
|
159
|
+
f" {self.loading_messages[self.current_message_index]} ⏳",
|
|
160
|
+
style="bold yellow",
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Use expanded panel with more visible formatting
|
|
165
|
+
return Panel(
|
|
166
|
+
status_text,
|
|
167
|
+
title="[bold blue]Muse Status[/bold blue]",
|
|
168
|
+
border_style="bright_blue",
|
|
169
|
+
expand=False,
|
|
170
|
+
padding=(1, 2),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def _get_status_text(self) -> Text:
|
|
174
|
+
"""Generate a status text with current rate and animated message"""
|
|
175
|
+
rate_text = (
|
|
176
|
+
f"{self.current_rate:.1f} t/s" if self.current_rate > 0 else "Warming up..."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Update spinner
|
|
180
|
+
self.spinner.update()
|
|
181
|
+
|
|
182
|
+
# Rotate through loading messages
|
|
183
|
+
self.current_message_index = (self.current_message_index + 1) % len(
|
|
184
|
+
self.loading_messages
|
|
185
|
+
)
|
|
186
|
+
message = self.loading_messages[self.current_message_index]
|
|
187
|
+
|
|
188
|
+
# Create a highly visible status text
|
|
189
|
+
return Text.assemble(
|
|
190
|
+
Text(f"[>>] {rate_text}", style="bold green"),
|
|
191
|
+
Text(f" {message}", style="yellow"),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
async def _update_display(self) -> None:
|
|
195
|
+
"""Update the display continuously while active using Rich Live display"""
|
|
196
|
+
# Lazy import to avoid circular dependency during module initialization
|
|
197
|
+
from code_muse.messaging import emit_info
|
|
198
|
+
|
|
199
|
+
# Add a newline to ensure we're below the blue bar
|
|
200
|
+
emit_info("")
|
|
201
|
+
|
|
202
|
+
# Create a Live display that will update in-place
|
|
203
|
+
with Live(
|
|
204
|
+
self._get_status_text(),
|
|
205
|
+
console=self.console,
|
|
206
|
+
refresh_per_second=2, # Update twice per second
|
|
207
|
+
transient=False, # Keep the final state visible
|
|
208
|
+
) as live:
|
|
209
|
+
# Keep updating the live display while active
|
|
210
|
+
while self.is_active:
|
|
211
|
+
live.update(self._get_status_text())
|
|
212
|
+
await asyncio.sleep(0.5)
|
|
213
|
+
|
|
214
|
+
def start(self) -> None:
|
|
215
|
+
"""Start the status display"""
|
|
216
|
+
if not self.is_active:
|
|
217
|
+
self.is_active = True
|
|
218
|
+
self.start_time = time.time()
|
|
219
|
+
self.last_update_time = self.start_time
|
|
220
|
+
self.token_count = 0
|
|
221
|
+
self.last_token_count = 0
|
|
222
|
+
self.current_rate = 0
|
|
223
|
+
self.task = asyncio.create_task(self._update_display())
|
|
224
|
+
|
|
225
|
+
def stop(self) -> None:
|
|
226
|
+
"""Stop the status display"""
|
|
227
|
+
# Lazy import to avoid circular dependency during module initialization
|
|
228
|
+
from code_muse.messaging import emit_info
|
|
229
|
+
|
|
230
|
+
if self.is_active:
|
|
231
|
+
self.is_active = False
|
|
232
|
+
if self.task:
|
|
233
|
+
self.task.cancel()
|
|
234
|
+
self.task = None
|
|
235
|
+
|
|
236
|
+
# Print final stats
|
|
237
|
+
elapsed = time.time() - self.start_time if self.start_time else 0
|
|
238
|
+
avg_rate = self.token_count / elapsed if elapsed > 0 else 0
|
|
239
|
+
emit_info(
|
|
240
|
+
f"Completed: {self.token_count} tokens in {elapsed:.1f}s ({avg_rate:.1f} t/s avg)"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Reset state
|
|
244
|
+
self.start_time = None
|
|
245
|
+
self.token_count = 0
|
|
246
|
+
self.last_update_time = None
|
|
247
|
+
self.last_token_count = 0
|
|
248
|
+
self.current_rate = 0
|
|
249
|
+
|
|
250
|
+
# Reset global rate to 0 to avoid affecting subsequent tasks
|
|
251
|
+
global CURRENT_TOKEN_RATE
|
|
252
|
+
with _TOKEN_RATE_LOCK:
|
|
253
|
+
CURRENT_TOKEN_RATE = 0.0
|
|
254
|
+
else:
|
|
255
|
+
# Even if not active, ensure we print stats when stop is called
|
|
256
|
+
# This is for testing purposes
|
|
257
|
+
elapsed = time.time() - self.start_time if self.start_time else 0
|
|
258
|
+
avg_rate = self.token_count / elapsed if elapsed > 0 else 0
|
|
259
|
+
emit_info(
|
|
260
|
+
f"Completed: {self.token_count} tokens in {elapsed:.1f}s ({avg_rate:.1f} t/s avg)"
|
|
261
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Streaming text parsers for assistant markup.
|
|
2
|
+
|
|
3
|
+
This module ports Codex's Rust ``utils/stream-parser`` crate to idiomatic
|
|
4
|
+
Python 3.11+. Parsers are composable: one parser can wrap another,
|
|
5
|
+
delegating and merging output.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from code_muse.stream_parser.assistant_text_parser import (
|
|
9
|
+
AssistantTextChunk,
|
|
10
|
+
AssistantTextStreamParser,
|
|
11
|
+
)
|
|
12
|
+
from code_muse.stream_parser.citation_parser import (
|
|
13
|
+
CITATION_CLOSE,
|
|
14
|
+
CITATION_OPEN,
|
|
15
|
+
CitationStreamParser,
|
|
16
|
+
strip_citations,
|
|
17
|
+
)
|
|
18
|
+
from code_muse.stream_parser.inline_hidden_tag_parser import (
|
|
19
|
+
ExtractedInlineTag,
|
|
20
|
+
InlineHiddenTagParser,
|
|
21
|
+
InlineTagSpec,
|
|
22
|
+
)
|
|
23
|
+
from code_muse.stream_parser.proposed_plan_parser import (
|
|
24
|
+
ProposedPlanParser,
|
|
25
|
+
ProposedPlanSegment,
|
|
26
|
+
ProposedPlanSegmentType,
|
|
27
|
+
extract_proposed_plan_text,
|
|
28
|
+
strip_proposed_plan_blocks,
|
|
29
|
+
)
|
|
30
|
+
from code_muse.stream_parser.stream_text_chunk import StreamTextChunk
|
|
31
|
+
from code_muse.stream_parser.stream_text_parser import StreamTextParser
|
|
32
|
+
from code_muse.stream_parser.tagged_line_parser import (
|
|
33
|
+
TaggedLineParser,
|
|
34
|
+
TaggedLineSegment,
|
|
35
|
+
TaggedLineSegmentNormal,
|
|
36
|
+
TaggedLineSegmentTagDelta,
|
|
37
|
+
TaggedLineSegmentTagEnd,
|
|
38
|
+
TaggedLineSegmentTagStart,
|
|
39
|
+
TagSpec,
|
|
40
|
+
)
|
|
41
|
+
from code_muse.stream_parser.utf8_stream_parser import (
|
|
42
|
+
IncompleteUtf8AtEof,
|
|
43
|
+
InvalidUtf8,
|
|
44
|
+
Utf8StreamParser,
|
|
45
|
+
Utf8StreamParserError,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"AssistantTextChunk",
|
|
50
|
+
"AssistantTextStreamParser",
|
|
51
|
+
"CITATION_CLOSE",
|
|
52
|
+
"CITATION_OPEN",
|
|
53
|
+
"CitationStreamParser",
|
|
54
|
+
"ExtractedInlineTag",
|
|
55
|
+
"IncompleteUtf8AtEof",
|
|
56
|
+
"InlineHiddenTagParser",
|
|
57
|
+
"InlineTagSpec",
|
|
58
|
+
"InvalidUtf8",
|
|
59
|
+
"ProposedPlanParser",
|
|
60
|
+
"ProposedPlanSegment",
|
|
61
|
+
"ProposedPlanSegmentType",
|
|
62
|
+
"StreamTextChunk",
|
|
63
|
+
"StreamTextParser",
|
|
64
|
+
"TagSpec",
|
|
65
|
+
"TaggedLineParser",
|
|
66
|
+
"TaggedLineSegment",
|
|
67
|
+
"TaggedLineSegmentNormal",
|
|
68
|
+
"TaggedLineSegmentTagDelta",
|
|
69
|
+
"TaggedLineSegmentTagEnd",
|
|
70
|
+
"TaggedLineSegmentTagStart",
|
|
71
|
+
"Utf8StreamParser",
|
|
72
|
+
"Utf8StreamParserError",
|
|
73
|
+
"extract_proposed_plan_text",
|
|
74
|
+
"strip_citations",
|
|
75
|
+
"strip_proposed_plan_blocks",
|
|
76
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Combined assistant text parser: citations + plan segments in one pass."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from code_muse.stream_parser.citation_parser import CitationStreamParser
|
|
6
|
+
from code_muse.stream_parser.proposed_plan_parser import (
|
|
7
|
+
ProposedPlanParser,
|
|
8
|
+
ProposedPlanSegment,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class AssistantTextChunk:
|
|
14
|
+
"""Result chunk from :class:`AssistantTextStreamParser`.
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
visible_text: Text safe to render immediately (tags stripped).
|
|
18
|
+
citations: Citation bodies extracted in this chunk.
|
|
19
|
+
plan_segments: Plan boundary / delta segments when plan mode is on.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
visible_text: str = ""
|
|
23
|
+
citations: list[str] = field(default_factory=list)
|
|
24
|
+
plan_segments: list[ProposedPlanSegment] = field(default_factory=list)
|
|
25
|
+
|
|
26
|
+
def is_empty(self) -> bool:
|
|
27
|
+
"""Return True when nothing was produced in this chunk."""
|
|
28
|
+
return not self.visible_text and not self.citations and not self.plan_segments
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AssistantTextStreamParser:
|
|
32
|
+
"""Parses assistant text streaming markup in one pass.
|
|
33
|
+
|
|
34
|
+
Strips ``<oai-mem-citation>`` tags and extracts citation payloads.
|
|
35
|
+
In plan mode, also strips ``<proposed_plan>`` blocks and emits plan
|
|
36
|
+
segments so callers can render or hide plan content independently.
|
|
37
|
+
|
|
38
|
+
The two sub-parsers are wired in series: incoming text first goes through
|
|
39
|
+
:class:`CitationStreamParser`; the resulting visible text is then fed into
|
|
40
|
+
:class:`ProposedPlanParser` when plan mode is enabled.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, plan_mode: bool = False) -> None:
|
|
44
|
+
self.plan_mode = plan_mode
|
|
45
|
+
self._citations = CitationStreamParser()
|
|
46
|
+
self._plan = ProposedPlanParser()
|
|
47
|
+
|
|
48
|
+
def push_str(self, chunk: str) -> AssistantTextChunk:
|
|
49
|
+
"""Feed a new text chunk.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
chunk: Raw assistant text delta.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Chunk containing visible text, any newly-extracted citations, and
|
|
56
|
+
any plan segments when plan mode is enabled.
|
|
57
|
+
"""
|
|
58
|
+
citation_chunk = self._citations.push_str(chunk)
|
|
59
|
+
out = self._parse_visible_text(citation_chunk.visible_text)
|
|
60
|
+
out.citations = citation_chunk.extracted
|
|
61
|
+
return out
|
|
62
|
+
|
|
63
|
+
def finish(self) -> AssistantTextChunk:
|
|
64
|
+
"""Flush any buffered state.
|
|
65
|
+
|
|
66
|
+
Unterminated citations and plan blocks are auto-closed.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Final chunk with trailing visible text, citations, and plan
|
|
70
|
+
segments.
|
|
71
|
+
"""
|
|
72
|
+
citation_chunk = self._citations.finish()
|
|
73
|
+
out = self._parse_visible_text(citation_chunk.visible_text)
|
|
74
|
+
if self.plan_mode:
|
|
75
|
+
tail = self._plan.finish()
|
|
76
|
+
if not tail.is_empty():
|
|
77
|
+
out.visible_text += tail.visible_text
|
|
78
|
+
out.plan_segments.extend(tail.extracted)
|
|
79
|
+
out.citations = citation_chunk.extracted
|
|
80
|
+
return out
|
|
81
|
+
|
|
82
|
+
def _parse_visible_text(self, visible_text: str) -> AssistantTextChunk:
|
|
83
|
+
"""Route citation-visible text through the plan parser when needed."""
|
|
84
|
+
if not self.plan_mode:
|
|
85
|
+
return AssistantTextChunk(visible_text=visible_text)
|
|
86
|
+
plan_chunk = self._plan.push_str(visible_text)
|
|
87
|
+
return AssistantTextChunk(
|
|
88
|
+
visible_text=plan_chunk.visible_text,
|
|
89
|
+
plan_segments=plan_chunk.extracted,
|
|
90
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Stream parser for <oai-mem-citation> tags."""
|
|
2
|
+
|
|
3
|
+
from code_muse.stream_parser.inline_hidden_tag_parser import (
|
|
4
|
+
InlineHiddenTagParser,
|
|
5
|
+
InlineTagSpec,
|
|
6
|
+
)
|
|
7
|
+
from code_muse.stream_parser.stream_text_chunk import StreamTextChunk
|
|
8
|
+
from code_muse.stream_parser.stream_text_parser import StreamTextParser
|
|
9
|
+
|
|
10
|
+
CITATION_OPEN = "<oai-mem-citation>"
|
|
11
|
+
CITATION_CLOSE = "</oai-mem-citation>"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CitationStreamParser(StreamTextParser[str]):
|
|
15
|
+
"""Stream parser for ``<oai-mem-citation>…</oai-mem-citation>`` tags.
|
|
16
|
+
|
|
17
|
+
Thin wrapper around :class:`InlineHiddenTagParser` that returns citation
|
|
18
|
+
bodies as plain strings and omits the citation tags from visible text.
|
|
19
|
+
Matching is literal and non-nested. Unterminated tags auto-close at
|
|
20
|
+
:meth:`finish`.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
self._inner = InlineHiddenTagParser(
|
|
25
|
+
[InlineTagSpec("citation", CITATION_OPEN, CITATION_CLOSE)]
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def push_str(self, chunk: str) -> StreamTextChunk[str]:
|
|
29
|
+
"""Feed a new text chunk.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
chunk: Incoming assistant text delta.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Visible text with citation tags stripped, plus any citation
|
|
36
|
+
payloads extracted from the chunk.
|
|
37
|
+
"""
|
|
38
|
+
inner = self._inner.push_str(chunk)
|
|
39
|
+
return StreamTextChunk(
|
|
40
|
+
visible_text=inner.visible_text,
|
|
41
|
+
extracted=[tag.content for tag in inner.extracted],
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def finish(self) -> StreamTextChunk[str]:
|
|
45
|
+
"""Flush any buffered state.
|
|
46
|
+
|
|
47
|
+
Unterminated citation tags are auto-closed and their accumulated
|
|
48
|
+
content is returned as a citation payload.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Final visible text and any trailing citations.
|
|
52
|
+
"""
|
|
53
|
+
inner = self._inner.finish()
|
|
54
|
+
return StreamTextChunk(
|
|
55
|
+
visible_text=inner.visible_text,
|
|
56
|
+
extracted=[tag.content for tag in inner.extracted],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def strip_citations(text: str) -> tuple[str, list[str]]:
|
|
61
|
+
"""Strip citation tags from a complete string.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
text: Full assistant response text.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
``(visible_text, citations)`` where ``visible_text`` has all
|
|
68
|
+
``<oai-mem-citation>…</oai-mem-citation>`` blocks removed and
|
|
69
|
+
``citations`` is the list of extracted citation bodies.
|
|
70
|
+
"""
|
|
71
|
+
parser = CitationStreamParser()
|
|
72
|
+
out = parser.push_str(text)
|
|
73
|
+
tail = parser.finish()
|
|
74
|
+
visible = out.visible_text + tail.visible_text
|
|
75
|
+
citations = out.extracted + tail.extracted
|
|
76
|
+
return visible, citations
|