crca 1.4.0__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.
- .github/ISSUE_TEMPLATE/bug_report.md +65 -0
- .github/ISSUE_TEMPLATE/feature_request.md +41 -0
- .github/PULL_REQUEST_TEMPLATE.md +20 -0
- .github/workflows/publish-manual.yml +61 -0
- .github/workflows/publish.yml +64 -0
- .gitignore +214 -0
- CRCA.py +4156 -0
- LICENSE +201 -0
- MANIFEST.in +43 -0
- PKG-INFO +5035 -0
- README.md +4959 -0
- __init__.py +17 -0
- branches/CRCA-Q.py +2728 -0
- branches/crca_cg/corposwarm.py +9065 -0
- branches/crca_cg/fix_rancher_docker_creds.ps1 +155 -0
- branches/crca_cg/package.json +5 -0
- branches/crca_cg/test_bolt_integration.py +446 -0
- branches/crca_cg/test_corposwarm_comprehensive.py +773 -0
- branches/crca_cg/test_new_features.py +163 -0
- branches/crca_sd/__init__.py +149 -0
- branches/crca_sd/crca_sd_core.py +770 -0
- branches/crca_sd/crca_sd_governance.py +1325 -0
- branches/crca_sd/crca_sd_mpc.py +1130 -0
- branches/crca_sd/crca_sd_realtime.py +1844 -0
- branches/crca_sd/crca_sd_tui.py +1133 -0
- crca-1.4.0.dist-info/METADATA +5035 -0
- crca-1.4.0.dist-info/RECORD +501 -0
- crca-1.4.0.dist-info/WHEEL +4 -0
- crca-1.4.0.dist-info/licenses/LICENSE +201 -0
- docs/CRCA-Q.md +2333 -0
- examples/config.yaml.example +25 -0
- examples/crca_sd_example.py +513 -0
- examples/data_broker_example.py +294 -0
- examples/logistics_corporation.py +861 -0
- examples/palantir_example.py +299 -0
- examples/policy_bench.py +934 -0
- examples/pridnestrovia-sd.py +705 -0
- examples/pridnestrovia_realtime.py +1902 -0
- prompts/__init__.py +10 -0
- prompts/default_crca.py +101 -0
- pyproject.toml +151 -0
- requirements.txt +76 -0
- schemas/__init__.py +43 -0
- schemas/mcpSchemas.py +51 -0
- schemas/policy.py +458 -0
- templates/__init__.py +38 -0
- templates/base_specialized_agent.py +195 -0
- templates/drift_detection.py +325 -0
- templates/examples/causal_agent_template.py +309 -0
- templates/examples/drag_drop_example.py +213 -0
- templates/examples/logistics_agent_template.py +207 -0
- templates/examples/trading_agent_template.py +206 -0
- templates/feature_mixins.py +253 -0
- templates/graph_management.py +442 -0
- templates/llm_integration.py +194 -0
- templates/module_registry.py +276 -0
- templates/mpc_planner.py +280 -0
- templates/policy_loop.py +1168 -0
- templates/prediction_framework.py +448 -0
- templates/statistical_methods.py +778 -0
- tests/sanity.yml +31 -0
- tests/sanity_check +406 -0
- tests/test_core.py +47 -0
- tests/test_crca_excel.py +166 -0
- tests/test_crca_sd.py +780 -0
- tests/test_data_broker.py +424 -0
- tests/test_palantir.py +349 -0
- tools/__init__.py +38 -0
- tools/actuators.py +437 -0
- tools/bolt.diy/Dockerfile +103 -0
- tools/bolt.diy/app/components/@settings/core/AvatarDropdown.tsx +175 -0
- tools/bolt.diy/app/components/@settings/core/ControlPanel.tsx +345 -0
- tools/bolt.diy/app/components/@settings/core/constants.tsx +108 -0
- tools/bolt.diy/app/components/@settings/core/types.ts +114 -0
- tools/bolt.diy/app/components/@settings/index.ts +12 -0
- tools/bolt.diy/app/components/@settings/shared/components/TabTile.tsx +151 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionForm.tsx +193 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionTestIndicator.tsx +60 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ErrorState.tsx +102 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/LoadingState.tsx +94 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ServiceHeader.tsx +72 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/index.ts +6 -0
- tools/bolt.diy/app/components/@settings/tabs/data/DataTab.tsx +721 -0
- tools/bolt.diy/app/components/@settings/tabs/data/DataVisualization.tsx +384 -0
- tools/bolt.diy/app/components/@settings/tabs/event-logs/EventLogsTab.tsx +1013 -0
- tools/bolt.diy/app/components/@settings/tabs/features/FeaturesTab.tsx +295 -0
- tools/bolt.diy/app/components/@settings/tabs/github/GitHubTab.tsx +281 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubAuthDialog.tsx +173 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubCacheManager.tsx +367 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubConnection.tsx +233 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubErrorBoundary.tsx +105 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubProgressiveLoader.tsx +266 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositoryCard.tsx +121 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositorySelector.tsx +312 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubStats.tsx +291 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubUserProfile.tsx +46 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/GitHubStateIndicators.tsx +264 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/RepositoryCard.tsx +361 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/index.ts +11 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/GitLabTab.tsx +305 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabAuthDialog.tsx +186 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabConnection.tsx +253 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabRepositorySelector.tsx +358 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryCard.tsx +79 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryList.tsx +142 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/StatsDisplay.tsx +91 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/index.ts +4 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerList.tsx +99 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerListItem.tsx +70 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpStatusBadge.tsx +37 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpTab.tsx +239 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/NetlifyTab.tsx +1393 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/components/NetlifyConnection.tsx +990 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/components/index.ts +1 -0
- tools/bolt.diy/app/components/@settings/tabs/notifications/NotificationsTab.tsx +300 -0
- tools/bolt.diy/app/components/@settings/tabs/profile/ProfileTab.tsx +181 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx +308 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ErrorBoundary.tsx +68 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/HealthStatusBadge.tsx +64 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/LoadingSkeleton.tsx +107 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx +556 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ModelCard.tsx +106 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ProviderCard.tsx +120 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/SetupGuide.tsx +671 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/StatusDashboard.tsx +91 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/types.ts +44 -0
- tools/bolt.diy/app/components/@settings/tabs/settings/SettingsTab.tsx +215 -0
- tools/bolt.diy/app/components/@settings/tabs/supabase/SupabaseTab.tsx +1089 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/VercelTab.tsx +909 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/components/VercelConnection.tsx +368 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/components/index.ts +1 -0
- tools/bolt.diy/app/components/@settings/utils/tab-helpers.ts +54 -0
- tools/bolt.diy/app/components/chat/APIKeyManager.tsx +169 -0
- tools/bolt.diy/app/components/chat/Artifact.tsx +296 -0
- tools/bolt.diy/app/components/chat/AssistantMessage.tsx +192 -0
- tools/bolt.diy/app/components/chat/BaseChat.module.scss +47 -0
- tools/bolt.diy/app/components/chat/BaseChat.tsx +522 -0
- tools/bolt.diy/app/components/chat/Chat.client.tsx +670 -0
- tools/bolt.diy/app/components/chat/ChatAlert.tsx +108 -0
- tools/bolt.diy/app/components/chat/ChatBox.tsx +334 -0
- tools/bolt.diy/app/components/chat/CodeBlock.module.scss +10 -0
- tools/bolt.diy/app/components/chat/CodeBlock.tsx +85 -0
- tools/bolt.diy/app/components/chat/DicussMode.tsx +17 -0
- tools/bolt.diy/app/components/chat/ExamplePrompts.tsx +37 -0
- tools/bolt.diy/app/components/chat/FilePreview.tsx +38 -0
- tools/bolt.diy/app/components/chat/GitCloneButton.tsx +327 -0
- tools/bolt.diy/app/components/chat/ImportFolderButton.tsx +141 -0
- tools/bolt.diy/app/components/chat/LLMApiAlert.tsx +109 -0
- tools/bolt.diy/app/components/chat/MCPTools.tsx +129 -0
- tools/bolt.diy/app/components/chat/Markdown.module.scss +171 -0
- tools/bolt.diy/app/components/chat/Markdown.spec.ts +48 -0
- tools/bolt.diy/app/components/chat/Markdown.tsx +252 -0
- tools/bolt.diy/app/components/chat/Messages.client.tsx +102 -0
- tools/bolt.diy/app/components/chat/ModelSelector.tsx +797 -0
- tools/bolt.diy/app/components/chat/NetlifyDeploymentLink.client.tsx +51 -0
- tools/bolt.diy/app/components/chat/ProgressCompilation.tsx +110 -0
- tools/bolt.diy/app/components/chat/ScreenshotStateManager.tsx +33 -0
- tools/bolt.diy/app/components/chat/SendButton.client.tsx +39 -0
- tools/bolt.diy/app/components/chat/SpeechRecognition.tsx +28 -0
- tools/bolt.diy/app/components/chat/StarterTemplates.tsx +38 -0
- tools/bolt.diy/app/components/chat/SupabaseAlert.tsx +199 -0
- tools/bolt.diy/app/components/chat/SupabaseConnection.tsx +339 -0
- tools/bolt.diy/app/components/chat/ThoughtBox.tsx +43 -0
- tools/bolt.diy/app/components/chat/ToolInvocations.tsx +409 -0
- tools/bolt.diy/app/components/chat/UserMessage.tsx +101 -0
- tools/bolt.diy/app/components/chat/VercelDeploymentLink.client.tsx +158 -0
- tools/bolt.diy/app/components/chat/chatExportAndImport/ExportChatButton.tsx +49 -0
- tools/bolt.diy/app/components/chat/chatExportAndImport/ImportButtons.tsx +96 -0
- tools/bolt.diy/app/components/deploy/DeployAlert.tsx +197 -0
- tools/bolt.diy/app/components/deploy/DeployButton.tsx +277 -0
- tools/bolt.diy/app/components/deploy/GitHubDeploy.client.tsx +171 -0
- tools/bolt.diy/app/components/deploy/GitHubDeploymentDialog.tsx +1041 -0
- tools/bolt.diy/app/components/deploy/GitLabDeploy.client.tsx +171 -0
- tools/bolt.diy/app/components/deploy/GitLabDeploymentDialog.tsx +764 -0
- tools/bolt.diy/app/components/deploy/NetlifyDeploy.client.tsx +246 -0
- tools/bolt.diy/app/components/deploy/VercelDeploy.client.tsx +235 -0
- tools/bolt.diy/app/components/editor/codemirror/BinaryContent.tsx +7 -0
- tools/bolt.diy/app/components/editor/codemirror/CodeMirrorEditor.tsx +555 -0
- tools/bolt.diy/app/components/editor/codemirror/EnvMasking.ts +80 -0
- tools/bolt.diy/app/components/editor/codemirror/cm-theme.ts +192 -0
- tools/bolt.diy/app/components/editor/codemirror/indent.ts +68 -0
- tools/bolt.diy/app/components/editor/codemirror/languages.ts +112 -0
- tools/bolt.diy/app/components/git/GitUrlImport.client.tsx +147 -0
- tools/bolt.diy/app/components/header/Header.tsx +42 -0
- tools/bolt.diy/app/components/header/HeaderActionButtons.client.tsx +54 -0
- tools/bolt.diy/app/components/mandate/MandateSubmission.tsx +167 -0
- tools/bolt.diy/app/components/observability/DeploymentStatus.tsx +168 -0
- tools/bolt.diy/app/components/observability/EventTimeline.tsx +119 -0
- tools/bolt.diy/app/components/observability/FileDiffViewer.tsx +121 -0
- tools/bolt.diy/app/components/observability/GovernanceStatus.tsx +197 -0
- tools/bolt.diy/app/components/observability/GovernorMetrics.tsx +246 -0
- tools/bolt.diy/app/components/observability/LogStream.tsx +244 -0
- tools/bolt.diy/app/components/observability/MandateDetails.tsx +201 -0
- tools/bolt.diy/app/components/observability/ObservabilityDashboard.tsx +200 -0
- tools/bolt.diy/app/components/sidebar/HistoryItem.tsx +187 -0
- tools/bolt.diy/app/components/sidebar/Menu.client.tsx +536 -0
- tools/bolt.diy/app/components/sidebar/date-binning.ts +59 -0
- tools/bolt.diy/app/components/txt +1 -0
- tools/bolt.diy/app/components/ui/BackgroundRays/index.tsx +18 -0
- tools/bolt.diy/app/components/ui/BackgroundRays/styles.module.scss +246 -0
- tools/bolt.diy/app/components/ui/Badge.tsx +53 -0
- tools/bolt.diy/app/components/ui/BranchSelector.tsx +270 -0
- tools/bolt.diy/app/components/ui/Breadcrumbs.tsx +101 -0
- tools/bolt.diy/app/components/ui/Button.tsx +46 -0
- tools/bolt.diy/app/components/ui/Card.tsx +55 -0
- tools/bolt.diy/app/components/ui/Checkbox.tsx +32 -0
- tools/bolt.diy/app/components/ui/CloseButton.tsx +49 -0
- tools/bolt.diy/app/components/ui/CodeBlock.tsx +103 -0
- tools/bolt.diy/app/components/ui/Collapsible.tsx +9 -0
- tools/bolt.diy/app/components/ui/ColorSchemeDialog.tsx +378 -0
- tools/bolt.diy/app/components/ui/Dialog.tsx +449 -0
- tools/bolt.diy/app/components/ui/Dropdown.tsx +63 -0
- tools/bolt.diy/app/components/ui/EmptyState.tsx +154 -0
- tools/bolt.diy/app/components/ui/FileIcon.tsx +346 -0
- tools/bolt.diy/app/components/ui/FilterChip.tsx +92 -0
- tools/bolt.diy/app/components/ui/GlowingEffect.tsx +192 -0
- tools/bolt.diy/app/components/ui/GradientCard.tsx +100 -0
- tools/bolt.diy/app/components/ui/IconButton.tsx +84 -0
- tools/bolt.diy/app/components/ui/Input.tsx +22 -0
- tools/bolt.diy/app/components/ui/Label.tsx +20 -0
- tools/bolt.diy/app/components/ui/LoadingDots.tsx +27 -0
- tools/bolt.diy/app/components/ui/LoadingOverlay.tsx +32 -0
- tools/bolt.diy/app/components/ui/PanelHeader.tsx +20 -0
- tools/bolt.diy/app/components/ui/PanelHeaderButton.tsx +36 -0
- tools/bolt.diy/app/components/ui/Popover.tsx +29 -0
- tools/bolt.diy/app/components/ui/Progress.tsx +22 -0
- tools/bolt.diy/app/components/ui/RepositoryStats.tsx +87 -0
- tools/bolt.diy/app/components/ui/ScrollArea.tsx +41 -0
- tools/bolt.diy/app/components/ui/SearchInput.tsx +80 -0
- tools/bolt.diy/app/components/ui/SearchResultItem.tsx +134 -0
- tools/bolt.diy/app/components/ui/Separator.tsx +22 -0
- tools/bolt.diy/app/components/ui/SettingsButton.tsx +35 -0
- tools/bolt.diy/app/components/ui/Slider.tsx +73 -0
- tools/bolt.diy/app/components/ui/StatusIndicator.tsx +90 -0
- tools/bolt.diy/app/components/ui/Switch.tsx +37 -0
- tools/bolt.diy/app/components/ui/Tabs.tsx +52 -0
- tools/bolt.diy/app/components/ui/TabsWithSlider.tsx +112 -0
- tools/bolt.diy/app/components/ui/ThemeSwitch.tsx +29 -0
- tools/bolt.diy/app/components/ui/Tooltip.tsx +122 -0
- tools/bolt.diy/app/components/ui/index.ts +38 -0
- tools/bolt.diy/app/components/ui/use-toast.ts +66 -0
- tools/bolt.diy/app/components/workbench/DiffView.tsx +796 -0
- tools/bolt.diy/app/components/workbench/EditorPanel.tsx +174 -0
- tools/bolt.diy/app/components/workbench/ExpoQrModal.tsx +55 -0
- tools/bolt.diy/app/components/workbench/FileBreadcrumb.tsx +150 -0
- tools/bolt.diy/app/components/workbench/FileTree.tsx +565 -0
- tools/bolt.diy/app/components/workbench/Inspector.tsx +126 -0
- tools/bolt.diy/app/components/workbench/InspectorPanel.tsx +146 -0
- tools/bolt.diy/app/components/workbench/LockManager.tsx +262 -0
- tools/bolt.diy/app/components/workbench/PortDropdown.tsx +91 -0
- tools/bolt.diy/app/components/workbench/Preview.tsx +1049 -0
- tools/bolt.diy/app/components/workbench/ScreenshotSelector.tsx +293 -0
- tools/bolt.diy/app/components/workbench/Search.tsx +257 -0
- tools/bolt.diy/app/components/workbench/Workbench.client.tsx +506 -0
- tools/bolt.diy/app/components/workbench/terminal/Terminal.tsx +131 -0
- tools/bolt.diy/app/components/workbench/terminal/TerminalManager.tsx +68 -0
- tools/bolt.diy/app/components/workbench/terminal/TerminalTabs.tsx +277 -0
- tools/bolt.diy/app/components/workbench/terminal/theme.ts +36 -0
- tools/bolt.diy/app/components/workflow/WorkflowPhase.tsx +109 -0
- tools/bolt.diy/app/components/workflow/WorkflowStatus.tsx +60 -0
- tools/bolt.diy/app/components/workflow/WorkflowTimeline.tsx +150 -0
- tools/bolt.diy/app/entry.client.tsx +7 -0
- tools/bolt.diy/app/entry.server.tsx +80 -0
- tools/bolt.diy/app/root.tsx +156 -0
- tools/bolt.diy/app/routes/_index.tsx +175 -0
- tools/bolt.diy/app/routes/api.bug-report.ts +254 -0
- tools/bolt.diy/app/routes/api.chat.ts +463 -0
- tools/bolt.diy/app/routes/api.check-env-key.ts +41 -0
- tools/bolt.diy/app/routes/api.configured-providers.ts +110 -0
- tools/bolt.diy/app/routes/api.corporate-swarm-status.ts +55 -0
- tools/bolt.diy/app/routes/api.enhancer.ts +137 -0
- tools/bolt.diy/app/routes/api.export-api-keys.ts +44 -0
- tools/bolt.diy/app/routes/api.git-info.ts +69 -0
- tools/bolt.diy/app/routes/api.git-proxy.$.ts +178 -0
- tools/bolt.diy/app/routes/api.github-branches.ts +166 -0
- tools/bolt.diy/app/routes/api.github-deploy.ts +67 -0
- tools/bolt.diy/app/routes/api.github-stats.ts +198 -0
- tools/bolt.diy/app/routes/api.github-template.ts +242 -0
- tools/bolt.diy/app/routes/api.github-user.ts +287 -0
- tools/bolt.diy/app/routes/api.gitlab-branches.ts +143 -0
- tools/bolt.diy/app/routes/api.gitlab-deploy.ts +67 -0
- tools/bolt.diy/app/routes/api.gitlab-projects.ts +105 -0
- tools/bolt.diy/app/routes/api.health.ts +8 -0
- tools/bolt.diy/app/routes/api.llmcall.ts +298 -0
- tools/bolt.diy/app/routes/api.mandate.ts +351 -0
- tools/bolt.diy/app/routes/api.mcp-check.ts +16 -0
- tools/bolt.diy/app/routes/api.mcp-update-config.ts +23 -0
- tools/bolt.diy/app/routes/api.models.$provider.ts +2 -0
- tools/bolt.diy/app/routes/api.models.ts +90 -0
- tools/bolt.diy/app/routes/api.netlify-deploy.ts +240 -0
- tools/bolt.diy/app/routes/api.netlify-user.ts +142 -0
- tools/bolt.diy/app/routes/api.supabase-user.ts +199 -0
- tools/bolt.diy/app/routes/api.supabase.query.ts +92 -0
- tools/bolt.diy/app/routes/api.supabase.ts +56 -0
- tools/bolt.diy/app/routes/api.supabase.variables.ts +32 -0
- tools/bolt.diy/app/routes/api.system.diagnostics.ts +142 -0
- tools/bolt.diy/app/routes/api.system.disk-info.ts +311 -0
- tools/bolt.diy/app/routes/api.system.git-info.ts +332 -0
- tools/bolt.diy/app/routes/api.update.ts +21 -0
- tools/bolt.diy/app/routes/api.vercel-deploy.ts +497 -0
- tools/bolt.diy/app/routes/api.vercel-user.ts +161 -0
- tools/bolt.diy/app/routes/api.workflow-status.$proposalId.ts +309 -0
- tools/bolt.diy/app/routes/chat.$id.tsx +8 -0
- tools/bolt.diy/app/routes/execute.$mandateId.tsx +432 -0
- tools/bolt.diy/app/routes/git.tsx +25 -0
- tools/bolt.diy/app/routes/observability.$mandateId.tsx +50 -0
- tools/bolt.diy/app/routes/webcontainer.connect.$id.tsx +32 -0
- tools/bolt.diy/app/routes/webcontainer.preview.$id.tsx +97 -0
- tools/bolt.diy/app/routes/workflow.$proposalId.tsx +170 -0
- tools/bolt.diy/app/styles/animations.scss +49 -0
- tools/bolt.diy/app/styles/components/code.scss +9 -0
- tools/bolt.diy/app/styles/components/editor.scss +135 -0
- tools/bolt.diy/app/styles/components/resize-handle.scss +30 -0
- tools/bolt.diy/app/styles/components/terminal.scss +3 -0
- tools/bolt.diy/app/styles/components/toast.scss +23 -0
- tools/bolt.diy/app/styles/diff-view.css +72 -0
- tools/bolt.diy/app/styles/index.scss +73 -0
- tools/bolt.diy/app/styles/variables.scss +255 -0
- tools/bolt.diy/app/styles/z-index.scss +37 -0
- tools/bolt.diy/app/types/GitHub.ts +182 -0
- tools/bolt.diy/app/types/GitLab.ts +103 -0
- tools/bolt.diy/app/types/actions.ts +85 -0
- tools/bolt.diy/app/types/artifact.ts +5 -0
- tools/bolt.diy/app/types/context.ts +26 -0
- tools/bolt.diy/app/types/design-scheme.ts +93 -0
- tools/bolt.diy/app/types/global.d.ts +13 -0
- tools/bolt.diy/app/types/mandate.ts +333 -0
- tools/bolt.diy/app/types/model.ts +25 -0
- tools/bolt.diy/app/types/netlify.ts +94 -0
- tools/bolt.diy/app/types/supabase.ts +54 -0
- tools/bolt.diy/app/types/template.ts +8 -0
- tools/bolt.diy/app/types/terminal.ts +9 -0
- tools/bolt.diy/app/types/theme.ts +1 -0
- tools/bolt.diy/app/types/vercel.ts +67 -0
- tools/bolt.diy/app/utils/buffer.ts +29 -0
- tools/bolt.diy/app/utils/classNames.ts +65 -0
- tools/bolt.diy/app/utils/constants.ts +147 -0
- tools/bolt.diy/app/utils/debounce.ts +13 -0
- tools/bolt.diy/app/utils/debugLogger.ts +1284 -0
- tools/bolt.diy/app/utils/diff.spec.ts +11 -0
- tools/bolt.diy/app/utils/diff.ts +117 -0
- tools/bolt.diy/app/utils/easings.ts +3 -0
- tools/bolt.diy/app/utils/fileLocks.ts +96 -0
- tools/bolt.diy/app/utils/fileUtils.ts +121 -0
- tools/bolt.diy/app/utils/folderImport.ts +73 -0
- tools/bolt.diy/app/utils/formatSize.ts +12 -0
- tools/bolt.diy/app/utils/getLanguageFromExtension.ts +24 -0
- tools/bolt.diy/app/utils/githubStats.ts +9 -0
- tools/bolt.diy/app/utils/gitlabStats.ts +54 -0
- tools/bolt.diy/app/utils/logger.ts +162 -0
- tools/bolt.diy/app/utils/markdown.ts +155 -0
- tools/bolt.diy/app/utils/mobile.ts +4 -0
- tools/bolt.diy/app/utils/os.ts +4 -0
- tools/bolt.diy/app/utils/path.ts +19 -0
- tools/bolt.diy/app/utils/projectCommands.ts +197 -0
- tools/bolt.diy/app/utils/promises.ts +19 -0
- tools/bolt.diy/app/utils/react.ts +6 -0
- tools/bolt.diy/app/utils/sampler.ts +49 -0
- tools/bolt.diy/app/utils/selectStarterTemplate.ts +255 -0
- tools/bolt.diy/app/utils/shell.ts +384 -0
- tools/bolt.diy/app/utils/stacktrace.ts +27 -0
- tools/bolt.diy/app/utils/stripIndent.ts +23 -0
- tools/bolt.diy/app/utils/terminal.ts +11 -0
- tools/bolt.diy/app/utils/unreachable.ts +3 -0
- tools/bolt.diy/app/vite-env.d.ts +2 -0
- tools/bolt.diy/assets/entitlements.mac.plist +25 -0
- tools/bolt.diy/assets/icons/icon.icns +0 -0
- tools/bolt.diy/assets/icons/icon.ico +0 -0
- tools/bolt.diy/assets/icons/icon.png +0 -0
- tools/bolt.diy/bindings.js +78 -0
- tools/bolt.diy/bindings.sh +33 -0
- tools/bolt.diy/docker-compose.yaml +145 -0
- tools/bolt.diy/electron/main/index.ts +201 -0
- tools/bolt.diy/electron/main/tsconfig.json +30 -0
- tools/bolt.diy/electron/main/ui/menu.ts +29 -0
- tools/bolt.diy/electron/main/ui/window.ts +54 -0
- tools/bolt.diy/electron/main/utils/auto-update.ts +110 -0
- tools/bolt.diy/electron/main/utils/constants.ts +4 -0
- tools/bolt.diy/electron/main/utils/cookie.ts +40 -0
- tools/bolt.diy/electron/main/utils/reload.ts +35 -0
- tools/bolt.diy/electron/main/utils/serve.ts +71 -0
- tools/bolt.diy/electron/main/utils/store.ts +3 -0
- tools/bolt.diy/electron/main/utils/vite-server.ts +44 -0
- tools/bolt.diy/electron/main/vite.config.ts +44 -0
- tools/bolt.diy/electron/preload/index.ts +22 -0
- tools/bolt.diy/electron/preload/tsconfig.json +7 -0
- tools/bolt.diy/electron/preload/vite.config.ts +31 -0
- tools/bolt.diy/electron-builder.yml +64 -0
- tools/bolt.diy/electron-update.yml +4 -0
- tools/bolt.diy/eslint.config.mjs +57 -0
- tools/bolt.diy/functions/[[path]].ts +12 -0
- tools/bolt.diy/icons/angular.svg +1 -0
- tools/bolt.diy/icons/astro.svg +8 -0
- tools/bolt.diy/icons/chat.svg +1 -0
- tools/bolt.diy/icons/expo-brand.svg +1 -0
- tools/bolt.diy/icons/expo.svg +4 -0
- tools/bolt.diy/icons/logo-text.svg +1 -0
- tools/bolt.diy/icons/logo.svg +4 -0
- tools/bolt.diy/icons/mcp.svg +1 -0
- tools/bolt.diy/icons/nativescript.svg +1 -0
- tools/bolt.diy/icons/netlify.svg +10 -0
- tools/bolt.diy/icons/nextjs.svg +1 -0
- tools/bolt.diy/icons/nuxt.svg +1 -0
- tools/bolt.diy/icons/qwik.svg +1 -0
- tools/bolt.diy/icons/react.svg +1 -0
- tools/bolt.diy/icons/remix.svg +24 -0
- tools/bolt.diy/icons/remotion.svg +1 -0
- tools/bolt.diy/icons/shadcn.svg +21 -0
- tools/bolt.diy/icons/slidev.svg +60 -0
- tools/bolt.diy/icons/solidjs.svg +1 -0
- tools/bolt.diy/icons/stars.svg +1 -0
- tools/bolt.diy/icons/svelte.svg +1 -0
- tools/bolt.diy/icons/typescript.svg +1 -0
- tools/bolt.diy/icons/vite.svg +1 -0
- tools/bolt.diy/icons/vue.svg +1 -0
- tools/bolt.diy/load-context.ts +9 -0
- tools/bolt.diy/notarize.cjs +31 -0
- tools/bolt.diy/package.json +218 -0
- tools/bolt.diy/playwright.config.preview.ts +35 -0
- tools/bolt.diy/pre-start.cjs +26 -0
- tools/bolt.diy/public/apple-touch-icon-precomposed.png +0 -0
- tools/bolt.diy/public/apple-touch-icon.png +0 -0
- tools/bolt.diy/public/favicon.ico +0 -0
- tools/bolt.diy/public/favicon.svg +4 -0
- tools/bolt.diy/public/icons/AmazonBedrock.svg +1 -0
- tools/bolt.diy/public/icons/Anthropic.svg +4 -0
- tools/bolt.diy/public/icons/Cohere.svg +4 -0
- tools/bolt.diy/public/icons/Deepseek.svg +5 -0
- tools/bolt.diy/public/icons/Default.svg +4 -0
- tools/bolt.diy/public/icons/Google.svg +4 -0
- tools/bolt.diy/public/icons/Groq.svg +4 -0
- tools/bolt.diy/public/icons/HuggingFace.svg +4 -0
- tools/bolt.diy/public/icons/Hyperbolic.svg +3 -0
- tools/bolt.diy/public/icons/LMStudio.svg +5 -0
- tools/bolt.diy/public/icons/Mistral.svg +4 -0
- tools/bolt.diy/public/icons/Ollama.svg +4 -0
- tools/bolt.diy/public/icons/OpenAI.svg +4 -0
- tools/bolt.diy/public/icons/OpenAILike.svg +4 -0
- tools/bolt.diy/public/icons/OpenRouter.svg +4 -0
- tools/bolt.diy/public/icons/Perplexity.svg +4 -0
- tools/bolt.diy/public/icons/Together.svg +4 -0
- tools/bolt.diy/public/icons/xAI.svg +5 -0
- tools/bolt.diy/public/inspector-script.js +292 -0
- tools/bolt.diy/public/logo-dark-styled.png +0 -0
- tools/bolt.diy/public/logo-dark.png +0 -0
- tools/bolt.diy/public/logo-light-styled.png +0 -0
- tools/bolt.diy/public/logo-light.png +0 -0
- tools/bolt.diy/public/logo.svg +15 -0
- tools/bolt.diy/public/social_preview_index.jpg +0 -0
- tools/bolt.diy/scripts/clean.js +45 -0
- tools/bolt.diy/scripts/electron-dev.mjs +181 -0
- tools/bolt.diy/scripts/setup-env.sh +41 -0
- tools/bolt.diy/scripts/update-imports.sh +7 -0
- tools/bolt.diy/scripts/update.sh +52 -0
- tools/bolt.diy/services/execution-governor/Dockerfile +41 -0
- tools/bolt.diy/services/execution-governor/config.ts +42 -0
- tools/bolt.diy/services/execution-governor/index.ts +683 -0
- tools/bolt.diy/services/execution-governor/metrics.ts +141 -0
- tools/bolt.diy/services/execution-governor/package.json +31 -0
- tools/bolt.diy/services/execution-governor/priority-queue.ts +139 -0
- tools/bolt.diy/services/execution-governor/tsconfig.json +21 -0
- tools/bolt.diy/services/execution-governor/types.ts +145 -0
- tools/bolt.diy/services/headless-executor/Dockerfile +43 -0
- tools/bolt.diy/services/headless-executor/executor.ts +210 -0
- tools/bolt.diy/services/headless-executor/index.ts +323 -0
- tools/bolt.diy/services/headless-executor/package.json +27 -0
- tools/bolt.diy/services/headless-executor/tsconfig.json +21 -0
- tools/bolt.diy/services/headless-executor/types.ts +38 -0
- tools/bolt.diy/test-workflows.sh +240 -0
- tools/bolt.diy/tests/integration/corporate-swarm.test.ts +208 -0
- tools/bolt.diy/tests/mandates/budget-limited.json +34 -0
- tools/bolt.diy/tests/mandates/complex.json +53 -0
- tools/bolt.diy/tests/mandates/constraint-enforced.json +36 -0
- tools/bolt.diy/tests/mandates/simple.json +35 -0
- tools/bolt.diy/tsconfig.json +37 -0
- tools/bolt.diy/types/istextorbinary.d.ts +15 -0
- tools/bolt.diy/uno.config.ts +279 -0
- tools/bolt.diy/vite-electron.config.ts +76 -0
- tools/bolt.diy/vite.config.ts +112 -0
- tools/bolt.diy/worker-configuration.d.ts +22 -0
- tools/bolt.diy/wrangler.toml +6 -0
- tools/code_generator.py +461 -0
- tools/file_operations.py +465 -0
- tools/mandate_generator.py +337 -0
- tools/mcpClientUtils.py +1216 -0
- tools/sensors.py +285 -0
- utils/Agent_types.py +15 -0
- utils/AnyToStr.py +0 -0
- utils/HHCS.py +277 -0
- utils/__init__.py +30 -0
- utils/agent.py +3627 -0
- utils/aop.py +2948 -0
- utils/canonical.py +143 -0
- utils/conversation.py +1195 -0
- utils/doctrine_versioning +230 -0
- utils/formatter.py +474 -0
- utils/ledger.py +311 -0
- utils/out_types.py +16 -0
- utils/rollback.py +339 -0
- utils/router.py +929 -0
- utils/tui.py +1908 -0
|
@@ -0,0 +1,1325 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CRCA-SD Governance: Boards, Logistics, Visualization, Config, Metrics
|
|
3
|
+
|
|
4
|
+
This module implements:
|
|
5
|
+
- Board-of-agents governance (adapted from CorporateSwarm)
|
|
6
|
+
- Multi-board arbitration with weighted voting
|
|
7
|
+
- Logistics network optimization
|
|
8
|
+
- Visualization and dashboard
|
|
9
|
+
- Configuration management (Pydantic + YAML)
|
|
10
|
+
- Risk assessment
|
|
11
|
+
- Metrics collection
|
|
12
|
+
|
|
13
|
+
Incorporates patterns from CorporateSwarm for board structure, voting, and config management.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import Dict, List, Optional, Tuple, Any, Union
|
|
17
|
+
import numpy as np
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from enum import Enum
|
|
20
|
+
from functools import lru_cache
|
|
21
|
+
import time
|
|
22
|
+
import uuid
|
|
23
|
+
import os
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
from loguru import logger
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import yaml
|
|
29
|
+
YAML_AVAILABLE = True
|
|
30
|
+
except ImportError:
|
|
31
|
+
YAML_AVAILABLE = False
|
|
32
|
+
logger.warning("yaml not available, config file loading disabled")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
from pydantic import BaseModel, Field
|
|
36
|
+
PYDANTIC_AVAILABLE = True
|
|
37
|
+
except ImportError:
|
|
38
|
+
PYDANTIC_AVAILABLE = False
|
|
39
|
+
logger.warning("pydantic not available, using dataclass fallback")
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
import cvxpy as cp
|
|
43
|
+
import networkx as nx
|
|
44
|
+
CVXPY_AVAILABLE = True
|
|
45
|
+
NETWORKX_AVAILABLE = True
|
|
46
|
+
except ImportError:
|
|
47
|
+
CVXPY_AVAILABLE = False
|
|
48
|
+
NETWORKX_AVAILABLE = False
|
|
49
|
+
logger.warning("cvxpy/networkx not available, logistics optimization limited")
|
|
50
|
+
|
|
51
|
+
from crca_sd.crca_sd_core import StateVector, ControlVector
|
|
52
|
+
from crca_sd.crca_sd_mpc import ObjectiveVector
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _generate_uuid() -> str:
|
|
56
|
+
"""Generate a new UUID string."""
|
|
57
|
+
return str(uuid.uuid4())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BoardType(str, Enum):
|
|
61
|
+
"""Types of governance boards."""
|
|
62
|
+
GROWTH = "growth"
|
|
63
|
+
WELFARE = "welfare"
|
|
64
|
+
SUSTAINABILITY = "sustainability"
|
|
65
|
+
STABILITY = "stability"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class VoteResult(str, Enum):
|
|
69
|
+
"""Voting result outcomes (from CorporateSwarm pattern)."""
|
|
70
|
+
APPROVED = "approved"
|
|
71
|
+
REJECTED = "rejected"
|
|
72
|
+
TABLED = "tabled"
|
|
73
|
+
FAILED = "failed"
|
|
74
|
+
UNANIMOUS = "unanimous"
|
|
75
|
+
MAJORITY = "majority"
|
|
76
|
+
MINORITY = "minority"
|
|
77
|
+
ABSTAINED = "abstained"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# BoardMember: Adapted from CorporateMember (lines 353-369 in CorporateSwarm)
|
|
81
|
+
if PYDANTIC_AVAILABLE:
|
|
82
|
+
class BoardMember(BaseModel):
|
|
83
|
+
"""Board member with role, expertise, and objective preferences."""
|
|
84
|
+
|
|
85
|
+
member_id: str = Field(default_factory=_generate_uuid)
|
|
86
|
+
name: str = Field(default="")
|
|
87
|
+
board_type: BoardType = Field(default=BoardType.GROWTH)
|
|
88
|
+
expertise_areas: List[str] = Field(default_factory=list)
|
|
89
|
+
voting_weight: float = Field(default=1.0, ge=0.0, le=5.0)
|
|
90
|
+
preferences: Dict[str, float] = Field(default_factory=dict) # Objective weights
|
|
91
|
+
independence_status: bool = Field(default=False)
|
|
92
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
93
|
+
else:
|
|
94
|
+
@dataclass
|
|
95
|
+
class BoardMember:
|
|
96
|
+
"""Board member with role, expertise, and objective preferences."""
|
|
97
|
+
member_id: str = field(default_factory=_generate_uuid)
|
|
98
|
+
name: str = ""
|
|
99
|
+
board_type: BoardType = BoardType.GROWTH
|
|
100
|
+
expertise_areas: List[str] = field(default_factory=list)
|
|
101
|
+
voting_weight: float = 1.0
|
|
102
|
+
preferences: Dict[str, float] = field(default_factory=dict)
|
|
103
|
+
independence_status: bool = False
|
|
104
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Board:
|
|
108
|
+
"""
|
|
109
|
+
Governance board with members and objective priorities.
|
|
110
|
+
|
|
111
|
+
Inspired by CorporateSwarm board structure.
|
|
112
|
+
Each board type has different objective priorities:
|
|
113
|
+
- Growth: Prioritizes Y (output), then U (unemployment)
|
|
114
|
+
- Welfare: Prioritizes U (unemployment), inequality, literacy
|
|
115
|
+
- Sustainability: Prioritizes C (ecological damage), long-term resilience
|
|
116
|
+
- Stability: Prioritizes low variance, low CVaR
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
board_id: Unique board identifier
|
|
120
|
+
board_type: Type of board
|
|
121
|
+
members: List of board members
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def __init__(
|
|
125
|
+
self,
|
|
126
|
+
board_id: str,
|
|
127
|
+
board_type: BoardType,
|
|
128
|
+
members: List[BoardMember]
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Initialize board."""
|
|
131
|
+
self.board_id = board_id
|
|
132
|
+
self.board_type = board_type
|
|
133
|
+
self.members = members
|
|
134
|
+
|
|
135
|
+
def get_preference_weights(self) -> Dict[str, float]:
|
|
136
|
+
"""
|
|
137
|
+
Get objective weight vector for this board type.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Dict[str, float]: Mapping from objective names to weights
|
|
141
|
+
"""
|
|
142
|
+
if self.board_type == BoardType.GROWTH:
|
|
143
|
+
return {
|
|
144
|
+
"J_U": 0.2, # Unemployment
|
|
145
|
+
"J_ℓ": 0.1, # Literacy
|
|
146
|
+
"J_Y": 0.5, # Output (high priority)
|
|
147
|
+
"J_ineq": 0.1, # Inequality
|
|
148
|
+
"J_C": 0.05, # Ecological damage
|
|
149
|
+
"J_risk": 0.05, # Risk
|
|
150
|
+
}
|
|
151
|
+
elif self.board_type == BoardType.WELFARE:
|
|
152
|
+
return {
|
|
153
|
+
"J_U": 0.4, # Unemployment (high priority)
|
|
154
|
+
"J_ℓ": 0.3, # Literacy (high priority)
|
|
155
|
+
"J_Y": 0.1, # Output
|
|
156
|
+
"J_ineq": 0.15, # Inequality (high priority)
|
|
157
|
+
"J_C": 0.03, # Ecological damage
|
|
158
|
+
"J_risk": 0.02, # Risk
|
|
159
|
+
}
|
|
160
|
+
elif self.board_type == BoardType.SUSTAINABILITY:
|
|
161
|
+
return {
|
|
162
|
+
"J_U": 0.1, # Unemployment
|
|
163
|
+
"J_ℓ": 0.1, # Literacy
|
|
164
|
+
"J_Y": 0.1, # Output
|
|
165
|
+
"J_ineq": 0.1, # Inequality
|
|
166
|
+
"J_C": 0.5, # Ecological damage (high priority)
|
|
167
|
+
"J_risk": 0.1, # Risk
|
|
168
|
+
}
|
|
169
|
+
elif self.board_type == BoardType.STABILITY:
|
|
170
|
+
return {
|
|
171
|
+
"J_U": 0.2, # Unemployment
|
|
172
|
+
"J_ℓ": 0.1, # Literacy
|
|
173
|
+
"J_Y": 0.1, # Output
|
|
174
|
+
"J_ineq": 0.1, # Inequality
|
|
175
|
+
"J_C": 0.1, # Ecological damage
|
|
176
|
+
"J_risk": 0.4, # Risk (high priority)
|
|
177
|
+
}
|
|
178
|
+
else:
|
|
179
|
+
# Default: equal weights
|
|
180
|
+
return {f"J_{i}": 1.0/6 for i in ["U", "ℓ", "Y", "ineq", "C", "risk"]}
|
|
181
|
+
|
|
182
|
+
def evaluate_policies(
|
|
183
|
+
self,
|
|
184
|
+
policies: List[ControlVector],
|
|
185
|
+
objectives: np.ndarray
|
|
186
|
+
) -> List[Tuple[ControlVector, float]]:
|
|
187
|
+
"""
|
|
188
|
+
Rank policies by board's objective priorities.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
policies: List of candidate policies
|
|
192
|
+
objectives: Objective matrix (n_policies x n_objectives)
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
List[Tuple[ControlVector, float]]: Ranked policies with scores
|
|
196
|
+
"""
|
|
197
|
+
if len(policies) == 0:
|
|
198
|
+
return []
|
|
199
|
+
|
|
200
|
+
if objectives.ndim == 1:
|
|
201
|
+
objectives = objectives.reshape(1, -1)
|
|
202
|
+
|
|
203
|
+
# Get board preference weights
|
|
204
|
+
weights = self.get_preference_weights()
|
|
205
|
+
weight_vec = np.array([
|
|
206
|
+
weights.get("J_U", 0.0),
|
|
207
|
+
weights.get("J_ℓ", 0.0),
|
|
208
|
+
weights.get("J_Y", 0.0),
|
|
209
|
+
weights.get("J_ineq", 0.0),
|
|
210
|
+
weights.get("J_C", 0.0),
|
|
211
|
+
weights.get("J_risk", 0.0),
|
|
212
|
+
])
|
|
213
|
+
|
|
214
|
+
# Normalize weights
|
|
215
|
+
if weight_vec.sum() > 0:
|
|
216
|
+
weight_vec = weight_vec / weight_vec.sum()
|
|
217
|
+
|
|
218
|
+
# Compute weighted scores
|
|
219
|
+
scores = objectives @ weight_vec
|
|
220
|
+
|
|
221
|
+
# Rank policies (lower score = better for minimization)
|
|
222
|
+
ranked = list(zip(policies, scores))
|
|
223
|
+
ranked.sort(key=lambda x: x[1])
|
|
224
|
+
|
|
225
|
+
return ranked
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class Arbitration:
|
|
229
|
+
"""
|
|
230
|
+
Multi-board arbitration using weighted voting.
|
|
231
|
+
|
|
232
|
+
Adapted from CorporateSwarm's _analyze_vote_results pattern (lines 2026-2062).
|
|
233
|
+
Implements multiple arbitration methods:
|
|
234
|
+
- Pareto intersection
|
|
235
|
+
- Minimax regret
|
|
236
|
+
- Borda voting
|
|
237
|
+
- Weighted voting
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def pareto_intersection(
|
|
242
|
+
boards: List[Board],
|
|
243
|
+
policies: List[ControlVector],
|
|
244
|
+
objectives: np.ndarray
|
|
245
|
+
) -> List[ControlVector]:
|
|
246
|
+
"""
|
|
247
|
+
Find policies that are non-dominated under all boards.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
boards: List of boards
|
|
251
|
+
policies: Candidate policies
|
|
252
|
+
objectives: Objective matrix
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
List[ControlVector]: Consensus policies
|
|
256
|
+
"""
|
|
257
|
+
if not boards or not policies:
|
|
258
|
+
return []
|
|
259
|
+
|
|
260
|
+
# Get Pareto-efficient policies for each board
|
|
261
|
+
board_pareto_sets = []
|
|
262
|
+
for board in boards:
|
|
263
|
+
ranked = board.evaluate_policies(policies, objectives)
|
|
264
|
+
# Take top 50% as "preferred"
|
|
265
|
+
n_top = max(1, len(ranked) // 2)
|
|
266
|
+
top_policies = [p for p, _ in ranked[:n_top]]
|
|
267
|
+
board_pareto_sets.append(set(id(p) for p in top_policies))
|
|
268
|
+
|
|
269
|
+
# Intersection: policies preferred by all boards
|
|
270
|
+
consensus_ids = set.intersection(*board_pareto_sets) if board_pareto_sets else set()
|
|
271
|
+
consensus_policies = [p for p in policies if id(p) in consensus_ids]
|
|
272
|
+
|
|
273
|
+
return consensus_policies
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def minimax_regret(
|
|
277
|
+
boards: List[Board],
|
|
278
|
+
policies: List[ControlVector],
|
|
279
|
+
objectives: np.ndarray
|
|
280
|
+
) -> Optional[ControlVector]:
|
|
281
|
+
"""
|
|
282
|
+
Minimize worst-case regret across boards.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
boards: List of boards
|
|
286
|
+
policies: Candidate policies
|
|
287
|
+
objectives: Objective matrix
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Optional[ControlVector]: Optimal policy
|
|
291
|
+
"""
|
|
292
|
+
if not boards or not policies:
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
# For each board, find best policy
|
|
296
|
+
board_best_scores = []
|
|
297
|
+
for board in boards:
|
|
298
|
+
ranked = board.evaluate_policies(policies, objectives)
|
|
299
|
+
if ranked:
|
|
300
|
+
best_score = ranked[0][1] # Best score for this board
|
|
301
|
+
board_best_scores.append(best_score)
|
|
302
|
+
|
|
303
|
+
# For each policy, compute maximum regret
|
|
304
|
+
max_regrets = []
|
|
305
|
+
for i, policy in enumerate(policies):
|
|
306
|
+
regrets = []
|
|
307
|
+
for j, board in enumerate(boards):
|
|
308
|
+
ranked = board.evaluate_policies(policies, objectives)
|
|
309
|
+
policy_score = next((s for p, s in ranked if p is policy), float('inf'))
|
|
310
|
+
regret = policy_score - board_best_scores[j]
|
|
311
|
+
regrets.append(regret)
|
|
312
|
+
max_regrets.append(max(regrets))
|
|
313
|
+
|
|
314
|
+
# Policy with minimum maximum regret
|
|
315
|
+
min_regret_idx = np.argmin(max_regrets)
|
|
316
|
+
return policies[min_regret_idx]
|
|
317
|
+
|
|
318
|
+
@staticmethod
|
|
319
|
+
def borda_vote(
|
|
320
|
+
boards: List[Board],
|
|
321
|
+
policies: List[ControlVector],
|
|
322
|
+
objectives: np.ndarray
|
|
323
|
+
) -> Optional[ControlVector]:
|
|
324
|
+
"""
|
|
325
|
+
Borda count aggregation across boards.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
boards: List of boards
|
|
329
|
+
policies: Candidate policies
|
|
330
|
+
objectives: Objective matrix
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Optional[ControlVector]: Selected policy
|
|
334
|
+
"""
|
|
335
|
+
if not boards or not policies:
|
|
336
|
+
return None
|
|
337
|
+
|
|
338
|
+
# Borda scores: rank 0 gets n-1 points, rank 1 gets n-2, etc.
|
|
339
|
+
borda_scores = {id(p): 0.0 for p in policies}
|
|
340
|
+
|
|
341
|
+
for board in boards:
|
|
342
|
+
ranked = board.evaluate_policies(policies, objectives)
|
|
343
|
+
n = len(ranked)
|
|
344
|
+
for rank, (policy, _) in enumerate(ranked):
|
|
345
|
+
borda_scores[id(policy)] += (n - rank - 1) * board.members[0].voting_weight if board.members else (n - rank - 1)
|
|
346
|
+
|
|
347
|
+
# Policy with highest Borda score
|
|
348
|
+
best_policy_id = max(borda_scores, key=borda_scores.get)
|
|
349
|
+
return next(p for p in policies if id(p) == best_policy_id)
|
|
350
|
+
|
|
351
|
+
@staticmethod
|
|
352
|
+
def weighted_vote(
|
|
353
|
+
boards: List[Board],
|
|
354
|
+
policies: List[ControlVector],
|
|
355
|
+
objectives: np.ndarray
|
|
356
|
+
) -> Optional[ControlVector]:
|
|
357
|
+
"""
|
|
358
|
+
Weighted voting using CorporateSwarm's _analyze_vote_results pattern.
|
|
359
|
+
|
|
360
|
+
Adapted from CorporateSwarm lines 2026-2062.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
boards: List of boards
|
|
364
|
+
policies: Candidate policies
|
|
365
|
+
objectives: Objective matrix
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
Optional[ControlVector]: Selected policy
|
|
369
|
+
"""
|
|
370
|
+
if not boards or not policies:
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
# Collect individual "votes" from each board
|
|
374
|
+
individual_votes: Dict[str, Dict[str, Any]] = {}
|
|
375
|
+
|
|
376
|
+
for board in boards:
|
|
377
|
+
ranked = board.evaluate_policies(policies, objectives)
|
|
378
|
+
if not ranked:
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
# Board votes for top policy
|
|
382
|
+
top_policy, top_score = ranked[0]
|
|
383
|
+
|
|
384
|
+
# Compute board voting weight (sum of member weights)
|
|
385
|
+
board_weight = sum(m.voting_weight for m in board.members)
|
|
386
|
+
|
|
387
|
+
individual_votes[board.board_id] = {
|
|
388
|
+
"vote": "APPROVE" if top_score < float('inf') else "REJECT",
|
|
389
|
+
"policy": top_policy,
|
|
390
|
+
"score": top_score,
|
|
391
|
+
"voting_weight": board_weight,
|
|
392
|
+
"board_type": board.board_type.value,
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if not individual_votes:
|
|
396
|
+
return None
|
|
397
|
+
|
|
398
|
+
# Aggregate votes by policy (weighted)
|
|
399
|
+
policy_scores: Dict[int, float] = {} # policy_id -> weighted score
|
|
400
|
+
|
|
401
|
+
for vote_data in individual_votes.values():
|
|
402
|
+
policy = vote_data["policy"]
|
|
403
|
+
weight = vote_data["voting_weight"]
|
|
404
|
+
score = vote_data["score"]
|
|
405
|
+
|
|
406
|
+
policy_id = id(policy)
|
|
407
|
+
if policy_id not in policy_scores:
|
|
408
|
+
policy_scores[policy_id] = 0.0
|
|
409
|
+
|
|
410
|
+
# Lower score is better, so we use negative for voting
|
|
411
|
+
policy_scores[policy_id] += weight / (1.0 + abs(score))
|
|
412
|
+
|
|
413
|
+
# Policy with highest weighted score
|
|
414
|
+
if not policy_scores:
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
best_policy_id = max(policy_scores, key=policy_scores.get)
|
|
418
|
+
return next(p for p in policies if id(p) == best_policy_id)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class GovernanceSystem:
|
|
422
|
+
"""
|
|
423
|
+
Governance system orchestrating boards and arbitration.
|
|
424
|
+
|
|
425
|
+
Pattern similar to CorporateSwarm's conduct_corporate_vote (lines 1035-1057).
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
boards: List of governance boards
|
|
429
|
+
arbitration_method: Method for policy selection
|
|
430
|
+
"""
|
|
431
|
+
|
|
432
|
+
def __init__(
|
|
433
|
+
self,
|
|
434
|
+
boards: List[Board],
|
|
435
|
+
arbitration_method: str = "weighted_vote",
|
|
436
|
+
major_change_threshold: float = 0.10, # 10% = major change
|
|
437
|
+
) -> None:
|
|
438
|
+
"""Initialize governance system."""
|
|
439
|
+
self.boards = boards
|
|
440
|
+
self.arbitration_method = arbitration_method
|
|
441
|
+
self.major_change_threshold = major_change_threshold
|
|
442
|
+
self.pending_approvals: Dict[str, Dict[str, Any]] = {}
|
|
443
|
+
self.approval_history: List[Dict[str, Any]] = []
|
|
444
|
+
self.emergency_stop_active = False
|
|
445
|
+
|
|
446
|
+
def select_policy(
|
|
447
|
+
self,
|
|
448
|
+
candidate_policies: List[ControlVector],
|
|
449
|
+
objectives: np.ndarray
|
|
450
|
+
) -> Optional[ControlVector]:
|
|
451
|
+
"""
|
|
452
|
+
Select final policy using arbitration method.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
candidate_policies: List of candidate policies
|
|
456
|
+
objectives: Objective matrix
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
Optional[ControlVector]: Selected policy
|
|
460
|
+
"""
|
|
461
|
+
if not candidate_policies:
|
|
462
|
+
return None
|
|
463
|
+
|
|
464
|
+
if self.arbitration_method == "pareto_intersection":
|
|
465
|
+
consensus = Arbitration.pareto_intersection(self.boards, candidate_policies, objectives)
|
|
466
|
+
return consensus[0] if consensus else None
|
|
467
|
+
|
|
468
|
+
elif self.arbitration_method == "minimax_regret":
|
|
469
|
+
return Arbitration.minimax_regret(self.boards, candidate_policies, objectives)
|
|
470
|
+
|
|
471
|
+
elif self.arbitration_method == "borda":
|
|
472
|
+
return Arbitration.borda_vote(self.boards, candidate_policies, objectives)
|
|
473
|
+
|
|
474
|
+
elif self.arbitration_method == "weighted_vote":
|
|
475
|
+
return Arbitration.weighted_vote(self.boards, candidate_policies, objectives)
|
|
476
|
+
|
|
477
|
+
else:
|
|
478
|
+
logger.warning(f"Unknown arbitration method: {self.arbitration_method}, using weighted_vote")
|
|
479
|
+
return Arbitration.weighted_vote(self.boards, candidate_policies, objectives)
|
|
480
|
+
|
|
481
|
+
def requires_approval(
|
|
482
|
+
self,
|
|
483
|
+
policy: ControlVector,
|
|
484
|
+
previous_policy: Optional[ControlVector]
|
|
485
|
+
) -> Tuple[bool, str]:
|
|
486
|
+
"""
|
|
487
|
+
Check if policy requires human approval (major change > 10%).
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
policy: Proposed policy
|
|
491
|
+
previous_policy: Previous policy (None if first)
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Tuple[bool, str]: (requires_approval, reason)
|
|
495
|
+
"""
|
|
496
|
+
if self.emergency_stop_active:
|
|
497
|
+
return True, "Emergency stop active"
|
|
498
|
+
|
|
499
|
+
if previous_policy is None:
|
|
500
|
+
return True, "First policy requires approval"
|
|
501
|
+
|
|
502
|
+
# Check if major change (> 10% in any category)
|
|
503
|
+
for cat in set(list(policy.budget_shares.keys()) + list(previous_policy.budget_shares.keys())):
|
|
504
|
+
prev_val = previous_policy.budget_shares.get(cat, 0.0)
|
|
505
|
+
curr_val = policy.budget_shares.get(cat, 0.0)
|
|
506
|
+
change = abs(curr_val - prev_val)
|
|
507
|
+
|
|
508
|
+
if change > self.major_change_threshold:
|
|
509
|
+
return True, f"Major change in {cat}: {change:.1%} > {self.major_change_threshold:.1%}"
|
|
510
|
+
|
|
511
|
+
return False, "Minor change, automated execution allowed"
|
|
512
|
+
|
|
513
|
+
def request_approval(
|
|
514
|
+
self,
|
|
515
|
+
policy: ControlVector,
|
|
516
|
+
reason: str,
|
|
517
|
+
user_id: str,
|
|
518
|
+
objectives: Optional[np.ndarray] = None
|
|
519
|
+
) -> str:
|
|
520
|
+
"""
|
|
521
|
+
Request human approval for a policy.
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
policy: Policy requiring approval
|
|
525
|
+
reason: Reason for approval request
|
|
526
|
+
user_id: User requesting approval
|
|
527
|
+
objectives: Optional objective values for context
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
str: Approval request ID
|
|
531
|
+
"""
|
|
532
|
+
approval_id = str(uuid.uuid4())
|
|
533
|
+
|
|
534
|
+
self.pending_approvals[approval_id] = {
|
|
535
|
+
"approval_id": approval_id,
|
|
536
|
+
"policy": policy,
|
|
537
|
+
"reason": reason,
|
|
538
|
+
"requested_by": user_id,
|
|
539
|
+
"objectives": objectives.tolist() if objectives is not None else None,
|
|
540
|
+
"timestamp": time.time(),
|
|
541
|
+
"status": "pending",
|
|
542
|
+
"approved_by": [],
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
logger.info(f"Approval requested: {approval_id} by {user_id} - {reason}")
|
|
546
|
+
|
|
547
|
+
return approval_id
|
|
548
|
+
|
|
549
|
+
def approve_policy(
|
|
550
|
+
self,
|
|
551
|
+
approval_id: str,
|
|
552
|
+
user_id: str,
|
|
553
|
+
comment: Optional[str] = None
|
|
554
|
+
) -> Tuple[bool, str]:
|
|
555
|
+
"""
|
|
556
|
+
Approve a pending policy.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
approval_id: Approval request ID
|
|
560
|
+
user_id: User approving
|
|
561
|
+
comment: Optional comment
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
Tuple[bool, str]: (success, message)
|
|
565
|
+
"""
|
|
566
|
+
if approval_id not in self.pending_approvals:
|
|
567
|
+
return False, "Approval request not found"
|
|
568
|
+
|
|
569
|
+
approval = self.pending_approvals[approval_id]
|
|
570
|
+
|
|
571
|
+
if approval["status"] != "pending":
|
|
572
|
+
return False, f"Approval already {approval['status']}"
|
|
573
|
+
|
|
574
|
+
approval["approved_by"].append(user_id)
|
|
575
|
+
approval["status"] = "approved"
|
|
576
|
+
approval["approval_comment"] = comment
|
|
577
|
+
|
|
578
|
+
# Move to history
|
|
579
|
+
self.approval_history.append(approval.copy())
|
|
580
|
+
del self.pending_approvals[approval_id]
|
|
581
|
+
|
|
582
|
+
logger.info(f"Policy approved: {approval_id} by {user_id}")
|
|
583
|
+
|
|
584
|
+
return True, "Policy approved"
|
|
585
|
+
|
|
586
|
+
def reject_policy(
|
|
587
|
+
self,
|
|
588
|
+
approval_id: str,
|
|
589
|
+
user_id: str,
|
|
590
|
+
reason: str
|
|
591
|
+
) -> Tuple[bool, str]:
|
|
592
|
+
"""
|
|
593
|
+
Reject a pending policy.
|
|
594
|
+
|
|
595
|
+
Args:
|
|
596
|
+
approval_id: Approval request ID
|
|
597
|
+
user_id: User rejecting
|
|
598
|
+
reason: Rejection reason
|
|
599
|
+
|
|
600
|
+
Returns:
|
|
601
|
+
Tuple[bool, str]: (success, message)
|
|
602
|
+
"""
|
|
603
|
+
if approval_id not in self.pending_approvals:
|
|
604
|
+
return False, "Approval request not found"
|
|
605
|
+
|
|
606
|
+
approval = self.pending_approvals[approval_id]
|
|
607
|
+
approval["status"] = "rejected"
|
|
608
|
+
approval["rejected_by"] = user_id
|
|
609
|
+
approval["rejection_reason"] = reason
|
|
610
|
+
|
|
611
|
+
# Move to history
|
|
612
|
+
self.approval_history.append(approval.copy())
|
|
613
|
+
del self.pending_approvals[approval_id]
|
|
614
|
+
|
|
615
|
+
logger.info(f"Policy rejected: {approval_id} by {user_id} - {reason}")
|
|
616
|
+
|
|
617
|
+
return True, "Policy rejected"
|
|
618
|
+
|
|
619
|
+
def override_policy(
|
|
620
|
+
self,
|
|
621
|
+
policy: ControlVector,
|
|
622
|
+
user_id: str,
|
|
623
|
+
reason: str
|
|
624
|
+
) -> Tuple[bool, str]:
|
|
625
|
+
"""
|
|
626
|
+
Override automated decision with manual policy.
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
policy: Override policy
|
|
630
|
+
user_id: User overriding
|
|
631
|
+
reason: Reason for override
|
|
632
|
+
|
|
633
|
+
Returns:
|
|
634
|
+
Tuple[bool, str]: (success, message)
|
|
635
|
+
"""
|
|
636
|
+
override_id = str(uuid.uuid4())
|
|
637
|
+
|
|
638
|
+
override_entry = {
|
|
639
|
+
"override_id": override_id,
|
|
640
|
+
"policy": policy,
|
|
641
|
+
"user_id": user_id,
|
|
642
|
+
"reason": reason,
|
|
643
|
+
"timestamp": time.time(),
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
self.approval_history.append(override_entry)
|
|
647
|
+
|
|
648
|
+
logger.warning(f"Policy override: {override_id} by {user_id} - {reason}")
|
|
649
|
+
|
|
650
|
+
return True, f"Override executed: {override_id}"
|
|
651
|
+
|
|
652
|
+
def emergency_stop(self, user_id: str, reason: str) -> None:
|
|
653
|
+
"""
|
|
654
|
+
Activate emergency stop (halt all automation).
|
|
655
|
+
|
|
656
|
+
Args:
|
|
657
|
+
user_id: User activating emergency stop
|
|
658
|
+
reason: Reason for emergency stop
|
|
659
|
+
"""
|
|
660
|
+
self.emergency_stop_active = True
|
|
661
|
+
|
|
662
|
+
logger.critical(f"EMERGENCY STOP activated by {user_id}: {reason}")
|
|
663
|
+
|
|
664
|
+
def resume_operations(self, user_id: str) -> None:
|
|
665
|
+
"""
|
|
666
|
+
Resume operations after emergency stop.
|
|
667
|
+
|
|
668
|
+
Args:
|
|
669
|
+
user_id: User resuming operations
|
|
670
|
+
"""
|
|
671
|
+
self.emergency_stop_active = False
|
|
672
|
+
|
|
673
|
+
logger.info(f"Operations resumed by {user_id}")
|
|
674
|
+
|
|
675
|
+
def get_pending_approvals(self) -> List[Dict[str, Any]]:
|
|
676
|
+
"""Get list of pending approvals."""
|
|
677
|
+
return list(self.pending_approvals.values())
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
# RiskAssessment: Adapted from CorporateSwarm (lines 427-440)
|
|
681
|
+
if PYDANTIC_AVAILABLE:
|
|
682
|
+
class RiskAssessment(BaseModel):
|
|
683
|
+
"""Risk assessment model for constraint violations."""
|
|
684
|
+
|
|
685
|
+
risk_id: str = Field(default_factory=_generate_uuid)
|
|
686
|
+
risk_category: str = Field(default="constraint_violation")
|
|
687
|
+
risk_level: str = Field(default="medium")
|
|
688
|
+
probability: float = Field(default=0.5, ge=0.0, le=1.0)
|
|
689
|
+
impact: float = Field(default=0.5, ge=0.0, le=1.0)
|
|
690
|
+
risk_score: float = Field(default=0.25, ge=0.0, le=1.0)
|
|
691
|
+
mitigation_strategies: List[str] = Field(default_factory=list)
|
|
692
|
+
owner: str = Field(default="")
|
|
693
|
+
status: str = Field(default="active")
|
|
694
|
+
last_reviewed: float = Field(default_factory=time.time)
|
|
695
|
+
else:
|
|
696
|
+
@dataclass
|
|
697
|
+
class RiskAssessment:
|
|
698
|
+
"""Risk assessment model for constraint violations."""
|
|
699
|
+
risk_id: str = field(default_factory=_generate_uuid)
|
|
700
|
+
risk_category: str = "constraint_violation"
|
|
701
|
+
risk_level: str = "medium"
|
|
702
|
+
probability: float = 0.5
|
|
703
|
+
impact: float = 0.5
|
|
704
|
+
risk_score: float = 0.25
|
|
705
|
+
mitigation_strategies: List[str] = field(default_factory=list)
|
|
706
|
+
owner: str = ""
|
|
707
|
+
status: str = "active"
|
|
708
|
+
last_reviewed: float = field(default_factory=time.time)
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
class LogisticsNetwork:
|
|
712
|
+
"""
|
|
713
|
+
Multi-commodity flow optimization network.
|
|
714
|
+
|
|
715
|
+
Graph G=(V,E) with nodes (regions, ports, warehouses) and edges (transport links).
|
|
716
|
+
Solves linear program for optimal flow allocation.
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
nodes: List of node identifiers
|
|
720
|
+
edges: List of (source, target) edge tuples
|
|
721
|
+
capacities: Dictionary mapping edges to capacities
|
|
722
|
+
costs: Dictionary mapping edges to transport costs
|
|
723
|
+
"""
|
|
724
|
+
|
|
725
|
+
def __init__(
|
|
726
|
+
self,
|
|
727
|
+
nodes: List[str],
|
|
728
|
+
edges: List[Tuple[str, str]],
|
|
729
|
+
capacities: Optional[Dict[Tuple[str, str], float]] = None,
|
|
730
|
+
costs: Optional[Dict[Tuple[str, str], float]] = None,
|
|
731
|
+
) -> None:
|
|
732
|
+
"""Initialize logistics network."""
|
|
733
|
+
self.nodes = nodes
|
|
734
|
+
self.edges = edges
|
|
735
|
+
self.capacities = capacities or {}
|
|
736
|
+
self.costs = costs or {}
|
|
737
|
+
|
|
738
|
+
if NETWORKX_AVAILABLE:
|
|
739
|
+
self.graph = nx.DiGraph()
|
|
740
|
+
self.graph.add_nodes_from(nodes)
|
|
741
|
+
self.graph.add_edges_from(edges)
|
|
742
|
+
|
|
743
|
+
def solve_flow_problem(
|
|
744
|
+
self,
|
|
745
|
+
demands: Dict[str, Dict[str, float]], # node -> commodity -> demand
|
|
746
|
+
commodities: List[str],
|
|
747
|
+
capacities: Optional[Dict[Tuple[str, str], float]] = None,
|
|
748
|
+
costs: Optional[Dict[Tuple[str, str], float]] = None
|
|
749
|
+
) -> Dict[Tuple[str, str, str], float]:
|
|
750
|
+
"""
|
|
751
|
+
Solve multi-commodity flow optimization problem.
|
|
752
|
+
|
|
753
|
+
Args:
|
|
754
|
+
demands: Node demands by commodity
|
|
755
|
+
commodities: List of commodity types
|
|
756
|
+
capacities: Edge capacities (default: use instance)
|
|
757
|
+
costs: Edge costs (default: use instance)
|
|
758
|
+
|
|
759
|
+
Returns:
|
|
760
|
+
Dict[Tuple[str, str, str], float]: Flow values (source, target, commodity) -> flow
|
|
761
|
+
"""
|
|
762
|
+
if not CVXPY_AVAILABLE:
|
|
763
|
+
logger.warning("cvxpy not available, using heuristic flow allocation")
|
|
764
|
+
return self._heuristic_flow(demands, commodities)
|
|
765
|
+
|
|
766
|
+
capacities = capacities or self.capacities
|
|
767
|
+
costs = costs or self.costs
|
|
768
|
+
|
|
769
|
+
# Decision variables: flow[e, q] for each edge and commodity
|
|
770
|
+
flows = {}
|
|
771
|
+
for edge in self.edges:
|
|
772
|
+
for commodity in commodities:
|
|
773
|
+
flows[(edge[0], edge[1], commodity)] = cp.Variable(nonneg=True)
|
|
774
|
+
|
|
775
|
+
# Objective: minimize total cost
|
|
776
|
+
objective = cp.Minimize(
|
|
777
|
+
sum(
|
|
778
|
+
costs.get((edge[0], edge[1]), 1.0) * flows[(edge[0], edge[1], commodity)]
|
|
779
|
+
for edge in self.edges
|
|
780
|
+
for commodity in commodities
|
|
781
|
+
)
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
# Constraints
|
|
785
|
+
constraints = []
|
|
786
|
+
|
|
787
|
+
# Flow capacity per edge
|
|
788
|
+
for edge in self.edges:
|
|
789
|
+
total_flow = sum(flows[(edge[0], edge[1], q)] for q in commodities)
|
|
790
|
+
cap = capacities.get(edge, float('inf'))
|
|
791
|
+
constraints.append(total_flow <= cap)
|
|
792
|
+
|
|
793
|
+
# Flow balance per node and commodity
|
|
794
|
+
for node in self.nodes:
|
|
795
|
+
for commodity in commodities:
|
|
796
|
+
inflow = sum(
|
|
797
|
+
flows.get((src, node, commodity), 0)
|
|
798
|
+
for src in self.nodes
|
|
799
|
+
if (src, node) in self.edges
|
|
800
|
+
)
|
|
801
|
+
outflow = sum(
|
|
802
|
+
flows.get((node, tgt, commodity), 0)
|
|
803
|
+
for tgt in self.nodes
|
|
804
|
+
if (node, tgt) in self.edges
|
|
805
|
+
)
|
|
806
|
+
demand = demands.get(node, {}).get(commodity, 0.0)
|
|
807
|
+
constraints.append(inflow - outflow >= demand)
|
|
808
|
+
|
|
809
|
+
# Solve
|
|
810
|
+
problem = cp.Problem(objective, constraints)
|
|
811
|
+
try:
|
|
812
|
+
problem.solve(solver=cp.ECOS if hasattr(cp, 'ECOS') else cp.SCS)
|
|
813
|
+
|
|
814
|
+
if problem.status not in ["optimal", "optimal_inaccurate"]:
|
|
815
|
+
logger.warning(f"Flow problem status: {problem.status}, using heuristic")
|
|
816
|
+
return self._heuristic_flow(demands, commodities)
|
|
817
|
+
|
|
818
|
+
# Extract solution
|
|
819
|
+
solution = {}
|
|
820
|
+
for key, var in flows.items():
|
|
821
|
+
solution[key] = float(var.value) if var.value is not None else 0.0
|
|
822
|
+
|
|
823
|
+
return solution
|
|
824
|
+
|
|
825
|
+
except Exception as e:
|
|
826
|
+
logger.error(f"Flow optimization failed: {e}, using heuristic")
|
|
827
|
+
return self._heuristic_flow(demands, commodities)
|
|
828
|
+
|
|
829
|
+
def _heuristic_flow(
|
|
830
|
+
self,
|
|
831
|
+
demands: Dict[str, Dict[str, float]],
|
|
832
|
+
commodities: List[str]
|
|
833
|
+
) -> Dict[Tuple[str, str, str], float]:
|
|
834
|
+
"""Heuristic flow allocation (fallback)."""
|
|
835
|
+
flows = {}
|
|
836
|
+
for edge in self.edges:
|
|
837
|
+
for commodity in commodities:
|
|
838
|
+
# Simple heuristic: allocate based on demand
|
|
839
|
+
source_demand = demands.get(edge[0], {}).get(commodity, 0.0)
|
|
840
|
+
flows[(edge[0], edge[1], commodity)] = source_demand * 0.1 # 10% flow
|
|
841
|
+
return flows
|
|
842
|
+
|
|
843
|
+
def compute_shadow_prices(self) -> Dict[Tuple[str, str], float]:
|
|
844
|
+
"""
|
|
845
|
+
Compute shadow prices (dual variables) for bottleneck analysis.
|
|
846
|
+
|
|
847
|
+
Returns:
|
|
848
|
+
Dict[Tuple[str, str], float]: Edge -> shadow price
|
|
849
|
+
"""
|
|
850
|
+
# Placeholder: would extract dual variables from LP solution
|
|
851
|
+
logger.warning("Shadow price computation requires solved LP, returning zeros")
|
|
852
|
+
return {edge: 0.0 for edge in self.edges}
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
class Visualization:
|
|
856
|
+
"""
|
|
857
|
+
Visualization and dashboard components.
|
|
858
|
+
|
|
859
|
+
Provides:
|
|
860
|
+
- State dashboard
|
|
861
|
+
- Pareto frontier plots
|
|
862
|
+
- Constraint violation maps
|
|
863
|
+
- Bottleneck analysis
|
|
864
|
+
|
|
865
|
+
Can use Formatter from utils for enhanced rich formatting.
|
|
866
|
+
"""
|
|
867
|
+
|
|
868
|
+
_formatter: Optional[Any] = None
|
|
869
|
+
|
|
870
|
+
@classmethod
|
|
871
|
+
def _get_formatter(cls) -> Optional[Any]:
|
|
872
|
+
"""Get Formatter instance from utils if available."""
|
|
873
|
+
if cls._formatter is None:
|
|
874
|
+
try:
|
|
875
|
+
from utils.formatter import Formatter
|
|
876
|
+
cls._formatter = Formatter(md=True)
|
|
877
|
+
except ImportError:
|
|
878
|
+
cls._formatter = None
|
|
879
|
+
return cls._formatter
|
|
880
|
+
|
|
881
|
+
@staticmethod
|
|
882
|
+
def state_dashboard(x_t: StateVector) -> str:
|
|
883
|
+
"""
|
|
884
|
+
Generate text dashboard for state vector.
|
|
885
|
+
|
|
886
|
+
Args:
|
|
887
|
+
x_t: State vector
|
|
888
|
+
|
|
889
|
+
Returns:
|
|
890
|
+
str: Formatted dashboard string
|
|
891
|
+
"""
|
|
892
|
+
dashboard = f"""
|
|
893
|
+
=== CRCA-SD State Dashboard ===
|
|
894
|
+
Population (P): {x_t.P:,.0f}
|
|
895
|
+
Labor Force (L): {x_t.L:,.0f}
|
|
896
|
+
Unemployment (U): {x_t.U:.1%}
|
|
897
|
+
Wage (W): {x_t.W:.2f}
|
|
898
|
+
Stability (S): {x_t.S:.1%}
|
|
899
|
+
Literacy: {x_t.literacy:.1%}
|
|
900
|
+
Education Capacity: {x_t.Ecap:,.0f}
|
|
901
|
+
Healthcare Capacity: {x_t.Hcap:,.0f}
|
|
902
|
+
Capital Stock (K): {x_t.K:,.0f}
|
|
903
|
+
Infrastructure (I): {x_t.I:.1%}
|
|
904
|
+
Transport Capacity: {x_t.Tcap:,.0f}
|
|
905
|
+
Energy Stock: {x_t.E_stock:,.0f}
|
|
906
|
+
Food Stock: {x_t.F_stock:,.0f}
|
|
907
|
+
Materials Stock: {x_t.M_stock:,.0f}
|
|
908
|
+
Ecological Damage (C): {x_t.C:,.0f}
|
|
909
|
+
Output (Y): {x_t.Y:,.0f}
|
|
910
|
+
===============================
|
|
911
|
+
"""
|
|
912
|
+
return dashboard
|
|
913
|
+
|
|
914
|
+
@staticmethod
|
|
915
|
+
def pareto_visualizer(
|
|
916
|
+
policies: List[ControlVector],
|
|
917
|
+
objectives: np.ndarray,
|
|
918
|
+
objective_names: Optional[List[str]] = None
|
|
919
|
+
) -> str:
|
|
920
|
+
"""
|
|
921
|
+
Generate text visualization of Pareto frontier.
|
|
922
|
+
|
|
923
|
+
Args:
|
|
924
|
+
policies: List of policies
|
|
925
|
+
objectives: Objective matrix
|
|
926
|
+
objective_names: Names of objectives (default: J_U, J_ℓ, etc.)
|
|
927
|
+
|
|
928
|
+
Returns:
|
|
929
|
+
str: Formatted visualization string
|
|
930
|
+
"""
|
|
931
|
+
if objective_names is None:
|
|
932
|
+
objective_names = ["J_U", "J_ℓ", "J_Y", "J_ineq", "J_C", "J_risk"]
|
|
933
|
+
|
|
934
|
+
if objectives.ndim == 1:
|
|
935
|
+
objectives = objectives.reshape(1, -1)
|
|
936
|
+
|
|
937
|
+
viz = "=== Pareto Frontier ===\n"
|
|
938
|
+
viz += f"Policies: {len(policies)}\n"
|
|
939
|
+
viz += f"Objectives: {', '.join(objective_names)}\n\n"
|
|
940
|
+
|
|
941
|
+
for i, (policy, obj_vec) in enumerate(zip(policies, objectives)):
|
|
942
|
+
viz += f"Policy {i+1}:\n"
|
|
943
|
+
for name, value in zip(objective_names, obj_vec):
|
|
944
|
+
viz += f" {name}: {value:.4f}\n"
|
|
945
|
+
viz += "\n"
|
|
946
|
+
|
|
947
|
+
return viz
|
|
948
|
+
|
|
949
|
+
@staticmethod
|
|
950
|
+
def constraint_map(
|
|
951
|
+
violations: List[str],
|
|
952
|
+
x_t: StateVector
|
|
953
|
+
) -> str:
|
|
954
|
+
"""
|
|
955
|
+
Generate constraint violation map.
|
|
956
|
+
|
|
957
|
+
Args:
|
|
958
|
+
violations: List of violation messages
|
|
959
|
+
x_t: State vector
|
|
960
|
+
|
|
961
|
+
Returns:
|
|
962
|
+
str: Formatted violation map
|
|
963
|
+
"""
|
|
964
|
+
map_str = "=== Constraint Violation Map ===\n"
|
|
965
|
+
if not violations:
|
|
966
|
+
map_str += "✓ All constraints satisfied\n"
|
|
967
|
+
else:
|
|
968
|
+
map_str += f"✗ {len(violations)} violations:\n"
|
|
969
|
+
for violation in violations:
|
|
970
|
+
map_str += f" - {violation}\n"
|
|
971
|
+
return map_str
|
|
972
|
+
|
|
973
|
+
@staticmethod
|
|
974
|
+
def realtime_dashboard(
|
|
975
|
+
x_t: StateVector,
|
|
976
|
+
execution_status: Optional[Dict[str, Any]] = None,
|
|
977
|
+
violations: Optional[List[str]] = None,
|
|
978
|
+
pending_approvals: Optional[List[Dict[str, Any]]] = None,
|
|
979
|
+
system_health: Optional[Dict[str, Any]] = None
|
|
980
|
+
) -> str:
|
|
981
|
+
"""
|
|
982
|
+
Generate real-time dashboard with execution status and monitoring.
|
|
983
|
+
|
|
984
|
+
Args:
|
|
985
|
+
x_t: Current state vector
|
|
986
|
+
execution_status: Policy execution status
|
|
987
|
+
violations: Constraint violations
|
|
988
|
+
pending_approvals: Pending approval requests
|
|
989
|
+
system_health: System health status
|
|
990
|
+
|
|
991
|
+
Returns:
|
|
992
|
+
str: Formatted real-time dashboard
|
|
993
|
+
"""
|
|
994
|
+
dashboard = "=== CRCA-SD Real-Time Dashboard ===\n"
|
|
995
|
+
dashboard += f"Last Update: {datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')}\n\n"
|
|
996
|
+
|
|
997
|
+
# State section
|
|
998
|
+
dashboard += "--- Current State ---\n"
|
|
999
|
+
dashboard += Visualization.state_dashboard(x_t)
|
|
1000
|
+
dashboard += "\n"
|
|
1001
|
+
|
|
1002
|
+
# Execution status
|
|
1003
|
+
if execution_status:
|
|
1004
|
+
dashboard += "--- Execution Status ---\n"
|
|
1005
|
+
status = execution_status.get("status", "unknown")
|
|
1006
|
+
dashboard += f"Status: {status}\n"
|
|
1007
|
+
if "execution_id" in execution_status:
|
|
1008
|
+
dashboard += f"Execution ID: {execution_status['execution_id']}\n"
|
|
1009
|
+
if "requires_approval" in execution_status:
|
|
1010
|
+
dashboard += f"Requires Approval: {execution_status['requires_approval']}\n"
|
|
1011
|
+
dashboard += "\n"
|
|
1012
|
+
|
|
1013
|
+
# Violations
|
|
1014
|
+
if violations:
|
|
1015
|
+
dashboard += "--- Constraint Violations ---\n"
|
|
1016
|
+
dashboard += Visualization.constraint_map(violations, x_t)
|
|
1017
|
+
dashboard += "\n"
|
|
1018
|
+
|
|
1019
|
+
# Pending approvals
|
|
1020
|
+
if pending_approvals:
|
|
1021
|
+
dashboard += "--- Pending Approvals ---\n"
|
|
1022
|
+
dashboard += f"Count: {len(pending_approvals)}\n"
|
|
1023
|
+
for approval in pending_approvals[:5]: # Show first 5
|
|
1024
|
+
dashboard += f" - {approval.get('approval_id', 'unknown')}: {approval.get('reason', 'N/A')}\n"
|
|
1025
|
+
dashboard += "\n"
|
|
1026
|
+
|
|
1027
|
+
# System health
|
|
1028
|
+
if system_health:
|
|
1029
|
+
dashboard += "--- System Health ---\n"
|
|
1030
|
+
dashboard += f"Status: {system_health.get('status', 'unknown')}\n"
|
|
1031
|
+
dashboard += f"Violations (24h): {system_health.get('n_violations_24h', 0)}\n"
|
|
1032
|
+
dashboard += "\n"
|
|
1033
|
+
|
|
1034
|
+
dashboard += "===============================\n"
|
|
1035
|
+
|
|
1036
|
+
return dashboard
|
|
1037
|
+
|
|
1038
|
+
@staticmethod
|
|
1039
|
+
def execution_history_visualization(
|
|
1040
|
+
execution_history: List[Dict[str, Any]],
|
|
1041
|
+
n_recent: int = 10
|
|
1042
|
+
) -> str:
|
|
1043
|
+
"""
|
|
1044
|
+
Visualize execution history.
|
|
1045
|
+
|
|
1046
|
+
Args:
|
|
1047
|
+
execution_history: List of execution records
|
|
1048
|
+
n_recent: Number of recent executions to show
|
|
1049
|
+
|
|
1050
|
+
Returns:
|
|
1051
|
+
str: Formatted visualization
|
|
1052
|
+
"""
|
|
1053
|
+
viz = "=== Execution History ===\n"
|
|
1054
|
+
|
|
1055
|
+
recent = execution_history[-n_recent:] if len(execution_history) > n_recent else execution_history
|
|
1056
|
+
|
|
1057
|
+
for exec_record in recent:
|
|
1058
|
+
exec_id = exec_record.get("execution_id", "unknown")
|
|
1059
|
+
status = exec_record.get("status", "unknown")
|
|
1060
|
+
timestamp = exec_record.get("timestamp", 0)
|
|
1061
|
+
time_str = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M') if timestamp > 0 else "N/A"
|
|
1062
|
+
|
|
1063
|
+
viz += f"{time_str} | {exec_id[:8]}... | {status}\n"
|
|
1064
|
+
|
|
1065
|
+
return viz
|
|
1066
|
+
|
|
1067
|
+
@staticmethod
|
|
1068
|
+
def policy_comparison_visualization(
|
|
1069
|
+
policies: List[ControlVector],
|
|
1070
|
+
policy_names: Optional[List[str]] = None
|
|
1071
|
+
) -> str:
|
|
1072
|
+
"""
|
|
1073
|
+
Visualize comparison of multiple policies.
|
|
1074
|
+
|
|
1075
|
+
Args:
|
|
1076
|
+
policies: List of policies to compare
|
|
1077
|
+
policy_names: Optional names for policies
|
|
1078
|
+
|
|
1079
|
+
Returns:
|
|
1080
|
+
str: Formatted comparison
|
|
1081
|
+
"""
|
|
1082
|
+
if policy_names is None:
|
|
1083
|
+
policy_names = [f"Policy {i+1}" for i in range(len(policies))]
|
|
1084
|
+
|
|
1085
|
+
viz = "=== Policy Comparison ===\n"
|
|
1086
|
+
|
|
1087
|
+
# Get all budget categories
|
|
1088
|
+
all_categories = set()
|
|
1089
|
+
for policy in policies:
|
|
1090
|
+
all_categories.update(policy.budget_shares.keys())
|
|
1091
|
+
|
|
1092
|
+
# Header
|
|
1093
|
+
viz += "Category".ljust(20)
|
|
1094
|
+
for name in policy_names:
|
|
1095
|
+
viz += name[:15].ljust(18)
|
|
1096
|
+
viz += "\n"
|
|
1097
|
+
viz += "-" * (20 + 18 * len(policy_names)) + "\n"
|
|
1098
|
+
|
|
1099
|
+
# Rows
|
|
1100
|
+
for cat in sorted(all_categories):
|
|
1101
|
+
viz += cat[:19].ljust(20)
|
|
1102
|
+
for policy in policies:
|
|
1103
|
+
value = policy.budget_shares.get(cat, 0.0)
|
|
1104
|
+
viz += f"{value:.1%}".ljust(18)
|
|
1105
|
+
viz += "\n"
|
|
1106
|
+
|
|
1107
|
+
return viz
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
# Config: Using CorporateSwarm CorporateConfigModel pattern (lines 75-158)
|
|
1111
|
+
if PYDANTIC_AVAILABLE:
|
|
1112
|
+
class CRCA_SD_ConfigModel(BaseModel):
|
|
1113
|
+
"""Configuration model for CRCA-SD (Pydantic)."""
|
|
1114
|
+
|
|
1115
|
+
# State bounds
|
|
1116
|
+
U_max: float = Field(default=0.2, ge=0.0, le=1.0)
|
|
1117
|
+
S_min: float = Field(default=0.3, ge=0.0, le=1.0)
|
|
1118
|
+
c_min: float = Field(default=0.02, ge=0.0)
|
|
1119
|
+
|
|
1120
|
+
# Dynamics parameters
|
|
1121
|
+
delta_K: float = Field(default=0.05, ge=0.0, le=1.0)
|
|
1122
|
+
delta_I: float = Field(default=0.02, ge=0.0, le=1.0)
|
|
1123
|
+
alpha: float = Field(default=0.3, ge=0.0, le=1.0)
|
|
1124
|
+
kappa_K: float = Field(default=0.8, ge=0.0)
|
|
1125
|
+
kappa_I: float = Field(default=0.7, ge=0.0)
|
|
1126
|
+
kappa_literacy: float = Field(default=0.1, ge=0.0)
|
|
1127
|
+
|
|
1128
|
+
# MPC parameters
|
|
1129
|
+
horizon: int = Field(default=10, ge=1, le=50)
|
|
1130
|
+
n_scenarios: int = Field(default=10, ge=1, le=100)
|
|
1131
|
+
objective_weights: List[float] = Field(default_factory=lambda: [1.0/6] * 6)
|
|
1132
|
+
|
|
1133
|
+
# Stability parameters
|
|
1134
|
+
max_budget_change: float = Field(default=0.2, ge=0.0, le=1.0)
|
|
1135
|
+
investment_smoothing: float = Field(default=0.7, ge=0.0, le=1.0)
|
|
1136
|
+
|
|
1137
|
+
# Board parameters
|
|
1138
|
+
n_boards: int = Field(default=4, ge=1, le=10)
|
|
1139
|
+
arbitration_method: str = Field(default="weighted_vote")
|
|
1140
|
+
|
|
1141
|
+
# Logistics parameters
|
|
1142
|
+
default_transport_capacity: float = Field(default=10000.0, ge=0.0)
|
|
1143
|
+
|
|
1144
|
+
# Risk parameters
|
|
1145
|
+
cvar_alpha: float = Field(default=0.05, ge=0.0, le=1.0)
|
|
1146
|
+
else:
|
|
1147
|
+
@dataclass
|
|
1148
|
+
class CRCA_SD_ConfigModel:
|
|
1149
|
+
"""Configuration model for CRCA-SD (dataclass fallback)."""
|
|
1150
|
+
U_max: float = 0.2
|
|
1151
|
+
S_min: float = 0.3
|
|
1152
|
+
c_min: float = 0.02
|
|
1153
|
+
delta_K: float = 0.05
|
|
1154
|
+
delta_I: float = 0.02
|
|
1155
|
+
alpha: float = 0.3
|
|
1156
|
+
kappa_K: float = 0.8
|
|
1157
|
+
kappa_I: float = 0.7
|
|
1158
|
+
kappa_literacy: float = 0.1
|
|
1159
|
+
horizon: int = 10
|
|
1160
|
+
n_scenarios: int = 10
|
|
1161
|
+
objective_weights: List[float] = field(default_factory=lambda: [1.0/6] * 6)
|
|
1162
|
+
max_budget_change: float = 0.2
|
|
1163
|
+
investment_smoothing: float = 0.7
|
|
1164
|
+
n_boards: int = 4
|
|
1165
|
+
arbitration_method: str = "weighted_vote"
|
|
1166
|
+
default_transport_capacity: float = 10000.0
|
|
1167
|
+
cvar_alpha: float = 0.05
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
@dataclass
|
|
1171
|
+
class CRCA_SD_Config:
|
|
1172
|
+
"""Configuration manager (pattern from CorporateSwarm lines 160-227)."""
|
|
1173
|
+
|
|
1174
|
+
config_file_path: Optional[str] = None
|
|
1175
|
+
config_data: Optional[Dict[str, Any]] = None
|
|
1176
|
+
config: CRCA_SD_ConfigModel = field(init=False)
|
|
1177
|
+
|
|
1178
|
+
def __post_init__(self) -> None:
|
|
1179
|
+
"""Load configuration."""
|
|
1180
|
+
self._load_config()
|
|
1181
|
+
|
|
1182
|
+
def _load_config(self) -> None:
|
|
1183
|
+
"""Load configuration with priority: explicit data > file > defaults."""
|
|
1184
|
+
try:
|
|
1185
|
+
self.config = CRCA_SD_ConfigModel()
|
|
1186
|
+
if self.config_file_path and os.path.exists(self.config_file_path):
|
|
1187
|
+
self._load_from_file()
|
|
1188
|
+
if self.config_data:
|
|
1189
|
+
self._load_from_dict(self.config_data)
|
|
1190
|
+
except Exception as e:
|
|
1191
|
+
logger.error(f"Configuration loading failed: {e}")
|
|
1192
|
+
raise ValueError(f"Configuration loading failed: {e}") from e
|
|
1193
|
+
|
|
1194
|
+
def _load_from_file(self) -> None:
|
|
1195
|
+
"""Load configuration from YAML file."""
|
|
1196
|
+
if not YAML_AVAILABLE:
|
|
1197
|
+
logger.warning("YAML not available, cannot load config file")
|
|
1198
|
+
return
|
|
1199
|
+
|
|
1200
|
+
try:
|
|
1201
|
+
with open(self.config_file_path, "r") as f:
|
|
1202
|
+
self._load_from_dict(yaml.safe_load(f))
|
|
1203
|
+
logger.info(f"Loaded config from: {self.config_file_path}")
|
|
1204
|
+
except Exception as e:
|
|
1205
|
+
logger.warning(f"File loading failed {self.config_file_path}: {e}")
|
|
1206
|
+
raise ValueError(f"Configuration file loading failed: {e}") from e
|
|
1207
|
+
|
|
1208
|
+
def _load_from_dict(self, config_dict: Dict[str, Any]) -> None:
|
|
1209
|
+
"""Load configuration from dictionary."""
|
|
1210
|
+
for key, value in config_dict.items():
|
|
1211
|
+
if hasattr(self.config, key):
|
|
1212
|
+
try:
|
|
1213
|
+
setattr(self.config, key, value)
|
|
1214
|
+
except (ValueError, TypeError) as e:
|
|
1215
|
+
logger.warning(f"Config {key} failed: {e}")
|
|
1216
|
+
raise ValueError(f"Invalid configuration value for {key}: {e}") from e
|
|
1217
|
+
|
|
1218
|
+
def get_config(self) -> CRCA_SD_ConfigModel:
|
|
1219
|
+
"""Get configuration model."""
|
|
1220
|
+
return self.config
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
# Global config cache (pattern from CorporateSwarm lines 233-239)
|
|
1224
|
+
_crca_sd_config: Optional[CRCA_SD_Config] = None
|
|
1225
|
+
|
|
1226
|
+
@lru_cache(maxsize=1)
|
|
1227
|
+
def get_crca_sd_config(config_file_path: Optional[str] = None) -> CRCA_SD_Config:
|
|
1228
|
+
"""Get global CRCA-SD configuration instance."""
|
|
1229
|
+
global _crca_sd_config
|
|
1230
|
+
if _crca_sd_config is None:
|
|
1231
|
+
_crca_sd_config = CRCA_SD_Config(config_file_path=config_file_path)
|
|
1232
|
+
return _crca_sd_config
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
class MetricsCollector:
|
|
1236
|
+
"""
|
|
1237
|
+
Metrics collector for feasibility, stability, robustness.
|
|
1238
|
+
|
|
1239
|
+
Tracks:
|
|
1240
|
+
- Feasibility: scenario feasibility rate, minimum slack
|
|
1241
|
+
- Stability: variance of U_t, Y_t, C_t, oscillation frequency, control effort
|
|
1242
|
+
- Robustness: worst-case performance, CVaR at different α
|
|
1243
|
+
- Bottleneck causality: which constraints bind most often
|
|
1244
|
+
"""
|
|
1245
|
+
|
|
1246
|
+
def __init__(self) -> None:
|
|
1247
|
+
"""Initialize metrics collector."""
|
|
1248
|
+
self.metrics_history: List[Dict[str, Any]] = []
|
|
1249
|
+
|
|
1250
|
+
def compute_all_metrics(
|
|
1251
|
+
self,
|
|
1252
|
+
trajectories: List[List[StateVector]],
|
|
1253
|
+
policies: List[ControlVector]
|
|
1254
|
+
) -> Dict[str, Any]:
|
|
1255
|
+
"""
|
|
1256
|
+
Compute all metrics from trajectories and policies.
|
|
1257
|
+
|
|
1258
|
+
Args:
|
|
1259
|
+
trajectories: List of state trajectories
|
|
1260
|
+
policies: List of control policies
|
|
1261
|
+
|
|
1262
|
+
Returns:
|
|
1263
|
+
Dict[str, Any]: Metrics dictionary
|
|
1264
|
+
"""
|
|
1265
|
+
metrics = {}
|
|
1266
|
+
|
|
1267
|
+
# Feasibility metrics
|
|
1268
|
+
n_feasible = sum(1 for traj in trajectories if len(traj) > 1)
|
|
1269
|
+
metrics["feasibility_rate"] = n_feasible / len(trajectories) if trajectories else 0.0
|
|
1270
|
+
|
|
1271
|
+
# Stability metrics (variance)
|
|
1272
|
+
if trajectories:
|
|
1273
|
+
U_values = [x.U for traj in trajectories for x in traj[1:]]
|
|
1274
|
+
Y_values = [x.Y for traj in trajectories for x in traj[1:]]
|
|
1275
|
+
C_values = [x.C for traj in trajectories for x in traj[1:]]
|
|
1276
|
+
|
|
1277
|
+
metrics["U_variance"] = float(np.var(U_values)) if U_values else 0.0
|
|
1278
|
+
metrics["Y_variance"] = float(np.var(Y_values)) if Y_values else 0.0
|
|
1279
|
+
metrics["C_variance"] = float(np.var(C_values)) if C_values else 0.0
|
|
1280
|
+
|
|
1281
|
+
# Control effort (sum of changes)
|
|
1282
|
+
if len(policies) > 1:
|
|
1283
|
+
effort = 0.0
|
|
1284
|
+
for i in range(1, len(policies)):
|
|
1285
|
+
prev_shares = policies[i-1].budget_shares
|
|
1286
|
+
curr_shares = policies[i].budget_shares
|
|
1287
|
+
change = sum(abs(curr_shares.get(k, 0) - prev_shares.get(k, 0)) for k in set(list(prev_shares.keys()) + list(curr_shares.keys())))
|
|
1288
|
+
effort += change
|
|
1289
|
+
metrics["control_effort"] = effort
|
|
1290
|
+
else:
|
|
1291
|
+
metrics["control_effort"] = 0.0
|
|
1292
|
+
|
|
1293
|
+
# Robustness (worst-case)
|
|
1294
|
+
if trajectories:
|
|
1295
|
+
final_states = [traj[-1] for traj in trajectories if traj]
|
|
1296
|
+
worst_U = max((x.U for x in final_states), default=0.0)
|
|
1297
|
+
worst_S = min((x.S for x in final_states), default=1.0)
|
|
1298
|
+
metrics["worst_case_U"] = float(worst_U)
|
|
1299
|
+
metrics["worst_case_S"] = float(worst_S)
|
|
1300
|
+
|
|
1301
|
+
self.metrics_history.append(metrics)
|
|
1302
|
+
return metrics
|
|
1303
|
+
|
|
1304
|
+
def generate_report(self) -> str:
|
|
1305
|
+
"""
|
|
1306
|
+
Generate formatted metrics report.
|
|
1307
|
+
|
|
1308
|
+
Returns:
|
|
1309
|
+
str: Formatted report string
|
|
1310
|
+
"""
|
|
1311
|
+
if not self.metrics_history:
|
|
1312
|
+
return "No metrics collected yet."
|
|
1313
|
+
|
|
1314
|
+
latest = self.metrics_history[-1]
|
|
1315
|
+
report = "=== CRCA-SD Metrics Report ===\n"
|
|
1316
|
+
report += f"Feasibility Rate: {latest.get('feasibility_rate', 0.0):.1%}\n"
|
|
1317
|
+
report += f"Unemployment Variance: {latest.get('U_variance', 0.0):.4f}\n"
|
|
1318
|
+
report += f"Output Variance: {latest.get('Y_variance', 0.0):.2f}\n"
|
|
1319
|
+
report += f"Control Effort: {latest.get('control_effort', 0.0):.4f}\n"
|
|
1320
|
+
report += f"Worst Case Unemployment: {latest.get('worst_case_U', 0.0):.1%}\n"
|
|
1321
|
+
report += f"Worst Case Stability: {latest.get('worst_case_S', 0.0):.1%}\n"
|
|
1322
|
+
report += "============================\n"
|
|
1323
|
+
|
|
1324
|
+
return report
|
|
1325
|
+
|