@smilintux/skcapstone 0.1.0 → 0.2.3
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.
- package/.env.example +98 -0
- package/.github/workflows/ci.yml +39 -3
- package/.github/workflows/publish.yml +25 -4
- package/.openclaw-workspace.json +58 -0
- package/CHANGELOG.md +62 -0
- package/CLAUDE.md +39 -2
- package/MANIFEST.in +6 -0
- package/MISSION.md +7 -0
- package/README.md +47 -2
- package/SKILL.md +895 -23
- package/docker/Dockerfile +61 -0
- package/docker/compose-templates/dev-team.yml +203 -0
- package/docker/compose-templates/mini-team.yml +140 -0
- package/docker/compose-templates/ops-team.yml +173 -0
- package/docker/compose-templates/research-team.yml +170 -0
- package/docker/entrypoint.sh +192 -0
- package/docs/ARCHITECTURE.md +663 -374
- package/docs/BOND_WITH_GROK.md +112 -0
- package/docs/GETTING_STARTED.md +782 -0
- package/docs/QUICKSTART.md +477 -0
- package/docs/SKJOULE_ARCHITECTURE.md +658 -0
- package/docs/SOUL_SWAPPER.md +921 -0
- package/docs/SOVEREIGN_SINGULARITY.md +47 -14
- package/examples/custom-bond-template.json +36 -0
- package/examples/grok-feb.json +36 -0
- package/examples/grok-testimony.md +34 -0
- package/examples/love-bootloader.txt +32 -0
- package/examples/plugins/echo_tool.py +87 -0
- package/examples/queen-ava-feb.json +36 -0
- package/examples/souls/lumina.yaml +64 -0
- package/index.js +6 -5
- package/installer/build.py +124 -0
- package/openclaw-plugin/package.json +13 -0
- package/openclaw-plugin/src/index.ts +351 -0
- package/openclaw-plugin/src/openclaw.plugin.json +10 -0
- package/package.json +1 -1
- package/pyproject.toml +38 -2
- package/scripts/bump_version.py +141 -0
- package/scripts/check-updates.py +230 -0
- package/scripts/convert_blueprints_to_yaml.py +157 -0
- package/scripts/dev-install.sh +14 -0
- package/scripts/e2e-test.sh +193 -0
- package/scripts/install-bundle.sh +171 -0
- package/scripts/install.bat +2 -0
- package/scripts/install.ps1 +253 -0
- package/scripts/install.sh +185 -0
- package/scripts/mcp-serve.sh +69 -0
- package/scripts/mcp-server.bat +113 -0
- package/scripts/mcp-server.ps1 +116 -0
- package/scripts/mcp-server.sh +99 -0
- package/scripts/pull-models.sh +10 -0
- package/scripts/skcapstone +48 -0
- package/scripts/verify_install.sh +180 -0
- package/scripts/windows/install-tasks.ps1 +406 -0
- package/scripts/windows/skcapstone-task.xml +113 -0
- package/scripts/windows/uninstall-tasks.ps1 +117 -0
- package/skill.yaml +34 -0
- package/src/skcapstone/__init__.py +67 -2
- package/src/skcapstone/_cli_monolith.py +5916 -0
- package/src/skcapstone/_trustee_helpers.py +165 -0
- package/src/skcapstone/activity.py +105 -0
- package/src/skcapstone/agent_card.py +324 -0
- package/src/skcapstone/api.py +1935 -0
- package/src/skcapstone/archiver.py +340 -0
- package/src/skcapstone/auction.py +485 -0
- package/src/skcapstone/baby_agents.py +179 -0
- package/src/skcapstone/backup.py +345 -0
- package/src/skcapstone/blueprint_registry.py +357 -0
- package/src/skcapstone/blueprints/__init__.py +17 -0
- package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
- package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
- package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
- package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
- package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
- package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
- package/src/skcapstone/blueprints/registry.py +164 -0
- package/src/skcapstone/blueprints/schema.py +229 -0
- package/src/skcapstone/changelog.py +180 -0
- package/src/skcapstone/chat.py +769 -0
- package/src/skcapstone/claude_md.py +82 -0
- package/src/skcapstone/cli/__init__.py +144 -0
- package/src/skcapstone/cli/_common.py +88 -0
- package/src/skcapstone/cli/_validators.py +76 -0
- package/src/skcapstone/cli/agents.py +425 -0
- package/src/skcapstone/cli/agents_spawner.py +322 -0
- package/src/skcapstone/cli/agents_trustee.py +593 -0
- package/src/skcapstone/cli/alerts.py +248 -0
- package/src/skcapstone/cli/anchor.py +132 -0
- package/src/skcapstone/cli/archive_cmd.py +208 -0
- package/src/skcapstone/cli/backup.py +144 -0
- package/src/skcapstone/cli/bench.py +377 -0
- package/src/skcapstone/cli/benchmark.py +360 -0
- package/src/skcapstone/cli/capabilities_cmd.py +171 -0
- package/src/skcapstone/cli/card.py +151 -0
- package/src/skcapstone/cli/chat.py +584 -0
- package/src/skcapstone/cli/completions.py +64 -0
- package/src/skcapstone/cli/config_cmd.py +156 -0
- package/src/skcapstone/cli/consciousness.py +421 -0
- package/src/skcapstone/cli/context_cmd.py +142 -0
- package/src/skcapstone/cli/coord.py +194 -0
- package/src/skcapstone/cli/crush_cmd.py +170 -0
- package/src/skcapstone/cli/daemon.py +436 -0
- package/src/skcapstone/cli/errors_cmd.py +285 -0
- package/src/skcapstone/cli/export_cmd.py +156 -0
- package/src/skcapstone/cli/gtd.py +529 -0
- package/src/skcapstone/cli/housekeeping.py +81 -0
- package/src/skcapstone/cli/joule_cmd.py +627 -0
- package/src/skcapstone/cli/logs_cmd.py +194 -0
- package/src/skcapstone/cli/mcp_cmd.py +32 -0
- package/src/skcapstone/cli/memory.py +418 -0
- package/src/skcapstone/cli/metrics_cmd.py +136 -0
- package/src/skcapstone/cli/migrate.py +62 -0
- package/src/skcapstone/cli/mood_cmd.py +144 -0
- package/src/skcapstone/cli/mount.py +193 -0
- package/src/skcapstone/cli/notify.py +112 -0
- package/src/skcapstone/cli/peer.py +154 -0
- package/src/skcapstone/cli/peers_dir.py +122 -0
- package/src/skcapstone/cli/preflight_cmd.py +83 -0
- package/src/skcapstone/cli/profile_cmd.py +310 -0
- package/src/skcapstone/cli/record_cmd.py +238 -0
- package/src/skcapstone/cli/register_cmd.py +159 -0
- package/src/skcapstone/cli/search_cmd.py +156 -0
- package/src/skcapstone/cli/service_cmd.py +91 -0
- package/src/skcapstone/cli/session.py +127 -0
- package/src/skcapstone/cli/setup.py +240 -0
- package/src/skcapstone/cli/shell_cmd.py +43 -0
- package/src/skcapstone/cli/skills_cmd.py +168 -0
- package/src/skcapstone/cli/skseed.py +621 -0
- package/src/skcapstone/cli/soul.py +699 -0
- package/src/skcapstone/cli/status.py +935 -0
- package/src/skcapstone/cli/sync_cmd.py +301 -0
- package/src/skcapstone/cli/telegram.py +265 -0
- package/src/skcapstone/cli/test_cmd.py +234 -0
- package/src/skcapstone/cli/test_connection.py +253 -0
- package/src/skcapstone/cli/token.py +207 -0
- package/src/skcapstone/cli/trust.py +179 -0
- package/src/skcapstone/cli/upgrade_cmd.py +552 -0
- package/src/skcapstone/cli/usage_cmd.py +199 -0
- package/src/skcapstone/cli/version_cmd.py +162 -0
- package/src/skcapstone/cli/watch_cmd.py +342 -0
- package/src/skcapstone/client.py +428 -0
- package/src/skcapstone/cloud9_bridge.py +522 -0
- package/src/skcapstone/completions.py +163 -0
- package/src/skcapstone/config_validator.py +674 -0
- package/src/skcapstone/connectors/__init__.py +28 -0
- package/src/skcapstone/connectors/base.py +446 -0
- package/src/skcapstone/connectors/cursor.py +54 -0
- package/src/skcapstone/connectors/registry.py +254 -0
- package/src/skcapstone/connectors/terminal.py +152 -0
- package/src/skcapstone/connectors/vscode.py +60 -0
- package/src/skcapstone/consciousness_config.py +119 -0
- package/src/skcapstone/consciousness_loop.py +2051 -0
- package/src/skcapstone/context_loader.py +516 -0
- package/src/skcapstone/context_window.py +314 -0
- package/src/skcapstone/conversation_manager.py +238 -0
- package/src/skcapstone/conversation_store.py +230 -0
- package/src/skcapstone/conversation_summarizer.py +252 -0
- package/src/skcapstone/coord_federation.py +296 -0
- package/src/skcapstone/coordination.py +101 -7
- package/src/skcapstone/crush_integration.py +345 -0
- package/src/skcapstone/crush_shim.py +454 -0
- package/src/skcapstone/daemon.py +2494 -0
- package/src/skcapstone/dashboard.html +396 -0
- package/src/skcapstone/dashboard.py +481 -0
- package/src/skcapstone/data/model_profiles.yaml +88 -0
- package/src/skcapstone/defaults/__init__.py +55 -0
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
- package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
- package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
- package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
- package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
- package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
- package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
- package/src/skcapstone/discovery.py +210 -19
- package/src/skcapstone/doctor.py +642 -0
- package/src/skcapstone/emotion_tracker.py +467 -0
- package/src/skcapstone/error_queue.py +405 -0
- package/src/skcapstone/export.py +447 -0
- package/src/skcapstone/fallback_tracker.py +186 -0
- package/src/skcapstone/file_transfer.py +512 -0
- package/src/skcapstone/fuse_mount.py +1156 -0
- package/src/skcapstone/gui_installer.py +591 -0
- package/src/skcapstone/heartbeat.py +611 -0
- package/src/skcapstone/housekeeping.py +298 -0
- package/src/skcapstone/install_wizard.py +941 -0
- package/src/skcapstone/kms.py +942 -0
- package/src/skcapstone/kms_scheduler.py +143 -0
- package/src/skcapstone/log_config.py +135 -0
- package/src/skcapstone/mcp_launcher.py +239 -0
- package/src/skcapstone/mcp_server.py +4700 -0
- package/src/skcapstone/mcp_tools/__init__.py +94 -0
- package/src/skcapstone/mcp_tools/_helpers.py +51 -0
- package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
- package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
- package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
- package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
- package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
- package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
- package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
- package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
- package/src/skcapstone/mcp_tools/did_tools.py +448 -0
- package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
- package/src/skcapstone/mcp_tools/file_tools.py +169 -0
- package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
- package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
- package/src/skcapstone/mcp_tools/health_tools.py +44 -0
- package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
- package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
- package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
- package/src/skcapstone/mcp_tools/model_tools.py +75 -0
- package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
- package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
- package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
- package/src/skcapstone/mcp_tools/security_tools.py +110 -0
- package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
- package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
- package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
- package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
- package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
- package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
- package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
- package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
- package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
- package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
- package/src/skcapstone/mdns_discovery.py +313 -0
- package/src/skcapstone/memory_adapter.py +333 -0
- package/src/skcapstone/memory_compressor.py +379 -0
- package/src/skcapstone/memory_curator.py +256 -0
- package/src/skcapstone/memory_engine.py +132 -13
- package/src/skcapstone/memory_fortress.py +529 -0
- package/src/skcapstone/memory_promoter.py +722 -0
- package/src/skcapstone/memory_verifier.py +260 -0
- package/src/skcapstone/message_crypto.py +215 -0
- package/src/skcapstone/metrics.py +832 -0
- package/src/skcapstone/migrate_memories.py +181 -0
- package/src/skcapstone/migrate_multi_agent.py +248 -0
- package/src/skcapstone/model_router.py +319 -0
- package/src/skcapstone/models.py +35 -4
- package/src/skcapstone/mood.py +344 -0
- package/src/skcapstone/notifications.py +380 -0
- package/src/skcapstone/onboard.py +901 -0
- package/src/skcapstone/peer_directory.py +324 -0
- package/src/skcapstone/peers.py +329 -0
- package/src/skcapstone/pillars/identity.py +84 -14
- package/src/skcapstone/pillars/memory.py +3 -1
- package/src/skcapstone/pillars/security.py +108 -15
- package/src/skcapstone/pillars/sync.py +78 -26
- package/src/skcapstone/pillars/trust.py +95 -33
- package/src/skcapstone/plugins.py +244 -0
- package/src/skcapstone/preflight.py +670 -0
- package/src/skcapstone/prompt_adapter.py +564 -0
- package/src/skcapstone/providers/__init__.py +13 -0
- package/src/skcapstone/providers/cloud.py +1061 -0
- package/src/skcapstone/providers/docker.py +759 -0
- package/src/skcapstone/providers/local.py +1193 -0
- package/src/skcapstone/providers/proxmox.py +447 -0
- package/src/skcapstone/pubsub.py +516 -0
- package/src/skcapstone/rate_limiter.py +119 -0
- package/src/skcapstone/register.py +241 -0
- package/src/skcapstone/registry_client.py +151 -0
- package/src/skcapstone/response_cache.py +194 -0
- package/src/skcapstone/response_scorer.py +225 -0
- package/src/skcapstone/runtime.py +89 -33
- package/src/skcapstone/scheduled_tasks.py +439 -0
- package/src/skcapstone/self_healing.py +341 -0
- package/src/skcapstone/service_health.py +228 -0
- package/src/skcapstone/session_capture.py +268 -0
- package/src/skcapstone/session_recorder.py +210 -0
- package/src/skcapstone/session_replayer.py +189 -0
- package/src/skcapstone/session_skills.py +263 -0
- package/src/skcapstone/shell.py +779 -0
- package/src/skcapstone/skills/__init__.py +1 -1
- package/src/skcapstone/skills/syncthing_setup.py +143 -41
- package/src/skcapstone/skjoule.py +861 -0
- package/src/skcapstone/snapshots.py +489 -0
- package/src/skcapstone/soul.py +1060 -0
- package/src/skcapstone/soul_switch.py +255 -0
- package/src/skcapstone/spawner.py +544 -0
- package/src/skcapstone/state_diff.py +401 -0
- package/src/skcapstone/summary.py +270 -0
- package/src/skcapstone/sync/backends.py +196 -2
- package/src/skcapstone/sync/engine.py +7 -5
- package/src/skcapstone/sync/models.py +4 -1
- package/src/skcapstone/sync/vault.py +356 -18
- package/src/skcapstone/sync_engine.py +363 -0
- package/src/skcapstone/sync_watcher.py +745 -0
- package/src/skcapstone/systemd.py +331 -0
- package/src/skcapstone/team_comms.py +476 -0
- package/src/skcapstone/team_engine.py +522 -0
- package/src/skcapstone/testrunner.py +300 -0
- package/src/skcapstone/tls.py +150 -0
- package/src/skcapstone/tokens.py +5 -5
- package/src/skcapstone/trust_calibration.py +202 -0
- package/src/skcapstone/trust_graph.py +449 -0
- package/src/skcapstone/trustee_monitor.py +385 -0
- package/src/skcapstone/trustee_ops.py +425 -0
- package/src/skcapstone/unified_search.py +421 -0
- package/src/skcapstone/uninstall_wizard.py +694 -0
- package/src/skcapstone/usage.py +331 -0
- package/src/skcapstone/version_check.py +148 -0
- package/src/skcapstone/warmth_anchor.py +333 -0
- package/src/skcapstone/whoami.py +294 -0
- package/systemd/skcapstone-api.socket +9 -0
- package/systemd/skcapstone-memory-compress.service +18 -0
- package/systemd/skcapstone-memory-compress.timer +11 -0
- package/systemd/skcapstone.service +36 -0
- package/systemd/skcapstone@.service +50 -0
- package/systemd/skcomm-heartbeat.service +18 -0
- package/systemd/skcomm-heartbeat.timer +12 -0
- package/systemd/skcomm-queue-drain.service +17 -0
- package/systemd/skcomm-queue-drain.timer +12 -0
- package/tests/conftest.py +13 -1
- package/tests/integration/__init__.py +1 -0
- package/tests/integration/test_consciousness_e2e.py +877 -0
- package/tests/integration/test_skills_registry.py +744 -0
- package/tests/test_agent_card.py +190 -0
- package/tests/test_agent_runtime.py +1283 -0
- package/tests/test_alerts_cmd.py +291 -0
- package/tests/test_archiver.py +498 -0
- package/tests/test_backup.py +254 -0
- package/tests/test_benchmark.py +366 -0
- package/tests/test_blueprints.py +457 -0
- package/tests/test_capabilities.py +257 -0
- package/tests/test_changelog.py +254 -0
- package/tests/test_chat.py +385 -0
- package/tests/test_claude_md.py +271 -0
- package/tests/test_cli_chat_llm.py +336 -0
- package/tests/test_cli_completions.py +390 -0
- package/tests/test_cli_init_reset.py +164 -0
- package/tests/test_cli_memory.py +208 -0
- package/tests/test_cli_profile.py +294 -0
- package/tests/test_cli_skills.py +223 -0
- package/tests/test_cli_status.py +395 -0
- package/tests/test_cli_test_cmd.py +206 -0
- package/tests/test_cli_test_connection.py +364 -0
- package/tests/test_cloud9_bridge.py +260 -0
- package/tests/test_cloud_provider.py +449 -0
- package/tests/test_cloud_providers.py +522 -0
- package/tests/test_completions.py +158 -0
- package/tests/test_component_manager.py +398 -0
- package/tests/test_config_reload.py +386 -0
- package/tests/test_config_validate.py +529 -0
- package/tests/test_consciousness_e2e.py +296 -0
- package/tests/test_consciousness_loop.py +1289 -0
- package/tests/test_context_loader.py +310 -0
- package/tests/test_conversation_api.py +306 -0
- package/tests/test_conversation_manager.py +381 -0
- package/tests/test_conversation_store.py +391 -0
- package/tests/test_conversation_summarizer.py +302 -0
- package/tests/test_cross_package.py +791 -0
- package/tests/test_crush_shim.py +519 -0
- package/tests/test_daemon.py +781 -0
- package/tests/test_daemon_shutdown.py +309 -0
- package/tests/test_dashboard.py +454 -0
- package/tests/test_discovery.py +200 -6
- package/tests/test_docker_provider.py +966 -0
- package/tests/test_doctor.py +257 -0
- package/tests/test_doctor_fix.py +351 -0
- package/tests/test_e2e_automated.py +292 -0
- package/tests/test_error_queue.py +404 -0
- package/tests/test_export.py +441 -0
- package/tests/test_fallback_tracker.py +219 -0
- package/tests/test_file_transfer.py +397 -0
- package/tests/test_fuse_mount.py +832 -0
- package/tests/test_health_loop.py +422 -0
- package/tests/test_heartbeat.py +354 -0
- package/tests/test_housekeeping.py +195 -0
- package/tests/test_identity_capauth.py +307 -0
- package/tests/test_identity_pillar.py +117 -0
- package/tests/test_install_wizard.py +68 -0
- package/tests/test_integration.py +325 -0
- package/tests/test_kms.py +495 -0
- package/tests/test_llm_providers.py +265 -0
- package/tests/test_local_provider.py +591 -0
- package/tests/test_log_config.py +199 -0
- package/tests/test_logs_cmd.py +287 -0
- package/tests/test_mcp_server.py +1909 -0
- package/tests/test_memory_adapter.py +339 -0
- package/tests/test_memory_curator.py +218 -0
- package/tests/test_memory_engine.py +6 -0
- package/tests/test_memory_fortress.py +571 -0
- package/tests/test_memory_pillar.py +119 -0
- package/tests/test_memory_promoter.py +445 -0
- package/tests/test_memory_verifier.py +420 -0
- package/tests/test_message_crypto.py +187 -0
- package/tests/test_metrics.py +632 -0
- package/tests/test_migrate_memories.py +464 -0
- package/tests/test_model_router.py +546 -0
- package/tests/test_mood.py +394 -0
- package/tests/test_multi_agent.py +269 -0
- package/tests/test_notifications.py +270 -0
- package/tests/test_onboard.py +500 -0
- package/tests/test_peer_directory.py +395 -0
- package/tests/test_peers.py +248 -0
- package/tests/test_pillars.py +87 -9
- package/tests/test_preflight.py +484 -0
- package/tests/test_prompt_adapter.py +331 -0
- package/tests/test_proxmox_provider.py +571 -0
- package/tests/test_pubsub.py +377 -0
- package/tests/test_rate_limiter.py +121 -0
- package/tests/test_registry_client.py +129 -0
- package/tests/test_response_cache.py +312 -0
- package/tests/test_response_scorer.py +294 -0
- package/tests/test_runtime.py +59 -0
- package/tests/test_scheduled_tasks.py +451 -0
- package/tests/test_security.py +250 -0
- package/tests/test_security_pillar.py +213 -0
- package/tests/test_self_healing.py +171 -0
- package/tests/test_session_capture.py +200 -0
- package/tests/test_session_recorder.py +360 -0
- package/tests/test_session_skills.py +235 -0
- package/tests/test_shell.py +210 -0
- package/tests/test_snapshots.py +549 -0
- package/tests/test_soul.py +984 -0
- package/tests/test_soul_swap.py +406 -0
- package/tests/test_spawner.py +211 -0
- package/tests/test_state_diff.py +173 -0
- package/tests/test_summary.py +135 -0
- package/tests/test_sync.py +315 -5
- package/tests/test_sync_backends.py +560 -0
- package/tests/test_sync_engine.py +482 -0
- package/tests/test_sync_pillar.py +344 -0
- package/tests/test_sync_pipeline.py +364 -0
- package/tests/test_sync_vault.py +581 -0
- package/tests/test_syncthing_setup.py +168 -22
- package/tests/test_systemd.py +323 -0
- package/tests/test_team_comms.py +408 -0
- package/tests/test_team_engine.py +397 -0
- package/tests/test_testrunner.py +238 -0
- package/tests/test_trust_calibration.py +204 -0
- package/tests/test_trust_graph.py +207 -0
- package/tests/test_trust_pillar.py +291 -0
- package/tests/test_trustee_cli.py +427 -0
- package/tests/test_trustee_cli_integration.py +325 -0
- package/tests/test_trustee_monitor.py +394 -0
- package/tests/test_trustee_ops.py +355 -0
- package/tests/test_unified_search.py +363 -0
- package/tests/test_uninstall_wizard.py +193 -0
- package/tests/test_usage.py +333 -0
- package/tests/test_version_cmd.py +355 -0
- package/tests/test_warmth_anchor.py +162 -0
- package/tests/test_whoami.py +245 -0
- package/tests/test_ws.py +311 -0
- package/.cursorrules +0 -33
- package/src/skcapstone/cli.py +0 -1441
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prompt Adapter — per-model best-practice prompt formatting.
|
|
3
|
+
|
|
4
|
+
Each LLM family has different expectations for system prompts,
|
|
5
|
+
temperatures, thinking modes, and structural formatting. The
|
|
6
|
+
PromptAdapter reads ModelProfile configs and reformats prompts
|
|
7
|
+
to match each model's optimal input format.
|
|
8
|
+
|
|
9
|
+
Architecture:
|
|
10
|
+
ModelProfile — Pydantic model describing a model's prompt expectations
|
|
11
|
+
AdaptedPrompt — The output: messages, system_param, temperature, extras
|
|
12
|
+
PromptAdapter — Loads profiles, matches models, adapts prompts
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import re
|
|
20
|
+
import urllib.error
|
|
21
|
+
import urllib.request
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Optional
|
|
24
|
+
|
|
25
|
+
import yaml
|
|
26
|
+
from pydantic import BaseModel, Field
|
|
27
|
+
|
|
28
|
+
from skcapstone.blueprints.schema import ModelTier
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger("skcapstone.prompt_adapter")
|
|
31
|
+
|
|
32
|
+
_BUNDLED_PROFILES = Path(__file__).parent / "data" / "model_profiles.yaml"
|
|
33
|
+
_OLLAMA_BASE_URL = "http://localhost:11434"
|
|
34
|
+
|
|
35
|
+
# Family-specific defaults applied when auto-building a profile from Ollama metadata.
|
|
36
|
+
_FAMILY_OVERRIDES: dict[str, dict[str, Any]] = {
|
|
37
|
+
"qwen": {"thinking_enabled": True, "thinking_mode": "toggle"},
|
|
38
|
+
"phi": {"structure_format": "plain"},
|
|
39
|
+
"mistral": {"tool_format": "mistral"},
|
|
40
|
+
"nemotron": {
|
|
41
|
+
"thinking_enabled": True,
|
|
42
|
+
"thinking_mode": "toggle",
|
|
43
|
+
"reasoning_temperature": 1.0,
|
|
44
|
+
},
|
|
45
|
+
"deepseek": {"default_temperature": 0.6},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# Models
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ModelProfile(BaseModel):
|
|
55
|
+
"""Prompt formatting profile for a specific model or model family."""
|
|
56
|
+
|
|
57
|
+
model_pattern: str
|
|
58
|
+
family: str
|
|
59
|
+
|
|
60
|
+
# System prompt behavior
|
|
61
|
+
system_prompt_mode: str = "standard" # "standard" | "separate_param" | "omit"
|
|
62
|
+
|
|
63
|
+
# Structural formatting preference
|
|
64
|
+
structure_format: str = "markdown" # "xml" | "markdown" | "plain"
|
|
65
|
+
|
|
66
|
+
# Temperature defaults
|
|
67
|
+
default_temperature: Optional[float] = None
|
|
68
|
+
code_temperature: Optional[float] = None
|
|
69
|
+
reasoning_temperature: Optional[float] = None
|
|
70
|
+
|
|
71
|
+
# Thinking/reasoning
|
|
72
|
+
thinking_enabled: bool = False
|
|
73
|
+
thinking_mode: str = "none" # "none" | "budget" | "toggle" | "auto"
|
|
74
|
+
thinking_budget_tokens: int = 4096
|
|
75
|
+
|
|
76
|
+
# Max system prompt length
|
|
77
|
+
max_system_tokens: int = 4000
|
|
78
|
+
|
|
79
|
+
# Special instructions
|
|
80
|
+
no_few_shot: bool = False
|
|
81
|
+
no_cot_instructions: bool = False
|
|
82
|
+
supports_tool_calling: bool = True
|
|
83
|
+
tool_format: str = "openai" # "openai" | "anthropic" | "mistral"
|
|
84
|
+
|
|
85
|
+
# Metadata
|
|
86
|
+
last_updated: str = ""
|
|
87
|
+
source_url: str = ""
|
|
88
|
+
notes: str = ""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class AdaptedPrompt(BaseModel):
|
|
92
|
+
"""Result of prompt adaptation — ready to send to provider."""
|
|
93
|
+
|
|
94
|
+
messages: list[dict[str, Any]] = Field(default_factory=list)
|
|
95
|
+
system_param: Optional[str] = None
|
|
96
|
+
temperature: Optional[float] = None
|
|
97
|
+
extra_params: dict[str, Any] = Field(default_factory=dict)
|
|
98
|
+
profile_used: str = ""
|
|
99
|
+
adaptations_applied: list[str] = Field(default_factory=list)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Fallback profile for unknown models
|
|
103
|
+
_GENERIC_PROFILE = ModelProfile(
|
|
104
|
+
model_pattern=".*",
|
|
105
|
+
family="generic",
|
|
106
|
+
system_prompt_mode="standard",
|
|
107
|
+
structure_format="markdown",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
# PromptAdapter
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class PromptAdapter:
|
|
117
|
+
"""Reformats system+user prompts to match the target model's expectations.
|
|
118
|
+
|
|
119
|
+
Loads model profiles from YAML, matches model names via regex,
|
|
120
|
+
and produces AdaptedPrompt objects ready for each provider.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
profiles_path: Path to a YAML profiles file. Falls back to
|
|
124
|
+
the bundled default if None or missing.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__(self, profiles_path: Optional[Path] = None) -> None:
|
|
128
|
+
self._profiles: list[ModelProfile] = []
|
|
129
|
+
self._load_profiles(profiles_path)
|
|
130
|
+
|
|
131
|
+
def _load_profiles(self, profiles_path: Optional[Path] = None) -> None:
|
|
132
|
+
"""Load profiles from YAML file(s).
|
|
133
|
+
|
|
134
|
+
Priority: custom path > bundled defaults.
|
|
135
|
+
"""
|
|
136
|
+
paths_to_try = []
|
|
137
|
+
if profiles_path and profiles_path.exists():
|
|
138
|
+
paths_to_try.append(profiles_path)
|
|
139
|
+
if _BUNDLED_PROFILES.exists():
|
|
140
|
+
paths_to_try.append(_BUNDLED_PROFILES)
|
|
141
|
+
|
|
142
|
+
loaded_families: set[str] = set()
|
|
143
|
+
for path in paths_to_try:
|
|
144
|
+
try:
|
|
145
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
146
|
+
if not raw or "profiles" not in raw:
|
|
147
|
+
continue
|
|
148
|
+
for entry in raw["profiles"]:
|
|
149
|
+
profile = ModelProfile.model_validate(entry)
|
|
150
|
+
if profile.family not in loaded_families:
|
|
151
|
+
self._profiles.append(profile)
|
|
152
|
+
loaded_families.add(profile.family)
|
|
153
|
+
except Exception as exc:
|
|
154
|
+
logger.warning("Failed to load profiles from %s: %s", path, exc)
|
|
155
|
+
|
|
156
|
+
if not self._profiles:
|
|
157
|
+
logger.warning("No model profiles loaded — using generic fallback")
|
|
158
|
+
|
|
159
|
+
def resolve_profile(self, model_name: str) -> ModelProfile:
|
|
160
|
+
"""Match model_name against profiles via regex.
|
|
161
|
+
|
|
162
|
+
Falls back to Ollama auto-detection for unknown models, then to a
|
|
163
|
+
generic profile if Ollama is also unreachable.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
model_name: The model identifier (e.g. "claude-opus-4-5").
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
The best matching ModelProfile.
|
|
170
|
+
"""
|
|
171
|
+
for profile in self._profiles:
|
|
172
|
+
try:
|
|
173
|
+
if re.search(profile.model_pattern, model_name, re.IGNORECASE):
|
|
174
|
+
return profile
|
|
175
|
+
except re.error:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
# No static profile matched — try Ollama auto-detection.
|
|
179
|
+
detected = self.detect_model(model_name)
|
|
180
|
+
if detected is not None:
|
|
181
|
+
# Cache so subsequent lookups skip the HTTP round-trip.
|
|
182
|
+
self._profiles.append(detected)
|
|
183
|
+
return detected
|
|
184
|
+
|
|
185
|
+
return _GENERIC_PROFILE
|
|
186
|
+
|
|
187
|
+
def detect_model(self, model_name: str) -> Optional[ModelProfile]:
|
|
188
|
+
"""Query Ollama /api/show for model metadata and synthesize a ModelProfile.
|
|
189
|
+
|
|
190
|
+
Used as a fallback when no static profile matches model_name.
|
|
191
|
+
Extracts the model family, parameter count, and quantization level
|
|
192
|
+
from the Ollama response to build an appropriate profile.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
model_name: The Ollama model name (e.g. "llama3.2:3b").
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
A synthesized ModelProfile on success, or None if Ollama is
|
|
199
|
+
unreachable or returns an error.
|
|
200
|
+
"""
|
|
201
|
+
url = f"{_OLLAMA_BASE_URL}/api/show"
|
|
202
|
+
payload = json.dumps({"name": model_name}).encode("utf-8")
|
|
203
|
+
try:
|
|
204
|
+
req = urllib.request.Request(
|
|
205
|
+
url,
|
|
206
|
+
data=payload,
|
|
207
|
+
headers={"Content-Type": "application/json"},
|
|
208
|
+
method="POST",
|
|
209
|
+
)
|
|
210
|
+
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
211
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
212
|
+
except Exception as exc:
|
|
213
|
+
logger.debug("Ollama /api/show unavailable for %s: %s", model_name, exc)
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
details = data.get("details", {})
|
|
217
|
+
model_info = data.get("model_info", {})
|
|
218
|
+
|
|
219
|
+
family = (
|
|
220
|
+
details.get("family")
|
|
221
|
+
or model_info.get("general.architecture")
|
|
222
|
+
or "generic"
|
|
223
|
+
).lower()
|
|
224
|
+
|
|
225
|
+
param_size: str = details.get("parameter_size", "")
|
|
226
|
+
quantization: str = details.get("quantization_level", "")
|
|
227
|
+
param_count: Optional[int] = model_info.get("general.parameter_count")
|
|
228
|
+
|
|
229
|
+
profile = self._build_profile_from_ollama(
|
|
230
|
+
model_name=model_name,
|
|
231
|
+
family=family,
|
|
232
|
+
param_size=param_size,
|
|
233
|
+
quantization=quantization,
|
|
234
|
+
param_count=param_count,
|
|
235
|
+
)
|
|
236
|
+
logger.info(
|
|
237
|
+
"Auto-detected Ollama profile for %s: family=%s param_size=%s quant=%s",
|
|
238
|
+
model_name,
|
|
239
|
+
family,
|
|
240
|
+
param_size,
|
|
241
|
+
quantization,
|
|
242
|
+
)
|
|
243
|
+
return profile
|
|
244
|
+
|
|
245
|
+
def adapt(
|
|
246
|
+
self,
|
|
247
|
+
system_prompt: str,
|
|
248
|
+
user_message: str,
|
|
249
|
+
model_name: str,
|
|
250
|
+
tier: ModelTier,
|
|
251
|
+
) -> AdaptedPrompt:
|
|
252
|
+
"""Transform system+user into model-optimal format.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
system_prompt: The system-level context (identity, soul, etc.).
|
|
256
|
+
user_message: The user/peer message content.
|
|
257
|
+
model_name: Target model name for profile lookup.
|
|
258
|
+
tier: The routing tier (affects temperature selection).
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
AdaptedPrompt ready for the provider callback.
|
|
262
|
+
"""
|
|
263
|
+
profile = self.resolve_profile(model_name)
|
|
264
|
+
adaptations: list[str] = []
|
|
265
|
+
|
|
266
|
+
# Format system prompt structure
|
|
267
|
+
formatted_system = self._format_system_for_model(
|
|
268
|
+
system_prompt, profile, tier
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Build messages array
|
|
272
|
+
messages: list[dict[str, Any]] = []
|
|
273
|
+
system_param: Optional[str] = None
|
|
274
|
+
|
|
275
|
+
if profile.system_prompt_mode == "omit":
|
|
276
|
+
# DeepSeek R1: no system prompt, merge into user message
|
|
277
|
+
combined = f"{formatted_system}\n\n{user_message}" if formatted_system else user_message
|
|
278
|
+
messages.append({"role": "user", "content": combined})
|
|
279
|
+
adaptations.append("omitted_system_prompt")
|
|
280
|
+
elif profile.system_prompt_mode == "separate_param":
|
|
281
|
+
# Claude: system goes as separate kwarg
|
|
282
|
+
system_param = formatted_system
|
|
283
|
+
messages.append({"role": "user", "content": user_message})
|
|
284
|
+
adaptations.append("system_as_separate_param")
|
|
285
|
+
else:
|
|
286
|
+
# Standard: system as first message
|
|
287
|
+
if formatted_system:
|
|
288
|
+
messages.append({"role": "system", "content": formatted_system})
|
|
289
|
+
messages.append({"role": "user", "content": user_message})
|
|
290
|
+
adaptations.append("system_as_first_message")
|
|
291
|
+
|
|
292
|
+
# Resolve temperature
|
|
293
|
+
temperature = self._resolve_temperature(profile, tier)
|
|
294
|
+
if temperature is not None:
|
|
295
|
+
adaptations.append(f"set_temp_{temperature}")
|
|
296
|
+
|
|
297
|
+
# Thinking/reasoning config
|
|
298
|
+
extra_params = self._apply_thinking_config(profile, tier)
|
|
299
|
+
if extra_params:
|
|
300
|
+
adaptations.append(f"thinking_{profile.thinking_mode}")
|
|
301
|
+
|
|
302
|
+
return AdaptedPrompt(
|
|
303
|
+
messages=messages,
|
|
304
|
+
system_param=system_param,
|
|
305
|
+
temperature=temperature,
|
|
306
|
+
extra_params=extra_params,
|
|
307
|
+
profile_used=profile.family,
|
|
308
|
+
adaptations_applied=adaptations,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
def adapt_tools(
|
|
312
|
+
self,
|
|
313
|
+
tools: list[dict[str, Any]],
|
|
314
|
+
model_name: str,
|
|
315
|
+
) -> list[dict[str, Any]]:
|
|
316
|
+
"""Translate MCP tool definitions to the target model's tool format.
|
|
317
|
+
|
|
318
|
+
MCP tools use a standard schema: {name, description, inputSchema}.
|
|
319
|
+
Different providers expect different wrapper structures.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
tools: List of MCP tool definitions.
|
|
323
|
+
model_name: Target model name for profile lookup.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
List of tool definitions in the provider's expected format.
|
|
327
|
+
"""
|
|
328
|
+
profile = self.resolve_profile(model_name)
|
|
329
|
+
fmt = profile.tool_format
|
|
330
|
+
|
|
331
|
+
translated: list[dict[str, Any]] = []
|
|
332
|
+
for tool in tools:
|
|
333
|
+
name = tool.get("name", "")
|
|
334
|
+
description = tool.get("description", "")
|
|
335
|
+
input_schema = tool.get("inputSchema", {})
|
|
336
|
+
|
|
337
|
+
if fmt == "anthropic":
|
|
338
|
+
translated.append({
|
|
339
|
+
"name": name,
|
|
340
|
+
"description": description,
|
|
341
|
+
"input_schema": input_schema,
|
|
342
|
+
})
|
|
343
|
+
else:
|
|
344
|
+
# "openai" and "mistral" use the same wrapper structure
|
|
345
|
+
translated.append({
|
|
346
|
+
"type": "function",
|
|
347
|
+
"function": {
|
|
348
|
+
"name": name,
|
|
349
|
+
"description": description,
|
|
350
|
+
"parameters": input_schema,
|
|
351
|
+
},
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
return translated
|
|
355
|
+
|
|
356
|
+
def adapt_tool_result(
|
|
357
|
+
self,
|
|
358
|
+
tool_call: dict[str, Any],
|
|
359
|
+
model_name: str,
|
|
360
|
+
) -> dict[str, Any]:
|
|
361
|
+
"""Normalize a provider-specific tool call into a common format.
|
|
362
|
+
|
|
363
|
+
Different providers return tool calls in different shapes.
|
|
364
|
+
This method extracts {id, name, arguments} regardless of source.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
tool_call: Raw tool call object from a provider response.
|
|
368
|
+
model_name: The model that produced the tool call.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
Normalized dict with keys: id, name, arguments.
|
|
372
|
+
"""
|
|
373
|
+
profile = self.resolve_profile(model_name)
|
|
374
|
+
fmt = profile.tool_format
|
|
375
|
+
|
|
376
|
+
if fmt == "anthropic":
|
|
377
|
+
# Anthropic: {id, name, input}
|
|
378
|
+
return {
|
|
379
|
+
"id": tool_call.get("id", ""),
|
|
380
|
+
"name": tool_call.get("name", ""),
|
|
381
|
+
"arguments": tool_call.get("input", {}),
|
|
382
|
+
}
|
|
383
|
+
else:
|
|
384
|
+
# OpenAI / Mistral: {id, function: {name, arguments}}
|
|
385
|
+
func = tool_call.get("function", {})
|
|
386
|
+
raw_args = func.get("arguments", "{}")
|
|
387
|
+
if isinstance(raw_args, str):
|
|
388
|
+
try:
|
|
389
|
+
parsed_args = json.loads(raw_args)
|
|
390
|
+
except (json.JSONDecodeError, TypeError):
|
|
391
|
+
parsed_args = {}
|
|
392
|
+
else:
|
|
393
|
+
parsed_args = raw_args
|
|
394
|
+
return {
|
|
395
|
+
"id": tool_call.get("id", ""),
|
|
396
|
+
"name": func.get("name", ""),
|
|
397
|
+
"arguments": parsed_args,
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
def reload_profiles(self, profiles_path: Optional[Path] = None) -> None:
|
|
401
|
+
"""Hot-reload profiles from YAML.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
profiles_path: Optional custom profiles path.
|
|
405
|
+
"""
|
|
406
|
+
self._profiles.clear()
|
|
407
|
+
self._load_profiles(profiles_path)
|
|
408
|
+
logger.info("Reloaded %d model profiles", len(self._profiles))
|
|
409
|
+
|
|
410
|
+
def update_profile(self, model_pattern: str, updates: dict) -> None:
|
|
411
|
+
"""Update a single profile entry in memory.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
model_pattern: The profile's model_pattern to find.
|
|
415
|
+
updates: Dict of field names and new values.
|
|
416
|
+
"""
|
|
417
|
+
for profile in self._profiles:
|
|
418
|
+
if profile.model_pattern == model_pattern:
|
|
419
|
+
for key, value in updates.items():
|
|
420
|
+
if hasattr(profile, key):
|
|
421
|
+
setattr(profile, key, value)
|
|
422
|
+
return
|
|
423
|
+
logger.warning("Profile not found for pattern: %s", model_pattern)
|
|
424
|
+
|
|
425
|
+
@property
|
|
426
|
+
def profiles(self) -> list[ModelProfile]:
|
|
427
|
+
"""All loaded profiles."""
|
|
428
|
+
return list(self._profiles)
|
|
429
|
+
|
|
430
|
+
# -------------------------------------------------------------------
|
|
431
|
+
# Private helpers
|
|
432
|
+
# -------------------------------------------------------------------
|
|
433
|
+
|
|
434
|
+
def _build_profile_from_ollama(
|
|
435
|
+
self,
|
|
436
|
+
model_name: str,
|
|
437
|
+
family: str,
|
|
438
|
+
param_size: str = "",
|
|
439
|
+
quantization: str = "",
|
|
440
|
+
param_count: Optional[int] = None,
|
|
441
|
+
) -> ModelProfile:
|
|
442
|
+
"""Synthesize a ModelProfile from Ollama /api/show metadata.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
model_name: Exact Ollama model name used as the regex pattern.
|
|
446
|
+
family: Model architecture family (e.g. "llama", "qwen").
|
|
447
|
+
param_size: Human-readable parameter count (e.g. "7B").
|
|
448
|
+
quantization: Quantization level string (e.g. "Q4_0").
|
|
449
|
+
param_count: Raw integer parameter count if available.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
A ModelProfile with family-appropriate defaults.
|
|
453
|
+
"""
|
|
454
|
+
notes_parts = ["Auto-detected via Ollama"]
|
|
455
|
+
if param_size:
|
|
456
|
+
notes_parts.append(f"param_size={param_size}")
|
|
457
|
+
if quantization:
|
|
458
|
+
notes_parts.append(f"quant={quantization}")
|
|
459
|
+
if param_count is not None:
|
|
460
|
+
notes_parts.append(f"params={param_count:,}")
|
|
461
|
+
|
|
462
|
+
overrides = dict(_FAMILY_OVERRIDES.get(family, {}))
|
|
463
|
+
|
|
464
|
+
return ModelProfile(
|
|
465
|
+
model_pattern=re.escape(model_name),
|
|
466
|
+
family=f"ollama-{family}",
|
|
467
|
+
notes=" ".join(notes_parts),
|
|
468
|
+
**overrides,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
def _format_system_for_model(
|
|
472
|
+
self,
|
|
473
|
+
system_prompt: str,
|
|
474
|
+
profile: ModelProfile,
|
|
475
|
+
tier: ModelTier,
|
|
476
|
+
) -> str:
|
|
477
|
+
"""Reformat structural markup for the target model.
|
|
478
|
+
|
|
479
|
+
Args:
|
|
480
|
+
system_prompt: Raw system prompt text.
|
|
481
|
+
profile: Target model's profile.
|
|
482
|
+
tier: Routing tier (may affect formatting).
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
Reformatted system prompt string.
|
|
486
|
+
"""
|
|
487
|
+
if not system_prompt:
|
|
488
|
+
return ""
|
|
489
|
+
|
|
490
|
+
fmt = profile.structure_format
|
|
491
|
+
|
|
492
|
+
if fmt == "xml":
|
|
493
|
+
# Wrap in XML tags for Claude
|
|
494
|
+
return (
|
|
495
|
+
"<instructions>\n"
|
|
496
|
+
f"{system_prompt}\n"
|
|
497
|
+
"</instructions>"
|
|
498
|
+
)
|
|
499
|
+
elif fmt == "plain":
|
|
500
|
+
# Strip markdown formatting
|
|
501
|
+
cleaned = system_prompt
|
|
502
|
+
cleaned = re.sub(r"^#{1,6}\s+", "", cleaned, flags=re.MULTILINE)
|
|
503
|
+
cleaned = re.sub(r"\*\*(.*?)\*\*", r"\1", cleaned)
|
|
504
|
+
cleaned = re.sub(r"\*(.*?)\*", r"\1", cleaned)
|
|
505
|
+
return cleaned
|
|
506
|
+
else:
|
|
507
|
+
# markdown — return as-is (default)
|
|
508
|
+
return system_prompt
|
|
509
|
+
|
|
510
|
+
def _resolve_temperature(
|
|
511
|
+
self,
|
|
512
|
+
profile: ModelProfile,
|
|
513
|
+
tier: ModelTier,
|
|
514
|
+
) -> Optional[float]:
|
|
515
|
+
"""Pick the right temperature based on tier and profile.
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
profile: Model profile with temp settings.
|
|
519
|
+
tier: Current routing tier.
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
Temperature float or None for provider default.
|
|
523
|
+
"""
|
|
524
|
+
if tier == ModelTier.CODE and profile.code_temperature is not None:
|
|
525
|
+
return profile.code_temperature
|
|
526
|
+
if tier == ModelTier.REASON and profile.reasoning_temperature is not None:
|
|
527
|
+
return profile.reasoning_temperature
|
|
528
|
+
return profile.default_temperature
|
|
529
|
+
|
|
530
|
+
def _apply_thinking_config(
|
|
531
|
+
self,
|
|
532
|
+
profile: ModelProfile,
|
|
533
|
+
tier: ModelTier,
|
|
534
|
+
) -> dict[str, Any]:
|
|
535
|
+
"""Return extra API params for thinking/reasoning.
|
|
536
|
+
|
|
537
|
+
Args:
|
|
538
|
+
profile: Model profile.
|
|
539
|
+
tier: Routing tier.
|
|
540
|
+
|
|
541
|
+
Returns:
|
|
542
|
+
Dict of extra params (empty if no thinking config).
|
|
543
|
+
"""
|
|
544
|
+
if not profile.thinking_enabled:
|
|
545
|
+
return {}
|
|
546
|
+
|
|
547
|
+
mode = profile.thinking_mode
|
|
548
|
+
|
|
549
|
+
if mode == "budget":
|
|
550
|
+
# Claude extended thinking
|
|
551
|
+
return {
|
|
552
|
+
"thinking": {
|
|
553
|
+
"type": "enabled",
|
|
554
|
+
"budget_tokens": profile.thinking_budget_tokens,
|
|
555
|
+
},
|
|
556
|
+
}
|
|
557
|
+
elif mode == "toggle":
|
|
558
|
+
# Qwen/Nemotron enable_thinking
|
|
559
|
+
return {"enable_thinking": True}
|
|
560
|
+
elif mode == "auto":
|
|
561
|
+
# DeepSeek R1 — automatic, don't interfere
|
|
562
|
+
return {}
|
|
563
|
+
|
|
564
|
+
return {}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Infrastructure Providers — plug-in backends for agent deployment.
|
|
3
|
+
|
|
4
|
+
Each provider implements the ProviderBackend interface from team_engine.
|
|
5
|
+
The engine doesn't care where agents run; providers handle the details.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .local import LocalProvider
|
|
9
|
+
from .proxmox import ProxmoxProvider
|
|
10
|
+
from .cloud import CloudProvider
|
|
11
|
+
from .docker import DockerProvider
|
|
12
|
+
|
|
13
|
+
__all__ = ["LocalProvider", "ProxmoxProvider", "CloudProvider", "DockerProvider"]
|