janito 2.15.0__py3-none-any.whl → 2.16.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/cli/chat_mode/session.py +42 -1
- janito/cli/cli_commands/list_drivers.py +117 -93
- janito/cli/cli_commands/list_providers_region.py +85 -0
- janito/cli/core/getters.py +7 -3
- janito/cli/main_cli.py +38 -3
- janito/cli/single_shot_mode/handler.py +43 -1
- janito/drivers/azure_openai/driver.py +14 -5
- 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/alibaba/provider.py +13 -9
- janito/providers/anthropic/provider.py +4 -5
- janito/providers/azure_openai/provider.py +4 -5
- 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.16.0.dist-info}/METADATA +1 -1
- {janito-2.15.0.dist-info → janito-2.16.0.dist-info}/RECORD +29 -23
- {janito-2.15.0.dist-info → janito-2.16.0.dist-info}/WHEEL +0 -0
- {janito-2.15.0.dist-info → janito-2.16.0.dist-info}/entry_points.txt +0 -0
- {janito-2.15.0.dist-info → janito-2.16.0.dist-info}/licenses/LICENSE +0 -0
- {janito-2.15.0.dist-info → janito-2.16.0.dist-info}/top_level.txt +0 -0
janito/drivers/zai/driver.py
CHANGED
@@ -12,18 +12,20 @@ from janito.llm.driver_input import DriverInput
|
|
12
12
|
from janito.driver_events import RequestFinished, RequestStatus, RateLimitRetry
|
13
13
|
from janito.llm.message_parts import TextMessagePart, FunctionCallMessagePart
|
14
14
|
|
15
|
-
|
16
|
-
import openai
|
17
|
-
available = True
|
18
|
-
unavailable_reason = None
|
19
|
-
except ImportError:
|
20
|
-
available = False
|
21
|
-
unavailable_reason = "openai module not installed"
|
15
|
+
import openai
|
22
16
|
|
23
17
|
|
24
18
|
class ZAIModelDriver(LLMDriver):
|
25
|
-
|
26
|
-
|
19
|
+
# Check if required dependencies are available
|
20
|
+
try:
|
21
|
+
from zai import ZaiClient
|
22
|
+
|
23
|
+
available = True
|
24
|
+
unavailable_reason = None
|
25
|
+
except ImportError as e:
|
26
|
+
available = False
|
27
|
+
unavailable_reason = f"Missing dependency: {str(e)}"
|
28
|
+
|
27
29
|
def _get_message_from_result(self, result):
|
28
30
|
"""Extract the message object from the provider result (Z.AI-specific)."""
|
29
31
|
if hasattr(result, "choices") and result.choices:
|
@@ -256,14 +258,11 @@ class ZAIModelDriver(LLMDriver):
|
|
256
258
|
try:
|
257
259
|
if not config.api_key:
|
258
260
|
provider_name = getattr(self, "provider_name", "ZAI")
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
print(
|
264
|
-
f"Or set the {provider_name.upper()}_API_KEY environment variable."
|
261
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
262
|
+
|
263
|
+
handle_missing_api_key(
|
264
|
+
provider_name, f"{provider_name.upper()}_API_KEY"
|
265
265
|
)
|
266
|
-
raise ValueError(f"API key is required for provider '{provider_name}'")
|
267
266
|
|
268
267
|
api_key_display = str(config.api_key)
|
269
268
|
if api_key_display and len(api_key_display) > 8:
|
@@ -284,12 +283,10 @@ class ZAIModelDriver(LLMDriver):
|
|
284
283
|
flush=True,
|
285
284
|
)
|
286
285
|
|
287
|
-
# Use
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
raise ImportError("openai module is not available. Please install it with: pip install openai")
|
292
|
-
client = openai.OpenAI(
|
286
|
+
# Use the official Z.ai SDK
|
287
|
+
from zai import ZaiClient
|
288
|
+
|
289
|
+
client = ZaiClient(
|
293
290
|
api_key=config.api_key, base_url="https://api.z.ai/api/paas/v4/"
|
294
291
|
)
|
295
292
|
return client
|
janito/formatting_token.py
CHANGED
@@ -29,7 +29,7 @@ def format_token_message_summary(
|
|
29
29
|
msg_count, usage, width=96, use_rich=False, elapsed=None
|
30
30
|
):
|
31
31
|
"""
|
32
|
-
Returns a string (rich or pt markup) summarizing message count, last token usage, and
|
32
|
+
Returns a string (rich or pt markup) summarizing message count, last token usage, elapsed time, and tokens per second.
|
33
33
|
"""
|
34
34
|
left = f" Messages: {'[' if use_rich else '<'}msg_count{']' if use_rich else '>'}{msg_count}{'[/msg_count]' if use_rich else '</msg_count>'}"
|
35
35
|
tokens_part = ""
|
@@ -42,10 +42,14 @@ def format_token_message_summary(
|
|
42
42
|
f"Completion: {format_tokens(completion_tokens, 'tokens_out', use_rich)}, "
|
43
43
|
f"Total: {format_tokens(total_tokens, 'tokens_total', use_rich)}"
|
44
44
|
)
|
45
|
-
elapsed_part =
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
elapsed_part = ""
|
46
|
+
tps_part = ""
|
47
|
+
if elapsed is not None and elapsed > 0:
|
48
|
+
elapsed_part = f" | Elapsed: [cyan]{elapsed:.2f}s[/cyan]"
|
49
|
+
if usage and total_tokens:
|
50
|
+
tokens_per_second = total_tokens / elapsed
|
51
|
+
tps_part = f" | TPS: {int(tokens_per_second)}"
|
52
|
+
return f"{left}{tokens_part}{elapsed_part}{tps_part}"
|
49
53
|
|
50
54
|
|
51
55
|
def print_token_message_summary(
|
janito/llm/auth_utils.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
"""
|
2
|
+
Authentication utilities for LLM providers.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import sys
|
6
|
+
|
7
|
+
|
8
|
+
def handle_missing_api_key(provider_name: str, env_var_name: str) -> None:
|
9
|
+
"""
|
10
|
+
Handle missing API key by printing error message and exiting.
|
11
|
+
|
12
|
+
Args:
|
13
|
+
provider_name: Name of the provider (e.g., 'alibaba', 'openai')
|
14
|
+
env_var_name: Environment variable name (e.g., 'ALIBABA_API_KEY')
|
15
|
+
"""
|
16
|
+
print(
|
17
|
+
f"[ERROR] No API key found for provider '{provider_name}'. Please set the API key using:"
|
18
|
+
)
|
19
|
+
print(f" janito --set-api-key YOUR_API_KEY -p {provider_name}")
|
20
|
+
print(f"Or set the {env_var_name} environment variable.")
|
21
|
+
sys.exit(1)
|
@@ -17,7 +17,9 @@ class AlibabaProvider(LLMProvider):
|
|
17
17
|
NAME = "alibaba"
|
18
18
|
MAINTAINER = "João Pinto <janito@ikignosis.org>"
|
19
19
|
MODEL_SPECS = MODEL_SPECS
|
20
|
-
DEFAULT_MODEL =
|
20
|
+
DEFAULT_MODEL = (
|
21
|
+
"qwen3-coder-plus" # Options: qwen-turbo, qwen-plus, qwen-max, qwen3-coder-plus
|
22
|
+
)
|
21
23
|
|
22
24
|
def __init__(
|
23
25
|
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
@@ -25,27 +27,29 @@ class AlibabaProvider(LLMProvider):
|
|
25
27
|
# Always set a tools adapter so that even if the driver is unavailable,
|
26
28
|
# generic code paths that expect provider.execute_tool() continue to work.
|
27
29
|
self._tools_adapter = get_local_tools_adapter()
|
28
|
-
|
30
|
+
|
29
31
|
# Always initialize _driver_config to avoid AttributeError
|
30
32
|
self._driver_config = config or LLMDriverConfig(model=None)
|
31
|
-
|
33
|
+
|
32
34
|
if not self.available:
|
33
35
|
self._driver = None
|
34
36
|
else:
|
35
37
|
self.auth_manager = auth_manager or LLMAuthManager()
|
36
38
|
self._api_key = self.auth_manager.get_credentials(type(self).NAME)
|
37
39
|
if not self._api_key:
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
41
|
+
|
42
|
+
handle_missing_api_key(self.name, "ALIBABA_API_KEY")
|
43
|
+
|
42
44
|
if not self._driver_config.model:
|
43
45
|
self._driver_config.model = self.DEFAULT_MODEL
|
44
46
|
if not self._driver_config.api_key:
|
45
47
|
self._driver_config.api_key = self._api_key
|
46
48
|
# Set Alibaba international endpoint as default base_url if not provided
|
47
49
|
if not getattr(self._driver_config, "base_url", None):
|
48
|
-
self._driver_config.base_url =
|
50
|
+
self._driver_config.base_url = (
|
51
|
+
"https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
52
|
+
)
|
49
53
|
self.fill_missing_device_info(self._driver_config)
|
50
54
|
self._driver = None # to be provided by factory/agent
|
51
55
|
|
@@ -99,4 +103,4 @@ class AlibabaProvider(LLMProvider):
|
|
99
103
|
return self._tools_adapter.execute_by_name(tool_name, *args, **kwargs)
|
100
104
|
|
101
105
|
|
102
|
-
LLMProviderRegistry.register(AlibabaProvider.NAME, AlibabaProvider)
|
106
|
+
LLMProviderRegistry.register(AlibabaProvider.NAME, AlibabaProvider)
|
@@ -22,11 +22,10 @@ class AnthropicProvider(LLMProvider):
|
|
22
22
|
self.auth_manager = auth_manager or LLMAuthManager()
|
23
23
|
self._api_key = self.auth_manager.get_credentials(type(self).NAME)
|
24
24
|
if not self._api_key:
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
26
|
+
|
27
|
+
handle_missing_api_key(self.name, "ANTHROPIC_API_KEY")
|
28
|
+
|
30
29
|
self._tools_adapter = get_local_tools_adapter()
|
31
30
|
self._driver_config = config or LLMDriverConfig(model=None)
|
32
31
|
if not getattr(self._driver_config, "model", None):
|
@@ -33,11 +33,10 @@ class AzureOpenAIProvider(LLMProvider):
|
|
33
33
|
self._auth_manager = auth_manager or LLMAuthManager()
|
34
34
|
self._api_key = self._auth_manager.get_credentials(type(self).NAME)
|
35
35
|
if not self._api_key:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
37
|
+
|
38
|
+
handle_missing_api_key(self.name, "AZURE_OPENAI_API_KEY")
|
39
|
+
|
41
40
|
self._tools_adapter = get_local_tools_adapter()
|
42
41
|
self._driver_config = config or LLMDriverConfig(model=None)
|
43
42
|
if not self._driver_config.model:
|
@@ -31,11 +31,10 @@ class DeepSeekProvider(LLMProvider):
|
|
31
31
|
self.auth_manager = auth_manager or LLMAuthManager()
|
32
32
|
self._api_key = self.auth_manager.get_credentials(type(self).NAME)
|
33
33
|
if not self._api_key:
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
35
|
+
|
36
|
+
handle_missing_api_key(self.name, "DEEPSEEK_API_KEY")
|
37
|
+
|
39
38
|
self._tools_adapter = get_local_tools_adapter()
|
40
39
|
self._driver_config = config or LLMDriverConfig(model=None)
|
41
40
|
if not self._driver_config.model:
|
@@ -33,11 +33,10 @@ class GoogleProvider(LLMProvider):
|
|
33
33
|
self.auth_manager = auth_manager or LLMAuthManager()
|
34
34
|
self._api_key = self.auth_manager.get_credentials(type(self).name)
|
35
35
|
if not self._api_key:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
37
|
+
|
38
|
+
handle_missing_api_key(self.name, "GOOGLE_API_KEY")
|
39
|
+
|
41
40
|
self._tools_adapter = get_local_tools_adapter()
|
42
41
|
self._driver_config = config or LLMDriverConfig(model=None)
|
43
42
|
# Only set default if model is not set by CLI/config
|
@@ -17,44 +17,53 @@ class MoonshotAIProvider(LLMProvider):
|
|
17
17
|
def __init__(
|
18
18
|
self, auth_manager: LLMAuthManager = None, config: LLMDriverConfig = None
|
19
19
|
):
|
20
|
+
self._tools_adapter = get_local_tools_adapter()
|
21
|
+
self._driver = None
|
22
|
+
|
20
23
|
if not self.available:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
24
|
+
return
|
25
|
+
|
26
|
+
self._initialize_config(auth_manager, config)
|
27
|
+
self._setup_model_config()
|
28
|
+
self._driver_config.base_url = "https://api.moonshot.ai/v1"
|
29
|
+
|
30
|
+
def _initialize_config(self, auth_manager, config):
|
31
|
+
"""Initialize configuration and API key."""
|
32
|
+
self.auth_manager = auth_manager or LLMAuthManager()
|
33
|
+
self._api_key = self.auth_manager.get_credentials(type(self).name)
|
34
|
+
if not self._api_key:
|
35
|
+
from janito.llm.auth_utils import handle_missing_api_key
|
36
|
+
|
37
|
+
handle_missing_api_key(self.name, "MOONSHOTAI_API_KEY")
|
38
|
+
|
39
|
+
self._driver_config = config or LLMDriverConfig(model=None)
|
40
|
+
if not self._driver_config.model:
|
41
|
+
self._driver_config.model = self.DEFAULT_MODEL
|
42
|
+
if not self._driver_config.api_key:
|
43
|
+
self._driver_config.api_key = self._api_key
|
44
|
+
|
45
|
+
def _setup_model_config(self):
|
46
|
+
"""Configure token limits based on model specifications."""
|
47
|
+
model_name = self._driver_config.model
|
48
|
+
model_spec = self.MODEL_SPECS.get(model_name)
|
49
|
+
|
50
|
+
# Reset token parameters
|
51
|
+
if hasattr(self._driver_config, "max_tokens"):
|
52
|
+
self._driver_config.max_tokens = None
|
53
|
+
if hasattr(self._driver_config, "max_completion_tokens"):
|
54
|
+
self._driver_config.max_completion_tokens = None
|
55
|
+
|
56
|
+
if model_spec:
|
57
|
+
if getattr(model_spec, "thinking_supported", False):
|
58
|
+
max_cot = getattr(model_spec, "max_cot", None)
|
59
|
+
if max_cot and max_cot != "N/A":
|
60
|
+
self._driver_config.max_completion_tokens = int(max_cot)
|
61
|
+
else:
|
62
|
+
max_response = getattr(model_spec, "max_response", None)
|
63
|
+
if max_response and max_response != "N/A":
|
64
|
+
self._driver_config.max_tokens = int(max_response)
|
65
|
+
|
66
|
+
self.fill_missing_device_info(self._driver_config)
|
58
67
|
|
59
68
|
@property
|
60
69
|
def driver(self) -> OpenAIModelDriver:
|
@@ -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()
|