Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys
from datetime import date
from pathlib import Path
import platform

from setuptools import setup
from setuptools.command.build_py import build_py
Expand Down Expand Up @@ -110,6 +111,8 @@ def _compile_translations():


def _replace_release_date():
if platform.system() == 'Darwin':
return
content = INIT_PATH.read_text(encoding="utf-8")
today = date.today().isoformat()
content = re.sub(
Expand Down
2 changes: 1 addition & 1 deletion src/iac_code/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "0.2.0"
__release_date__ = "2026-05-20"
__release_date__ = ""
58 changes: 33 additions & 25 deletions src/iac_code/commands/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
_save_yaml,
get_active_provider_key,
get_credentials_path,
get_llm_source,
get_provider_config,
get_settings_path,
)
Expand Down Expand Up @@ -564,33 +563,15 @@ async def auth_command(context: "CommandContext | None" = None, **kwargs) -> str
# immediately — _on_state_change may skip reinit when only the API key
# changed but the model and provider config stayed the same.
if context and hasattr(context, "repl") and context.repl:
context.repl._reinitialize_provider(context.repl.store.get_state().model)
repl = context.repl
repl._reinitialize_provider(repl.store.get_state().model)

return result


def _auth_flow(console, store) -> str | None:
"""Auth flow running inside alternate screen."""
llm_source = get_llm_source()
if llm_source != "local":
lock_notice = _("LLM provider is locked by '{source}'. To change, modify llm_source in settings.yml.").format(
source=llm_source
)
options = [_cloud_provider_display(p["name"]) for p in CLOUD_PROVIDERS]
idx = _select("{}\n\n{}".format(lock_notice, _("Select Cloud Provider")), options)
if idx is None:
return _("Auth cancelled")
provider = CLOUD_PROVIDERS[idx]
if provider["name"] == "aliyun":
result = _aliyun_auth_flow()
else:
result = _BACK
if isinstance(result, _BackSentinel):
return _("Auth cancelled")
return result

while True:
# Step 0: Select category
categories = [
_("Configure LLM Provider"),
_("Configure IaC Cloud Service"),
Expand All @@ -605,7 +586,7 @@ def _auth_flow(console, store) -> str | None:
result = _cloud_auth_flow(console)

if isinstance(result, _BackSentinel):
continue # Go back to category selection
continue
return result


Expand Down Expand Up @@ -640,22 +621,49 @@ def _llm_auth_flow(console, store) -> str | None | _BackSentinel:

provider_map: dict[str, LLMProvider] = {str(p["key_name"]): p for p in PROVIDERS}

from iac_code.config import PARTNER_SOURCES, get_llm_source

num_partner_sources = len(PARTNER_SOURCES)

current_llm_source = get_llm_source()

while True:
# Step 1: Select vendor group
# Step 1: Select vendor group (partner sources shown at the top)
group_options: list[str] = []
group_default_idx = 0

for i, ps in enumerate(PARTNER_SOURCES):
label = ps["display_name"]
if current_llm_source == ps["key"]:
label += _(" (current)")
group_default_idx = i
group_options.append(label)

for i, (group_name, keys) in enumerate(provider_groups):
label = _(group_name)
if active_key_name in keys:
label += _(" (current)")
group_default_idx = i
group_default_idx = i + num_partner_sources
group_options.append(label)

group_idx = _select(_("Select provider"), group_options, default_index=group_default_idx)
if group_idx is None:
return _BACK

group_name, group_keys = provider_groups[group_idx]
# Handle partner source selection
if group_idx < num_partner_sources:
partner = PARTNER_SOURCES[group_idx]
settings_path = get_settings_path()
config = _load_yaml(settings_path)
config.pop("activeProvider", None)
config["llm_source"] = partner["key"]
_save_yaml(settings_path, config)
return _("{status}: {provider}").format(
status=_("Configured"),
provider=partner["display_name"],
)

group_name, group_keys = provider_groups[group_idx - num_partner_sources]
group_providers = [provider_map[k] for k in group_keys if k in provider_map]

# Step 2: Select provider within group (skip if only one)
Expand Down
2 changes: 2 additions & 0 deletions src/iac_code/commands/clear.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ async def clear_command(context=None, **kwargs) -> str:
agent_loop = getattr(context.repl, "_agent_loop", None)
if agent_loop:
agent_loop.context_manager.reset()
if hasattr(context.repl, "_command_log"):
context.repl._command_log.clear()

if context and hasattr(context, "console"):
console = context.console
Expand Down
13 changes: 10 additions & 3 deletions src/iac_code/commands/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,16 @@ async def model_command(context: "CommandContext | None" = None, args: list[str]
"""Switch or display current model."""
llm_source = get_llm_source()
if llm_source != "local":
return _("Model selection is locked by '{source}'. To change, modify llm_source in settings.yml.").format(
source=llm_source
)
from iac_code.config import PARTNER_SOURCES

display_name = llm_source
for ps in PARTNER_SOURCES:
if ps["key"] == llm_source:
display_name = ps["display_name"]
break
return _(
"Model is managed by '{source}'. To change model, modify it in {source} or switch provider via /auth."
).format(source=display_name)

store = context.store if context else kwargs.get("store")
args = args or []
Expand Down
17 changes: 16 additions & 1 deletion src/iac_code/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,20 +155,35 @@ def _read(name: str) -> str | None:


def get_llm_source() -> str:
"""Return the LLM source: 'qwenpaw', 'local', or 'env'."""
"""Return the effective LLM source based on priority chain.

Priority (highest to lowest):
1. Environment variable (IAC_CODE_API_KEY) → "env"
2. activeProvider in settings → "local"
3. llm_source in settings → partner source value (e.g. "qwenpaw")
4. Nothing → "local"
"""
env = _get_env_overrides()
if env["api_key"]:
return "env"
try:
settings = _load_yaml(get_settings_path())
except Exception:
return "local"
active = settings.get("activeProvider")
if isinstance(active, str) and active.strip():
return "local"
source = settings.get("llm_source")
if isinstance(source, str) and source.strip():
return source.strip()
return "local"


PARTNER_SOURCES: list[dict[str, str]] = [
{"key": "qwenpaw", "display_name": "QwenPaw"},
]


# ---------------------------------------------------------------------------
# Path helpers
# ---------------------------------------------------------------------------
Expand Down
Loading
Loading