model-mesh-ai 1.0.0__tar.gz

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.
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.1
2
+ Name: model-mesh-ai
3
+ Version: 1.0.0
4
+ Summary: Model Mesh: Unified AI Gateway SDK with Standardized Requests, Load Balancing, and Rate Limiting
5
+ Author: Model Mesh Team
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Operating System :: OS Independent
8
+ Requires-Python: >=3.8
@@ -0,0 +1,40 @@
1
+ # Model Mesh - Python SDK
2
+
3
+ A unified, high-performance package for interacting with Large Language Models with **Standardized Request/Response**, **Load Balancing**, and **Rate Limiting**.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ cd sdk/python
9
+ pip install -e .
10
+ ```
11
+
12
+ ## Quick Start
13
+
14
+ ```python
15
+ import model_mesh
16
+
17
+ # 1. Initialize Model Mesh
18
+ mesh = model_mesh.Client(
19
+ base_url="http://localhost:8000/v1",
20
+ api_key="sk-modelmesh-client-key",
21
+ rate_limit="20/min",
22
+ load_balancing="round_robin"
23
+ )
24
+
25
+ # 2. Method 1: Standardized Chat (Unified Request & Response)
26
+ response = mesh.chat(
27
+ model="gpt-4o",
28
+ messages=[{"role": "user", "content": "Explain Model Mesh!"}]
29
+ )
30
+ print("Assistant:", response.content)
31
+ print("Tokens Used:", response.usage)
32
+
33
+ # 3. Method 2: Inspect Load Balancing
34
+ route = mesh.balance(model="claude-3-5-sonnet")
35
+ print("Routed to channel:", route["selected_channel"])
36
+
37
+ # 4. Method 3: Check Rate Limiting
38
+ status = mesh.check_rate_limit(client_id="user_123")
39
+ print("Remaining calls:", status["remaining_requests"])
40
+ ```
@@ -0,0 +1,20 @@
1
+ """
2
+ Model Mesh Python SDK
3
+ A unified client and library for Model Mesh - The High-Performance AI Gateway.
4
+ """
5
+
6
+ from .standardizer import Standardizer, ChatResponse
7
+ from .balancer import LoadBalancer
8
+ from .rate_limiter import RateLimiter, RateLimitExceededError
9
+ from .client import ModelMesh, Client
10
+
11
+ __version__ = "1.0.0"
12
+ __all__ = [
13
+ "ModelMesh",
14
+ "Client",
15
+ "LoadBalancer",
16
+ "RateLimiter",
17
+ "Standardizer",
18
+ "ChatResponse",
19
+ "RateLimitExceededError",
20
+ ]
@@ -0,0 +1,189 @@
1
+ """
2
+ Model Mesh Load Balancer & Fault-Tolerance Engine
3
+ Provides round-robin, weighted, priority failover, and circuit-breaker cooling across AI channels.
4
+ """
5
+
6
+ import time
7
+ import random
8
+ from dataclasses import dataclass, field
9
+ from typing import Any, Dict, List, Optional, Tuple, Set
10
+
11
+
12
+ @dataclass
13
+ class Channel:
14
+ name: str
15
+ base_url: str
16
+ api_keys: List[str]
17
+ models: List[str] = field(default_factory=lambda: ["*"])
18
+ weight: int = 1
19
+ priority: int = 1 # 1 = primary, 2 = secondary/fallback, etc.
20
+ key_index: int = 0
21
+ failure_count: int = 0
22
+ success_count: int = 0
23
+ cooldown_until: float = 0.0
24
+ simulated_error: Optional[str] = None # e.g., "429: Too Many Requests" or "500: Internal Server Error"
25
+
26
+ def is_healthy(self) -> bool:
27
+ """Returns True if the channel is not currently cooled down and not manually faulted."""
28
+ return time.time() >= self.cooldown_until and self.simulated_error is None
29
+
30
+ def get_next_key(self) -> str:
31
+ """Round-robin through the keys of this channel."""
32
+ if not self.api_keys:
33
+ return ""
34
+ key = self.api_keys[self.key_index % len(self.api_keys)]
35
+ self.key_index = (self.key_index + 1) % len(self.api_keys)
36
+ return key
37
+
38
+ def trigger_cooldown(self, seconds: float = 30.0, reason: str = "Failure"):
39
+ """Mark channel as cooled down after an upstream failure."""
40
+ self.failure_count += 1
41
+ self.cooldown_until = time.time() + seconds
42
+
43
+ def mark_success(self):
44
+ """Record success and reset cooldown."""
45
+ self.success_count += 1
46
+ self.cooldown_until = 0.0
47
+
48
+ def to_dict(self) -> Dict[str, Any]:
49
+ now = time.time()
50
+ return {
51
+ "name": self.name,
52
+ "base_url": self.base_url,
53
+ "weight": self.weight,
54
+ "priority": self.priority,
55
+ "key_count": len(self.api_keys),
56
+ "healthy": self.is_healthy(),
57
+ "failure_count": self.failure_count,
58
+ "success_count": self.success_count,
59
+ "cooldown_remaining_sec": max(0.0, round(self.cooldown_until - now, 1)),
60
+ "simulated_error": self.simulated_error,
61
+ "supported_models": self.models,
62
+ }
63
+
64
+
65
+ class LoadBalancer:
66
+ """
67
+ Intelligent router with Round-Robin, Weighted balancing, Priority Failover,
68
+ and Circuit-Breaker Cooldown recovery.
69
+ """
70
+
71
+ def __init__(self, algorithm: str = "round_robin"):
72
+ self.algorithm = algorithm.lower()
73
+ self.channels: Dict[str, Channel] = {}
74
+ self._round_robin_index: int = 0
75
+
76
+ def add_channel(
77
+ self,
78
+ name: str,
79
+ base_url: str,
80
+ api_keys: Optional[List[str]] = None,
81
+ models: Optional[List[str]] = None,
82
+ weight: int = 1,
83
+ priority: int = 1
84
+ ) -> Channel:
85
+ """Register a provider channel with custom keys, models, weight, and priority."""
86
+ if api_keys is None:
87
+ api_keys = []
88
+ elif isinstance(api_keys, str):
89
+ api_keys = [api_keys]
90
+
91
+ channel = Channel(
92
+ name=name,
93
+ base_url=base_url,
94
+ api_keys=api_keys,
95
+ models=models or ["*"],
96
+ weight=max(1, weight),
97
+ priority=priority
98
+ )
99
+ self.channels[name] = channel
100
+ return channel
101
+
102
+ def remove_channel(self, name: str) -> bool:
103
+ """Remove a channel from the balancer."""
104
+ if name in self.channels:
105
+ del self.channels[name]
106
+ return True
107
+ return False
108
+
109
+ def simulate_fault(self, name: str, error_message: str = "HTTP 429: Rate limit reached on upstream"):
110
+ """Inject a simulated failure into a channel to test fallback."""
111
+ if name in self.channels:
112
+ self.channels[name].simulated_error = error_message
113
+ self.channels[name].trigger_cooldown(45.0)
114
+
115
+ def clear_fault(self, name: str):
116
+ """Restore channel to healthy state."""
117
+ if name in self.channels:
118
+ self.channels[name].simulated_error = None
119
+ self.channels[name].cooldown_until = 0.0
120
+
121
+ def _get_eligible_channels(
122
+ self,
123
+ model: Optional[str] = None,
124
+ excluded: Optional[Set[str]] = None,
125
+ include_faulted: bool = False
126
+ ) -> List[Channel]:
127
+ """Find channels that support the requested model and are not excluded."""
128
+ excluded = excluded or set()
129
+ eligible = []
130
+ for ch in self.channels.values():
131
+ if ch.name in excluded:
132
+ continue
133
+ if not include_faulted and not ch.is_healthy():
134
+ continue
135
+ if model is None or "*" in ch.models or model in ch.models:
136
+ eligible.append(ch)
137
+ return eligible
138
+
139
+ def select_channel(
140
+ self,
141
+ model: Optional[str] = None,
142
+ excluded: Optional[Set[str]] = None,
143
+ include_faulted: bool = False
144
+ ) -> Tuple[Channel, str]:
145
+ """
146
+ Select best channel and key. If channels are tiered by priority,
147
+ highest priority (lowest number) healthy channels are picked first.
148
+ """
149
+ eligible = self._get_eligible_channels(model, excluded, include_faulted=include_faulted)
150
+
151
+ # Fallback 1: If no healthy channels with priority, try any non-excluded channel
152
+ if not eligible and self.channels:
153
+ non_excluded = [c for c in self.channels.values() if c.name not in (excluded or set())]
154
+ if non_excluded:
155
+ # pick the one with earliest cooldown expiry
156
+ eligible = sorted(non_excluded, key=lambda c: c.cooldown_until)[:1]
157
+
158
+ if not eligible:
159
+ raise RuntimeError(f"No available channels found in Model Mesh for model '{model}'.")
160
+
161
+ # Priority grouping: pick channels in the lowest available priority level
162
+ min_priority = min(c.priority for c in eligible)
163
+ tier = [c for c in eligible if c.priority == min_priority]
164
+
165
+ # 1. Weighted Selection within Tier
166
+ if self.algorithm == "weighted":
167
+ weights = [c.weight for c in tier]
168
+ chosen_channel = random.choices(tier, weights=weights, k=1)[0]
169
+ # 2. Round Robin Selection
170
+ else:
171
+ chosen_channel = tier[self._round_robin_index % len(tier)]
172
+ self._round_robin_index = (self._round_robin_index + 1) % len(tier)
173
+
174
+ selected_key = chosen_channel.get_next_key()
175
+ return chosen_channel, selected_key
176
+
177
+ def report_failure(self, channel_name: str, cooldown_seconds: float = 30.0):
178
+ """Record upstream error and cooldown."""
179
+ if channel_name in self.channels:
180
+ self.channels[channel_name].trigger_cooldown(cooldown_seconds)
181
+
182
+ def report_success(self, channel_name: str):
183
+ """Record successful response."""
184
+ if channel_name in self.channels:
185
+ self.channels[channel_name].mark_success()
186
+
187
+ def get_status(self) -> List[Dict[str, Any]]:
188
+ """Return snapshot of all channels."""
189
+ return [c.to_dict() for c in self.channels.values()]