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,556 @@
|
|
|
1
|
+
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
|
2
|
+
import { Switch } from '~/components/ui/Switch';
|
|
3
|
+
import { Card, CardContent, CardHeader } from '~/components/ui/Card';
|
|
4
|
+
import { Button } from '~/components/ui/Button';
|
|
5
|
+
import { useSettings } from '~/lib/hooks/useSettings';
|
|
6
|
+
import { LOCAL_PROVIDERS } from '~/lib/stores/settings';
|
|
7
|
+
import type { IProviderConfig } from '~/types/model';
|
|
8
|
+
import { logStore } from '~/lib/stores/logs';
|
|
9
|
+
import { providerBaseUrlEnvKeys } from '~/utils/constants';
|
|
10
|
+
import { useToast } from '~/components/ui/use-toast';
|
|
11
|
+
import { useLocalModelHealth } from '~/lib/hooks/useLocalModelHealth';
|
|
12
|
+
import ErrorBoundary from './ErrorBoundary';
|
|
13
|
+
import { ModelCardSkeleton } from './LoadingSkeleton';
|
|
14
|
+
import SetupGuide from './SetupGuide';
|
|
15
|
+
import StatusDashboard from './StatusDashboard';
|
|
16
|
+
import ProviderCard from './ProviderCard';
|
|
17
|
+
import ModelCard from './ModelCard';
|
|
18
|
+
import { OLLAMA_API_URL } from './types';
|
|
19
|
+
import type { OllamaModel, LMStudioModel } from './types';
|
|
20
|
+
import { Cpu, Server, BookOpen, Activity, PackageOpen, Monitor, Loader2, RotateCw, ExternalLink } from 'lucide-react';
|
|
21
|
+
|
|
22
|
+
// Type definitions
|
|
23
|
+
type ViewMode = 'dashboard' | 'guide' | 'status';
|
|
24
|
+
|
|
25
|
+
export default function LocalProvidersTab() {
|
|
26
|
+
const { providers, updateProviderSettings } = useSettings();
|
|
27
|
+
const [viewMode, setViewMode] = useState<ViewMode>('dashboard');
|
|
28
|
+
const [editingProvider, setEditingProvider] = useState<string | null>(null);
|
|
29
|
+
const [ollamaModels, setOllamaModels] = useState<OllamaModel[]>([]);
|
|
30
|
+
const [lmStudioModels, setLMStudioModels] = useState<LMStudioModel[]>([]);
|
|
31
|
+
const [isLoadingModels, setIsLoadingModels] = useState(false);
|
|
32
|
+
const [isLoadingLMStudioModels, setIsLoadingLMStudioModels] = useState(false);
|
|
33
|
+
const { toast } = useToast();
|
|
34
|
+
const { startMonitoring, stopMonitoring } = useLocalModelHealth();
|
|
35
|
+
|
|
36
|
+
// Memoized filtered providers to prevent unnecessary re-renders
|
|
37
|
+
const filteredProviders = useMemo(() => {
|
|
38
|
+
return Object.entries(providers || {})
|
|
39
|
+
.filter(([key]) => [...LOCAL_PROVIDERS, 'OpenAILike'].includes(key))
|
|
40
|
+
.map(([key, value]) => {
|
|
41
|
+
const provider = value as IProviderConfig;
|
|
42
|
+
const envKey = providerBaseUrlEnvKeys[key]?.baseUrlKey;
|
|
43
|
+
const envUrl = envKey ? (import.meta.env[envKey] as string | undefined) : undefined;
|
|
44
|
+
|
|
45
|
+
// Set default base URLs for local providers
|
|
46
|
+
let defaultBaseUrl = provider.settings.baseUrl || envUrl;
|
|
47
|
+
|
|
48
|
+
if (!defaultBaseUrl) {
|
|
49
|
+
if (key === 'Ollama') {
|
|
50
|
+
defaultBaseUrl = 'http://127.0.0.1:11434';
|
|
51
|
+
} else if (key === 'LMStudio') {
|
|
52
|
+
defaultBaseUrl = 'http://127.0.0.1:1234';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
name: key,
|
|
58
|
+
settings: {
|
|
59
|
+
...provider.settings,
|
|
60
|
+
baseUrl: defaultBaseUrl,
|
|
61
|
+
},
|
|
62
|
+
staticModels: provider.staticModels || [],
|
|
63
|
+
getDynamicModels: provider.getDynamicModels,
|
|
64
|
+
getApiKeyLink: provider.getApiKeyLink,
|
|
65
|
+
labelForGetApiKey: provider.labelForGetApiKey,
|
|
66
|
+
icon: provider.icon,
|
|
67
|
+
} as IProviderConfig;
|
|
68
|
+
})
|
|
69
|
+
.sort((a, b) => {
|
|
70
|
+
// Custom sort: Ollama first, then LMStudio, then OpenAILike
|
|
71
|
+
const order = { Ollama: 0, LMStudio: 1, OpenAILike: 2 };
|
|
72
|
+
return (order[a.name as keyof typeof order] || 3) - (order[b.name as keyof typeof order] || 3);
|
|
73
|
+
});
|
|
74
|
+
}, [providers]);
|
|
75
|
+
|
|
76
|
+
const categoryEnabled = useMemo(() => {
|
|
77
|
+
return filteredProviders.length > 0 && filteredProviders.every((p) => p.settings.enabled);
|
|
78
|
+
}, [filteredProviders]);
|
|
79
|
+
|
|
80
|
+
// Start/stop health monitoring for enabled providers
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
filteredProviders.forEach((provider) => {
|
|
83
|
+
const baseUrl = provider.settings.baseUrl;
|
|
84
|
+
|
|
85
|
+
if (provider.settings.enabled && baseUrl) {
|
|
86
|
+
console.log(`[LocalProvidersTab] Starting monitoring for ${provider.name} at ${baseUrl}`);
|
|
87
|
+
startMonitoring(provider.name as 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl);
|
|
88
|
+
} else if (!provider.settings.enabled && baseUrl) {
|
|
89
|
+
console.log(`[LocalProvidersTab] Stopping monitoring for ${provider.name} at ${baseUrl}`);
|
|
90
|
+
stopMonitoring(provider.name as 'Ollama' | 'LMStudio' | 'OpenAILike', baseUrl);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}, [filteredProviders, startMonitoring, stopMonitoring]);
|
|
94
|
+
|
|
95
|
+
// Fetch Ollama models when enabled
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const ollamaProvider = filteredProviders.find((p) => p.name === 'Ollama');
|
|
98
|
+
|
|
99
|
+
if (ollamaProvider?.settings.enabled) {
|
|
100
|
+
fetchOllamaModels();
|
|
101
|
+
}
|
|
102
|
+
}, [filteredProviders]);
|
|
103
|
+
|
|
104
|
+
// Fetch LM Studio models when enabled
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const lmStudioProvider = filteredProviders.find((p) => p.name === 'LMStudio');
|
|
107
|
+
|
|
108
|
+
if (lmStudioProvider?.settings.enabled && lmStudioProvider.settings.baseUrl) {
|
|
109
|
+
fetchLMStudioModels(lmStudioProvider.settings.baseUrl);
|
|
110
|
+
}
|
|
111
|
+
}, [filteredProviders]);
|
|
112
|
+
|
|
113
|
+
const fetchOllamaModels = async () => {
|
|
114
|
+
try {
|
|
115
|
+
setIsLoadingModels(true);
|
|
116
|
+
|
|
117
|
+
const response = await fetch(`${OLLAMA_API_URL}/api/tags`);
|
|
118
|
+
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
throw new Error('Failed to fetch models');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const data = (await response.json()) as { models: OllamaModel[] };
|
|
124
|
+
setOllamaModels(
|
|
125
|
+
data.models.map((model) => ({
|
|
126
|
+
...model,
|
|
127
|
+
status: 'idle' as const,
|
|
128
|
+
})),
|
|
129
|
+
);
|
|
130
|
+
} catch {
|
|
131
|
+
console.error('Error fetching Ollama models');
|
|
132
|
+
} finally {
|
|
133
|
+
setIsLoadingModels(false);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const fetchLMStudioModels = async (baseUrl: string) => {
|
|
138
|
+
try {
|
|
139
|
+
setIsLoadingLMStudioModels(true);
|
|
140
|
+
|
|
141
|
+
const response = await fetch(`${baseUrl}/v1/models`);
|
|
142
|
+
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
throw new Error('Failed to fetch LM Studio models');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const data = (await response.json()) as { data: LMStudioModel[] };
|
|
148
|
+
setLMStudioModels(data.data || []);
|
|
149
|
+
} catch {
|
|
150
|
+
console.error('Error fetching LM Studio models');
|
|
151
|
+
setLMStudioModels([]);
|
|
152
|
+
} finally {
|
|
153
|
+
setIsLoadingLMStudioModels(false);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const handleToggleCategory = useCallback(
|
|
158
|
+
async (enabled: boolean) => {
|
|
159
|
+
filteredProviders.forEach((provider) => {
|
|
160
|
+
updateProviderSettings(provider.name, { ...provider.settings, enabled });
|
|
161
|
+
});
|
|
162
|
+
toast(enabled ? 'All local providers enabled' : 'All local providers disabled');
|
|
163
|
+
},
|
|
164
|
+
[filteredProviders, updateProviderSettings, toast],
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const handleToggleProvider = useCallback(
|
|
168
|
+
(provider: IProviderConfig, enabled: boolean) => {
|
|
169
|
+
updateProviderSettings(provider.name, {
|
|
170
|
+
...provider.settings,
|
|
171
|
+
enabled,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
logStore.logProvider(`Provider ${provider.name} ${enabled ? 'enabled' : 'disabled'}`, {
|
|
175
|
+
provider: provider.name,
|
|
176
|
+
});
|
|
177
|
+
toast(`${provider.name} ${enabled ? 'enabled' : 'disabled'}`);
|
|
178
|
+
},
|
|
179
|
+
[updateProviderSettings, toast],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const handleUpdateBaseUrl = useCallback(
|
|
183
|
+
(provider: IProviderConfig, newBaseUrl: string) => {
|
|
184
|
+
updateProviderSettings(provider.name, {
|
|
185
|
+
...provider.settings,
|
|
186
|
+
baseUrl: newBaseUrl,
|
|
187
|
+
});
|
|
188
|
+
toast(`${provider.name} base URL updated`);
|
|
189
|
+
},
|
|
190
|
+
[updateProviderSettings, toast],
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const handleUpdateOllamaModel = async (modelName: string) => {
|
|
194
|
+
try {
|
|
195
|
+
setOllamaModels((prev) => prev.map((m) => (m.name === modelName ? { ...m, status: 'updating' } : m)));
|
|
196
|
+
|
|
197
|
+
const response = await fetch(`${OLLAMA_API_URL}/api/pull`, {
|
|
198
|
+
method: 'POST',
|
|
199
|
+
headers: { 'Content-Type': 'application/json' },
|
|
200
|
+
body: JSON.stringify({ name: modelName }),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
throw new Error(`Failed to update ${modelName}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Handle streaming response
|
|
208
|
+
const reader = response.body?.getReader();
|
|
209
|
+
|
|
210
|
+
if (!reader) {
|
|
211
|
+
throw new Error('No response reader available');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
while (true) {
|
|
215
|
+
const { done, value } = await reader.read();
|
|
216
|
+
|
|
217
|
+
if (done) {
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const text = new TextDecoder().decode(value);
|
|
222
|
+
const lines = text.split('\n').filter(Boolean);
|
|
223
|
+
|
|
224
|
+
for (const line of lines) {
|
|
225
|
+
try {
|
|
226
|
+
const data = JSON.parse(line);
|
|
227
|
+
|
|
228
|
+
if (data.status && data.completed && data.total) {
|
|
229
|
+
setOllamaModels((current) =>
|
|
230
|
+
current.map((m) =>
|
|
231
|
+
m.name === modelName
|
|
232
|
+
? {
|
|
233
|
+
...m,
|
|
234
|
+
progress: {
|
|
235
|
+
current: data.completed,
|
|
236
|
+
total: data.total,
|
|
237
|
+
status: data.status,
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
: m,
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
} catch {
|
|
245
|
+
// Ignore parsing errors
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
setOllamaModels((prev) =>
|
|
251
|
+
prev.map((m) => (m.name === modelName ? { ...m, status: 'updated', progress: undefined } : m)),
|
|
252
|
+
);
|
|
253
|
+
toast(`Successfully updated ${modelName}`);
|
|
254
|
+
} catch {
|
|
255
|
+
setOllamaModels((prev) =>
|
|
256
|
+
prev.map((m) => (m.name === modelName ? { ...m, status: 'error', progress: undefined } : m)),
|
|
257
|
+
);
|
|
258
|
+
toast(`Failed to update ${modelName}`, { type: 'error' });
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const handleDeleteOllamaModel = async (modelName: string) => {
|
|
263
|
+
if (!window.confirm(`Are you sure you want to delete ${modelName}?`)) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const response = await fetch(`${OLLAMA_API_URL}/api/delete`, {
|
|
269
|
+
method: 'DELETE',
|
|
270
|
+
headers: { 'Content-Type': 'application/json' },
|
|
271
|
+
body: JSON.stringify({ name: modelName }),
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (!response.ok) {
|
|
275
|
+
throw new Error(`Failed to delete ${modelName}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
setOllamaModels((current) => current.filter((m) => m.name !== modelName));
|
|
279
|
+
toast(`Deleted ${modelName}`);
|
|
280
|
+
} catch {
|
|
281
|
+
toast(`Failed to delete ${modelName}`, { type: 'error' });
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// Render different views based on viewMode
|
|
286
|
+
if (viewMode === 'guide') {
|
|
287
|
+
return (
|
|
288
|
+
<ErrorBoundary>
|
|
289
|
+
<SetupGuide onBack={() => setViewMode('dashboard')} />
|
|
290
|
+
</ErrorBoundary>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (viewMode === 'status') {
|
|
295
|
+
return (
|
|
296
|
+
<ErrorBoundary>
|
|
297
|
+
<StatusDashboard onBack={() => setViewMode('dashboard')} />
|
|
298
|
+
</ErrorBoundary>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return (
|
|
303
|
+
<ErrorBoundary>
|
|
304
|
+
<div className="space-y-6">
|
|
305
|
+
{/* Header */}
|
|
306
|
+
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8">
|
|
307
|
+
<div className="flex items-center gap-4">
|
|
308
|
+
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-500/20 to-blue-500/20 flex items-center justify-center ring-1 ring-purple-500/30">
|
|
309
|
+
<Cpu className="w-6 h-6 text-purple-500" />
|
|
310
|
+
</div>
|
|
311
|
+
<div>
|
|
312
|
+
<h2 className="text-2xl font-semibold text-bolt-elements-textPrimary">Local AI Providers</h2>
|
|
313
|
+
<p className="text-sm text-bolt-elements-textSecondary">Configure and manage your local AI models</p>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-3">
|
|
317
|
+
<div className="flex items-center gap-2">
|
|
318
|
+
<span className="text-sm font-medium text-bolt-elements-textSecondary">Enable All</span>
|
|
319
|
+
<Switch
|
|
320
|
+
checked={categoryEnabled}
|
|
321
|
+
onCheckedChange={handleToggleCategory}
|
|
322
|
+
aria-label="Toggle all local providers"
|
|
323
|
+
/>
|
|
324
|
+
</div>
|
|
325
|
+
<div className="flex items-center gap-2">
|
|
326
|
+
<Button
|
|
327
|
+
variant="outline"
|
|
328
|
+
size="sm"
|
|
329
|
+
onClick={() => setViewMode('guide')}
|
|
330
|
+
className="bg-bolt-elements-background-depth-2 hover:bg-bolt-elements-background-depth-3 border-bolt-elements-borderColor hover:border-purple-500/30 transition-all duration-200 gap-2"
|
|
331
|
+
>
|
|
332
|
+
<BookOpen className="w-4 h-4" />
|
|
333
|
+
Setup Guide
|
|
334
|
+
</Button>
|
|
335
|
+
<Button
|
|
336
|
+
variant="outline"
|
|
337
|
+
size="sm"
|
|
338
|
+
onClick={() => setViewMode('status')}
|
|
339
|
+
className="bg-bolt-elements-background-depth-2 hover:bg-bolt-elements-background-depth-3 border-bolt-elements-borderColor hover:border-purple-500/30 transition-all duration-200 gap-2"
|
|
340
|
+
>
|
|
341
|
+
<Activity className="w-4 h-4" />
|
|
342
|
+
Status
|
|
343
|
+
</Button>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
{/* Provider Cards */}
|
|
349
|
+
<div className="space-y-6">
|
|
350
|
+
{filteredProviders.map((provider) => (
|
|
351
|
+
<div key={provider.name} className="space-y-4">
|
|
352
|
+
<ProviderCard
|
|
353
|
+
provider={provider}
|
|
354
|
+
onToggle={(enabled) => handleToggleProvider(provider, enabled)}
|
|
355
|
+
onUpdateBaseUrl={(url) => handleUpdateBaseUrl(provider, url)}
|
|
356
|
+
isEditing={editingProvider === provider.name}
|
|
357
|
+
onStartEditing={() => setEditingProvider(provider.name)}
|
|
358
|
+
onStopEditing={() => setEditingProvider(null)}
|
|
359
|
+
/>
|
|
360
|
+
|
|
361
|
+
{/* Ollama Models Section */}
|
|
362
|
+
{provider.name === 'Ollama' && provider.settings.enabled && (
|
|
363
|
+
<Card className="mt-4 bg-bolt-elements-background-depth-2">
|
|
364
|
+
<CardHeader className="pb-3">
|
|
365
|
+
<div className="flex items-center justify-between">
|
|
366
|
+
<div className="flex items-center gap-2">
|
|
367
|
+
<PackageOpen className="w-5 h-5 text-purple-500" />
|
|
368
|
+
<h3 className="text-lg font-semibold text-bolt-elements-textPrimary">Installed Models</h3>
|
|
369
|
+
</div>
|
|
370
|
+
<Button
|
|
371
|
+
variant="outline"
|
|
372
|
+
size="sm"
|
|
373
|
+
onClick={fetchOllamaModels}
|
|
374
|
+
disabled={isLoadingModels}
|
|
375
|
+
className="bg-transparent hover:bg-bolt-elements-background-depth-2"
|
|
376
|
+
>
|
|
377
|
+
{isLoadingModels ? (
|
|
378
|
+
<Loader2 className="w-4 h-4 animate-spin mr-2" />
|
|
379
|
+
) : (
|
|
380
|
+
<RotateCw className="w-4 h-4 mr-2" />
|
|
381
|
+
)}
|
|
382
|
+
Refresh
|
|
383
|
+
</Button>
|
|
384
|
+
</div>
|
|
385
|
+
</CardHeader>
|
|
386
|
+
<CardContent className="space-y-4">
|
|
387
|
+
{isLoadingModels ? (
|
|
388
|
+
<div className="space-y-4">
|
|
389
|
+
{Array.from({ length: 3 }).map((_, i) => (
|
|
390
|
+
<ModelCardSkeleton key={i} />
|
|
391
|
+
))}
|
|
392
|
+
</div>
|
|
393
|
+
) : ollamaModels.length === 0 ? (
|
|
394
|
+
<div className="text-center py-8">
|
|
395
|
+
<PackageOpen className="w-16 h-16 mx-auto text-bolt-elements-textTertiary mb-4" />
|
|
396
|
+
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-2">No Models Installed</h3>
|
|
397
|
+
<p className="text-sm text-bolt-elements-textSecondary mb-4">
|
|
398
|
+
Visit{' '}
|
|
399
|
+
<a
|
|
400
|
+
href="https://ollama.com/library"
|
|
401
|
+
target="_blank"
|
|
402
|
+
rel="noopener noreferrer"
|
|
403
|
+
className="text-purple-500 hover:underline inline-flex items-center gap-1"
|
|
404
|
+
>
|
|
405
|
+
ollama.com/library
|
|
406
|
+
<ExternalLink className="w-3 h-3" />
|
|
407
|
+
</a>{' '}
|
|
408
|
+
to browse available models
|
|
409
|
+
</p>
|
|
410
|
+
<Button
|
|
411
|
+
variant="outline"
|
|
412
|
+
size="sm"
|
|
413
|
+
className="bg-gradient-to-r from-purple-500/8 to-purple-600/8 hover:from-purple-500/15 hover:to-purple-600/15 border-purple-500/25 hover:border-purple-500/40 transition-all duration-300 gap-2 group shadow-sm hover:shadow-md font-medium"
|
|
414
|
+
_asChild
|
|
415
|
+
>
|
|
416
|
+
<a
|
|
417
|
+
href="https://ollama.com/library"
|
|
418
|
+
target="_blank"
|
|
419
|
+
rel="noopener noreferrer"
|
|
420
|
+
className="flex items-center justify-center gap-2"
|
|
421
|
+
>
|
|
422
|
+
<ExternalLink className="w-4 h-4 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all duration-300 flex-shrink-0" />
|
|
423
|
+
<span className="flex-1 text-center font-medium">Browse Models</span>
|
|
424
|
+
</a>
|
|
425
|
+
</Button>
|
|
426
|
+
</div>
|
|
427
|
+
) : (
|
|
428
|
+
<div className="grid gap-4">
|
|
429
|
+
{ollamaModels.map((model) => (
|
|
430
|
+
<ModelCard
|
|
431
|
+
key={model.name}
|
|
432
|
+
model={model}
|
|
433
|
+
onUpdate={() => handleUpdateOllamaModel(model.name)}
|
|
434
|
+
onDelete={() => handleDeleteOllamaModel(model.name)}
|
|
435
|
+
/>
|
|
436
|
+
))}
|
|
437
|
+
</div>
|
|
438
|
+
)}
|
|
439
|
+
</CardContent>
|
|
440
|
+
</Card>
|
|
441
|
+
)}
|
|
442
|
+
|
|
443
|
+
{/* LM Studio Models Section */}
|
|
444
|
+
{provider.name === 'LMStudio' && provider.settings.enabled && (
|
|
445
|
+
<Card className="mt-4 bg-bolt-elements-background-depth-2">
|
|
446
|
+
<CardHeader className="pb-3">
|
|
447
|
+
<div className="flex items-center justify-between">
|
|
448
|
+
<div className="flex items-center gap-2">
|
|
449
|
+
<Monitor className="w-5 h-5 text-blue-500" />
|
|
450
|
+
<h3 className="text-lg font-semibold text-bolt-elements-textPrimary">Available Models</h3>
|
|
451
|
+
</div>
|
|
452
|
+
<Button
|
|
453
|
+
variant="outline"
|
|
454
|
+
size="sm"
|
|
455
|
+
onClick={() => fetchLMStudioModels(provider.settings.baseUrl!)}
|
|
456
|
+
disabled={isLoadingLMStudioModels}
|
|
457
|
+
className="bg-transparent hover:bg-bolt-elements-background-depth-2"
|
|
458
|
+
>
|
|
459
|
+
{isLoadingLMStudioModels ? (
|
|
460
|
+
<Loader2 className="w-4 h-4 animate-spin mr-2" />
|
|
461
|
+
) : (
|
|
462
|
+
<RotateCw className="w-4 h-4 mr-2" />
|
|
463
|
+
)}
|
|
464
|
+
Refresh
|
|
465
|
+
</Button>
|
|
466
|
+
</div>
|
|
467
|
+
</CardHeader>
|
|
468
|
+
<CardContent className="space-y-4">
|
|
469
|
+
{isLoadingLMStudioModels ? (
|
|
470
|
+
<div className="space-y-4">
|
|
471
|
+
{Array.from({ length: 3 }).map((_, i) => (
|
|
472
|
+
<ModelCardSkeleton key={i} />
|
|
473
|
+
))}
|
|
474
|
+
</div>
|
|
475
|
+
) : lmStudioModels.length === 0 ? (
|
|
476
|
+
<div className="text-center py-8">
|
|
477
|
+
<Monitor className="w-16 h-16 mx-auto text-bolt-elements-textTertiary mb-4" />
|
|
478
|
+
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-2">No Models Available</h3>
|
|
479
|
+
<p className="text-sm text-bolt-elements-textSecondary mb-4">
|
|
480
|
+
Make sure LM Studio is running with the local server started and CORS enabled.
|
|
481
|
+
</p>
|
|
482
|
+
<Button
|
|
483
|
+
variant="outline"
|
|
484
|
+
size="sm"
|
|
485
|
+
className="bg-gradient-to-r from-blue-500/8 to-blue-600/8 hover:from-blue-500/15 hover:to-blue-600/15 border-blue-500/25 hover:border-blue-500/40 transition-all duration-300 gap-2 group shadow-sm hover:shadow-md font-medium"
|
|
486
|
+
_asChild
|
|
487
|
+
>
|
|
488
|
+
<a
|
|
489
|
+
href="https://lmstudio.ai/"
|
|
490
|
+
target="_blank"
|
|
491
|
+
rel="noopener noreferrer"
|
|
492
|
+
className="flex items-center justify-center gap-2"
|
|
493
|
+
>
|
|
494
|
+
<ExternalLink className="w-4 h-4 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-all duration-300 flex-shrink-0" />
|
|
495
|
+
<span className="flex-1 text-center font-medium">Get LM Studio</span>
|
|
496
|
+
</a>
|
|
497
|
+
</Button>
|
|
498
|
+
</div>
|
|
499
|
+
) : (
|
|
500
|
+
<div className="grid gap-4">
|
|
501
|
+
{lmStudioModels.map((model) => (
|
|
502
|
+
<Card key={model.id} className="bg-bolt-elements-background-depth-3">
|
|
503
|
+
<CardContent className="p-4">
|
|
504
|
+
<div className="space-y-2">
|
|
505
|
+
<div className="flex items-center gap-2">
|
|
506
|
+
<h4 className="text-sm font-medium text-bolt-elements-textPrimary font-mono">
|
|
507
|
+
{model.id}
|
|
508
|
+
</h4>
|
|
509
|
+
<span className="px-2 py-0.5 rounded-full text-xs font-medium bg-blue-500/10 text-blue-500">
|
|
510
|
+
Available
|
|
511
|
+
</span>
|
|
512
|
+
</div>
|
|
513
|
+
<div className="flex items-center gap-4 text-xs text-bolt-elements-textSecondary">
|
|
514
|
+
<div className="flex items-center gap-1">
|
|
515
|
+
<Server className="w-3 h-3" />
|
|
516
|
+
<span>{model.object}</span>
|
|
517
|
+
</div>
|
|
518
|
+
<div className="flex items-center gap-1">
|
|
519
|
+
<Activity className="w-3 h-3" />
|
|
520
|
+
<span>Owned by: {model.owned_by}</span>
|
|
521
|
+
</div>
|
|
522
|
+
{model.created && (
|
|
523
|
+
<div className="flex items-center gap-1">
|
|
524
|
+
<Activity className="w-3 h-3" />
|
|
525
|
+
<span>Created: {new Date(model.created * 1000).toLocaleDateString()}</span>
|
|
526
|
+
</div>
|
|
527
|
+
)}
|
|
528
|
+
</div>
|
|
529
|
+
</div>
|
|
530
|
+
</CardContent>
|
|
531
|
+
</Card>
|
|
532
|
+
))}
|
|
533
|
+
</div>
|
|
534
|
+
)}
|
|
535
|
+
</CardContent>
|
|
536
|
+
</Card>
|
|
537
|
+
)}
|
|
538
|
+
</div>
|
|
539
|
+
))}
|
|
540
|
+
</div>
|
|
541
|
+
|
|
542
|
+
{filteredProviders.length === 0 && (
|
|
543
|
+
<Card className="bg-bolt-elements-background-depth-2">
|
|
544
|
+
<CardContent className="p-8 text-center">
|
|
545
|
+
<Server className="w-16 h-16 mx-auto text-bolt-elements-textTertiary mb-4" />
|
|
546
|
+
<h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-2">No Local Providers Available</h3>
|
|
547
|
+
<p className="text-sm text-bolt-elements-textSecondary">
|
|
548
|
+
Local providers will appear here when they're configured in the system.
|
|
549
|
+
</p>
|
|
550
|
+
</CardContent>
|
|
551
|
+
</Card>
|
|
552
|
+
)}
|
|
553
|
+
</div>
|
|
554
|
+
</ErrorBoundary>
|
|
555
|
+
);
|
|
556
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Card, CardContent } from '~/components/ui/Card';
|
|
3
|
+
import { Progress } from '~/components/ui/Progress';
|
|
4
|
+
import { RotateCw, Trash2, Code, Database, Package, Loader2 } from 'lucide-react';
|
|
5
|
+
import { classNames } from '~/utils/classNames';
|
|
6
|
+
import type { OllamaModel } from './types';
|
|
7
|
+
|
|
8
|
+
// Model Card Component
|
|
9
|
+
interface ModelCardProps {
|
|
10
|
+
model: OllamaModel;
|
|
11
|
+
onUpdate: () => void;
|
|
12
|
+
onDelete: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ModelCard({ model, onUpdate, onDelete }: ModelCardProps) {
|
|
16
|
+
return (
|
|
17
|
+
<Card className="bg-bolt-elements-background-depth-3 hover:bg-bolt-elements-background-depth-4 transition-all duration-200 shadow-sm hover:shadow-md border border-bolt-elements-borderColor hover:border-purple-500/20">
|
|
18
|
+
<CardContent className="p-5">
|
|
19
|
+
<div className="flex items-center justify-between">
|
|
20
|
+
<div className="space-y-2">
|
|
21
|
+
<div className="flex items-center gap-2">
|
|
22
|
+
<h4 className="text-sm font-medium text-bolt-elements-textPrimary font-mono">{model.name}</h4>
|
|
23
|
+
{model.status && model.status !== 'idle' && (
|
|
24
|
+
<span
|
|
25
|
+
className={classNames('px-2 py-0.5 rounded-full text-xs font-medium', {
|
|
26
|
+
'bg-yellow-500/10 text-yellow-500': model.status === 'updating',
|
|
27
|
+
'bg-green-500/10 text-green-500': model.status === 'updated',
|
|
28
|
+
'bg-red-500/10 text-red-500': model.status === 'error',
|
|
29
|
+
})}
|
|
30
|
+
>
|
|
31
|
+
{model.status === 'updating' && 'Updating'}
|
|
32
|
+
{model.status === 'updated' && 'Updated'}
|
|
33
|
+
{model.status === 'error' && 'Error'}
|
|
34
|
+
</span>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
<div className="flex items-center gap-4 text-xs text-bolt-elements-textSecondary">
|
|
38
|
+
<div className="flex items-center gap-1">
|
|
39
|
+
<Code className="w-3 h-3" />
|
|
40
|
+
<span>{model.digest.substring(0, 8)}</span>
|
|
41
|
+
</div>
|
|
42
|
+
{model.details && (
|
|
43
|
+
<>
|
|
44
|
+
<div className="flex items-center gap-1">
|
|
45
|
+
<Database className="w-3 h-3" />
|
|
46
|
+
<span>{model.details.parameter_size}</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div className="flex items-center gap-1">
|
|
49
|
+
<Package className="w-3 h-3" />
|
|
50
|
+
<span>{model.details.quantization_level}</span>
|
|
51
|
+
</div>
|
|
52
|
+
</>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div className="flex items-center gap-2">
|
|
57
|
+
<button
|
|
58
|
+
onClick={onUpdate}
|
|
59
|
+
disabled={model.status === 'updating'}
|
|
60
|
+
className={classNames(
|
|
61
|
+
'flex items-center gap-2 px-3 py-2 text-xs rounded-lg transition-all duration-200',
|
|
62
|
+
'bg-purple-500/10 text-purple-500 hover:bg-purple-500/20 hover:shadow-sm',
|
|
63
|
+
'disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-purple-500/10',
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{model.status === 'updating' ? (
|
|
67
|
+
<>
|
|
68
|
+
<Loader2 className="w-3 h-3 animate-spin" />
|
|
69
|
+
Updating
|
|
70
|
+
</>
|
|
71
|
+
) : (
|
|
72
|
+
<>
|
|
73
|
+
<RotateCw className="w-3 h-3" />
|
|
74
|
+
Update
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
</button>
|
|
78
|
+
<button
|
|
79
|
+
onClick={onDelete}
|
|
80
|
+
disabled={model.status === 'updating'}
|
|
81
|
+
className={classNames(
|
|
82
|
+
'flex items-center gap-2 px-3 py-2 text-xs rounded-lg transition-all duration-200',
|
|
83
|
+
'bg-red-500/10 text-red-500 hover:bg-red-500/20 hover:shadow-sm',
|
|
84
|
+
'disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-red-500/10',
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
<Trash2 className="w-3 h-3" />
|
|
88
|
+
Delete
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
{model.progress && (
|
|
93
|
+
<div className="mt-3 space-y-2">
|
|
94
|
+
<div className="flex justify-between text-xs text-bolt-elements-textSecondary">
|
|
95
|
+
<span>{model.progress.status}</span>
|
|
96
|
+
<span>{Math.round((model.progress.current / model.progress.total) * 100)}%</span>
|
|
97
|
+
</div>
|
|
98
|
+
<Progress value={Math.round((model.progress.current / model.progress.total) * 100)} className="h-1" />
|
|
99
|
+
</div>
|
|
100
|
+
)}
|
|
101
|
+
</CardContent>
|
|
102
|
+
</Card>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default ModelCard;
|