cct-cli 0.7.9.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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT — Backup Provider & Resilience System: Core Types & Enums
|
|
3
|
+
Author: Kazi Zillani
|
|
4
|
+
|
|
5
|
+
Defines failure categories, security levels, cost policies, task requirements,
|
|
6
|
+
and health state contracts for CAT's unified AI Orchestrator.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Dict, List, Optional, Set
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FailureType(str, Enum):
|
|
15
|
+
"""Normalized taxonomy of provider and model failure modes."""
|
|
16
|
+
TRANSIENT = "transient" # Network blip, reset, 502/503 -> retryable
|
|
17
|
+
TIMEOUT = "timeout" # Total or idle read timeout -> retryable/failover
|
|
18
|
+
RATE_LIMIT = "rate_limit" # 429, request cap exceeded -> immediate failover
|
|
19
|
+
QUOTA_EXHAUSTED = "quota_exhausted" # Insufficient funds/credits -> immediate failover
|
|
20
|
+
AUTH_FAILURE = "auth_failure" # 401, 403, invalid key -> immediate failover
|
|
21
|
+
MODEL_UNAVAILABLE = "model_unavailable" # 404, not loaded, deprecated -> model fallback
|
|
22
|
+
CONTEXT_TOO_LARGE = "context_too_large" # Token overflow -> high-ctx model fallback
|
|
23
|
+
TOOL_CALLING_FAILURE = "tool_calling_failure" # Invalid function-calling or parsing collapse
|
|
24
|
+
CAPABILITY_MISMATCH = "capability_mismatch" # Missing vision/tools/coding -> skip target
|
|
25
|
+
PROVIDER_OUTAGE = "provider_outage" # Host completely unreachable / 500 storm
|
|
26
|
+
SAFETY_RESTRICTION = "safety_restriction" # Refusal or safety block -> fallback
|
|
27
|
+
SERVER_ERROR = "server_error" # Internal 500 from provider
|
|
28
|
+
UNKNOWN = "unknown"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SecurityLevel(str, Enum):
|
|
32
|
+
"""Configurable security modes for automatic failover."""
|
|
33
|
+
STRICT = "strict" # Only explicitly approved backups, no auto-switch without permission
|
|
34
|
+
BALANCED = "balanced" # Configured backups may be used automatically; strict privacy rules
|
|
35
|
+
AUTOMATIC = "automatic" # CAT may select any enabled compatible provider adhering to privacy
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CostPolicy(str, Enum):
|
|
39
|
+
"""Cost-aware fallback constraints."""
|
|
40
|
+
ANY = "any" # Any available provider
|
|
41
|
+
FREE_ONLY = "free_only" # Strictly free providers (e.g. free tiers, Ollama local)
|
|
42
|
+
LOCAL_ONLY = "local_only" # Strictly zero-cost local hardware inference
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class TaskRequirements:
|
|
47
|
+
"""Explicit capability requirements for a task turn."""
|
|
48
|
+
vision: bool = False
|
|
49
|
+
tools: bool = False
|
|
50
|
+
coding: bool = False
|
|
51
|
+
reasoning: bool = False
|
|
52
|
+
long_context: bool = False
|
|
53
|
+
min_context: int = 0
|
|
54
|
+
structured_output: bool = False
|
|
55
|
+
audio: bool = False
|
|
56
|
+
preferred_provider: Optional[str] = None
|
|
57
|
+
preferred_model: Optional[str] = None
|
|
58
|
+
|
|
59
|
+
def matches(self, caps: Any) -> bool:
|
|
60
|
+
"""Check whether a ModelCapabilities object or dict satisfies this task."""
|
|
61
|
+
if isinstance(caps, dict):
|
|
62
|
+
if self.vision and not caps.get("vision", False):
|
|
63
|
+
return False
|
|
64
|
+
if self.tools and not caps.get("tools", True):
|
|
65
|
+
return False
|
|
66
|
+
if self.min_context and caps.get("context_window", 32000) < self.min_context:
|
|
67
|
+
return False
|
|
68
|
+
if self.long_context and caps.get("context_window", 32000) < 60000:
|
|
69
|
+
return False
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
# Assume ModelCapabilities dataclass
|
|
73
|
+
if self.vision and not getattr(caps, "vision", False):
|
|
74
|
+
return False
|
|
75
|
+
if self.tools and not getattr(caps, "tools", True):
|
|
76
|
+
return False
|
|
77
|
+
ctx = getattr(caps, "context_window", 32000)
|
|
78
|
+
if self.min_context and ctx < self.min_context:
|
|
79
|
+
return False
|
|
80
|
+
if self.long_context and ctx < 60000:
|
|
81
|
+
return False
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class ProviderHealthState:
|
|
87
|
+
"""Real-time observed health metrics for a provider/model endpoint."""
|
|
88
|
+
provider: str
|
|
89
|
+
model: str
|
|
90
|
+
status: str = "disconnected" # healthy, slow, rate_limited, offline, local, disconnected
|
|
91
|
+
latency_ms: Optional[float] = None
|
|
92
|
+
consecutive_failures: int = 0
|
|
93
|
+
circuit_open: bool = False
|
|
94
|
+
cooldown_until: float = 0.0
|
|
95
|
+
last_error: str = ""
|
|
96
|
+
last_success: float = 0.0
|
|
97
|
+
last_checked: float = 0.0
|
|
98
|
+
success_count: int = 0
|
|
99
|
+
failure_count: int = 0
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def is_healthy(self) -> bool:
|
|
103
|
+
import time
|
|
104
|
+
if self.circuit_open and time.time() < self.cooldown_until:
|
|
105
|
+
return False
|
|
106
|
+
return self.status in ("healthy", "local", "disconnected", "slow")
|
|
107
|
+
|
|
108
|
+
def to_dict(self) -> dict:
|
|
109
|
+
return {
|
|
110
|
+
"provider": self.provider,
|
|
111
|
+
"model": self.model,
|
|
112
|
+
"status": self.status,
|
|
113
|
+
"latency_ms": round(self.latency_ms, 1) if self.latency_ms is not None else None,
|
|
114
|
+
"consecutive_failures": self.consecutive_failures,
|
|
115
|
+
"circuit_open": self.circuit_open,
|
|
116
|
+
"cooldown_until": self.cooldown_until,
|
|
117
|
+
"last_error": self.last_error,
|
|
118
|
+
"last_success": self.last_success,
|
|
119
|
+
"success_count": self.success_count,
|
|
120
|
+
"failure_count": self.failure_count,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class CandidateTarget:
|
|
126
|
+
"""A scored, capability-checked candidate provider/model for failover."""
|
|
127
|
+
provider: str
|
|
128
|
+
model: str
|
|
129
|
+
config: dict
|
|
130
|
+
entry: Optional[dict] = None
|
|
131
|
+
is_backup: bool = False
|
|
132
|
+
is_local: bool = False
|
|
133
|
+
score: float = 0.0
|
|
134
|
+
tier: int = 1 # 1 = same-provider fallback model, 2 = cross-provider backup
|
calc_terminal/sandbox.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CCT Sandbox — runs /codepad Python in a subprocess with real OS resource
|
|
3
|
+
limits, not just a timeout. On POSIX (Linux/macOS) this sets hard
|
|
4
|
+
RLIMIT_CPU, RLIMIT_AS (address space) and RLIMIT_NPROC via a
|
|
5
|
+
`preexec_fn`, so a runaway loop or a fork-bomb attempt genuinely gets
|
|
6
|
+
killed by the kernel rather than merely by an outer wall-clock timeout.
|
|
7
|
+
|
|
8
|
+
On Windows there's no equivalent rlimit API, so this honestly falls
|
|
9
|
+
back to timeout-only isolation (same as before) and says so — it does
|
|
10
|
+
not pretend to sandbox on a platform where it structurally can't.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
|
|
18
|
+
CPU_SECONDS = 10
|
|
19
|
+
MEMORY_BYTES = 256 * 1024 * 1024 # 256MB
|
|
20
|
+
MAX_PROCESSES = 16
|
|
21
|
+
WALL_TIMEOUT = 15
|
|
22
|
+
|
|
23
|
+
_IS_POSIX = os.name == "posix"
|
|
24
|
+
|
|
25
|
+
if _IS_POSIX:
|
|
26
|
+
import resource
|
|
27
|
+
|
|
28
|
+
def _limit_resources():
|
|
29
|
+
try:
|
|
30
|
+
resource.setrlimit(resource.RLIMIT_CPU, (CPU_SECONDS, CPU_SECONDS))
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
try:
|
|
34
|
+
resource.setrlimit(resource.RLIMIT_AS, (MEMORY_BYTES, MEMORY_BYTES))
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
37
|
+
try:
|
|
38
|
+
resource.setrlimit(resource.RLIMIT_NPROC, (MAX_PROCESSES, MAX_PROCESSES))
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
else:
|
|
42
|
+
_limit_resources = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def sandbox_available():
|
|
46
|
+
return _IS_POSIX
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def run_sandboxed(code, timeout=WALL_TIMEOUT):
|
|
50
|
+
"""Runs `code` as a standalone Python script in a subprocess. Returns
|
|
51
|
+
a dict: {stdout, stderr, returncode, timed_out, killed_by_limit,
|
|
52
|
+
sandboxed}. Never raises."""
|
|
53
|
+
tmp_path = None
|
|
54
|
+
try:
|
|
55
|
+
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False, encoding="utf-8") as tmp:
|
|
56
|
+
tmp.write(code)
|
|
57
|
+
tmp_path = tmp.name
|
|
58
|
+
|
|
59
|
+
kwargs = dict(capture_output=True, text=True, timeout=timeout)
|
|
60
|
+
if _IS_POSIX:
|
|
61
|
+
kwargs["preexec_fn"] = _limit_resources
|
|
62
|
+
result = subprocess.run([sys.executable, tmp_path], **kwargs)
|
|
63
|
+
|
|
64
|
+
killed_by_limit = _IS_POSIX and result.returncode < 0
|
|
65
|
+
return {
|
|
66
|
+
"stdout": result.stdout,
|
|
67
|
+
"stderr": result.stderr,
|
|
68
|
+
"returncode": result.returncode,
|
|
69
|
+
"timed_out": False,
|
|
70
|
+
"killed_by_limit": killed_by_limit,
|
|
71
|
+
"sandboxed": _IS_POSIX,
|
|
72
|
+
}
|
|
73
|
+
except subprocess.TimeoutExpired as e:
|
|
74
|
+
return {
|
|
75
|
+
"stdout": e.stdout or "",
|
|
76
|
+
"stderr": (e.stderr or "") + f"\n[sandbox] killed after {timeout}s wall-clock timeout.",
|
|
77
|
+
"returncode": -1,
|
|
78
|
+
"timed_out": True,
|
|
79
|
+
"killed_by_limit": False,
|
|
80
|
+
"sandboxed": _IS_POSIX,
|
|
81
|
+
}
|
|
82
|
+
except Exception as e:
|
|
83
|
+
return {
|
|
84
|
+
"stdout": "", "stderr": f"[sandbox] could not run: {e}",
|
|
85
|
+
"returncode": -1, "timed_out": False, "killed_by_limit": False,
|
|
86
|
+
"sandboxed": _IS_POSIX,
|
|
87
|
+
}
|
|
88
|
+
finally:
|
|
89
|
+
if tmp_path:
|
|
90
|
+
try:
|
|
91
|
+
os.unlink(tmp_path)
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
94
|
+
try:
|
|
95
|
+
from . import terminal_identity
|
|
96
|
+
terminal_identity.set_terminal_title()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|