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,59 @@
|
|
|
1
|
+
"""Shared helpers for switching models and reloading agents safely."""
|
|
2
|
+
|
|
3
|
+
from code_muse.config import set_model_name
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _get_effective_agent_model(agent) -> str | None:
|
|
7
|
+
"""Safely fetch the effective model name for an agent."""
|
|
8
|
+
try:
|
|
9
|
+
return agent.get_model_name()
|
|
10
|
+
except Exception:
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def set_model_and_reload_agent(
|
|
15
|
+
model_name: str,
|
|
16
|
+
*,
|
|
17
|
+
warn_on_pinned_mismatch: bool = True,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Set the global model and reload the active agent.
|
|
20
|
+
|
|
21
|
+
This keeps model switching consistent across commands while avoiding
|
|
22
|
+
direct imports that can trigger circular dependencies.
|
|
23
|
+
"""
|
|
24
|
+
from code_muse.messaging import emit_info, emit_warning
|
|
25
|
+
|
|
26
|
+
set_model_name(model_name)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
from code_muse.agents import get_current_agent
|
|
30
|
+
|
|
31
|
+
current_agent = get_current_agent()
|
|
32
|
+
if current_agent is None:
|
|
33
|
+
emit_warning("Model changed but no active agent was found to reload")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
# JSON agents may need to refresh their config before reload
|
|
37
|
+
if hasattr(current_agent, "refresh_config"):
|
|
38
|
+
try:
|
|
39
|
+
current_agent.refresh_config()
|
|
40
|
+
except Exception:
|
|
41
|
+
# Non-fatal, continue to reload
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
if warn_on_pinned_mismatch:
|
|
45
|
+
effective_model = _get_effective_agent_model(current_agent)
|
|
46
|
+
if effective_model and effective_model != model_name:
|
|
47
|
+
display_name = getattr(
|
|
48
|
+
current_agent, "display_name", current_agent.name
|
|
49
|
+
)
|
|
50
|
+
emit_warning(
|
|
51
|
+
"Active agent "
|
|
52
|
+
f"'{display_name}' is pinned to '{effective_model}', "
|
|
53
|
+
f"so '{model_name}' will not take effect until unpinned."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
current_agent.reload_code_generation_agent()
|
|
57
|
+
emit_info("Active agent reloaded")
|
|
58
|
+
except Exception as exc:
|
|
59
|
+
emit_warning(f"Model changed but agent reload failed: {exc}")
|
code_muse/model_utils.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Model-related utilities shared across agents and tools.
|
|
2
|
+
|
|
3
|
+
This module is intentionally model-agnostic. Anything model-family-specific
|
|
4
|
+
(e.g. claude-code OAuth prompt handling) lives in its own plugin and hooks
|
|
5
|
+
into the ``prepare_model_prompt`` or ``get_model_system_prompt`` callbacks.
|
|
6
|
+
|
|
7
|
+
Plugins can register:
|
|
8
|
+
|
|
9
|
+
- ``prepare_model_prompt``: fully take over prompt prep for a model family.
|
|
10
|
+
- ``get_model_system_prompt``: augment/override the system prompt for a model.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class PreparedPrompt:
|
|
18
|
+
"""Result of preparing a prompt for a specific model.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
instructions: The system instructions to use for the agent
|
|
22
|
+
user_prompt: The user prompt (possibly modified)
|
|
23
|
+
is_claude_code: Whether this is a claude-code model (set by the
|
|
24
|
+
claude_code_oauth plugin via the ``prepare_model_prompt`` hook).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
instructions: str
|
|
28
|
+
user_prompt: str
|
|
29
|
+
is_claude_code: bool
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def prepare_prompt_for_model(
|
|
33
|
+
model_name: str,
|
|
34
|
+
system_prompt: str,
|
|
35
|
+
user_prompt: str,
|
|
36
|
+
prepend_system_to_user: bool = True,
|
|
37
|
+
) -> PreparedPrompt:
|
|
38
|
+
"""Prepare instructions and prompt for a specific model.
|
|
39
|
+
|
|
40
|
+
Core fires two hooks to let plugins customize prompt prep:
|
|
41
|
+
|
|
42
|
+
1. ``prepare_model_prompt`` — first winner with ``handled=True`` takes
|
|
43
|
+
over entirely (used by the claude_code_oauth plugin).
|
|
44
|
+
2. ``get_model_system_prompt`` — legacy per-model system-prompt hook;
|
|
45
|
+
still fired for compatibility with plugins (e.g. agent_skills) that
|
|
46
|
+
rely on it.
|
|
47
|
+
|
|
48
|
+
If no plugin handles the model, we return the original system/user prompt
|
|
49
|
+
unchanged.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
model_name: The name of the model being used.
|
|
53
|
+
system_prompt: The default system prompt from the agent.
|
|
54
|
+
user_prompt: The user's prompt/message.
|
|
55
|
+
prepend_system_to_user: Whether to prepend the system prompt to the
|
|
56
|
+
user prompt (only meaningful for plugins that opt into it).
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
PreparedPrompt ready for the model.
|
|
60
|
+
"""
|
|
61
|
+
from code_muse import callbacks
|
|
62
|
+
|
|
63
|
+
# 1) Give the dedicated prepare_model_prompt hook first crack. First
|
|
64
|
+
# plugin to claim ``handled=True`` wins.
|
|
65
|
+
for result in callbacks.on_prepare_model_prompt(
|
|
66
|
+
model_name, system_prompt, user_prompt, prepend_system_to_user
|
|
67
|
+
):
|
|
68
|
+
if result and isinstance(result, dict) and result.get("handled"):
|
|
69
|
+
return PreparedPrompt(
|
|
70
|
+
instructions=result.get("instructions", system_prompt),
|
|
71
|
+
user_prompt=result.get("user_prompt", user_prompt),
|
|
72
|
+
is_claude_code=bool(result.get("is_claude_code", False)),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# 2) Fall back to the legacy per-model system-prompt hook for plugins
|
|
76
|
+
# that still register there.
|
|
77
|
+
for result in callbacks.on_get_model_system_prompt(
|
|
78
|
+
model_name, system_prompt, user_prompt
|
|
79
|
+
):
|
|
80
|
+
if result and isinstance(result, dict) and result.get("handled"):
|
|
81
|
+
return PreparedPrompt(
|
|
82
|
+
instructions=result.get("instructions", system_prompt),
|
|
83
|
+
user_prompt=result.get("user_prompt", user_prompt),
|
|
84
|
+
is_claude_code=bool(result.get("is_claude_code", False)),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# 3) No plugin handled it — return the caller's prompts unchanged.
|
|
88
|
+
return PreparedPrompt(
|
|
89
|
+
instructions=system_prompt,
|
|
90
|
+
user_prompt=user_prompt,
|
|
91
|
+
is_claude_code=False,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def supports_adaptive_thinking(
|
|
96
|
+
model_name: str, actual_model_id: str | None = None
|
|
97
|
+
) -> bool:
|
|
98
|
+
"""Return whether a model should default to adaptive thinking.
|
|
99
|
+
|
|
100
|
+
Opus 4-6, Opus 4-7, and Sonnet 4-6 models support adaptive thinking.
|
|
101
|
+
Checks both the alias/key and the real model ID to handle Bedrock-style
|
|
102
|
+
names like ``us.anthropic.claude-opus-4-7``.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
model_name: The model alias/key (e.g. ``"bedrock-opus-4-7"``).
|
|
106
|
+
actual_model_id: The real model ID from config (e.g.
|
|
107
|
+
``"us.anthropic.claude-opus-4-7"``).
|
|
108
|
+
"""
|
|
109
|
+
candidates = [model_name.lower()]
|
|
110
|
+
if actual_model_id:
|
|
111
|
+
candidates.append(actual_model_id.lower())
|
|
112
|
+
|
|
113
|
+
_ADAPTIVE_TAGS = (
|
|
114
|
+
"opus-4-6",
|
|
115
|
+
"4-6-opus",
|
|
116
|
+
"opus-4-7",
|
|
117
|
+
"4-7-opus",
|
|
118
|
+
"sonnet-4-6",
|
|
119
|
+
"4-6-sonnet",
|
|
120
|
+
)
|
|
121
|
+
return any(tag in c for c in candidates for tag in _ADAPTIVE_TAGS)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_default_extended_thinking(
|
|
125
|
+
model_name: str, actual_model_id: str | None = None
|
|
126
|
+
) -> str:
|
|
127
|
+
"""Return the default extended_thinking mode for an Anthropic model.
|
|
128
|
+
|
|
129
|
+
Opus 4-6, Opus 4-7, and Sonnet 4-6 models default to ``"adaptive"``
|
|
130
|
+
thinking; all other Anthropic models default to ``"enabled"``.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
model_name: The model alias/key (e.g. ``"bedrock-opus-4-7"``).
|
|
134
|
+
actual_model_id: The real model ID from config (e.g.
|
|
135
|
+
``"us.anthropic.claude-opus-4-7"``).
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
``"adaptive"`` for supported variants, ``"enabled"`` otherwise.
|
|
139
|
+
"""
|
|
140
|
+
if supports_adaptive_thinking(model_name, actual_model_id):
|
|
141
|
+
return "adaptive"
|
|
142
|
+
return "enabled"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def should_use_anthropic_thinking_summary(
|
|
146
|
+
model_name: str, actual_model_id: str | None = None
|
|
147
|
+
) -> bool:
|
|
148
|
+
"""Return whether Anthropic adaptive thinking should request summary display.
|
|
149
|
+
|
|
150
|
+
Anthropic's newer Opus 4.7 models require ``display: \"summarized\"`` alongside
|
|
151
|
+
``thinking={"type": "adaptive"}``.
|
|
152
|
+
"""
|
|
153
|
+
candidates = [model_name.lower()]
|
|
154
|
+
if actual_model_id:
|
|
155
|
+
candidates.append(actual_model_id.lower())
|
|
156
|
+
return any("opus-4-7" in c or "4-7-opus" in c for c in candidates)
|
code_muse/models.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"wafer.ai-glm-5.1": {
|
|
3
|
+
"type": "custom_openai",
|
|
4
|
+
"provider": "wafer",
|
|
5
|
+
"name": "GLM-5.1",
|
|
6
|
+
"custom_endpoint": {
|
|
7
|
+
"url": "https://pass.wafer.ai/v1",
|
|
8
|
+
"api_key": "$WAFER_API_KEY"
|
|
9
|
+
},
|
|
10
|
+
"context_length": 200000,
|
|
11
|
+
"supported_settings": ["temperature", "seed", "top_p", "max_tokens"]
|
|
12
|
+
},
|
|
13
|
+
"wafer.ai-Qwen3.5-397B-A17B": {
|
|
14
|
+
"type": "custom_openai",
|
|
15
|
+
"provider": "wafer",
|
|
16
|
+
"name": "Qwen3.5-397B-A17B",
|
|
17
|
+
"custom_endpoint": {
|
|
18
|
+
"url": "https://pass.wafer.ai/v1",
|
|
19
|
+
"api_key": "$WAFER_API_KEY"
|
|
20
|
+
},
|
|
21
|
+
"context_length": 262144,
|
|
22
|
+
"supported_settings": ["temperature", "seed", "top_p", "max_tokens"]
|
|
23
|
+
},
|
|
24
|
+
"wafer-DeepSeek-V4-Pro": {
|
|
25
|
+
"type": "custom_openai",
|
|
26
|
+
"provider": "wafer",
|
|
27
|
+
"name": "DeepSeek-V4-Pro",
|
|
28
|
+
"custom_endpoint": {
|
|
29
|
+
"url": "https://pass.wafer.ai/v1",
|
|
30
|
+
"api_key": "$WAFER_API_KEY"
|
|
31
|
+
},
|
|
32
|
+
"context_length": 1000000,
|
|
33
|
+
"supported_settings": ["temperature", "seed", "top_p", "max_tokens"]
|
|
34
|
+
},
|
|
35
|
+
"wafer-MiniMax-M2.7": {
|
|
36
|
+
"type": "custom_openai",
|
|
37
|
+
"provider": "wafer",
|
|
38
|
+
"name": "MiniMax-M2.7",
|
|
39
|
+
"custom_endpoint": {
|
|
40
|
+
"url": "https://pass.wafer.ai/v1",
|
|
41
|
+
"api_key": "$WAFER_API_KEY"
|
|
42
|
+
},
|
|
43
|
+
"context_length": 204800,
|
|
44
|
+
"supported_settings": ["temperature", "seed", "top_p", "max_tokens"]
|
|
45
|
+
},
|
|
46
|
+
"crof-kimi-k2.5-lightning": {
|
|
47
|
+
"type": "custom_openai",
|
|
48
|
+
"provider": "crof",
|
|
49
|
+
"name": "kimi-k2.5-lightning",
|
|
50
|
+
"custom_endpoint": {
|
|
51
|
+
"url": "https://crof.ai/v1",
|
|
52
|
+
"api_key": "$CROF_API_KEY"
|
|
53
|
+
},
|
|
54
|
+
"context_length": 131072,
|
|
55
|
+
"streaming": false,
|
|
56
|
+
"strict_tools": false,
|
|
57
|
+
"supported_settings": ["temperature", "seed", "top_p"]
|
|
58
|
+
},
|
|
59
|
+
"zai-glm-5-coding": {
|
|
60
|
+
"type": "zai_coding",
|
|
61
|
+
"provider": "zai",
|
|
62
|
+
"name": "glm-5",
|
|
63
|
+
"context_length": 200000,
|
|
64
|
+
"supported_settings": ["temperature", "top_p"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Models Cache + LRU module for Muse.
|
|
2
|
+
|
|
3
|
+
Ports Codex's models_cache.json pattern and Oh-My-Pi's fs_cache LRU
|
|
4
|
+
to Python with thread-safe generics.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from code_muse.models_cache.blocking_lru_cache import BlockingLruCache
|
|
8
|
+
from code_muse.models_cache.cache_writer import write_models_cache
|
|
9
|
+
from code_muse.models_cache.sha256_hash import sha256_digest, sha256_digest_file
|
|
10
|
+
from code_muse.models_cache.startup_integration import (
|
|
11
|
+
CACHE_TTL,
|
|
12
|
+
MODELS_CACHE_PATH,
|
|
13
|
+
load_cached_models,
|
|
14
|
+
refresh_models_cache,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"BlockingLruCache",
|
|
19
|
+
"sha256_digest",
|
|
20
|
+
"sha256_digest_file",
|
|
21
|
+
"write_models_cache",
|
|
22
|
+
"load_cached_models",
|
|
23
|
+
"refresh_models_cache",
|
|
24
|
+
"CACHE_TTL",
|
|
25
|
+
"MODELS_CACHE_PATH",
|
|
26
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Thread-safe generic LRU cache using OrderedDict and threading.Lock.
|
|
2
|
+
|
|
3
|
+
FREE-THREADED: This cache uses threading.Lock because it may be accessed
|
|
4
|
+
from both sync and async contexts. If all callers become fully async,
|
|
5
|
+
consider switching to asyncio.Lock.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import threading
|
|
9
|
+
from collections import OrderedDict
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from typing import TypeVar
|
|
12
|
+
|
|
13
|
+
K = TypeVar("K")
|
|
14
|
+
V = TypeVar("V")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BlockingLruCache[K, V]:
|
|
18
|
+
"""Thread-safe LRU cache with get_or_insert_with pattern.
|
|
19
|
+
|
|
20
|
+
Uses OrderedDict for O(1) LRU eviction and threading.Lock for
|
|
21
|
+
thread safety. Capacity must be a positive integer (> 0).
|
|
22
|
+
Gracefully works outside asyncio/tokio runtimes.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, capacity: int) -> None:
|
|
26
|
+
if capacity < 1:
|
|
27
|
+
raise ValueError("capacity must be positive")
|
|
28
|
+
self._capacity = capacity
|
|
29
|
+
# FREE-THREADED: Generic cache — may be accessed from sync or async code.
|
|
30
|
+
# Keep threading.Lock; migrate to asyncio.Lock only if all callers are async.
|
|
31
|
+
self._lock = threading.Lock()
|
|
32
|
+
self._cache: OrderedDict[K, V] = OrderedDict()
|
|
33
|
+
|
|
34
|
+
def get(self, key: K) -> V | None:
|
|
35
|
+
"""Return cached value or None. Promotes to MRU on hit."""
|
|
36
|
+
with self._lock:
|
|
37
|
+
if key in self._cache:
|
|
38
|
+
self._cache.move_to_end(key)
|
|
39
|
+
return self._cache[key]
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
def get_or_insert_with(self, key: K, factory: Callable[[], V]) -> V:
|
|
43
|
+
"""Return cached value, or call factory on miss and cache it.
|
|
44
|
+
|
|
45
|
+
The factory is called OUTSIDE the lock to avoid holding it during
|
|
46
|
+
expensive computation. A double-check is performed under lock.
|
|
47
|
+
"""
|
|
48
|
+
# Fast path: check cache under lock
|
|
49
|
+
with self._lock:
|
|
50
|
+
if key in self._cache:
|
|
51
|
+
self._cache.move_to_end(key)
|
|
52
|
+
return self._cache[key]
|
|
53
|
+
|
|
54
|
+
# Slow path: compute outside lock
|
|
55
|
+
value = factory()
|
|
56
|
+
|
|
57
|
+
# Insert under lock with eviction check
|
|
58
|
+
with self._lock:
|
|
59
|
+
# Double-check: another thread may have inserted while we computed
|
|
60
|
+
if key in self._cache:
|
|
61
|
+
self._cache.move_to_end(key)
|
|
62
|
+
return self._cache[key]
|
|
63
|
+
|
|
64
|
+
# Evict oldest if at capacity
|
|
65
|
+
while len(self._cache) >= self._capacity:
|
|
66
|
+
self._cache.popitem(last=False)
|
|
67
|
+
|
|
68
|
+
self._cache[key] = value
|
|
69
|
+
return value
|
|
70
|
+
|
|
71
|
+
def insert(self, key: K, value: V) -> None:
|
|
72
|
+
"""Insert a value, evicting oldest if at capacity."""
|
|
73
|
+
with self._lock:
|
|
74
|
+
if key in self._cache:
|
|
75
|
+
self._cache.move_to_end(key)
|
|
76
|
+
self._cache[key] = value
|
|
77
|
+
return
|
|
78
|
+
while len(self._cache) >= self._capacity:
|
|
79
|
+
self._cache.popitem(last=False)
|
|
80
|
+
self._cache[key] = value
|
|
81
|
+
|
|
82
|
+
def remove(self, key: K) -> V | None:
|
|
83
|
+
"""Remove and return the value for key, or None."""
|
|
84
|
+
with self._lock:
|
|
85
|
+
return self._cache.pop(key, None)
|
|
86
|
+
|
|
87
|
+
def clear(self) -> None:
|
|
88
|
+
"""Remove all entries."""
|
|
89
|
+
with self._lock:
|
|
90
|
+
self._cache.clear()
|
|
91
|
+
|
|
92
|
+
def __len__(self) -> int:
|
|
93
|
+
with self._lock:
|
|
94
|
+
return len(self._cache)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def capacity(self) -> int:
|
|
98
|
+
return self._capacity
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Write models_cache.json to the data directory.
|
|
2
|
+
|
|
3
|
+
Ports Codex's models_cache.json pattern: a snapshot of the full model
|
|
4
|
+
catalog bundled with the client, refreshed on startup.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import dataclasses
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
from datetime import UTC, datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from code_muse.config import DATA_DIR
|
|
16
|
+
from code_muse.models_dev_parser import (
|
|
17
|
+
BUNDLED_JSON_FILENAME,
|
|
18
|
+
ModelInfo,
|
|
19
|
+
ModelsDevRegistry,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _client_version() -> str:
|
|
26
|
+
"""Return the installed code-muse version."""
|
|
27
|
+
try:
|
|
28
|
+
from importlib.metadata import version
|
|
29
|
+
|
|
30
|
+
return version("code-muse")
|
|
31
|
+
except Exception:
|
|
32
|
+
return "unknown"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _model_to_dict(model: ModelInfo, priority: int) -> dict[str, Any]:
|
|
36
|
+
"""Serialize a ModelInfo into the cache entry format."""
|
|
37
|
+
base = {
|
|
38
|
+
"slug": model.full_id,
|
|
39
|
+
"display_name": model.name,
|
|
40
|
+
"description": "",
|
|
41
|
+
"visibility": "List",
|
|
42
|
+
"priority": priority,
|
|
43
|
+
}
|
|
44
|
+
# Include all dataclass fields for completeness
|
|
45
|
+
fields = dataclasses.asdict(model)
|
|
46
|
+
# Avoid duplicating keys we already set explicitly
|
|
47
|
+
for key, value in fields.items():
|
|
48
|
+
if key not in base:
|
|
49
|
+
base[key] = value
|
|
50
|
+
return base
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def write_models_cache(models: list[ModelInfo] | None = None) -> Path | None:
|
|
54
|
+
"""Write the models cache file.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
models: Optional explicit list of ModelInfo objects. If None,
|
|
58
|
+
loads from the bundled models_dev_api.json.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Path to the written file, or None if writing failed.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
if models is None:
|
|
65
|
+
bundled_path = Path(__file__).parent.parent / BUNDLED_JSON_FILENAME
|
|
66
|
+
registry = ModelsDevRegistry(bundled_path)
|
|
67
|
+
models = list(registry.models.values())
|
|
68
|
+
|
|
69
|
+
cache_data: dict[str, Any] = {
|
|
70
|
+
"fetched_at": datetime.now(UTC).isoformat(),
|
|
71
|
+
"etag": None,
|
|
72
|
+
"client_version": _client_version(),
|
|
73
|
+
"models": [_model_to_dict(m, priority=idx) for idx, m in enumerate(models)],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
cache_path = Path(DATA_DIR) / "models_cache.json"
|
|
77
|
+
os.makedirs(cache_path.parent, exist_ok=True)
|
|
78
|
+
with open(cache_path, "w", encoding="utf-8") as f:
|
|
79
|
+
json.dump(cache_data, f, indent=2, default=str)
|
|
80
|
+
|
|
81
|
+
logger.info(f"Wrote models cache ({len(models)} models) to {cache_path}")
|
|
82
|
+
return cache_path
|
|
83
|
+
|
|
84
|
+
except (OSError, ValueError, TypeError) as exc:
|
|
85
|
+
logger.warning(f"Failed to write models cache: {exc}")
|
|
86
|
+
return None
|
|
Binary file
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# cython: language_level=3
|
|
2
|
+
"""SHA-256 content hash utility for content-addressed cache keys."""
|
|
3
|
+
|
|
4
|
+
import hashlib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def sha256_digest(data: bytes) -> str:
|
|
9
|
+
"""Return the SHA-256 hex digests of the given bytes.
|
|
10
|
+
|
|
11
|
+
Returns a 64-character lowercase hex string.
|
|
12
|
+
"""
|
|
13
|
+
return hashlib.sha256(data).hexdigest()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def sha256_digest_file(path: Path) -> str:
|
|
17
|
+
"""Return the SHA-256 hex digests of a file's contents.
|
|
18
|
+
|
|
19
|
+
Streams the file in 64KB chunks to handle large files efficiently.
|
|
20
|
+
The chunk-reading loop uses typed locals and bound methods to avoid
|
|
21
|
+
Python-level attribute lookups.
|
|
22
|
+
"""
|
|
23
|
+
cdef object hasher = hashlib.sha256()
|
|
24
|
+
cdef bytes chunk
|
|
25
|
+
cdef object update = hasher.update
|
|
26
|
+
cdef object read
|
|
27
|
+
with open(path, "rb") as f:
|
|
28
|
+
read = f.read
|
|
29
|
+
while True:
|
|
30
|
+
chunk = read(65536)
|
|
31
|
+
if len(chunk) == 0:
|
|
32
|
+
break
|
|
33
|
+
update(chunk)
|
|
34
|
+
return hasher.hexdigest()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Startup cache integration — load cached models on session start."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from datetime import UTC, datetime, timedelta
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from code_muse.config import MODELS_CACHE_FILE
|
|
10
|
+
from code_muse.models_cache.cache_writer import write_models_cache
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
MODELS_CACHE_PATH = Path(MODELS_CACHE_FILE)
|
|
15
|
+
CACHE_TTL = timedelta(hours=24)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_cached_models() -> list[dict[str, Any]] | None:
|
|
19
|
+
"""Load models from cache if fresh (< 24h old).
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
List of model dicts if cache is fresh, None if stale or missing.
|
|
23
|
+
Never raises — logs errors and returns None.
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
if not MODELS_CACHE_PATH.exists():
|
|
27
|
+
logger.debug("models_cache.json not found")
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
with open(MODELS_CACHE_PATH, encoding="utf-8") as f:
|
|
31
|
+
cache = json.load(f)
|
|
32
|
+
|
|
33
|
+
fetched_at_str = cache.get("fetched_at")
|
|
34
|
+
if not fetched_at_str:
|
|
35
|
+
logger.warning("models_cache.json missing fetched_at")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
fetched_at = datetime.fromisoformat(fetched_at_str)
|
|
39
|
+
age = datetime.now(UTC) - fetched_at
|
|
40
|
+
|
|
41
|
+
if age > CACHE_TTL:
|
|
42
|
+
logger.debug(f"models_cache.json is {age} old, needs refresh")
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
models = cache.get("models")
|
|
46
|
+
if not isinstance(models, list):
|
|
47
|
+
logger.warning("models_cache.json models is not a list")
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
logger.info(f"Using cached models ({len(models)} models, {age} old)")
|
|
51
|
+
return models
|
|
52
|
+
|
|
53
|
+
except (OSError, ValueError) as exc:
|
|
54
|
+
logger.warning(f"Failed to load models_cache.json: {exc}")
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def refresh_models_cache() -> list[dict[str, Any]]:
|
|
59
|
+
"""Fetch fresh models and update cache.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Fresh list of model dicts.
|
|
63
|
+
"""
|
|
64
|
+
write_models_cache()
|
|
65
|
+
cached = load_cached_models()
|
|
66
|
+
if cached is not None:
|
|
67
|
+
return cached
|
|
68
|
+
# If cache write failed, load from bundled as ultimate fallback
|
|
69
|
+
from code_muse.models_dev_parser import ModelsDevRegistry
|
|
70
|
+
|
|
71
|
+
bundled_path = Path(__file__).parent.parent / "models_dev_api.json"
|
|
72
|
+
registry = ModelsDevRegistry(bundled_path)
|
|
73
|
+
return [
|
|
74
|
+
{"slug": m.full_id, "display_name": m.name} for m in registry.models.values()
|
|
75
|
+
]
|