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
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"
__version__ = "0.2.1"
__release_date__ = ""
85 changes: 62 additions & 23 deletions src/iac_code/commands/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@

from iac_code.config import (
_LEGACY_KEY_NAME_ALIASES,
PARTNER_SOURCES,
PartnerSource,
_load_yaml,
_save_yaml,
get_active_provider_key,
get_available_partner_sources,
get_credentials_path,
get_llm_source,
get_provider_config,
get_settings_path,
)
Expand Down Expand Up @@ -595,6 +599,47 @@ def _get_active_key_name() -> str:
return get_active_provider_key() or ""


def _third_party_auth_flow(
available_partners: list[PartnerSource],
current_llm_source: str,
) -> str | None | _BackSentinel:
"""Second-level selection within the Third-party category."""
candidates = list(available_partners)
if any(ps.key == current_llm_source for ps in PARTNER_SOURCES):
if not any(ps.key == current_llm_source for ps in candidates):
for ps in PARTNER_SOURCES:
if ps.key == current_llm_source:
candidates.append(ps)
break

if len(candidates) == 0:
return _BACK

options: list[str] = []
default_idx = 0
for i, ps in enumerate(candidates):
label = ps.display_name
if current_llm_source == ps.key:
label += _(" (current)")
default_idx = i
options.append(label)

idx = _select(_("Select provider — {group}").format(group=_("Third-party")), options, default_index=default_idx)
if idx is None:
return _BACK
partner = candidates[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,
)


def _llm_auth_flow(console, store) -> str | None | _BackSentinel:
"""LLM provider auth flow with two-step vendor group selection."""
active_key_name = _get_active_key_name()
Expand All @@ -621,49 +666,42 @@ 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()
available_partners = get_available_partner_sources()
is_current_partner = any(ps.key == current_llm_source for ps in PARTNER_SOURCES)
show_third_party = len(available_partners) > 0 or is_current_partner

while True:
# 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"]:
if show_third_party:
label = _("Third-party")
if is_current_partner:
label += _(" (current)")
group_default_idx = i
group_default_idx = 0
group_options.append(label)

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

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

# 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"],
)
if show_third_party and group_idx == 0:
result = _third_party_auth_flow(available_partners, current_llm_source)
if isinstance(result, _BackSentinel):
continue
return result

group_name, group_keys = provider_groups[group_idx - num_partner_sources]
offset = 1 if show_third_party else 0
group_name, group_keys = provider_groups[group_idx - offset]
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 Expand Up @@ -755,6 +793,7 @@ def _llm_auth_flow(console, store) -> str | None | _BackSentinel:


_GROUP_NAME_MARKERS = [
_("Third-party"),
_("Alibaba Cloud"),
_("ZhiPu AI"),
_("Kimi"),
Expand Down
4 changes: 2 additions & 2 deletions src/iac_code/commands/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ async def model_command(context: "CommandContext | None" = None, args: list[str]

display_name = llm_source
for ps in PARTNER_SOURCES:
if ps["key"] == llm_source:
display_name = ps["display_name"]
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."
Expand Down
41 changes: 39 additions & 2 deletions src/iac_code/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any

Expand Down Expand Up @@ -179,11 +180,47 @@ def get_llm_source() -> str:
return "local"


PARTNER_SOURCES: list[dict[str, str]] = [
{"key": "qwenpaw", "display_name": "QwenPaw"},
@dataclass(frozen=True)
class PartnerSource:
key: str
display_name: str

def is_available(self) -> bool:
if self.key == "qwenpaw":
from iac_code.services.qwenpaw_source import _resolve_secret_dir

return _resolve_secret_dir() is not None
return False

def get_provider_display(self) -> str:
if self.key == "qwenpaw":
from iac_code.services.qwenpaw_source import load_from_qwenpaw

try:
config = load_from_qwenpaw()
except Exception:
return ""
if config:
from iac_code.providers.registry import PROVIDER_REGISTRY

desc = PROVIDER_REGISTRY.get(config.provider_key)
if desc:
from iac_code.i18n import _

return _(desc.display_name)
return config.provider_key
return ""


PARTNER_SOURCES: list[PartnerSource] = [
PartnerSource(key="qwenpaw", display_name="QwenPaw"),
]


def get_available_partner_sources() -> list[PartnerSource]:
return [ps for ps in PARTNER_SOURCES if ps.is_available()]


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