janito 2.15.0__py3-none-any.whl → 2.17.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.
- janito/agent/setup_agent.py +1 -1
- janito/cli/chat_mode/session.py +42 -1
- janito/cli/cli_commands/list_drivers.py +118 -93
- janito/cli/cli_commands/list_providers_region.py +85 -0
- janito/cli/cli_commands/set_api_key.py +16 -5
- janito/cli/core/getters.py +7 -3
- janito/cli/main_cli.py +38 -3
- janito/cli/prompt_setup.py +3 -0
- janito/cli/single_shot_mode/handler.py +43 -1
- janito/drivers/azure_openai/driver.py +14 -5
- janito/drivers/cerebras/__init__.py +1 -0
- janito/drivers/openai/driver.py +16 -5
- janito/drivers/zai/driver.py +19 -22
- janito/formatting_token.py +9 -5
- janito/llm/auth_utils.py +21 -0
- janito/providers/__init__.py +1 -0
- janito/providers/alibaba/provider.py +11 -9
- janito/providers/anthropic/provider.py +4 -5
- janito/providers/azure_openai/provider.py +4 -5
- janito/providers/cerebras/__init__.py +1 -0
- janito/providers/cerebras/model_info.py +76 -0
- janito/providers/cerebras/provider.py +145 -0
- janito/providers/deepseek/provider.py +4 -5
- janito/providers/google/provider.py +4 -5
- janito/providers/moonshotai/provider.py +46 -37
- janito/providers/openai/provider.py +45 -39
- janito/providers/zai/provider.py +3 -9
- janito/regions/__init__.py +16 -0
- janito/regions/cli.py +124 -0
- janito/regions/geo_utils.py +240 -0
- janito/regions/provider_regions.py +158 -0
- {janito-2.15.0.dist-info → janito-2.17.0.dist-info}/METADATA +1 -1
- {janito-2.15.0.dist-info → janito-2.17.0.dist-info}/RECORD +37 -27
- {janito-2.15.0.dist-info → janito-2.17.0.dist-info}/WHEEL +0 -0
- {janito-2.15.0.dist-info → janito-2.17.0.dist-info}/entry_points.txt +0 -0
- {janito-2.15.0.dist-info → janito-2.17.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.15.0.dist-info → janito-2.17.0.dist-info}/top_level.txt +0 -0
@@ -22,46 +22,52 @@ class OpenAIProvider(LLMProvider):
|
|
22
22
|
def __init__(
|
23
23
|
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
24
24
|
):
|
25
|
+
self._tools_adapter = get_local_tools_adapter()
|
26
|
+
self._driver = None
|
27
|
+
|
25
28
|
if not self.available:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
29
|
+
return
|
30
|
+
|
31
|
+
self._initialize_config(auth_manager, config)
|
32
|
+
self._setup_model_config()
|
33
|
+
|
34
|
+
def _initialize_config(self, auth_manager, config):
|
35
|
+
"""Initialize configuration and API key."""
|
36
|
+
self.auth_manager = auth_manager or LLMAuthManager()
|
37
|
+
self._api_key = self.auth_manager.get_credentials(type(self).NAME)
|
38
|
+
if not self._api_key:
|
39
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
40
|
+
|
41
|
+
handle_missing_api_key(self.name, "OPENAI_API_KEY")
|
42
|
+
|
43
|
+
self._driver_config = config or LLMDriverConfig(model=None)
|
44
|
+
if not self._driver_config.model:
|
45
|
+
self._driver_config.model = self.DEFAULT_MODEL
|
46
|
+
if not self._driver_config.api_key:
|
47
|
+
self._driver_config.api_key = self._api_key
|
48
|
+
|
49
|
+
def _setup_model_config(self):
|
50
|
+
"""Configure token limits based on model specifications."""
|
51
|
+
model_name = self._driver_config.model
|
52
|
+
model_spec = self.MODEL_SPECS.get(model_name)
|
53
|
+
|
54
|
+
# Reset token parameters
|
55
|
+
if hasattr(self._driver_config, "max_tokens"):
|
56
|
+
self._driver_config.max_tokens = None
|
57
|
+
if hasattr(self._driver_config, "max_completion_tokens"):
|
58
|
+
self._driver_config.max_completion_tokens = None
|
59
|
+
|
60
|
+
if model_spec:
|
61
|
+
if getattr(model_spec, "thinking_supported", False):
|
62
|
+
max_cot = getattr(model_spec, "max_cot", None)
|
63
|
+
if max_cot and max_cot != "N/A":
|
64
|
+
self._driver_config.max_completion_tokens = int(max_cot)
|
65
|
+
else:
|
66
|
+
max_response = getattr(model_spec, "max_response", None)
|
67
|
+
if max_response and max_response != "N/A":
|
68
|
+
self._driver_config.max_tokens = int(max_response)
|
69
|
+
|
70
|
+
self.fill_missing_device_info(self._driver_config)
|
65
71
|
|
66
72
|
@property
|
67
73
|
def driver(self) -> OpenAIModelDriver:
|
janito/providers/zai/provider.py
CHANGED
@@ -33,20 +33,14 @@ class ZAIProvider(LLMProvider):
|
|
33
33
|
# crash with an AttributeError when it tries to access `self._tools_adapter`.
|
34
34
|
self._tools_adapter = get_local_tools_adapter()
|
35
35
|
self._driver = None
|
36
|
-
# Initialize _driver_config to avoid AttributeError
|
37
|
-
self._driver_config = LLMDriverConfig(model=None)
|
38
36
|
|
39
37
|
def _setup_available(self, auth_manager, config):
|
40
38
|
self.auth_manager = auth_manager or LLMAuthManager()
|
41
39
|
self._api_key = self.auth_manager.get_credentials(type(self).NAME)
|
42
40
|
if not self._api_key:
|
43
|
-
|
44
|
-
|
45
|
-
)
|
46
|
-
print(f" janito --set-api-key YOUR_API_KEY -p {self.name}")
|
47
|
-
print(f"Or set the ZAI_API_KEY environment variable.")
|
48
|
-
self._tools_adapter = get_local_tools_adapter()
|
49
|
-
return
|
41
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
42
|
+
|
43
|
+
handle_missing_api_key(self.name, "ZAI_API_KEY")
|
50
44
|
|
51
45
|
self._tools_adapter = get_local_tools_adapter()
|
52
46
|
self._driver_config = config or LLMDriverConfig(model=None)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""
|
2
|
+
Region definitions and geolocation utilities for LLM providers.
|
3
|
+
|
4
|
+
This module provides static region definitions for various LLM providers
|
5
|
+
and utilities to determine optimal API endpoints based on user location.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .provider_regions import PROVIDER_REGIONS, get_optimal_endpoint
|
9
|
+
from .geo_utils import get_user_location, get_closest_region
|
10
|
+
|
11
|
+
__all__ = [
|
12
|
+
"PROVIDER_REGIONS",
|
13
|
+
"get_optimal_endpoint",
|
14
|
+
"get_user_location",
|
15
|
+
"get_closest_region",
|
16
|
+
]
|
janito/regions/cli.py
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
"""
|
2
|
+
CLI commands for region management and geolocation utilities.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import json
|
7
|
+
from typing import Optional
|
8
|
+
|
9
|
+
from .geo_utils import get_region_info
|
10
|
+
from .provider_regions import (
|
11
|
+
get_provider_regions,
|
12
|
+
get_optimal_endpoint,
|
13
|
+
get_all_providers,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def handle_region_info(args=None):
|
18
|
+
"""Display current region information."""
|
19
|
+
info = get_region_info()
|
20
|
+
print(json.dumps(info, indent=2))
|
21
|
+
|
22
|
+
|
23
|
+
def handle_provider_regions(args):
|
24
|
+
"""Display regions for a specific provider."""
|
25
|
+
provider = args.provider.lower()
|
26
|
+
regions = get_provider_regions(provider)
|
27
|
+
|
28
|
+
if not regions:
|
29
|
+
print(f"Provider '{provider}' not found")
|
30
|
+
return
|
31
|
+
|
32
|
+
result = {
|
33
|
+
"provider": provider,
|
34
|
+
"regions": [
|
35
|
+
{
|
36
|
+
"code": r.region_code,
|
37
|
+
"name": r.name,
|
38
|
+
"endpoint": r.endpoint,
|
39
|
+
"location": r.location,
|
40
|
+
"priority": r.priority,
|
41
|
+
}
|
42
|
+
for r in regions
|
43
|
+
],
|
44
|
+
}
|
45
|
+
print(json.dumps(result, indent=2))
|
46
|
+
|
47
|
+
|
48
|
+
def handle_optimal_endpoint(args):
|
49
|
+
"""Get optimal endpoint for a provider based on user location."""
|
50
|
+
from .geo_utils import get_user_location, get_closest_region
|
51
|
+
|
52
|
+
country_code, _ = get_user_location()
|
53
|
+
major_region = get_closest_region(country_code)
|
54
|
+
|
55
|
+
provider = args.provider.lower()
|
56
|
+
endpoint = get_optimal_endpoint(provider, major_region)
|
57
|
+
|
58
|
+
if not endpoint:
|
59
|
+
print(f"Provider '{provider}' not found")
|
60
|
+
return
|
61
|
+
|
62
|
+
result = {
|
63
|
+
"provider": provider,
|
64
|
+
"user_region": major_region,
|
65
|
+
"country_code": country_code,
|
66
|
+
"optimal_endpoint": endpoint,
|
67
|
+
}
|
68
|
+
print(json.dumps(result, indent=2))
|
69
|
+
|
70
|
+
|
71
|
+
def handle_list_providers(args=None):
|
72
|
+
"""List all supported providers."""
|
73
|
+
providers = get_all_providers()
|
74
|
+
result = {"providers": providers, "count": len(providers)}
|
75
|
+
print(json.dumps(result, indent=2))
|
76
|
+
|
77
|
+
|
78
|
+
def setup_region_parser(subparsers):
|
79
|
+
"""Setup region-related CLI commands."""
|
80
|
+
region_parser = subparsers.add_parser(
|
81
|
+
"region", help="Region and geolocation utilities"
|
82
|
+
)
|
83
|
+
region_subparsers = region_parser.add_subparsers(
|
84
|
+
dest="region_command", help="Region commands"
|
85
|
+
)
|
86
|
+
|
87
|
+
# region info
|
88
|
+
info_parser = region_subparsers.add_parser(
|
89
|
+
"info", help="Show current region information"
|
90
|
+
)
|
91
|
+
info_parser.set_defaults(func=handle_region_info)
|
92
|
+
|
93
|
+
# region providers
|
94
|
+
providers_parser = region_subparsers.add_parser(
|
95
|
+
"providers", help="List all supported providers"
|
96
|
+
)
|
97
|
+
providers_parser.set_defaults(func=handle_list_providers)
|
98
|
+
|
99
|
+
# region list
|
100
|
+
list_parser = region_subparsers.add_parser(
|
101
|
+
"list", help="List regions for a provider"
|
102
|
+
)
|
103
|
+
list_parser.add_argument("provider", help="Provider name (e.g., openai, anthropic)")
|
104
|
+
list_parser.set_defaults(func=handle_provider_regions)
|
105
|
+
|
106
|
+
# region endpoint
|
107
|
+
endpoint_parser = region_subparsers.add_parser(
|
108
|
+
"endpoint", help="Get optimal endpoint for provider"
|
109
|
+
)
|
110
|
+
endpoint_parser.add_argument(
|
111
|
+
"provider", help="Provider name (e.g., openai, anthropic)"
|
112
|
+
)
|
113
|
+
endpoint_parser.set_defaults(func=handle_optimal_endpoint)
|
114
|
+
|
115
|
+
|
116
|
+
if __name__ == "__main__":
|
117
|
+
parser = argparse.ArgumentParser(description="Region utilities")
|
118
|
+
setup_region_parser(parser.add_subparsers(dest="command"))
|
119
|
+
|
120
|
+
args = parser.parse_args()
|
121
|
+
if hasattr(args, "func"):
|
122
|
+
args.func(args)
|
123
|
+
else:
|
124
|
+
parser.print_help()
|
@@ -0,0 +1,240 @@
|
|
1
|
+
"""
|
2
|
+
Geolocation utilities for determining user location and optimal regions.
|
3
|
+
|
4
|
+
This module provides utilities to detect user location and determine
|
5
|
+
optimal API regions based on geographic proximity.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import json
|
10
|
+
import subprocess
|
11
|
+
import sys
|
12
|
+
from typing import Optional, Tuple
|
13
|
+
from pathlib import Path
|
14
|
+
|
15
|
+
|
16
|
+
def _get_geoip_location() -> Optional[str]:
|
17
|
+
"""Try to get location using geoip2 if available."""
|
18
|
+
try:
|
19
|
+
import geoip2.database
|
20
|
+
import geoip2.errors
|
21
|
+
import urllib.request
|
22
|
+
|
23
|
+
# Common GeoIP database locations
|
24
|
+
db_paths = [
|
25
|
+
"/usr/share/GeoIP/GeoLite2-City.mmdb",
|
26
|
+
"/var/lib/GeoIP/GeoLite2-City.mmdb",
|
27
|
+
str(Path.home() / ".local/share/GeoIP/GeoLite2-City.mmdb"),
|
28
|
+
]
|
29
|
+
|
30
|
+
for db_path in db_paths:
|
31
|
+
if Path(db_path).exists():
|
32
|
+
reader = geoip2.database.Reader(db_path)
|
33
|
+
try:
|
34
|
+
with urllib.request.urlopen(
|
35
|
+
"https://api.ipify.org", timeout=2
|
36
|
+
) as response:
|
37
|
+
ip = response.read().decode().strip()
|
38
|
+
|
39
|
+
response = reader.city(ip)
|
40
|
+
country_code = response.country.iso_code
|
41
|
+
reader.close()
|
42
|
+
|
43
|
+
if country_code:
|
44
|
+
return country_code.upper()
|
45
|
+
except Exception:
|
46
|
+
reader.close()
|
47
|
+
continue
|
48
|
+
except (ImportError, Exception):
|
49
|
+
pass
|
50
|
+
return None
|
51
|
+
|
52
|
+
|
53
|
+
def _get_ipinfo_location() -> Optional[str]:
|
54
|
+
"""Try to get location using ipinfo.io via curl."""
|
55
|
+
try:
|
56
|
+
import subprocess
|
57
|
+
|
58
|
+
result = subprocess.run(
|
59
|
+
["curl", "-s", "https://ipinfo.io/country"],
|
60
|
+
capture_output=True,
|
61
|
+
text=True,
|
62
|
+
timeout=3,
|
63
|
+
)
|
64
|
+
if result.returncode == 0 and result.stdout.strip():
|
65
|
+
return result.stdout.strip().upper()
|
66
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
|
67
|
+
pass
|
68
|
+
return None
|
69
|
+
|
70
|
+
|
71
|
+
def _get_locale_location() -> Optional[str]:
|
72
|
+
"""Try to get location from system locale."""
|
73
|
+
try:
|
74
|
+
import locale
|
75
|
+
|
76
|
+
loc = locale.getdefaultlocale()[0]
|
77
|
+
if loc:
|
78
|
+
parts = loc.split("_")
|
79
|
+
if len(parts) > 1:
|
80
|
+
return parts[-1].upper()
|
81
|
+
except Exception:
|
82
|
+
pass
|
83
|
+
return None
|
84
|
+
|
85
|
+
|
86
|
+
def get_user_location() -> Tuple[str, str]:
|
87
|
+
"""
|
88
|
+
Detect user's location using geoip lookup.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
Tuple of (country_code, region_name)
|
92
|
+
|
93
|
+
Note:
|
94
|
+
This is a best-effort detection. Falls back to environment variables
|
95
|
+
or defaults to 'US' if detection fails.
|
96
|
+
"""
|
97
|
+
# Try environment variable first
|
98
|
+
user_region = os.getenv("JANITO_REGION")
|
99
|
+
if user_region:
|
100
|
+
return user_region.upper(), f"Environment: {user_region}"
|
101
|
+
|
102
|
+
# Try different detection methods
|
103
|
+
country_code = (
|
104
|
+
_get_geoip_location()
|
105
|
+
or _get_ipinfo_location()
|
106
|
+
or _get_locale_location()
|
107
|
+
or "US"
|
108
|
+
)
|
109
|
+
|
110
|
+
source = (
|
111
|
+
"GeoIP"
|
112
|
+
if _get_geoip_location()
|
113
|
+
else (
|
114
|
+
"ipinfo.io"
|
115
|
+
if _get_ipinfo_location()
|
116
|
+
else ("Locale" if _get_locale_location() else "Default")
|
117
|
+
)
|
118
|
+
)
|
119
|
+
|
120
|
+
return country_code, f"{source}: {country_code}"
|
121
|
+
|
122
|
+
|
123
|
+
def get_closest_region(country_code: str) -> str:
|
124
|
+
"""
|
125
|
+
Map country code to closest major region.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
country_code: ISO country code (e.g., 'US', 'DE', 'CN')
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
Major region identifier (US, EU, CN, CH, ASIA)
|
132
|
+
"""
|
133
|
+
country_code = country_code.upper()
|
134
|
+
|
135
|
+
# US and Americas
|
136
|
+
if country_code in ["US", "CA", "MX", "BR", "AR", "CL", "CO", "PE"]:
|
137
|
+
return "US"
|
138
|
+
|
139
|
+
# European Union and Europe
|
140
|
+
if country_code in [
|
141
|
+
"AT",
|
142
|
+
"BE",
|
143
|
+
"BG",
|
144
|
+
"HR",
|
145
|
+
"CY",
|
146
|
+
"CZ",
|
147
|
+
"DK",
|
148
|
+
"EE",
|
149
|
+
"FI",
|
150
|
+
"FR",
|
151
|
+
"DE",
|
152
|
+
"GR",
|
153
|
+
"HU",
|
154
|
+
"IE",
|
155
|
+
"IT",
|
156
|
+
"LV",
|
157
|
+
"LT",
|
158
|
+
"LU",
|
159
|
+
"MT",
|
160
|
+
"NL",
|
161
|
+
"PL",
|
162
|
+
"PT",
|
163
|
+
"RO",
|
164
|
+
"SK",
|
165
|
+
"SI",
|
166
|
+
"ES",
|
167
|
+
"SE",
|
168
|
+
"GB",
|
169
|
+
"NO",
|
170
|
+
"CH",
|
171
|
+
"IS",
|
172
|
+
"LI",
|
173
|
+
]:
|
174
|
+
return "EU"
|
175
|
+
|
176
|
+
# China and nearby
|
177
|
+
if country_code in ["CN", "HK", "MO", "TW"]:
|
178
|
+
return "CN"
|
179
|
+
|
180
|
+
# Switzerland
|
181
|
+
if country_code == "CH":
|
182
|
+
return "CH"
|
183
|
+
|
184
|
+
# Asia Pacific
|
185
|
+
if country_code in [
|
186
|
+
"JP",
|
187
|
+
"KR",
|
188
|
+
"SG",
|
189
|
+
"MY",
|
190
|
+
"TH",
|
191
|
+
"VN",
|
192
|
+
"PH",
|
193
|
+
"ID",
|
194
|
+
"IN",
|
195
|
+
"AU",
|
196
|
+
"NZ",
|
197
|
+
"BD",
|
198
|
+
"LK",
|
199
|
+
"MM",
|
200
|
+
"KH",
|
201
|
+
"LA",
|
202
|
+
"BN",
|
203
|
+
"MV",
|
204
|
+
]:
|
205
|
+
return "ASIA"
|
206
|
+
|
207
|
+
# Middle East
|
208
|
+
if country_code in ["AE", "SA", "QA", "KW", "OM", "BH", "IL", "TR"]:
|
209
|
+
return "ASIA"
|
210
|
+
|
211
|
+
# Africa
|
212
|
+
if country_code in ["ZA", "NG", "KE", "EG", "MA", "TN", "GH", "UG"]:
|
213
|
+
return "EU" # Route through EU for better connectivity
|
214
|
+
|
215
|
+
# Default to US for unknown regions
|
216
|
+
return "US"
|
217
|
+
|
218
|
+
|
219
|
+
def get_region_info() -> dict:
|
220
|
+
"""
|
221
|
+
Get comprehensive region information for the current user.
|
222
|
+
|
223
|
+
Returns:
|
224
|
+
Dictionary with location details
|
225
|
+
"""
|
226
|
+
country_code, source = get_user_location()
|
227
|
+
major_region = get_closest_region(country_code)
|
228
|
+
|
229
|
+
return {
|
230
|
+
"country_code": country_code,
|
231
|
+
"major_region": major_region,
|
232
|
+
"source": source,
|
233
|
+
"timestamp": str(__import__("datetime").datetime.now()),
|
234
|
+
}
|
235
|
+
|
236
|
+
|
237
|
+
if __name__ == "__main__":
|
238
|
+
# Test the geolocation functionality
|
239
|
+
info = get_region_info()
|
240
|
+
print(json.dumps(info, indent=2))
|
@@ -0,0 +1,158 @@
|
|
1
|
+
"""
|
2
|
+
Static region definitions for LLM providers.
|
3
|
+
|
4
|
+
This module contains region mappings for major LLM providers with their
|
5
|
+
respective API endpoints and data center locations.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, List, Optional
|
9
|
+
from dataclasses import dataclass
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class RegionEndpoint:
|
14
|
+
"""Represents a provider's endpoint in a specific region."""
|
15
|
+
|
16
|
+
region_code: str
|
17
|
+
name: str
|
18
|
+
endpoint: str
|
19
|
+
location: str # City, Country format
|
20
|
+
priority: int = 1 # Lower = higher priority
|
21
|
+
|
22
|
+
|
23
|
+
# Region definitions for major LLM providers
|
24
|
+
PROVIDER_REGIONS: Dict[str, List[RegionEndpoint]] = {
|
25
|
+
"openai": [
|
26
|
+
RegionEndpoint(
|
27
|
+
"US-WEST", "US West", "https://api.openai.com/v1", "San Francisco, US", 1
|
28
|
+
),
|
29
|
+
],
|
30
|
+
"anthropic": [
|
31
|
+
RegionEndpoint(
|
32
|
+
"US-WEST", "US West", "https://api.anthropic.com", "San Francisco, US", 1
|
33
|
+
),
|
34
|
+
],
|
35
|
+
"google": [
|
36
|
+
RegionEndpoint(
|
37
|
+
"EU-WEST",
|
38
|
+
"EU West",
|
39
|
+
"https://generativelanguage.googleapis.com/v1beta",
|
40
|
+
"Delfzijl, NL",
|
41
|
+
1,
|
42
|
+
),
|
43
|
+
],
|
44
|
+
"azure-openai": [
|
45
|
+
RegionEndpoint(
|
46
|
+
"US-EAST",
|
47
|
+
"East US",
|
48
|
+
"https://{resource}.openai.azure.com",
|
49
|
+
"Virginia, US",
|
50
|
+
1,
|
51
|
+
),
|
52
|
+
RegionEndpoint(
|
53
|
+
"US-WEST",
|
54
|
+
"West US",
|
55
|
+
"https://{resource}.openai.azure.com",
|
56
|
+
"California, US",
|
57
|
+
2,
|
58
|
+
),
|
59
|
+
RegionEndpoint(
|
60
|
+
"EU-WEST",
|
61
|
+
"West Europe",
|
62
|
+
"https://{resource}.openai.azure.com",
|
63
|
+
"Netherlands, NL",
|
64
|
+
3,
|
65
|
+
),
|
66
|
+
RegionEndpoint(
|
67
|
+
"EU-NORTH",
|
68
|
+
"North Europe",
|
69
|
+
"https://{resource}.openai.azure.com",
|
70
|
+
"Ireland, IE",
|
71
|
+
4,
|
72
|
+
),
|
73
|
+
],
|
74
|
+
"alibaba": [
|
75
|
+
RegionEndpoint(
|
76
|
+
"SG",
|
77
|
+
"Singapore",
|
78
|
+
"https://dashscope-intl.aliyuncs.com/api/v1",
|
79
|
+
"Singapore, SG",
|
80
|
+
1,
|
81
|
+
),
|
82
|
+
RegionEndpoint(
|
83
|
+
"CN-EAST",
|
84
|
+
"China East",
|
85
|
+
"https://dashscope.aliyuncs.com/api/v1",
|
86
|
+
"Hangzhou, CN",
|
87
|
+
2,
|
88
|
+
),
|
89
|
+
],
|
90
|
+
"moonshot": [
|
91
|
+
RegionEndpoint(
|
92
|
+
"US-WEST", "US West", "https://api.moonshot.ai/v1", "San Francisco, US", 1
|
93
|
+
),
|
94
|
+
RegionEndpoint(
|
95
|
+
"CN-EAST", "China East", "https://api.moonshot.cn/v1", "Shanghai, CN", 2
|
96
|
+
),
|
97
|
+
RegionEndpoint(
|
98
|
+
"CN-NORTH", "China North", "https://api.moonshot.cn/v1", "Beijing, CN", 3
|
99
|
+
),
|
100
|
+
],
|
101
|
+
"zai": [
|
102
|
+
RegionEndpoint(
|
103
|
+
"ASIA-PACIFIC",
|
104
|
+
"Asia Pacific",
|
105
|
+
"https://api.z.ai/api/paas/v4",
|
106
|
+
"Singapore, SG",
|
107
|
+
1,
|
108
|
+
),
|
109
|
+
],
|
110
|
+
}
|
111
|
+
|
112
|
+
# Geographic region mappings
|
113
|
+
REGION_MAPPINGS = {
|
114
|
+
"US": ["US-EAST", "US-WEST", "US-CENTRAL", "US-NORTH", "US-SOUTH"],
|
115
|
+
"EU": ["EU-CENTRAL", "EU-WEST", "EU-NORTH", "EU-SOUTH", "EU-EAST"],
|
116
|
+
"CN": ["CN-EAST", "CN-NORTH", "CN-SOUTH", "CN-WEST", "CN-CENTRAL"],
|
117
|
+
"CH": ["CH-NORTH", "CH-SOUTH", "CH-EAST", "CH-WEST"],
|
118
|
+
"ASIA": ["ASIA-EAST", "ASIA-PACIFIC", "ASIA-SOUTH", "ASIA-CENTRAL"],
|
119
|
+
"GLOBAL": ["GLOBAL", "WORLDWIDE"],
|
120
|
+
}
|
121
|
+
|
122
|
+
|
123
|
+
def get_provider_regions(provider: str) -> List[RegionEndpoint]:
|
124
|
+
"""Get all regions for a specific provider."""
|
125
|
+
return PROVIDER_REGIONS.get(provider.lower(), [])
|
126
|
+
|
127
|
+
|
128
|
+
def get_optimal_endpoint(provider: str, user_region: str = "US") -> Optional[str]:
|
129
|
+
"""
|
130
|
+
Get the optimal endpoint for a provider based on user region.
|
131
|
+
|
132
|
+
Args:
|
133
|
+
provider: The provider name (e.g., 'openai', 'anthropic')
|
134
|
+
user_region: User's geographic region (US, EU, CN, CH, ASIA)
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
The optimal endpoint URL or None if provider not found
|
138
|
+
"""
|
139
|
+
regions = get_provider_regions(provider)
|
140
|
+
if not regions:
|
141
|
+
return None
|
142
|
+
|
143
|
+
# Map user region to provider regions
|
144
|
+
preferred_region_codes = REGION_MAPPINGS.get(user_region.upper(), [])
|
145
|
+
|
146
|
+
# Find the highest priority region that matches
|
147
|
+
for region_code in preferred_region_codes:
|
148
|
+
for region in regions:
|
149
|
+
if region.region_code == region_code:
|
150
|
+
return region.endpoint
|
151
|
+
|
152
|
+
# Fallback to first available region
|
153
|
+
return regions[0].endpoint if regions else None
|
154
|
+
|
155
|
+
|
156
|
+
def get_all_providers() -> List[str]:
|
157
|
+
"""Get list of all supported providers."""
|
158
|
+
return list(PROVIDER_REGIONS.keys())
|