2121from anton import __version__
2222
2323from anton .utils .prompt import prompt_or_cancel
24- from anton .core .llm .openai import build_chat_completion_kwargs
24+ from anton .core .llm .openai import build_chat_completion_kwargs , _is_azure_endpoint
2525
2626from anton .chat import ChatSession
2727from anton .core .session import ChatSessionConfig
@@ -236,6 +236,10 @@ def _make_console() -> Console:
236236
237237
238238console = _make_console ()
239+ _ensure_dependencies (console )
240+
241+ import openai
242+ from openai import AzureOpenAI
239243
240244
241245def _get_settings (ctx : typer .Context ):
@@ -281,8 +285,6 @@ def main(
281285 ),
282286) -> None :
283287 """Anton — a self-evolving autonomous system."""
284- _ensure_dependencies (console )
285-
286288 from anton .config .settings import AntonSettings
287289
288290 settings = AntonSettings ()
@@ -294,7 +296,9 @@ def main(
294296 from anton .updater import check_and_update
295297
296298 if check_and_update (console , settings ):
297- # Re-exec with the freshly installed code so no old modules remain in memory.
299+ # Mark the env before replacing the process so the next invocation
300+ # skips the update check and doesn't loop.
301+ os .environ ["_ANTON_UPDATED" ] = "1"
298302 _reexec ()
299303
300304 ctx .ensure_object (dict )
@@ -865,8 +869,6 @@ def _test():
865869
866870def _setup_openai (settings , ws ) -> None :
867871 """Set up OpenAI with a single model for both reasoning and coding."""
868- import openai
869-
870872 console .print ()
871873 while True :
872874 api_key = _setup_prompt ("API key" , is_password = True )
@@ -919,8 +921,6 @@ def _test():
919921
920922def _setup_gemini (settings , ws ) -> None :
921923 """Set up Google Gemini via its OpenAI-compatible endpoint."""
922- import openai
923-
924924 _GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
925925
926926 console .print ()
@@ -977,10 +977,22 @@ def _test():
977977 ws .set_secret ("ANTON_CODING_MODEL" , model )
978978
979979
980- def _setup_custom_openai (settings , ws ) -> None :
981- """Set up a custom OpenAI-compatible endpoint (Ollama, vLLM, Together, Groq, LM Studio, etc.)."""
982- import openai
983980
981+ def _strip_to_azure_endpoint (raw_url : str ) -> str :
982+ """Return only the scheme+host of a URL, stripping any path/query.
983+
984+ AzureOpenAI constructs the deployment path internally, so the endpoint
985+ must not include /openai/deployments/... or ?api-version=...
986+ """
987+ from urllib .parse import urlparse
988+ parsed = urlparse (raw_url if "://" in raw_url else f"https://{ raw_url } " )
989+ scheme = parsed .scheme or "https"
990+ host = parsed .netloc or parsed .path
991+ return f"{ scheme } ://{ host } "
992+
993+
994+ def _setup_custom_openai (settings , ws ) -> None :
995+ """Set up a custom OpenAI-compatible endpoint (Ollama, vLLM, Together, Groq, LM Studio, Azure, etc.)."""
984996 console .print ()
985997 console .print (
986998 " [anton.muted]Works with Ollama, vLLM, Together, Groq, LM Studio, or any OpenAI-compatible API.[/]"
@@ -994,7 +1006,6 @@ def _setup_custom_openai(settings, ws) -> None:
9941006 console .print (" [anton.warning]Base URL is required.[/]" )
9951007 if not base_url .startswith ("http://" ) and not base_url .startswith ("https://" ):
9961008 base_url = "http://" + base_url
997- base_url = base_url .rstrip ("/" )
9981009
9991010 api_key = _setup_prompt (
10001011 "API key (Enter to skip if not needed)" , is_password = True
@@ -1008,10 +1019,32 @@ def _setup_custom_openai(settings, ws) -> None:
10081019 break
10091020 console .print (" [anton.warning]Model name is required.[/]" )
10101021
1022+ api_version = _setup_prompt (
1023+ "API version (leave blank for standard endpoints, required for Azure)"
1024+ ).strip () or None
1025+ if api_version and _is_azure_endpoint (base_url ):
1026+ # Strip path/query — AzureOpenAI builds the deployment URL internally.
1027+ base_url = _strip_to_azure_endpoint (base_url )
1028+
1029+ base_url = base_url .rstrip ("/" )
1030+
10111031 try :
10121032
10131033 def _test ():
1014- client = openai .OpenAI (api_key = api_key , base_url = base_url )
1034+ if api_version and _is_azure_endpoint (base_url ):
1035+ client = AzureOpenAI (
1036+ azure_endpoint = base_url ,
1037+ api_key = api_key ,
1038+ api_version = api_version ,
1039+ )
1040+ elif api_version :
1041+ client = openai .OpenAI (
1042+ api_key = api_key ,
1043+ base_url = base_url ,
1044+ default_query = {"api-version" : api_version },
1045+ )
1046+ else :
1047+ client = openai .OpenAI (api_key = api_key , base_url = base_url )
10151048 response = client .chat .completions .create (
10161049 ** build_chat_completion_kwargs (
10171050 model = model ,
@@ -1032,12 +1065,14 @@ def _test():
10321065
10331066 settings .openai_api_key = api_key
10341067 settings .openai_base_url = base_url
1068+ settings .openai_api_version = api_version
10351069 settings .planning_provider = "openai-compatible"
10361070 settings .coding_provider = "openai-compatible"
10371071 settings .planning_model = model
10381072 settings .coding_model = model
10391073 ws .set_secret ("ANTON_OPENAI_API_KEY" , api_key )
10401074 ws .set_secret ("ANTON_OPENAI_BASE_URL" , base_url )
1075+ ws .set_secret ("ANTON_OPENAI_API_VERSION" , api_version or "" )
10411076 ws .set_secret ("ANTON_PLANNING_PROVIDER" , "openai-compatible" )
10421077 ws .set_secret ("ANTON_CODING_PROVIDER" , "openai-compatible" )
10431078 ws .set_secret ("ANTON_PLANNING_MODEL" , model )
0 commit comments