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,206 @@
|
|
|
1
|
+
# cython: language_level=3
|
|
2
|
+
"""UTF-8 byte-stream adapter that wraps a StreamTextParser.
|
|
3
|
+
|
|
4
|
+
Buffers partial code points across chunk boundaries and rolls back entire
|
|
5
|
+
chunks on invalid UTF-8 so the wrapped parser never sees malformed text.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TypeVar
|
|
9
|
+
|
|
10
|
+
from code_muse.stream_parser.stream_text_chunk import StreamTextChunk
|
|
11
|
+
from code_muse.stream_parser.stream_text_parser import StreamTextParser
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Utf8StreamParserError(Exception):
|
|
17
|
+
"""Base error for Utf8StreamParser."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InvalidUtf8(Utf8StreamParserError):
|
|
23
|
+
"""Raised when a pushed byte chunk contains an invalid UTF-8 sequence.
|
|
24
|
+
|
|
25
|
+
The chunk is rolled back; the parser's pending buffer is restored to its
|
|
26
|
+
state before the chunk was pushed.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, valid_up_to: int, error_len: int) -> None:
|
|
30
|
+
self.valid_up_to = valid_up_to
|
|
31
|
+
self.error_len = error_len
|
|
32
|
+
super().__init__(
|
|
33
|
+
f"invalid UTF-8 in streamed bytes at offset {valid_up_to} "
|
|
34
|
+
f"(error length {error_len})"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class IncompleteUtf8AtEof(Utf8StreamParserError):
|
|
39
|
+
"""Raised at finish() or into_inner() when an incomplete code point remains."""
|
|
40
|
+
|
|
41
|
+
def __init__(self) -> None:
|
|
42
|
+
super().__init__("incomplete UTF-8 code point at end of stream")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Utf8StreamParser:
|
|
46
|
+
"""Wraps a StreamTextParser and accepts raw bytes.
|
|
47
|
+
|
|
48
|
+
* Buffers incomplete UTF-8 code points across chunk boundaries.
|
|
49
|
+
* On invalid UTF-8, rolls back the **entire** chunk and raises.
|
|
50
|
+
* On incomplete-but-valid UTF-8, processes the valid prefix and keeps
|
|
51
|
+
the incomplete tail buffered.
|
|
52
|
+
* ``finish()`` flushes pending bytes; raises if a code point is incomplete.
|
|
53
|
+
* ``into_inner()`` returns the wrapped parser only when no bytes are pending.
|
|
54
|
+
* ``into_inner_lossy()`` returns the wrapped parser, dropping pending bytes.
|
|
55
|
+
|
|
56
|
+
The wrapped parser never receives malformed text.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, inner: StreamTextParser[T]) -> None:
|
|
60
|
+
self.inner = inner
|
|
61
|
+
self._pending_utf8: bytearray = bytearray()
|
|
62
|
+
|
|
63
|
+
def push_bytes(self, const unsigned char[:] chunk) -> StreamTextChunk[T]:
|
|
64
|
+
"""Feed a new byte chunk.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
chunk: Raw bytes to decode and forward to the inner parser.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Visible text and extracted payloads from the inner parser.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
InvalidUtf8: If the chunk (combined with any previously buffered
|
|
74
|
+
partial code point) contains an invalid UTF-8 sequence. The
|
|
75
|
+
entire chunk is rolled back so the buffer is restored to its
|
|
76
|
+
pre-call state.
|
|
77
|
+
"""
|
|
78
|
+
cdef int old_len
|
|
79
|
+
cdef int valid_up_to
|
|
80
|
+
cdef bint is_incomplete
|
|
81
|
+
|
|
82
|
+
# Fast path: no pending bytes and chunk decodes cleanly.
|
|
83
|
+
if not self._pending_utf8:
|
|
84
|
+
try:
|
|
85
|
+
text = bytes(chunk).decode("utf-8")
|
|
86
|
+
return self.inner.push_str(text)
|
|
87
|
+
except UnicodeDecodeError as err:
|
|
88
|
+
is_incomplete = (
|
|
89
|
+
err.reason == "unexpected end of data" and err.end == len(chunk)
|
|
90
|
+
)
|
|
91
|
+
if not is_incomplete:
|
|
92
|
+
raise InvalidUtf8(err.start, err.end - err.start) from err
|
|
93
|
+
# Buffer the chunk; valid prefix will be consumed below.
|
|
94
|
+
self._pending_utf8.extend(chunk)
|
|
95
|
+
valid_up_to = err.start
|
|
96
|
+
if valid_up_to == 0:
|
|
97
|
+
return StreamTextChunk()
|
|
98
|
+
text = bytes(chunk[:valid_up_to]).decode("utf-8")
|
|
99
|
+
out = self.inner.push_str(text)
|
|
100
|
+
del self._pending_utf8[:valid_up_to]
|
|
101
|
+
return out
|
|
102
|
+
|
|
103
|
+
# Slow path: append to existing pending buffer.
|
|
104
|
+
old_len = len(self._pending_utf8)
|
|
105
|
+
self._pending_utf8.extend(chunk)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
text = self._pending_utf8.decode("utf-8")
|
|
109
|
+
except UnicodeDecodeError as err:
|
|
110
|
+
# Distinguish "invalid byte sequence" from "incomplete at end".
|
|
111
|
+
# In Python an incomplete sequence at the very end reports
|
|
112
|
+
# ``reason == "unexpected end of data"`` and ``err.end`` equals
|
|
113
|
+
# ``len(self._pending_utf8)``. Anything else is an actual error.
|
|
114
|
+
is_incomplete = err.reason == "unexpected end of data" and err.end == len(
|
|
115
|
+
self._pending_utf8
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if not is_incomplete:
|
|
119
|
+
# Invalid sequence somewhere in the buffer. Roll back the
|
|
120
|
+
# entire chunk so the inner parser never sees malformed data.
|
|
121
|
+
del self._pending_utf8[old_len:]
|
|
122
|
+
raise InvalidUtf8(err.start, err.end - err.start) from err
|
|
123
|
+
|
|
124
|
+
# Incomplete code point at the end of the buffer.
|
|
125
|
+
valid_up_to = err.start
|
|
126
|
+
if valid_up_to == 0:
|
|
127
|
+
# Nothing valid to forward yet.
|
|
128
|
+
return StreamTextChunk()
|
|
129
|
+
|
|
130
|
+
# Process the valid prefix. Defend against the edge case where
|
|
131
|
+
# the prefix itself fails to decode (nested error).
|
|
132
|
+
try:
|
|
133
|
+
text = bytes(self._pending_utf8[:valid_up_to]).decode("utf-8")
|
|
134
|
+
except UnicodeDecodeError as nested_err:
|
|
135
|
+
del self._pending_utf8[old_len:]
|
|
136
|
+
raise InvalidUtf8(
|
|
137
|
+
nested_err.start, nested_err.end - nested_err.start
|
|
138
|
+
) from nested_err
|
|
139
|
+
|
|
140
|
+
out = self.inner.push_str(text)
|
|
141
|
+
del self._pending_utf8[:valid_up_to]
|
|
142
|
+
return out
|
|
143
|
+
|
|
144
|
+
# Full buffer decoded cleanly.
|
|
145
|
+
out = self.inner.push_str(text)
|
|
146
|
+
self._pending_utf8.clear()
|
|
147
|
+
return out
|
|
148
|
+
|
|
149
|
+
def finish(self) -> StreamTextChunk[T]:
|
|
150
|
+
"""Flush any buffered bytes at end-of-stream.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Any visible text and extracted payloads produced by the inner
|
|
154
|
+
parser for the final pending bytes.
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
IncompleteUtf8AtEof: If an incomplete UTF-8 code point remains
|
|
158
|
+
in the buffer. The bytes are **not** consumed; they stay
|
|
159
|
+
buffered so the caller can decide what to do next.
|
|
160
|
+
"""
|
|
161
|
+
if not self._pending_utf8:
|
|
162
|
+
return self.inner.finish()
|
|
163
|
+
|
|
164
|
+
# Attempt to decode whatever is left. If it is incomplete, raise.
|
|
165
|
+
try:
|
|
166
|
+
text = self._pending_utf8.decode("utf-8")
|
|
167
|
+
except UnicodeDecodeError as err:
|
|
168
|
+
if err.reason == "unexpected end of data" and err.end == len(
|
|
169
|
+
self._pending_utf8
|
|
170
|
+
):
|
|
171
|
+
raise IncompleteUtf8AtEof() from err
|
|
172
|
+
# Should be unreachable for a previously-validated buffer, but
|
|
173
|
+
# treat it as invalid UTF-8.
|
|
174
|
+
raise InvalidUtf8(err.start, err.end - err.start) from err
|
|
175
|
+
|
|
176
|
+
out = self.inner.push_str(text)
|
|
177
|
+
self._pending_utf8.clear()
|
|
178
|
+
tail = self.inner.finish()
|
|
179
|
+
return StreamTextChunk(
|
|
180
|
+
visible_text=out.visible_text + tail.visible_text,
|
|
181
|
+
extracted=out.extracted + tail.extracted,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def into_inner(self) -> StreamTextParser[T]:
|
|
185
|
+
"""Return the wrapped parser only if no bytes are pending.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
The inner StreamTextParser instance.
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
IncompleteUtf8AtEof: If there are pending bytes (incomplete code
|
|
192
|
+
point) in the buffer.
|
|
193
|
+
"""
|
|
194
|
+
if self._pending_utf8:
|
|
195
|
+
raise IncompleteUtf8AtEof()
|
|
196
|
+
return self.inner
|
|
197
|
+
|
|
198
|
+
def into_inner_lossy(self) -> StreamTextParser[T]:
|
|
199
|
+
"""Return the wrapped parser, discarding any pending bytes.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
The inner StreamTextParser instance. Any incomplete UTF-8 code
|
|
203
|
+
point buffered across previous chunks is silently dropped.
|
|
204
|
+
"""
|
|
205
|
+
self._pending_utf8.clear()
|
|
206
|
+
return self.inner
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import atexit
|
|
3
|
+
import hashlib
|
|
4
|
+
import logging
|
|
5
|
+
import pathlib
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
# FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14 —
|
|
9
|
+
# no GIL contention for I/O-bound summarization work.
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from pydantic_ai import Agent
|
|
14
|
+
|
|
15
|
+
from code_muse.config import get_summarization_model_name
|
|
16
|
+
from code_muse.model_factory import ModelFactory, make_model_settings
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Keep a module-level agent reference to avoid rebuilding per call
|
|
21
|
+
_summarization_agent = None
|
|
22
|
+
# FREE-THREADED: _agent_lock guards sync-only agent cache access.
|
|
23
|
+
_agent_lock = threading.Lock()
|
|
24
|
+
|
|
25
|
+
# P2-05/PERF-05: track the model name the cached agent was built for
|
|
26
|
+
_cached_model_name: str | None = None
|
|
27
|
+
|
|
28
|
+
# Safe sync runner for async agent.run calls
|
|
29
|
+
# Avoids "event loop is already running" by offloading to a separate thread loop when needed
|
|
30
|
+
_thread_pool: ThreadPoolExecutor | None = None
|
|
31
|
+
|
|
32
|
+
# Reload counter
|
|
33
|
+
_reload_count = 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# P2-05/PERF-05: Model config cache with mtime invalidation
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _models_config_fingerprint() -> tuple[float, str]:
|
|
42
|
+
"""Compute a lightweight fingerprint of all model config sources.
|
|
43
|
+
|
|
44
|
+
Returns (max_mtime, content_hash) — if either changes, the cached
|
|
45
|
+
config is stale and must be reloaded.
|
|
46
|
+
"""
|
|
47
|
+
source_paths: list[pathlib.Path] = []
|
|
48
|
+
|
|
49
|
+
# Bundled models.json is always loaded
|
|
50
|
+
bundled = pathlib.Path(__file__).parent / "models.json"
|
|
51
|
+
source_paths.append(bundled)
|
|
52
|
+
|
|
53
|
+
# Extra model sources (mirrors ModelFactory.load_config)
|
|
54
|
+
try:
|
|
55
|
+
from code_muse.config import (
|
|
56
|
+
CHATGPT_MODELS_FILE,
|
|
57
|
+
CLAUDE_MODELS_FILE,
|
|
58
|
+
COPILOT_MODELS_FILE,
|
|
59
|
+
EXTRA_MODELS_FILE,
|
|
60
|
+
GEMINI_MODELS_FILE,
|
|
61
|
+
MODELS_FILE,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
for p in (
|
|
65
|
+
MODELS_FILE,
|
|
66
|
+
EXTRA_MODELS_FILE,
|
|
67
|
+
CHATGPT_MODELS_FILE,
|
|
68
|
+
CLAUDE_MODELS_FILE,
|
|
69
|
+
GEMINI_MODELS_FILE,
|
|
70
|
+
COPILOT_MODELS_FILE,
|
|
71
|
+
):
|
|
72
|
+
source_paths.append(pathlib.Path(p))
|
|
73
|
+
except Exception:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
max_mtime = 0.0
|
|
77
|
+
hasher = hashlib.md5(usedforsecurity=False)
|
|
78
|
+
for sp in source_paths:
|
|
79
|
+
try:
|
|
80
|
+
if sp.exists():
|
|
81
|
+
stat = sp.stat()
|
|
82
|
+
max_mtime = max(max_mtime, stat.st_mtime)
|
|
83
|
+
# Hash file contents (or just size+mtime as a cheap proxy)
|
|
84
|
+
hasher.update(f"{sp}:{stat.st_size}:{stat.st_mtime}".encode())
|
|
85
|
+
except OSError:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
return max_mtime, hasher.hexdigest()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Module-level model config cache: (config_dict, fingerprint)
|
|
92
|
+
_models_config_cache: tuple[dict[str, Any | None], tuple[float, str | None]] = (
|
|
93
|
+
None,
|
|
94
|
+
None,
|
|
95
|
+
)
|
|
96
|
+
# FREE-THREADED: _models_config_lock guards sync-only config cache access.
|
|
97
|
+
_models_config_lock = threading.Lock()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_cached_models_config() -> dict[str, Any]:
|
|
101
|
+
"""Return the models config, using a cache invalidated by mtime/hash changes.
|
|
102
|
+
|
|
103
|
+
This avoids re-reading ``models.json`` and extra model files on every call
|
|
104
|
+
to ``ModelFactory.load_config()`` when nothing has changed. The cache is
|
|
105
|
+
invalidated when any source file's mtime changes.
|
|
106
|
+
|
|
107
|
+
Falls back to ``ModelFactory.load_config()`` on any error.
|
|
108
|
+
"""
|
|
109
|
+
global _models_config_cache
|
|
110
|
+
|
|
111
|
+
fingerprint = _models_config_fingerprint()
|
|
112
|
+
|
|
113
|
+
with _models_config_lock:
|
|
114
|
+
cached_config, cached_fp = _models_config_cache
|
|
115
|
+
if cached_config is not None and cached_fp == fingerprint:
|
|
116
|
+
return cached_config
|
|
117
|
+
|
|
118
|
+
# Cache miss — reload. Let exceptions propagate so callers
|
|
119
|
+
# (including reload_summarization_agent) see the same errors they
|
|
120
|
+
# would have seen without the cache.
|
|
121
|
+
config = ModelFactory.load_config()
|
|
122
|
+
_models_config_cache = (config, fingerprint)
|
|
123
|
+
return config
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def invalidate_models_config_cache() -> None:
|
|
127
|
+
"""Force the next ``get_cached_models_config()`` call to reload.
|
|
128
|
+
|
|
129
|
+
Call this when settings or model files are known to have changed
|
|
130
|
+
(e.g. after a ``/set`` command that modifies model config).
|
|
131
|
+
"""
|
|
132
|
+
global _models_config_cache
|
|
133
|
+
with _models_config_lock:
|
|
134
|
+
_models_config_cache = (None, None)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _ensure_thread_pool():
|
|
138
|
+
global _thread_pool
|
|
139
|
+
# Check if pool is None OR if it's been shutdown
|
|
140
|
+
if _thread_pool is None or _thread_pool._shutdown:
|
|
141
|
+
# FREE-THREADED: ThreadPoolExecutor is compatible with free-threaded Python 3.14.
|
|
142
|
+
_thread_pool = ThreadPoolExecutor(
|
|
143
|
+
max_workers=1, thread_name_prefix="summarizer-loop"
|
|
144
|
+
)
|
|
145
|
+
return _thread_pool
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _shutdown_thread_pool():
|
|
149
|
+
global _thread_pool
|
|
150
|
+
if _thread_pool is not None:
|
|
151
|
+
_thread_pool.shutdown(wait=False)
|
|
152
|
+
_thread_pool = None
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
atexit.register(_shutdown_thread_pool)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
async def _run_agent_async(agent: Agent, prompt: str, message_history: list):
|
|
159
|
+
return await agent.run(prompt, message_history=message_history)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class SummarizationError(Exception):
|
|
163
|
+
"""Raised when summarization fails with details about the failure."""
|
|
164
|
+
|
|
165
|
+
def __init__(self, message: str, original_error: Exception | None = None):
|
|
166
|
+
self.original_error = original_error
|
|
167
|
+
super().__init__(message)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def run_summarization_sync(prompt: str, message_history: list) -> list:
|
|
171
|
+
"""Run the summarization agent synchronously.
|
|
172
|
+
|
|
173
|
+
Raises:
|
|
174
|
+
SummarizationError: If summarization fails for any reason.
|
|
175
|
+
"""
|
|
176
|
+
try:
|
|
177
|
+
agent = get_summarization_agent()
|
|
178
|
+
except Exception as e:
|
|
179
|
+
raise SummarizationError(
|
|
180
|
+
f"Failed to initialize summarization agent: {type(e).__name__}: {e}",
|
|
181
|
+
original_error=e,
|
|
182
|
+
) from e
|
|
183
|
+
|
|
184
|
+
# Handle claude-code models: prepend system prompt to user prompt
|
|
185
|
+
from code_muse.model_utils import prepare_prompt_for_model
|
|
186
|
+
|
|
187
|
+
model_name = get_summarization_model_name()
|
|
188
|
+
prepared = prepare_prompt_for_model(
|
|
189
|
+
model_name, _get_summarization_instructions(), prompt
|
|
190
|
+
)
|
|
191
|
+
prompt = prepared.user_prompt
|
|
192
|
+
|
|
193
|
+
def _run_in_thread():
|
|
194
|
+
"""
|
|
195
|
+
Run the async agent in a dedicated thread with its own event loop.
|
|
196
|
+
Uses run_until_complete instead of asyncio.run to avoid shutting down
|
|
197
|
+
the default executor (which may break plugins in the main thread).
|
|
198
|
+
Does NOT touch global event loop state.
|
|
199
|
+
"""
|
|
200
|
+
loop = asyncio.new_event_loop()
|
|
201
|
+
try:
|
|
202
|
+
coro = agent.run(prompt, message_history=message_history)
|
|
203
|
+
return loop.run_until_complete(coro)
|
|
204
|
+
finally:
|
|
205
|
+
# Clean up without shutting down the default executor
|
|
206
|
+
try:
|
|
207
|
+
# Cancel pending tasks
|
|
208
|
+
pending = asyncio.all_tasks(loop)
|
|
209
|
+
for task in pending:
|
|
210
|
+
task.cancel()
|
|
211
|
+
if pending:
|
|
212
|
+
# NOTE: asyncio.gather for already-running tasks; TaskGroup cannot adopt existing tasks.
|
|
213
|
+
loop.run_until_complete(
|
|
214
|
+
asyncio.gather(*pending, return_exceptions=True)
|
|
215
|
+
)
|
|
216
|
+
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
217
|
+
finally:
|
|
218
|
+
loop.close()
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
# Always use thread pool since we're likely in an existing event loop
|
|
222
|
+
pool = _ensure_thread_pool()
|
|
223
|
+
result = pool.submit(_run_in_thread).result()
|
|
224
|
+
return result.new_messages()
|
|
225
|
+
except Exception as e:
|
|
226
|
+
error_type = type(e).__name__
|
|
227
|
+
error_msg = str(e) if str(e) else "(no details available)"
|
|
228
|
+
raise SummarizationError(
|
|
229
|
+
f"LLM call failed during summarization: [{error_type}] {error_msg}",
|
|
230
|
+
original_error=e,
|
|
231
|
+
) from e
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _get_summarization_instructions() -> str:
|
|
235
|
+
"""Get the system instructions for the summarization agent."""
|
|
236
|
+
return """You are a message summarization expert. Your task is to summarize conversation messages
|
|
237
|
+
while preserving important context and information. The summaries should be concise but capture the essential content
|
|
238
|
+
and intent of the original messages. This is to help manage token usage in a conversation history
|
|
239
|
+
while maintaining context for the AI to continue the conversation effectively.
|
|
240
|
+
|
|
241
|
+
When summarizing:
|
|
242
|
+
1. Keep summary concise but informative
|
|
243
|
+
2. Preserve important context and key information and decisions
|
|
244
|
+
3. Keep any important technical details
|
|
245
|
+
4. Don't summarize the system message
|
|
246
|
+
5. Make sure all tool calls and responses are summarized, as they are vital
|
|
247
|
+
6. Focus on token usage efficiency and system message preservation"""
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def reload_summarization_agent():
|
|
251
|
+
"""Create a specialized agent for summarizing messages when context limit is reached."""
|
|
252
|
+
from code_muse.model_utils import prepare_prompt_for_model
|
|
253
|
+
|
|
254
|
+
# Always bust the cache on explicit reload — the caller expects fresh config
|
|
255
|
+
invalidate_models_config_cache()
|
|
256
|
+
models_config = get_cached_models_config()
|
|
257
|
+
model_name = get_summarization_model_name()
|
|
258
|
+
model = ModelFactory.get_model(model_name, models_config)
|
|
259
|
+
|
|
260
|
+
# Handle claude-code models: swap instructions (prompt prepending happens in run_summarization_sync)
|
|
261
|
+
instructions = _get_summarization_instructions()
|
|
262
|
+
prepared = prepare_prompt_for_model(
|
|
263
|
+
model_name, instructions, "", prepend_system_to_user=False
|
|
264
|
+
)
|
|
265
|
+
instructions = prepared.instructions
|
|
266
|
+
|
|
267
|
+
model_settings = make_model_settings(model_name)
|
|
268
|
+
|
|
269
|
+
agent = Agent(
|
|
270
|
+
model=model,
|
|
271
|
+
instructions=instructions,
|
|
272
|
+
output_type=str,
|
|
273
|
+
retries=1, # Fewer retries for summarization
|
|
274
|
+
model_settings=model_settings,
|
|
275
|
+
)
|
|
276
|
+
# NOTE: We intentionally don't wrap the summarization agent.
|
|
277
|
+
# Summarization is a simple one-shot call that doesn't need durable execution,
|
|
278
|
+
# and wrapping can cause async event loop conflicts with run_sync().
|
|
279
|
+
return agent
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_summarization_agent(force_reload=False):
|
|
283
|
+
"""Retrieve the summarization agent, caching across calls.
|
|
284
|
+
|
|
285
|
+
P2-05/PERF-05: The default is now ``force_reload=False``. The agent is
|
|
286
|
+
rebuilt only when:
|
|
287
|
+
- ``force_reload=True`` is explicitly passed
|
|
288
|
+
- The summarization model name has changed since the last build
|
|
289
|
+
- No agent has been built yet (first call)
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
force_reload: When True, unconditionally rebuild the agent.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
A ``pydantic_ai.Agent`` configured for summarization.
|
|
296
|
+
"""
|
|
297
|
+
global _summarization_agent, _cached_model_name
|
|
298
|
+
current_model = get_summarization_model_name()
|
|
299
|
+
with _agent_lock:
|
|
300
|
+
needs_reload = (
|
|
301
|
+
force_reload
|
|
302
|
+
or _summarization_agent is None
|
|
303
|
+
or _cached_model_name != current_model
|
|
304
|
+
)
|
|
305
|
+
if needs_reload:
|
|
306
|
+
_summarization_agent = reload_summarization_agent()
|
|
307
|
+
_cached_model_name = current_model
|
|
308
|
+
return _summarization_agent
|
|
Binary file
|
|
Binary file
|