diff --git a/src/iac_code/cli/headless.py b/src/iac_code/cli/headless.py index 5671bfb2..7f7ed5c8 100644 --- a/src/iac_code/cli/headless.py +++ b/src/iac_code/cli/headless.py @@ -124,6 +124,7 @@ def __init__( verbose: bool = False, progress_stream: IO[str] | None = None, resume_session_id: str | bool | None = None, + thinking_enabled: bool | None = True, ) -> None: self._model = model self._output_format = output_format @@ -135,6 +136,7 @@ def __init__( self._verbose = verbose self._progress_stream = progress_stream or sys.stderr self._resume_session_id = resume_session_id + self._thinking_enabled = thinking_enabled self._mcp_config_warnings: list[Any] = [] self._mcp_warnings_printed_count = 0 self._runtime: Any | None = None @@ -162,10 +164,16 @@ def _record_structured_error(self, writer: Any, exc: Exception) -> None: def _create_agent_loop(self) -> Any: """Create and return a fully configured AgentLoop.""" + from iac_code.providers.request_policy import ProviderRequestPolicy from iac_code.services.agent_factory import AgentFactoryOptions, create_agent_runtime cwd = os.getcwd() session_id, resume_messages = self._resolve_resume_options() + request_policy_override = ( + ProviderRequestPolicy(thinking_enabled=self._thinking_enabled) + if self._thinking_enabled is not None + else None + ) runtime = create_agent_runtime( AgentFactoryOptions( @@ -173,6 +181,7 @@ def _create_agent_loop(self) -> Any: session_id=session_id, cwd=cwd, max_turns=self._max_turns, + request_policy_override=request_policy_override, cli_allowed_tools=self._cli_allowed_tools, cli_disallowed_tools=self._cli_disallowed_tools, cli_permission_mode=self._cli_permission_mode, diff --git a/src/iac_code/cli/main.py b/src/iac_code/cli/main.py index 5e73a9a5..4abfdf49 100644 --- a/src/iac_code/cli/main.py +++ b/src/iac_code/cli/main.py @@ -106,6 +106,11 @@ def main( max_turns: int = typer.Option(100, "--max-turns", help=_("Maximum agent turns in headless mode")), debug: bool = typer.Option(False, "--debug", "-d", help=_("Enable debug logging")), verbose: bool = typer.Option(False, "--verbose", help=_("Show headless progress on stderr")), + thinking_enabled: bool | None = typer.Option( + True, + "--thinking-enabled/--no-thinking-enabled", + help=_("Headless mode: whether to enable thinking for this request"), + ), version: bool = typer.Option(False, "--version", "-v", "-V", is_eager=True, help=_("Show version and exit")), resume: str = typer.Option("", "--resume", "-r", help=_("Resume a session by ID or name")), continue_session: bool = typer.Option(False, "--continue", "-c", help=_("Resume the most recent session")), @@ -282,6 +287,7 @@ async def _run_with_handler(coro): cli_permission_mode=permission_mode or None, verbose=verbose, resume_session_id=True if continue_session else (resume or None), + thinking_enabled=thinking_enabled, ) exit_code = asyncio.run(_run_with_handler(runner.run(prompt))) except _QwenPawError as exc: diff --git a/src/iac_code/commands/__init__.py b/src/iac_code/commands/__init__.py index 2bd05448..a0ddbe32 100644 --- a/src/iac_code/commands/__init__.py +++ b/src/iac_code/commands/__init__.py @@ -15,6 +15,7 @@ from iac_code.commands.resume import resume_command from iac_code.commands.skills import skills_command from iac_code.commands.status import status_command +from iac_code.commands.thinking_enabled import thinking_enabled_command from iac_code.i18n import _ @@ -56,6 +57,15 @@ def create_default_registry() -> CommandRegistry: history_mode="session", ) ) + registry.register( + LocalCommand( + name="thinking_enabled", + description=_("Show or switch whether thinking is enabled"), + handler=thinking_enabled_command, + arg_names=["on|off"], + history_mode="session", + ) + ) registry.register( LocalCommand( name="compact", diff --git a/src/iac_code/commands/auth.py b/src/iac_code/commands/auth.py index 2829afe7..ffedf2a5 100644 --- a/src/iac_code/commands/auth.py +++ b/src/iac_code/commands/auth.py @@ -132,7 +132,11 @@ def save_llm_key(key_name: str, api_key: str) -> None: def save_active_provider_config( - provider: LLMProvider | dict, model: str, effort: str | None = None, api_base: str | None = None + provider: LLMProvider | dict, + model: str, + effort: str | None = None, + api_base: str | None = None, + thinking_enabled: bool | None = None, ) -> None: """Persist the provider's per-provider config and mark it active.""" settings_path = get_settings_path() @@ -152,6 +156,8 @@ def save_active_provider_config( entry["apiBase"] = effective_api_base if effort is not None: entry["effort"] = effort + if thinking_enabled is not None: + entry["thinkingEnabled"] = thinking_enabled providers[key_name] = entry for legacy, canonical in _LEGACY_KEY_NAME_ALIASES.items(): diff --git a/src/iac_code/commands/thinking_enabled.py b/src/iac_code/commands/thinking_enabled.py new file mode 100644 index 00000000..e9f4b84c --- /dev/null +++ b/src/iac_code/commands/thinking_enabled.py @@ -0,0 +1,117 @@ +"""Thinking-enabled command -- show or change provider thinking mode.""" + +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING + +from iac_code.commands.auth import PROVIDERS, LLMProvider, _select, save_active_provider_config +from iac_code.config import get_active_provider_key, get_provider_config +from iac_code.i18n import _ +from iac_code.providers.request_policy import bool_or_none + +if TYPE_CHECKING: + from iac_code.ui.repl import CommandContext + + +class ThinkingEnabledCommand: + """Handle the /thinking_enabled command for the active provider.""" + + def __init__(self, context: "CommandContext | None", kwargs: dict) -> None: + self.context = context + self.store = context.store if context else kwargs.get("store") + + async def run(self, args: list[str] | None = None) -> str: + args = args or [] + provider = self._active_provider() + if provider is None: + return _("No configured providers. Run /auth first.") + + current_model = self._current_model() + if not current_model: + return _("No model selected. Run /model first.") + + if args: + enabled = self._parse_enabled(args[0]) + if enabled is None: + return _("Invalid thinking_enabled. Allowed: on, off.") + return self._apply_enabled(provider, current_model, enabled) + + saved = bool_or_none(get_provider_config(str(provider["key_name"])).get("thinkingEnabled")) + if not self.context or not self.context.console: + return self._current_message(saved) + + selected = self._select_enabled(saved) + if selected is None: + return self._current_message(saved) + return self._apply_enabled(provider, current_model, selected) + + def _active_provider(self) -> LLMProvider | None: + key = get_active_provider_key() + if not key: + return None + for provider in PROVIDERS: + if str(provider["key_name"]) == key: + return provider + return None + + def _current_model(self) -> str: + if self.store is None: + return "" + state = self.store.get_state() + model = getattr(state, "model", "") + return model if isinstance(model, str) else "" + + @staticmethod + def _parse_enabled(value: str) -> bool | None: + return bool_or_none(value) + + def _select_enabled(self, current: bool | None) -> bool | None: + options = [self._option_label(True), self._option_label(False)] + default_idx = 1 if current is False else 0 + sys.stdout.write("\033[?1049h") + sys.stdout.flush() + try: + idx = _select( + self._current_message(current), + options, + default_index=default_idx, + ) + finally: + sys.stdout.write("\033[?1049l") + sys.stdout.flush() + if idx is None: + return None + if idx < 0 or idx >= len(options): + return None + return idx == 0 + + @staticmethod + def _option_label(enabled: bool) -> str: + return _("on") if enabled else _("off") + + def _apply_enabled(self, provider: LLMProvider, model: str, enabled: bool) -> str: + save_active_provider_config(provider, model, thinking_enabled=enabled) + if self.store is not None: + self.store.set_state(thinking_enabled=enabled) + return _("thinking_enabled switched to: {state}").format(state=self._state_label(enabled)) + + def _current_message(self, enabled: bool | None) -> str: + return _("Current thinking_enabled: {state}").format(state=self._state_label(enabled)) + + @staticmethod + def _state_label(enabled: bool | None) -> str: + if enabled is True: + return _("enabled") + if enabled is False: + return _("disabled") + return _("not configured") + + +async def thinking_enabled_command( + context: "CommandContext | None" = None, + args: list[str] | None = None, + **kwargs, +) -> str: + """Show or change whether the active provider should request thinking.""" + return await ThinkingEnabledCommand(context, kwargs).run(args) diff --git a/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po index c0a26806..b04a4357 100644 --- a/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/de/LC_MESSAGES/messages.po @@ -719,6 +719,10 @@ msgstr "Debug-Protokollierung aktivieren" msgid "Show headless progress on stderr" msgstr "Headless-Fortschritt auf stderr anzeigen" +#: src/iac_code/cli/main.py +msgid "Headless mode: whether to enable thinking for this request" +msgstr "Headless-Modus: ob Thinking fuer diese Anfrage aktiviert wird" + #: src/iac_code/cli/main.py msgid "Show version and exit" msgstr "Version anzeigen und beenden" @@ -1088,6 +1092,10 @@ msgstr "Modell anzeigen oder wechseln" msgid "Show or switch thinking effort" msgstr "Thinking-Effort anzeigen oder wechseln" +#: src/iac_code/commands/__init__.py +msgid "Show or switch whether thinking is enabled" +msgstr "Thinking-Aktivierung anzeigen oder umschalten" + #: src/iac_code/commands/__init__.py msgid "Compact conversation context" msgstr "Konversationskontext komprimieren" @@ -1462,10 +1470,11 @@ msgid "No active session." msgstr "Keine aktive Sitzung." #: src/iac_code/commands/effort.py src/iac_code/commands/model.py +#: src/iac_code/commands/thinking_enabled.py msgid "No configured providers. Run /auth first." msgstr "Keine konfigurierten Anbieter. Führen Sie zuerst /auth aus." -#: src/iac_code/commands/effort.py +#: src/iac_code/commands/effort.py src/iac_code/commands/thinking_enabled.py msgid "No model selected. Run /model first." msgstr "Kein Modell ausgewählt. Führen Sie zuerst /model aus." @@ -1485,6 +1494,7 @@ msgid "Current effort: {effort}" msgstr "Aktueller Thinking-Effort: {effort}" #: src/iac_code/commands/effort.py src/iac_code/commands/status.py +#: src/iac_code/commands/thinking_enabled.py msgid "not configured" msgstr "nicht konfiguriert" @@ -2143,6 +2153,38 @@ msgstr "{session_id} (fortgesetzt)" msgid "{percent} used ({total} / {window})" msgstr "{percent} verwendet ({total} / {window})" +#: src/iac_code/commands/thinking_enabled.py +msgid "Invalid thinking_enabled. Allowed: on, off." +msgstr "Ungueltiger thinking_enabled-Wert. Erlaubt: on, off." + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "on" +msgstr "aktiviert" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "off" +msgstr "deaktiviert" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "thinking_enabled switched to: {state}" +msgstr "thinking_enabled umgeschaltet auf: {state}" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "Current thinking_enabled: {state}" +msgstr "Aktueller thinking_enabled-Wert: {state}" + +#: src/iac_code/commands/thinking_enabled.py +msgid "enabled" +msgstr "aktiviert" + +#: src/iac_code/commands/thinking_enabled.py +msgid "disabled" +msgstr "deaktiviert" + # Typer/Click built-in strings #: src/iac_code/i18n/__init__.py msgid "Options" @@ -6257,14 +6299,6 @@ msgstr "Speicher" msgid "Auto-memory: {state}" msgstr "Auto-Speicher: {state}" -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "on" -msgstr "aktiviert" - -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "off" -msgstr "deaktiviert" - #: src/iac_code/ui/dialogs/memory.py msgid "Enter to confirm · Esc to cancel" msgstr "Enter zum Bestätigen · Esc zum Abbrechen" @@ -6804,33 +6838,3 @@ msgstr "Öffnen einer nicht regulären Datei verweigert: {path}" #~ msgid "feature" #~ msgstr "Funktion" -#~ msgid "ECS instance" -#~ msgstr "ECS-Instanz" - -#~ msgid "ECS instance group" -#~ msgstr "ECS-Instanzgruppe" - -#~ msgid "Elastic IP address" -#~ msgstr "Elastische IP-Adresse" - -#~ msgid "SLB load balancer" -#~ msgstr "SLB-Load-Balancer" - -#~ msgid "NAT gateway" -#~ msgstr "NAT-Gateway" - -#~ msgid "Shared bandwidth package" -#~ msgstr "Paket für gemeinsam genutzte Bandbreite" - -#~ msgid "Database instance" -#~ msgstr "Datenbankinstanz" - -#~ msgid "VPC" -#~ msgstr "VPC" - -#~ msgid "VSwitch" -#~ msgstr "VSwitch" - -#~ msgid "Security group" -#~ msgstr "Sicherheitsgruppe" - diff --git a/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po index 5369b195..779ab003 100644 --- a/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/es/LC_MESSAGES/messages.po @@ -723,6 +723,10 @@ msgstr "Habilitar el registro de depuración" msgid "Show headless progress on stderr" msgstr "Mostrar el progreso headless en stderr" +#: src/iac_code/cli/main.py +msgid "Headless mode: whether to enable thinking for this request" +msgstr "Modo headless: si se habilita thinking para esta solicitud" + #: src/iac_code/cli/main.py msgid "Show version and exit" msgstr "Mostrar la versión y salir" @@ -1089,6 +1093,10 @@ msgstr "Mostrar o cambiar de modelo" msgid "Show or switch thinking effort" msgstr "Mostrar o cambiar el nivel de razonamiento (effort)" +#: src/iac_code/commands/__init__.py +msgid "Show or switch whether thinking is enabled" +msgstr "Mostrar o cambiar si thinking esta habilitado" + #: src/iac_code/commands/__init__.py msgid "Compact conversation context" msgstr "Compactar el contexto de la conversación" @@ -1463,10 +1471,11 @@ msgid "No active session." msgstr "No hay ninguna sesión activa." #: src/iac_code/commands/effort.py src/iac_code/commands/model.py +#: src/iac_code/commands/thinking_enabled.py msgid "No configured providers. Run /auth first." msgstr "No hay proveedores configurados. Ejecute /auth primero." -#: src/iac_code/commands/effort.py +#: src/iac_code/commands/effort.py src/iac_code/commands/thinking_enabled.py msgid "No model selected. Run /model first." msgstr "No hay ningún modelo seleccionado. Ejecute /model primero." @@ -1486,6 +1495,7 @@ msgid "Current effort: {effort}" msgstr "Effort actual: {effort}" #: src/iac_code/commands/effort.py src/iac_code/commands/status.py +#: src/iac_code/commands/thinking_enabled.py msgid "not configured" msgstr "no configurado" @@ -2148,6 +2158,38 @@ msgstr "{session_id} (reanudada)" msgid "{percent} used ({total} / {window})" msgstr "{percent} usado ({total} / {window})" +#: src/iac_code/commands/thinking_enabled.py +msgid "Invalid thinking_enabled. Allowed: on, off." +msgstr "Valor de thinking_enabled no valido. Permitidos: on, off." + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "on" +msgstr "activada" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "off" +msgstr "desactivada" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "thinking_enabled switched to: {state}" +msgstr "thinking_enabled cambiado a: {state}" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "Current thinking_enabled: {state}" +msgstr "thinking_enabled actual: {state}" + +#: src/iac_code/commands/thinking_enabled.py +msgid "enabled" +msgstr "habilitado" + +#: src/iac_code/commands/thinking_enabled.py +msgid "disabled" +msgstr "deshabilitado" + # Typer/Click built-in strings #: src/iac_code/i18n/__init__.py msgid "Options" @@ -6243,14 +6285,6 @@ msgstr "Memoria" msgid "Auto-memory: {state}" msgstr "Memoria automática: {state}" -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "on" -msgstr "activada" - -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "off" -msgstr "desactivada" - #: src/iac_code/ui/dialogs/memory.py msgid "Enter to confirm · Esc to cancel" msgstr "Enter para confirmar · Esc para cancelar" @@ -6781,33 +6815,3 @@ msgstr "Se rechazó abrir un archivo no regular: {path}" #~ msgid "feature" #~ msgstr "función" -#~ msgid "ECS instance" -#~ msgstr "Instancia ECS" - -#~ msgid "ECS instance group" -#~ msgstr "Grupo de instancias ECS" - -#~ msgid "Elastic IP address" -#~ msgstr "Dirección IP elástica" - -#~ msgid "SLB load balancer" -#~ msgstr "Balanceador de carga SLB" - -#~ msgid "NAT gateway" -#~ msgstr "Puerta de enlace NAT" - -#~ msgid "Shared bandwidth package" -#~ msgstr "Paquete de ancho de banda compartido" - -#~ msgid "Database instance" -#~ msgstr "Instancia de base de datos" - -#~ msgid "VPC" -#~ msgstr "VPC" - -#~ msgid "VSwitch" -#~ msgstr "VSwitch" - -#~ msgid "Security group" -#~ msgstr "Grupo de seguridad" - diff --git a/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po index 4617b6a7..9b1f6db2 100644 --- a/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/fr/LC_MESSAGES/messages.po @@ -720,6 +720,10 @@ msgstr "Activer la journalisation debug" msgid "Show headless progress on stderr" msgstr "Afficher la progression headless sur stderr" +#: src/iac_code/cli/main.py +msgid "Headless mode: whether to enable thinking for this request" +msgstr "Mode sans interface : active ou non thinking pour cette requete" + #: src/iac_code/cli/main.py msgid "Show version and exit" msgstr "Afficher la version et quitter" @@ -1086,6 +1090,10 @@ msgstr "Afficher ou changer de modèle" msgid "Show or switch thinking effort" msgstr "Afficher ou modifier l’effort de raisonnement" +#: src/iac_code/commands/__init__.py +msgid "Show or switch whether thinking is enabled" +msgstr "Afficher ou modifier si thinking est active" + #: src/iac_code/commands/__init__.py msgid "Compact conversation context" msgstr "Compacter le contexte de conversation" @@ -1466,10 +1474,11 @@ msgid "No active session." msgstr "Aucune session active." #: src/iac_code/commands/effort.py src/iac_code/commands/model.py +#: src/iac_code/commands/thinking_enabled.py msgid "No configured providers. Run /auth first." msgstr "Aucun fournisseur configuré. Exécutez d’abord /auth." -#: src/iac_code/commands/effort.py +#: src/iac_code/commands/effort.py src/iac_code/commands/thinking_enabled.py msgid "No model selected. Run /model first." msgstr "Aucun modèle sélectionné. Exécutez d’abord /model." @@ -1489,6 +1498,7 @@ msgid "Current effort: {effort}" msgstr "Effort de raisonnement actuel : {effort}" #: src/iac_code/commands/effort.py src/iac_code/commands/status.py +#: src/iac_code/commands/thinking_enabled.py msgid "not configured" msgstr "non configuré" @@ -2149,6 +2159,38 @@ msgstr "{session_id} (reprise)" msgid "{percent} used ({total} / {window})" msgstr "{percent} utilisé ({total} / {window})" +#: src/iac_code/commands/thinking_enabled.py +msgid "Invalid thinking_enabled. Allowed: on, off." +msgstr "Valeur thinking_enabled invalide. Valeurs autorisees : on, off." + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "on" +msgstr "activée" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "off" +msgstr "désactivée" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "thinking_enabled switched to: {state}" +msgstr "thinking_enabled defini sur : {state}" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "Current thinking_enabled: {state}" +msgstr "thinking_enabled actuel : {state}" + +#: src/iac_code/commands/thinking_enabled.py +msgid "enabled" +msgstr "active" + +#: src/iac_code/commands/thinking_enabled.py +msgid "disabled" +msgstr "desactive" + # Typer/Click built-in strings #: src/iac_code/i18n/__init__.py msgid "Options" @@ -6267,14 +6309,6 @@ msgstr "Mémoire" msgid "Auto-memory: {state}" msgstr "Mémoire automatique : {state}" -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "on" -msgstr "activée" - -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "off" -msgstr "désactivée" - #: src/iac_code/ui/dialogs/memory.py msgid "Enter to confirm · Esc to cancel" msgstr "Entrée pour confirmer · Échap pour annuler" @@ -6802,33 +6836,3 @@ msgstr "Refus d’ouvrir un fichier non ordinaire : {path}" #~ msgid "feature" #~ msgstr "fonctionnalité" -#~ msgid "ECS instance" -#~ msgstr "Instance ECS" - -#~ msgid "ECS instance group" -#~ msgstr "Groupe d’instances ECS" - -#~ msgid "Elastic IP address" -#~ msgstr "Adresse IP élastique" - -#~ msgid "SLB load balancer" -#~ msgstr "Équilibreur de charge SLB" - -#~ msgid "NAT gateway" -#~ msgstr "Passerelle NAT" - -#~ msgid "Shared bandwidth package" -#~ msgstr "Forfait de bande passante partagée" - -#~ msgid "Database instance" -#~ msgstr "Instance de base de données" - -#~ msgid "VPC" -#~ msgstr "VPC" - -#~ msgid "VSwitch" -#~ msgstr "VSwitch" - -#~ msgid "Security group" -#~ msgstr "Groupe de sécurité" - diff --git a/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po index de1aa986..e92e9558 100644 --- a/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/ja/LC_MESSAGES/messages.po @@ -682,6 +682,10 @@ msgstr "デバッグログを有効にする" msgid "Show headless progress on stderr" msgstr "ヘッドレス進行状況を stderr に表示" +#: src/iac_code/cli/main.py +msgid "Headless mode: whether to enable thinking for this request" +msgstr "ヘッドレスモード: このリクエストで thinking を有効にするか" + #: src/iac_code/cli/main.py msgid "Show version and exit" msgstr "バージョンを表示して終了する" @@ -1038,6 +1042,10 @@ msgstr "モデルを表示または切り替えます" msgid "Show or switch thinking effort" msgstr "思考の負荷(effort)を表示または切り替えます" +#: src/iac_code/commands/__init__.py +msgid "Show or switch whether thinking is enabled" +msgstr "thinking の有効/無効を表示または切り替えます" + #: src/iac_code/commands/__init__.py msgid "Compact conversation context" msgstr "会話コンテキストを圧縮します" @@ -1414,10 +1422,11 @@ msgid "No active session." msgstr "アクティブなセッションがありません。" #: src/iac_code/commands/effort.py src/iac_code/commands/model.py +#: src/iac_code/commands/thinking_enabled.py msgid "No configured providers. Run /auth first." msgstr "設定済みのプロバイダーがありません。先に /auth を実行してください。" -#: src/iac_code/commands/effort.py +#: src/iac_code/commands/effort.py src/iac_code/commands/thinking_enabled.py msgid "No model selected. Run /model first." msgstr "モデルが選択されていません。先に /model を実行してください。" @@ -1437,6 +1446,7 @@ msgid "Current effort: {effort}" msgstr "現在の effort:{effort}" #: src/iac_code/commands/effort.py src/iac_code/commands/status.py +#: src/iac_code/commands/thinking_enabled.py msgid "not configured" msgstr "未設定" @@ -2079,6 +2089,38 @@ msgstr "{session_id}(再開済み)" msgid "{percent} used ({total} / {window})" msgstr "{percent} 使用済み({total} / {window})" +#: src/iac_code/commands/thinking_enabled.py +msgid "Invalid thinking_enabled. Allowed: on, off." +msgstr "thinking_enabled が無効です。指定可能な値: on, off。" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "on" +msgstr "有効" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "off" +msgstr "無効" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "thinking_enabled switched to: {state}" +msgstr "thinking_enabled を {state} に切り替えました" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "Current thinking_enabled: {state}" +msgstr "現在の thinking_enabled: {state}" + +#: src/iac_code/commands/thinking_enabled.py +msgid "enabled" +msgstr "有効" + +#: src/iac_code/commands/thinking_enabled.py +msgid "disabled" +msgstr "無効" + # Typer/Click built-in strings #: src/iac_code/i18n/__init__.py msgid "Options" @@ -5946,14 +5988,6 @@ msgstr "メモリ" msgid "Auto-memory: {state}" msgstr "自動メモリ: {state}" -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "on" -msgstr "有効" - -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "off" -msgstr "無効" - #: src/iac_code/ui/dialogs/memory.py msgid "Enter to confirm · Esc to cancel" msgstr "Enter で確定 · Esc でキャンセル" @@ -6423,33 +6457,3 @@ msgstr "通常ファイルではないファイルを開くことを拒否しま #~ msgid "feature" #~ msgstr "機能" -#~ msgid "ECS instance" -#~ msgstr "ECS インスタンス" - -#~ msgid "ECS instance group" -#~ msgstr "ECS インスタンスグループ" - -#~ msgid "Elastic IP address" -#~ msgstr "Elastic IP アドレス" - -#~ msgid "SLB load balancer" -#~ msgstr "SLB ロードバランサー" - -#~ msgid "NAT gateway" -#~ msgstr "NAT ゲートウェイ" - -#~ msgid "Shared bandwidth package" -#~ msgstr "共有帯域幅パッケージ" - -#~ msgid "Database instance" -#~ msgstr "データベースインスタンス" - -#~ msgid "VPC" -#~ msgstr "VPC" - -#~ msgid "VSwitch" -#~ msgstr "VSwitch" - -#~ msgid "Security group" -#~ msgstr "セキュリティグループ" - diff --git a/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po index 1cb4942a..19d30b37 100644 --- a/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/pt/LC_MESSAGES/messages.po @@ -716,6 +716,10 @@ msgstr "Ativar debug" msgid "Show headless progress on stderr" msgstr "Mostrar progresso do modo headless no stderr" +#: src/iac_code/cli/main.py +msgid "Headless mode: whether to enable thinking for this request" +msgstr "Modo headless: se thinking deve ser habilitado para esta solicitacao" + #: src/iac_code/cli/main.py msgid "Show version and exit" msgstr "Mostrar versão e sair" @@ -1080,6 +1084,10 @@ msgstr "Exibir ou trocar modelo" msgid "Show or switch thinking effort" msgstr "Exibir ou alterar o nível de esforço de raciocínio" +#: src/iac_code/commands/__init__.py +msgid "Show or switch whether thinking is enabled" +msgstr "Exibir ou alterar se thinking esta habilitado" + #: src/iac_code/commands/__init__.py msgid "Compact conversation context" msgstr "Compactar contexto da conversa" @@ -1454,10 +1462,11 @@ msgid "No active session." msgstr "Nenhuma sessão ativa." #: src/iac_code/commands/effort.py src/iac_code/commands/model.py +#: src/iac_code/commands/thinking_enabled.py msgid "No configured providers. Run /auth first." msgstr "Nenhum provedor configurado. Execute /auth primeiro." -#: src/iac_code/commands/effort.py +#: src/iac_code/commands/effort.py src/iac_code/commands/thinking_enabled.py msgid "No model selected. Run /model first." msgstr "Nenhum modelo selecionado. Execute /model primeiro." @@ -1477,6 +1486,7 @@ msgid "Current effort: {effort}" msgstr "Esforço atual: {effort}" #: src/iac_code/commands/effort.py src/iac_code/commands/status.py +#: src/iac_code/commands/thinking_enabled.py msgid "not configured" msgstr "não configurado" @@ -2137,6 +2147,38 @@ msgstr "{session_id} (retomada)" msgid "{percent} used ({total} / {window})" msgstr "{percent} usado ({total} / {window})" +#: src/iac_code/commands/thinking_enabled.py +msgid "Invalid thinking_enabled. Allowed: on, off." +msgstr "thinking_enabled invalido. Permitidos: on, off." + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "on" +msgstr "ativada" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "off" +msgstr "desativada" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "thinking_enabled switched to: {state}" +msgstr "thinking_enabled alterado para: {state}" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "Current thinking_enabled: {state}" +msgstr "thinking_enabled atual: {state}" + +#: src/iac_code/commands/thinking_enabled.py +msgid "enabled" +msgstr "habilitado" + +#: src/iac_code/commands/thinking_enabled.py +msgid "disabled" +msgstr "desabilitado" + # Typer/Click built-in strings #: src/iac_code/i18n/__init__.py msgid "Options" @@ -6196,14 +6238,6 @@ msgstr "Memória" msgid "Auto-memory: {state}" msgstr "Memória automática: {state}" -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "on" -msgstr "ativada" - -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "off" -msgstr "desativada" - #: src/iac_code/ui/dialogs/memory.py msgid "Enter to confirm · Esc to cancel" msgstr "Enter para confirmar · Esc para cancelar" @@ -6720,33 +6754,3 @@ msgstr "Recusado abrir arquivo não regular: {path}" #~ msgid "feature" #~ msgstr "recurso" -#~ msgid "ECS instance" -#~ msgstr "Instância ECS" - -#~ msgid "ECS instance group" -#~ msgstr "Grupo de instâncias ECS" - -#~ msgid "Elastic IP address" -#~ msgstr "Endereço IP elástico" - -#~ msgid "SLB load balancer" -#~ msgstr "Balanceador de carga SLB" - -#~ msgid "NAT gateway" -#~ msgstr "Gateway NAT" - -#~ msgid "Shared bandwidth package" -#~ msgstr "Pacote de largura de banda compartilhada" - -#~ msgid "Database instance" -#~ msgstr "Instância de banco de dados" - -#~ msgid "VPC" -#~ msgstr "VPC" - -#~ msgid "VSwitch" -#~ msgstr "VSwitch" - -#~ msgid "Security group" -#~ msgstr "Grupo de segurança" - diff --git a/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po b/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po index 723d3fbb..26e5d83f 100644 --- a/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po +++ b/src/iac_code/i18n/locales/zh/LC_MESSAGES/messages.po @@ -676,6 +676,10 @@ msgstr "启用调试日志" msgid "Show headless progress on stderr" msgstr "在 stderr 显示无头模式进度" +#: src/iac_code/cli/main.py +msgid "Headless mode: whether to enable thinking for this request" +msgstr "无头模式:是否为此请求启用 thinking" + #: src/iac_code/cli/main.py msgid "Show version and exit" msgstr "显示版本号并退出" @@ -1028,6 +1032,10 @@ msgstr "显示或切换模型" msgid "Show or switch thinking effort" msgstr "显示或切换思考强度" +#: src/iac_code/commands/__init__.py +msgid "Show or switch whether thinking is enabled" +msgstr "显示或切换是否启用 thinking" + #: src/iac_code/commands/__init__.py msgid "Compact conversation context" msgstr "压缩对话上下文" @@ -1402,10 +1410,11 @@ msgid "No active session." msgstr "没有活动的会话。" #: src/iac_code/commands/effort.py src/iac_code/commands/model.py +#: src/iac_code/commands/thinking_enabled.py msgid "No configured providers. Run /auth first." msgstr "没有已配置的提供商。请先运行 /auth。" -#: src/iac_code/commands/effort.py +#: src/iac_code/commands/effort.py src/iac_code/commands/thinking_enabled.py msgid "No model selected. Run /model first." msgstr "没有选择模型。请先运行 /model。" @@ -1425,6 +1434,7 @@ msgid "Current effort: {effort}" msgstr "当前思考强度:{effort}" #: src/iac_code/commands/effort.py src/iac_code/commands/status.py +#: src/iac_code/commands/thinking_enabled.py msgid "not configured" msgstr "未配置" @@ -2065,6 +2075,38 @@ msgstr "{session_id}(已恢复)" msgid "{percent} used ({total} / {window})" msgstr "已使用 {percent}({total} / {window})" +#: src/iac_code/commands/thinking_enabled.py +msgid "Invalid thinking_enabled. Allowed: on, off." +msgstr "无效的 thinking_enabled。允许值:on、off。" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "on" +msgstr "启用" + +#: src/iac_code/commands/thinking_enabled.py src/iac_code/ui/dialogs/memory.py +#: src/iac_code/ui/dialogs/skills_picker.py +msgid "off" +msgstr "禁用" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "thinking_enabled switched to: {state}" +msgstr "thinking_enabled 已切换为:{state}" + +#: src/iac_code/commands/thinking_enabled.py +#, python-brace-format +msgid "Current thinking_enabled: {state}" +msgstr "当前 thinking_enabled:{state}" + +#: src/iac_code/commands/thinking_enabled.py +msgid "enabled" +msgstr "已启用" + +#: src/iac_code/commands/thinking_enabled.py +msgid "disabled" +msgstr "已禁用" + # Typer/Click built-in strings #: src/iac_code/i18n/__init__.py msgid "Options" @@ -5871,14 +5913,6 @@ msgstr "记忆" msgid "Auto-memory: {state}" msgstr "自动记忆:{state}" -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "on" -msgstr "启用" - -#: src/iac_code/ui/dialogs/memory.py src/iac_code/ui/dialogs/skills_picker.py -msgid "off" -msgstr "禁用" - #: src/iac_code/ui/dialogs/memory.py msgid "Enter to confirm · Esc to cancel" msgstr "Enter 确认 · Esc 取消" @@ -6326,33 +6360,3 @@ msgstr "拒绝打开非常规文件:{path}" #~ msgid "feature" #~ msgstr "功能" -#~ msgid "ECS instance" -#~ msgstr "ECS 实例" - -#~ msgid "ECS instance group" -#~ msgstr "ECS 实例组" - -#~ msgid "Elastic IP address" -#~ msgstr "弹性公网 IP" - -#~ msgid "SLB load balancer" -#~ msgstr "SLB 负载均衡" - -#~ msgid "NAT gateway" -#~ msgstr "NAT 网关" - -#~ msgid "Shared bandwidth package" -#~ msgstr "共享带宽包" - -#~ msgid "Database instance" -#~ msgstr "数据库实例" - -#~ msgid "VPC" -#~ msgstr "专有网络 VPC" - -#~ msgid "VSwitch" -#~ msgstr "交换机" - -#~ msgid "Security group" -#~ msgstr "安全组" - diff --git a/src/iac_code/services/agent_factory.py b/src/iac_code/services/agent_factory.py index d4be2ec3..19cc0392 100644 --- a/src/iac_code/services/agent_factory.py +++ b/src/iac_code/services/agent_factory.py @@ -17,6 +17,7 @@ class AgentFactoryOptions: session_id: str | None = None cwd: str | None = None max_turns: int = 100 + request_policy_override: Any = None cli_allowed_tools: list[str] | None = None cli_disallowed_tools: list[str] | None = None cli_permission_mode: str | None = None @@ -119,6 +120,7 @@ def create_agent_runtime(options: AgentFactoryOptions) -> AgentRuntime: credentials=credentials, provider_key_override=provider_key_override, base_url_override=base_url_override, + request_policy_override=options.request_policy_override, ) tool_registry = ToolRegistry() diff --git a/src/iac_code/state/app_state.py b/src/iac_code/state/app_state.py index 6c46d3d8..55550499 100644 --- a/src/iac_code/state/app_state.py +++ b/src/iac_code/state/app_state.py @@ -67,6 +67,7 @@ class AppState: spinner_text: str = "" context_usage_percent: float = 0.0 effort_level: Any | None = None # EffortLevel enum or None (avoid circular import) + thinking_enabled: bool | None = None class AppStateStore: diff --git a/tests/cli/test_headless.py b/tests/cli/test_headless.py index 32c48060..d9c6dbae 100644 --- a/tests/cli/test_headless.py +++ b/tests/cli/test_headless.py @@ -627,6 +627,150 @@ def test_verbose_passed_to_headless(self): call_kwargs = mock_runner.call_args[1] assert call_kwargs["verbose"] is True + def test_thinking_enabled_passed_to_headless(self): + from iac_code.cli.main import app + + with patch("iac_code.cli.headless.HeadlessRunner") as mock_runner: + mock_instance = MagicMock() + mock_instance.run = AsyncMock(return_value=0) + mock_runner.return_value = mock_instance + + result = runner_cli.invoke(app, ["-p", "hello", "--no-thinking-enabled"]) + + assert result.exit_code == 0 + call_kwargs = mock_runner.call_args[1] + assert call_kwargs["thinking_enabled"] is False + + def test_thinking_enabled_defaults_to_enabled_for_headless(self): + from iac_code.cli.main import app + + with patch("iac_code.cli.headless.HeadlessRunner") as mock_runner: + mock_instance = MagicMock() + mock_instance.run = AsyncMock(return_value=0) + mock_runner.return_value = mock_instance + + result = runner_cli.invoke(app, ["-p", "hello"]) + + assert result.exit_code == 0 + call_kwargs = mock_runner.call_args[1] + assert call_kwargs["thinking_enabled"] is True + + @pytest.mark.parametrize( + ("flag", "expected"), + [ + ("--thinking-enabled", True), + ("--no-thinking-enabled", False), + ], + ) + def test_headless_cli_thinking_enabled_reaches_agent_runtime(self, monkeypatch, flag, expected): + from iac_code.cli.main import app + + captured = {} + + class FakeLoop: + async def run_streaming(self, prompt): + captured["prompt"] = prompt + yield MessageEndEvent(stop_reason="end_turn", usage=Usage()) + + class FakeRuntime: + agent_loop = FakeLoop() + mcp_config_warnings = [] + + async def aclose(self): + captured["closed"] = True + + def fake_create_agent_runtime(options): + captured["options"] = options + return FakeRuntime() + + monkeypatch.setattr("iac_code.services.agent_factory.create_agent_runtime", fake_create_agent_runtime) + + result = runner_cli.invoke(app, ["-p", "hello", flag]) + + assert result.exit_code == 0 + assert captured["prompt"] == "hello" + assert captured["options"].request_policy_override.thinking_enabled is expected + assert captured["closed"] is True + + def test_headless_cli_without_thinking_flag_enables_thinking_by_default(self, monkeypatch): + from iac_code.cli.main import app + + captured = {} + + class FakeLoop: + async def run_streaming(self, prompt): + captured["prompt"] = prompt + yield MessageEndEvent(stop_reason="end_turn", usage=Usage()) + + class FakeRuntime: + agent_loop = FakeLoop() + mcp_config_warnings = [] + + async def aclose(self): + captured["closed"] = True + + def fake_create_agent_runtime(options): + captured["options"] = options + return FakeRuntime() + + monkeypatch.setattr("iac_code.services.agent_factory.create_agent_runtime", fake_create_agent_runtime) + + result = runner_cli.invoke( + app, + [ + "-p", + "hello", + "--output-format", + "json", + "--max-turns", + "3", + ], + ) + + assert result.exit_code == 0 + assert captured["prompt"] == "hello" + assert captured["options"].request_policy_override.thinking_enabled is True + assert captured["options"].max_turns == 3 + assert captured["closed"] is True + + def test_headless_cli_without_thinking_flag_keeps_dashscope_default_thinking_enabled(self, monkeypatch): + from iac_code.cli.main import app + from iac_code.providers.manager import create_provider + + captured = {} + + class FakeLoop: + async def run_streaming(self, prompt): + captured["prompt"] = prompt + yield MessageEndEvent(stop_reason="end_turn", usage=Usage()) + + class FakeRuntime: + agent_loop = FakeLoop() + mcp_config_warnings = [] + + async def aclose(self): + captured["closed"] = True + + def fake_create_agent_runtime(options): + captured["options"] = options + return FakeRuntime() + + monkeypatch.setattr("iac_code.services.agent_factory.create_agent_runtime", fake_create_agent_runtime) + monkeypatch.setattr("iac_code.config.get_provider_config", lambda provider_key: {}) + + result = runner_cli.invoke(app, ["-p", "hello", "--model", "glm-5.2"]) + + assert result.exit_code == 0 + assert captured["options"].model == "glm-5.2" + assert captured["options"].request_policy_override.thinking_enabled is True + provider = create_provider( + captured["options"].model, + credentials={"dashscope": "key"}, + provider_key_override="dashscope", + request_policy_override=captured["options"].request_policy_override, + ) + assert provider._build_thinking_kwargs() == {"extra_body": {"enable_thinking": True, "thinking_budget": 8192}} + def test_max_turns_passed_to_headless(self): from iac_code.cli.main import app @@ -965,8 +1109,20 @@ def _install_headless_fakes(monkeypatch, *, creds=None, skills=None, existing_co fake_session_dir = Path("/tmp/iac-config") class FakeProviderManager: - def __init__(self, *, model, credentials, provider_key_override=None, base_url_override=None): - captured["provider_manager"] = {"model": model, "credentials": credentials} + def __init__( + self, + *, + model, + credentials, + provider_key_override=None, + base_url_override=None, + request_policy_override=None, + ): + captured["provider_manager"] = { + "model": model, + "credentials": credentials, + "request_policy_override": request_policy_override, + } class FakeSessionStorage: def __init__(self, projects_dir=None): @@ -1134,6 +1290,37 @@ def test_create_agent_loop_builds_expected_dependencies(monkeypatch): assert fake_command_registry.registered == [] +def test_create_agent_loop_passes_headless_thinking_policy(monkeypatch): + runner = HeadlessRunner(model="test-model", thinking_enabled=False) + captured, _fake_registry, _fake_command_registry = _install_headless_fakes( + monkeypatch, + creds={"openai": "ok"}, + skills=[], + ) + + runner._create_agent_loop() + + policy = captured["provider_manager"]["request_policy_override"] + assert policy is not None + assert policy.thinking_enabled is False + assert policy.effort is None + + +def test_create_agent_loop_enables_thinking_by_default(monkeypatch): + runner = HeadlessRunner(model="test-model") + captured, _fake_registry, _fake_command_registry = _install_headless_fakes( + monkeypatch, + creds={"openai": "ok"}, + skills=[], + ) + + runner._create_agent_loop() + + policy = captured["provider_manager"]["request_policy_override"] + assert policy is not None + assert policy.thinking_enabled is True + + def test_create_agent_loop_handles_credential_load_failure_and_skill_conflict(monkeypatch): runner = _make_runner() existing_cmd = {"skill-one": object()} diff --git a/tests/commands/test_auth_storage.py b/tests/commands/test_auth_storage.py index 9af352ac..69f2a60a 100644 --- a/tests/commands/test_auth_storage.py +++ b/tests/commands/test_auth_storage.py @@ -62,6 +62,13 @@ def test_effort_override(self): assert get_provider_config("openai").get("effort") == "high" + def test_thinking_enabled_override(self): + provider = {"name": "OpenAI", "key_name": "openai", "api_base": None} + save_active_provider_config(provider, "gpt-5.5", thinking_enabled=False) + from iac_code.config import get_provider_config + + assert get_provider_config("openai").get("thinkingEnabled") is False + def test_api_base_override(self): provider = {"name": "X", "key_name": "custom", "api_base": None} save_active_provider_config(provider, "m", api_base="https://override/") diff --git a/tests/commands/test_registry.py b/tests/commands/test_registry.py index f0aff6b6..1d1597c4 100644 --- a/tests/commands/test_registry.py +++ b/tests/commands/test_registry.py @@ -214,11 +214,11 @@ def test_create_default_registry_returns_registry(self): registry = create_default_registry() assert isinstance(registry, CommandRegistry) - def test_create_default_registry_has_13_visible_commands(self): - """Test create_default_registry has 13 visible commands.""" + def test_create_default_registry_has_14_visible_commands(self): + """Test create_default_registry has 14 visible commands.""" registry = create_default_registry() all_cmds = registry.get_all() - assert len(all_cmds) == 13 + assert len(all_cmds) == 14 def test_create_default_registry_command_names(self): """Test create_default_registry has expected command names.""" @@ -234,6 +234,7 @@ def test_create_default_registry_command_names(self): "auth", "debug", "effort", + "thinking_enabled", "resume", "memory", "skills", diff --git a/tests/commands/test_thinking_enabled.py b/tests/commands/test_thinking_enabled.py new file mode 100644 index 00000000..ad5f03af --- /dev/null +++ b/tests/commands/test_thinking_enabled.py @@ -0,0 +1,140 @@ +"""Tests for the /thinking_enabled command.""" + +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest + + +@pytest.mark.asyncio +async def test_thinking_enabled_no_active_provider(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: None) + + result = await thinking_enabled_command(context=None, args=[]) + + assert "/auth" in result + + +@pytest.mark.asyncio +async def test_thinking_enabled_no_model(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: "openai") + store = MagicMock() + store.get_state.return_value = SimpleNamespace(model="") + + result = await thinking_enabled_command(context=None, args=[], store=store) + + assert "/model" in result + + +@pytest.mark.asyncio +async def test_thinking_enabled_off_persists_and_updates_state(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: "openai") + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_provider_config", lambda key: {}) + calls = [] + monkeypatch.setattr( + "iac_code.commands.thinking_enabled.save_active_provider_config", + lambda provider, model, thinking_enabled=None: calls.append((provider["key_name"], model, thinking_enabled)), + ) + store = MagicMock() + store.get_state.return_value = SimpleNamespace(model="gpt-5.5") + + result = await thinking_enabled_command(context=None, args=["off"], store=store) + + assert "disabled" in result + assert calls == [("openai", "gpt-5.5", False)] + store.set_state.assert_called_once_with(thinking_enabled=False) + + +@pytest.mark.asyncio +async def test_thinking_enabled_on_persists_and_updates_state(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: "openai") + monkeypatch.setattr( + "iac_code.commands.thinking_enabled.get_provider_config", + lambda key: {"thinkingEnabled": False}, + ) + calls = [] + monkeypatch.setattr( + "iac_code.commands.thinking_enabled.save_active_provider_config", + lambda provider, model, thinking_enabled=None: calls.append((provider["key_name"], model, thinking_enabled)), + ) + store = MagicMock() + store.get_state.return_value = SimpleNamespace(model="gpt-5.5") + + result = await thinking_enabled_command(context=None, args=["on"], store=store) + + assert "enabled" in result + assert calls == [("openai", "gpt-5.5", True)] + store.set_state.assert_called_once_with(thinking_enabled=True) + + +@pytest.mark.asyncio +async def test_thinking_enabled_rejects_invalid_value(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: "openai") + store = MagicMock() + store.get_state.return_value = SimpleNamespace(model="gpt-5.5") + + result = await thinking_enabled_command(context=None, args=["maybe"], store=store) + + assert "Invalid thinking_enabled" in result + store.set_state.assert_not_called() + + +@pytest.mark.asyncio +async def test_thinking_enabled_shows_current_value(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: "openai") + monkeypatch.setattr( + "iac_code.commands.thinking_enabled.get_provider_config", + lambda key: {"thinkingEnabled": False}, + ) + store = MagicMock() + store.get_state.return_value = SimpleNamespace(model="gpt-5.5") + + result = await thinking_enabled_command(context=None, args=[], store=store) + + assert "disabled" in result + + +@pytest.mark.asyncio +async def test_thinking_enabled_interactive_selects_value(monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + + monkeypatch.setattr("iac_code.commands.thinking_enabled.get_active_provider_key", lambda: "openai") + monkeypatch.setattr( + "iac_code.commands.thinking_enabled.get_provider_config", + lambda key: {"thinkingEnabled": True}, + ) + selected_options = [] + + def fake_select(title, options, default_index=0): + selected_options.extend(options) + return 1 + + monkeypatch.setattr("iac_code.commands.thinking_enabled._select", fake_select) + + calls = [] + monkeypatch.setattr( + "iac_code.commands.thinking_enabled.save_active_provider_config", + lambda provider, model, thinking_enabled=None: calls.append((provider["key_name"], model, thinking_enabled)), + ) + store = MagicMock() + store.get_state.return_value = SimpleNamespace(model="gpt-5.5") + context = SimpleNamespace(console=object(), store=store) + + result = await thinking_enabled_command(context=context, args=[]) + + assert selected_options == ["on", "off"] + assert "disabled" in result + assert calls == [("openai", "gpt-5.5", False)] + store.set_state.assert_called_once_with(thinking_enabled=False) diff --git a/tests/ui/test_repl_integration.py b/tests/ui/test_repl_integration.py index d6e22dcd..68a161f9 100644 --- a/tests/ui/test_repl_integration.py +++ b/tests/ui/test_repl_integration.py @@ -61,6 +61,77 @@ def _current_time_line(prompt: str) -> str: return next(line for line in prompt.splitlines() if line.startswith("- Current time: ")) +@pytest.mark.asyncio +async def test_thinking_enabled_command_reconfigures_active_repl_provider(tmp_path, monkeypatch): + from iac_code.commands.thinking_enabled import thinking_enabled_command + from iac_code.config import get_settings_path, load_active_provider_config + from iac_code.state.app_state import AppState, AppStateStore + from iac_code.ui.repl import InlineREPL + + monkeypatch.setenv("IAC_CODE_CONFIG_DIR", str(tmp_path / "config")) + settings_path = get_settings_path() + settings_path.parent.mkdir(parents=True, exist_ok=True) + settings_path.write_text( + "activeProvider: openai\nproviders:\n openai:\n name: OpenAI\n model: gpt-5.5\n", + encoding="utf-8", + ) + repl = InlineREPL.__new__(InlineREPL) + repl.store = AppStateStore(AppState(model="gpt-5.5")) + repl._current_model = "gpt-5.5" + repl._current_provider_config = load_active_provider_config() + repl._provider_key_override = None + repl._base_url_override = None + repl._load_credentials = Mock(return_value={"openai": "key"}) + repl._provider_manager = Mock() + repl._refresh_system_prompt = Mock() + repl.store.subscribe(repl._on_state_change) + context = SimpleNamespace(store=repl.store, console=None, repl=repl) + + result = await thinking_enabled_command(context=context, args=["off"]) + + assert "disabled" in result + assert load_active_provider_config()["thinkingEnabled"] is False + repl._provider_manager.reconfigure.assert_called_once() + + +@pytest.mark.asyncio +async def test_repl_handle_command_routes_thinking_enabled_toggle(tmp_path, monkeypatch): + from iac_code.commands import create_default_registry + from iac_code.config import get_settings_path, load_active_provider_config + from iac_code.state.app_state import AppState, AppStateStore + from iac_code.ui.repl import InlineREPL + + monkeypatch.setenv("IAC_CODE_CONFIG_DIR", str(tmp_path / "config")) + settings_path = get_settings_path() + settings_path.parent.mkdir(parents=True, exist_ok=True) + settings_path.write_text( + "activeProvider: openai\nproviders:\n openai:\n name: OpenAI\n model: gpt-5.5\n", + encoding="utf-8", + ) + repl = InlineREPL.__new__(InlineREPL) + repl.command_registry = create_default_registry() + repl._disabled_skill_commands = {} + repl._command_log = [] + repl.console = None + repl.store = AppStateStore(AppState(model="gpt-5.5")) + repl._current_model = "gpt-5.5" + repl._current_provider_config = load_active_provider_config() + repl._provider_key_override = None + repl._base_url_override = None + repl._load_credentials = Mock(return_value={"openai": "key"}) + repl._provider_manager = Mock() + repl._refresh_system_prompt = Mock() + repl.renderer = SimpleNamespace(print_system_message=Mock(), print_command_result=Mock()) + repl.store.subscribe(repl._on_state_change) + + queued = await repl._handle_command("/thinking_enabled off") + + assert queued == [] + assert load_active_provider_config()["thinkingEnabled"] is False + repl._provider_manager.reconfigure.assert_called_once() + repl.renderer.print_command_result.assert_called_once() + + class TestREPLProviderIntegration: @patch("iac_code.ui.repl.ProviderManager") @patch("iac_code.ui.repl.SessionStorage") diff --git a/website/docs/cli/command-line-options.md b/website/docs/cli/command-line-options.md index b5526cc3..66e7748b 100644 --- a/website/docs/cli/command-line-options.md +++ b/website/docs/cli/command-line-options.md @@ -15,6 +15,7 @@ Command line options change how IaC Code starts. Use them before entering the in | `-p `, `--prompt ` | Run a single prompt and exit. This enables non-interactive mode. Use `--prompt -` to read the prompt from standard input. | | `--output-format ` | Set output format for non-interactive mode. Supported values are `text`, `json`, and `stream-json`. The default is `text`. | | `--max-turns ` | Limit the maximum number of agent turns in non-interactive mode. The default is `100`. | +| `--thinking-enabled`, `--no-thinking-enabled` | Control whether one-shot non-interactive requests explicitly enable thinking. The default is `--thinking-enabled`; use `--no-thinking-enabled` to send `thinking_enabled=false` for this run without rewriting `settings.yml`. | | `-d`, `--debug` | Enable debug logging for the current run. In interactive mode, use `/debug` to inspect or change debug logging after startup. | | `-r `, `--resume ` | Resume a previous session by exact session ID, unique ID prefix, or unique session name. Cross-project resolved sessions print a `cd ... && iac-code --resume ` command instead of hot-swapping the current project. | | `-c`, `--continue` | Resume the most recent session. This cannot be used together with `--resume`. | @@ -55,6 +56,12 @@ Run a one-shot prompt: iac-code --prompt "Create an OSS Bucket" ``` +Run a one-shot prompt with thinking disabled for this request: + +```bash +iac-code --prompt "Create an OSS Bucket" --no-thinking-enabled +``` + Read the prompt from standard input: ```bash diff --git a/website/docs/cli/commands.md b/website/docs/cli/commands.md index ea195dc9..46b6108f 100644 --- a/website/docs/cli/commands.md +++ b/website/docs/cli/commands.md @@ -18,6 +18,7 @@ Text after the command name is passed as arguments. In the table below, `` | `/compact` | Summarize the current conversation to reduce context usage while preserving recent turns. Use it after a long session when you want to continue working with less accumulated context. If the conversation is empty or too short, the command reports that there is nothing to compact. | | `/debug [on\|off\|status]` | Inspect or change runtime debug logging for the active session. `/debug` and `/debug status` show whether logging is enabled and, when enabled, the log file path. `/debug on` enables logging for the current session. `/debug off` disables it. | | `/effort [level]` | Show or change thinking effort for the active model when the selected model supports effort control. With a level, it applies the requested value if valid for the model. Without a level, it opens an interactive picker in the REPL, or prints the current effort in non-interactive contexts. | +| `/thinking_enabled [on\|off]` | Show or change whether the active provider should request thinking. With `on` or `off`, it persists `thinkingEnabled` for the active provider and reconfigures the running REPL provider. Without an argument, it opens an interactive Enable/Disable picker in the REPL, or prints the current value in non-interactive contexts. | | `/exit` | Exit the interactive REPL. Aliases: `/quit`, `/q`. | | `/help` | Show available commands and common keyboard shortcuts inside the REPL. Alias: `/?`. | | `/memory` | Open the memory selector. Edit project or user `AGENTS.md` files, toggle auto-memory, and open the project auto-memory folder when auto-memory is on. | diff --git a/website/docs/configuration/runtime-configuration.md b/website/docs/configuration/runtime-configuration.md index fe9c627e..efa6e586 100644 --- a/website/docs/configuration/runtime-configuration.md +++ b/website/docs/configuration/runtime-configuration.md @@ -93,6 +93,8 @@ Model-level values under `providers..models.` override provider For Alibaba Cloud DashScope and DashScope Token Plan, IaC Code has a built-in `thinkingBudget` of `8192` for `glm-5.2` and `kimi-k2.7-code`. When `maxCompletionTokens` is not set, the request limit is computed as the normal answer token limit plus the effective thinking budget. +Interactive REPL users can persist the provider-level `thinkingEnabled` value with `/thinking_enabled on` or `/thinking_enabled off`; running `/thinking_enabled` without an argument opens the Enable/Disable picker when a console UI is available. One-shot headless runs can override thinking for that request with `--thinking-enabled` or `--no-thinking-enabled` without rewriting `settings.yml`. + A2A requests may override these settings for a single message turn through `message.metadata.iac_code.thinking` or the `iac-code a2a-client call` flags `--thinking-enabled`, `--thinking-effort`, and `--thinking-budget`. If no explicit A2A thinking metadata is sent, the runtime uses the settings above and the provider's normal defaults. For generic `openai_compatible` providers with a DashScope compatible-mode base URL, iac-code switches to the DashScope native thinking wire format only when an explicit thinking policy is present. ## Tool Permission Configuration diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cli/command-line-options.md b/website/i18n/de/docusaurus-plugin-content-docs/current/cli/command-line-options.md index 7bfd2a63..4a8af4f0 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cli/command-line-options.md +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cli/command-line-options.md @@ -15,6 +15,7 @@ Befehlszeilenoptionen steuern, wie IaC Code gestartet wird. Sie können vor dem | `-p `, `--prompt ` | Einen einzelnen Prompt ausführen und beenden. Dies aktiviert den nicht-interaktiven Modus. Verwenden Sie `--prompt -`, um den Prompt von der Standardeingabe zu lesen. | | `--output-format ` | Ausgabeformat für den nicht-interaktiven Modus festlegen. Unterstützte Werte sind `text`, `json` und `stream-json`. Standard ist `text`. | | `--max-turns ` | Maximale Anzahl der Agenten-Runden im nicht-interaktiven Modus begrenzen. Standard ist `100`. | +| `--thinking-enabled`, `--no-thinking-enabled` | Steuert, ob einmalige nicht-interaktive Anfragen Thinking explizit aktivieren. Standard ist `--thinking-enabled`; verwenden Sie `--no-thinking-enabled`, um fuer diesen Lauf `thinking_enabled=false` zu senden, ohne `settings.yml` umzuschreiben. | | `-d`, `--debug` | Debug-Protokollierung für den aktuellen Lauf aktivieren. Im interaktiven Modus verwenden Sie `/debug`, um die Debug-Protokollierung nach dem Start zu prüfen oder zu ändern. | | `-r `, `--resume ` | Eine vorherige Sitzung über die exakte Sitzungs-ID, ein eindeutiges ID-Präfix oder einen eindeutigen Sitzungsnamen fortsetzen. Projektübergreifend aufgelöste Sitzungen geben einen `cd ... && iac-code --resume `-Befehl aus, statt das aktuelle Projekt direkt zu wechseln. | | `-c`, `--continue` | Die letzte Sitzung fortsetzen. Kann nicht zusammen mit `--resume` verwendet werden. | @@ -55,6 +56,12 @@ Einen einmaligen Prompt ausführen: iac-code --prompt "Create an OSS Bucket" ``` +Einen einmaligen Prompt mit fuer diese Anfrage deaktiviertem Thinking ausfuehren: + +```bash +iac-code --prompt "Create an OSS Bucket" --no-thinking-enabled +``` + Prompt von der Standardeingabe lesen: ```bash diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/cli/commands.md b/website/i18n/de/docusaurus-plugin-content-docs/current/cli/commands.md index 43d852bb..47db0b02 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/cli/commands.md +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/cli/commands.md @@ -18,6 +18,7 @@ Text nach dem Befehlsnamen wird als Argumente uebergeben. In der folgenden Tabel | `/compact` | Fassen Sie das aktuelle Gespraech zusammen, um die Kontextnutzung zu reduzieren und gleichzeitig die letzten Durchgaenge beizubehalten. Verwenden Sie dies nach einer langen Sitzung, wenn Sie mit weniger angesammeltem Kontext weiterarbeiten moechten. Wenn das Gespraech leer oder zu kurz ist, meldet der Befehl, dass nichts zu komprimieren ist. | | `/debug [on\|off\|status]` | Pruefen oder aendern Sie die Laufzeit-Debug-Protokollierung fuer die aktive Sitzung. `/debug` und `/debug status` zeigen an, ob die Protokollierung aktiviert ist und, wenn aktiviert, den Pfad der Protokolldatei. `/debug on` aktiviert die Protokollierung fuer die aktuelle Sitzung. `/debug off` deaktiviert sie. | | `/effort [level]` | Zeigen oder aendern Sie den Denkaufwand fuer das aktive Modell, wenn das ausgewaehlte Modell Aufwandsteuerung unterstuetzt. Mit einem Level wird der angeforderte Wert angewendet, wenn er fuer das Modell gueltig ist. Ohne Level wird im REPL eine interaktive Auswahl geoeffnet, oder der aktuelle Aufwand wird in nicht-interaktiven Kontexten ausgegeben. | +| `/thinking_enabled [on\|off]` | Zeigen oder aendern Sie, ob der aktive Provider Thinking anfordern soll. Mit `on` oder `off` wird `thinkingEnabled` fuer den aktiven Provider gespeichert und der laufende REPL-Provider neu konfiguriert. Ohne Argument wird im REPL eine interaktive Aktivieren/Deaktivieren-Auswahl geoeffnet, oder der aktuelle Wert wird in nicht-interaktiven Kontexten ausgegeben. | | `/exit` | Beenden Sie das interaktive REPL. Aliase: `/quit`, `/q`. | | `/help` | Zeigen Sie verfuegbare Befehle und gaengige Tastenkuerzel im REPL an. Alias: `/?`. | | `/memory` | Öffnet die Speicherauswahl. Bearbeiten Sie Projekt- oder Benutzerdateien `AGENTS.md`, schalten Sie auto-memory ein oder aus und öffnen Sie den auto-memory-Ordner des Projekts, wenn auto-memory aktiviert ist. | diff --git a/website/i18n/de/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md b/website/i18n/de/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md index 64f36487..2e0da4c1 100644 --- a/website/i18n/de/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md +++ b/website/i18n/de/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md @@ -93,6 +93,8 @@ Gültige modellbezogene Werte unter `providers..models.` übers Für Alibaba Cloud DashScope und DashScope Token Plan hat IaC Code ein eingebautes `thinkingBudget=8192` für `glm-5.2` und `kimi-k2.7-code`. Wenn `maxCompletionTokens` nicht gesetzt ist, wird das Anfrage-Limit aus dem normalen Antwort-Token-Limit plus dem wirksamen Thinking Budget berechnet. +Interaktive REPL-Benutzer koennen den providerbezogenen Wert `thinkingEnabled` mit `/thinking_enabled on` oder `/thinking_enabled off` speichern. Wenn `/thinking_enabled` ohne Argument ausgefuehrt wird, oeffnet sich bei verfuegbarer Konsolen-UI die Aktivieren/Deaktivieren-Auswahl. Einmalige Headless-Laeufe koennen Thinking fuer diese Anfrage mit `--thinking-enabled` oder `--no-thinking-enabled` ueberschreiben, ohne `settings.yml` umzuschreiben. + A2A-Anfragen können diese Einstellungen für einen einzelnen Message-Turn über `message.metadata.iac_code.thinking` oder die Flags `--thinking-enabled`, `--thinking-effort` und `--thinking-budget` von `iac-code a2a-client call` überschreiben. Wenn keine expliziten A2A-Thinking-Metadata gesendet werden, verwendet der Runtime die obigen Einstellungen und die normalen Defaults des Providers. Für generische `openai_compatible`-Provider mit einer DashScope-compatible-mode-Base-URL wechselt iac-code nur dann zum nativen DashScope-Thinking-Wire-Format, wenn eine explizite Thinking-Richtlinie vorhanden ist. ## Werkzeug-Berechtigungskonfiguration diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/cli/command-line-options.md b/website/i18n/es/docusaurus-plugin-content-docs/current/cli/command-line-options.md index 634918f1..14be7985 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/cli/command-line-options.md +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/cli/command-line-options.md @@ -15,6 +15,7 @@ Las opciones de línea de comandos cambian cómo se inicia IaC Code. Úselas ant | `-p `, `--prompt ` | Ejecutar un único prompt y salir. Esto habilita el modo no interactivo. Use `--prompt -` para leer el prompt desde la entrada estándar. | | `--output-format ` | Establecer el formato de salida para el modo no interactivo. Los valores soportados son `text`, `json` y `stream-json`. El valor predeterminado es `text`. | | `--max-turns ` | Limitar el número máximo de turnos del agente en modo no interactivo. El valor predeterminado es `100`. | +| `--thinking-enabled`, `--no-thinking-enabled` | Controla si las solicitudes no interactivas de una sola ejecucion habilitan explicitamente thinking. El valor predeterminado es `--thinking-enabled`; usa `--no-thinking-enabled` para enviar `thinking_enabled=false` en esta ejecucion sin reescribir `settings.yml`. | | `-d`, `--debug` | Habilitar el registro de depuración para la ejecución actual. En modo interactivo, use `/debug` para inspeccionar o cambiar el registro de depuración después del inicio. | | `-r `, `--resume ` | Reanudar una sesión anterior por ID exacto, prefijo único de ID o nombre único de sesión. Las sesiones resueltas en otro proyecto imprimen un comando `cd ... && iac-code --resume ` en lugar de cambiar en caliente el proyecto actual. | | `-c`, `--continue` | Reanudar la sesión más reciente. No se puede usar junto con `--resume`. | @@ -55,6 +56,12 @@ Ejecutar un prompt único: iac-code --prompt "Create an OSS Bucket" ``` +Ejecutar un prompt único con thinking deshabilitado para esta solicitud: + +```bash +iac-code --prompt "Create an OSS Bucket" --no-thinking-enabled +``` + Leer el prompt desde la entrada estándar: ```bash diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/cli/commands.md b/website/i18n/es/docusaurus-plugin-content-docs/current/cli/commands.md index cecb42ce..929c9aed 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/cli/commands.md +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/cli/commands.md @@ -18,6 +18,7 @@ El texto despues del nombre del comando se pasa como argumentos. En la tabla sig | `/compact` | Resume la conversacion actual para reducir el uso de contexto, preservando los turnos recientes. Usalo despues de una sesion larga cuando quieras continuar trabajando con menos contexto acumulado. Si la conversacion esta vacia o es demasiado corta, el comando informa que no hay nada que compactar. | | `/debug [on\|off\|status]` | Inspecciona o cambia el registro de depuracion en tiempo de ejecucion para la sesion activa. `/debug` y `/debug status` muestran si el registro esta habilitado y, cuando lo esta, la ruta del archivo de registro. `/debug on` habilita el registro para la sesion actual. `/debug off` lo deshabilita. | | `/effort [level]` | Muestra o cambia el esfuerzo de pensamiento para el modelo activo cuando el modelo seleccionado admite control de esfuerzo. Con un nivel, aplica el valor solicitado si es valido para el modelo. Sin un nivel, abre un selector interactivo en el REPL, o imprime el esfuerzo actual en contextos no interactivos. | +| `/thinking_enabled [on\|off]` | Muestra o cambia si el proveedor activo debe solicitar thinking. Con `on` u `off`, persiste `thinkingEnabled` para el proveedor activo y reconfigura el proveedor del REPL en ejecucion. Sin argumento, abre un selector interactivo Habilitar/Deshabilitar en el REPL, o imprime el valor actual en contextos no interactivos. | | `/exit` | Sale del REPL interactivo. Alias: `/quit`, `/q`. | | `/help` | Muestra los comandos disponibles y los atajos de teclado comunes dentro del REPL. Alias: `/?`. | | `/memory` | Abre el selector de memoria. Edita los archivos `AGENTS.md` de proyecto o usuario, activa o desactiva auto-memory y abre la carpeta de auto-memory del proyecto cuando auto-memory está activada. | diff --git a/website/i18n/es/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md b/website/i18n/es/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md index 71887c2e..746e21ac 100644 --- a/website/i18n/es/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md +++ b/website/i18n/es/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md @@ -93,6 +93,8 @@ Los valores válidos a nivel de modelo bajo `providers..models. Para Alibaba Cloud DashScope y DashScope Token Plan, IaC Code incluye un `thinkingBudget=8192` integrado para `glm-5.2` y `kimi-k2.7-code`. Si `maxCompletionTokens` no está configurado, el límite de la solicitud se calcula como el límite normal de tokens de respuesta más el thinking budget efectivo. +En el REPL interactivo, `/thinking_enabled on` o `/thinking_enabled off` permite persistir el valor `thinkingEnabled` a nivel de proveedor. Ejecutar `/thinking_enabled` sin argumento abre el selector Habilitar/Deshabilitar cuando hay una interfaz de consola disponible. Las ejecuciones headless de una sola vez pueden anular thinking para esa solicitud con `--thinking-enabled` o `--no-thinking-enabled`, sin reescribir `settings.yml`. + Las solicitudes A2A pueden anular estos ajustes para un solo turno de mensaje mediante `message.metadata.iac_code.thinking` o los flags `--thinking-enabled`, `--thinking-effort` y `--thinking-budget` de `iac-code a2a-client call`. Si no se envían metadatos de thinking A2A explícitos, el runtime usa los ajustes anteriores y los valores predeterminados normales del proveedor. Para proveedores genéricos `openai_compatible` con una base URL en modo compatible de DashScope, iac-code cambia al formato wire nativo de thinking de DashScope solo cuando hay una política de thinking explícita. ## Configuración de permisos de herramientas diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/command-line-options.md b/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/command-line-options.md index 4bd91ef5..67892452 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/command-line-options.md +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/command-line-options.md @@ -15,6 +15,7 @@ Les options de ligne de commande modifient le démarrage d'IaC Code. Utilisez-le | `-p `, `--prompt ` | Exécuter un seul prompt et quitter. Ceci active le mode non interactif. Utilisez `--prompt -` pour lire le prompt depuis l'entrée standard. | | `--output-format ` | Définir le format de sortie pour le mode non interactif. Les valeurs prises en charge sont `text`, `json` et `stream-json`. La valeur par défaut est `text`. | | `--max-turns ` | Limiter le nombre maximum de tours de l'agent en mode non interactif. La valeur par défaut est `100`. | +| `--thinking-enabled`, `--no-thinking-enabled` | Contrôler si les requêtes non interactives ponctuelles activent explicitement le thinking. La valeur par défaut est `--thinking-enabled` ; utilisez `--no-thinking-enabled` pour envoyer `thinking_enabled=false` pour cette exécution sans réécrire `settings.yml`. | | `-d`, `--debug` | Activer la journalisation de débogage pour l'exécution en cours. En mode interactif, utilisez `/debug` pour inspecter ou modifier la journalisation de débogage après le démarrage. | | `-r `, `--resume ` | Reprendre une session précédente par identifiant exact, préfixe d'identifiant unique ou nom de session unique. Les sessions résolues dans un autre projet affichent une commande `cd ... && iac-code --resume ` au lieu de basculer le projet courant à chaud. | | `-c`, `--continue` | Reprendre la session la plus récente. Ne peut pas être utilisé avec `--resume`. | @@ -55,6 +56,12 @@ Exécuter un prompt unique : iac-code --prompt "Create an OSS Bucket" ``` +Exécuter un prompt unique avec le thinking désactivé pour cette requête : + +```bash +iac-code --prompt "Create an OSS Bucket" --no-thinking-enabled +``` + Lire le prompt depuis l'entrée standard : ```bash diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/commands.md b/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/commands.md index b265636b..75ef89d2 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/commands.md +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/cli/commands.md @@ -18,6 +18,7 @@ Le texte après le nom de la commande est transmis comme arguments. Dans le tabl | `/compact` | Résumer la conversation actuelle pour réduire l'utilisation du contexte tout en préservant les échanges récents. Utilisez cette commande après une longue session lorsque vous souhaitez continuer à travailler avec moins de contexte accumulé. Si la conversation est vide ou trop courte, la commande signale qu'il n'y a rien à compacter. | | `/debug [on\|off\|status]` | Inspecter ou modifier la journalisation de débogage à l'exécution pour la session active. `/debug` et `/debug status` indiquent si la journalisation est activée et, lorsqu'elle est activée, le chemin du fichier journal. `/debug on` active la journalisation pour la session en cours. `/debug off` la désactive. | | `/effort [level]` | Afficher ou modifier l'effort de réflexion pour le modèle actif lorsque le modèle sélectionné prend en charge le contrôle d'effort. Avec un niveau, il applique la valeur demandée si elle est valide pour le modèle. Sans niveau, il ouvre un sélecteur interactif dans le REPL, ou affiche l'effort actuel dans les contextes non interactifs. | +| `/thinking_enabled [on\|off]` | Afficher ou modifier si le provider actif doit demander le thinking. Avec `on` ou `off`, la commande persiste `thinkingEnabled` pour le provider actif et reconfigure le provider du REPL en cours d'exécution. Sans argument, elle ouvre un sélecteur interactif Activer/Désactiver dans le REPL, ou affiche la valeur actuelle dans les contextes non interactifs. | | `/exit` | Quitter le REPL interactif. Alias : `/quit`, `/q`. | | `/help` | Afficher les commandes disponibles et les raccourcis clavier courants dans le REPL. Alias : `/?`. | | `/memory` | Ouvrir le sélecteur de mémoire. Modifiez les fichiers `AGENTS.md` de projet ou d'utilisateur, activez ou désactivez auto-memory, et ouvrez le dossier auto-memory du projet lorsque auto-memory est activé. | diff --git a/website/i18n/fr/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md b/website/i18n/fr/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md index 21f534a8..22cd05e4 100644 --- a/website/i18n/fr/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md +++ b/website/i18n/fr/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md @@ -93,6 +93,8 @@ Les valeurs valides définies au niveau du modèle sous `providers..mo Pour Alibaba Cloud DashScope et DashScope Token Plan, IaC Code définit une valeur intégrée `thinkingBudget=8192` pour `glm-5.2` et `kimi-k2.7-code`. Si `maxCompletionTokens` n'est pas défini, la limite de requête est calculée comme la limite normale de tokens de réponse plus le thinking budget effectif. +Dans le REPL interactif, `/thinking_enabled on` ou `/thinking_enabled off` permet de persister la valeur `thinkingEnabled` au niveau du provider. Exécuter `/thinking_enabled` sans argument ouvre le sélecteur Activer/Désactiver lorsqu'une interface console est disponible. Les exécutions headless ponctuelles peuvent remplacer le thinking pour cette requête avec `--thinking-enabled` ou `--no-thinking-enabled`, sans réécrire `settings.yml`. + Les requêtes A2A peuvent remplacer ces réglages pour un seul tour de message via `message.metadata.iac_code.thinking` ou les flags `--thinking-enabled`, `--thinking-effort` et `--thinking-budget` de `iac-code a2a-client call`. Si aucune métadonnée de thinking A2A explicite n'est envoyée, le runtime utilise les réglages ci-dessus et les valeurs par défaut normales du provider. Pour les providers génériques `openai_compatible` avec une base URL en mode compatible DashScope, iac-code ne bascule vers le format wire natif de thinking DashScope que lorsqu'une politique de thinking explicite est présente. ## Configuration des permissions d'outils diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/command-line-options.md b/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/command-line-options.md index 1c6a0708..4c638496 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/command-line-options.md +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/command-line-options.md @@ -15,6 +15,7 @@ description: IaC Code の起動オプションとワンショット実行パラ | `-p `, `--prompt ` | 単一のプロンプトを実行して終了します。非対話モードが有効になります。`--prompt -` で標準入力からプロンプトを読み取ります。 | | `--output-format ` | 非対話モードの出力形式を設定します。サポートされる値は `text`、`json`、`stream-json` です。デフォルトは `text` です。 | | `--max-turns ` | 非対話モードでのエージェントの最大ターン数を制限します。デフォルトは `100` です。 | +| `--thinking-enabled`, `--no-thinking-enabled` | ワンショットの非対話リクエストで thinking を明示的に有効化するかどうかを制御します。デフォルトは `--thinking-enabled` です。`--no-thinking-enabled` はこの実行だけ `thinking_enabled=false` を送信し、`settings.yml` は書き換えません。 | | `-d`, `--debug` | 今回の実行でデバッグログを有効にします。対話モードでは、起動後に `/debug` を使用してデバッグログを確認または変更できます。 | | `-r <セッションIDまたは名前>`, `--resume <セッションIDまたは名前>` | 正確なセッション ID、一意な ID プレフィックス、または一意なセッション名で以前のセッションを再開します。別プロジェクトとして解決されたセッションは、現在のプロジェクトをその場で切り替えず、`cd ... && iac-code --resume ` コマンドを表示します。 | | `-c`, `--continue` | 最新のセッションを再開します。`--resume` と同時に使用できません。 | @@ -55,6 +56,12 @@ iac-code --model qwen3.6-plus iac-code --prompt "Create an OSS Bucket" ``` +このリクエストだけ thinking を無効にしてワンショットプロンプトを実行する: + +```bash +iac-code --prompt "Create an OSS Bucket" --no-thinking-enabled +``` + 標準入力からプロンプトを読み取る: ```bash diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/commands.md b/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/commands.md index 2db8c129..c654f2f4 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/commands.md +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/cli/commands.md @@ -18,6 +18,7 @@ description: 組み込み対話コマンドの完全リファレンス。 | `/compact` | 現在の会話を要約してコンテキスト使用量を削減し、最近のターンを保持します。長いセッションの後、蓄積されたコンテキストを減らして作業を続けたい場合に使用します。会話が空または短すぎる場合は、コンパクト化するものがないことを報告します。 | | `/debug [on\|off\|status]` | アクティブなセッションのランタイムデバッグログを検査または変更します。`/debug` と `/debug status` はログが有効かどうかと、有効な場合のログファイルパスを表示します。`/debug on` は現在のセッションのログを有効にします。`/debug off` は無効にします。 | | `/effort [level]` | 選択したモデルがエフォート制御をサポートしている場合、アクティブモデルの思考エフォートを表示または変更します。レベルを指定すると、モデルに対して有効な値であれば適用します。レベルなしでは REPL で対話ピッカーを開くか、非対話コンテキストでは現在のエフォートを表示します。 | +| `/thinking_enabled [on\|off]` | アクティブな provider が thinking を要求するかどうかを表示または変更します。`on` または `off` を指定すると、アクティブ provider の `thinkingEnabled` を永続化し、実行中の REPL provider を再設定します。引数なしでは REPL で有効化/無効化の対話ピッカーを開くか、非対話コンテキストでは現在値を表示します。 | | `/exit` | 対話 REPL を終了します。エイリアス:`/quit`、`/q`。 | | `/help` | REPL 内で利用可能なコマンドと一般的なキーボードショートカットを表示します。エイリアス:`/?`。 | | `/memory` | メモリセレクターを開きます。プロジェクトまたはユーザーの `AGENTS.md` ファイルを編集し、auto-memory を切り替え、auto-memory がオンのときはプロジェクトの auto-memory フォルダーを開けます。 | diff --git a/website/i18n/ja/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md b/website/i18n/ja/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md index 51d2fd70..2f61af80 100644 --- a/website/i18n/ja/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md +++ b/website/i18n/ja/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md @@ -93,6 +93,8 @@ providers: Alibaba Cloud DashScope と DashScope Token Plan では、IaC Code は `glm-5.2` と `kimi-k2.7-code` に組み込みで `thinkingBudget=8192` を設定しています。`maxCompletionTokens` が未設定の場合、リクエスト上限は通常の回答 token 上限に有効な thinking budget を加えて計算されます。 +対話 REPL では、`/thinking_enabled on` または `/thinking_enabled off` で provider 単位の `thinkingEnabled` 値を永続化できます。引数なしで `/thinking_enabled` を実行すると、コンソール UI が利用できる場合は有効化/無効化ピッカーが開きます。ワンショット headless 実行では、`--thinking-enabled` または `--no-thinking-enabled` でそのリクエストだけ thinking を上書きでき、`settings.yml` は書き換えません。 + A2A リクエストは、`message.metadata.iac_code.thinking` または `iac-code a2a-client call` の `--thinking-enabled`、`--thinking-effort`、`--thinking-budget` フラグを通じて、この設定を 1 回の message turn だけ上書きできます。明示的な A2A thinking metadata が送信されない場合、runtime は上記の設定と provider の通常のデフォルトを使用します。DashScope compatible-mode の base URL を持つ汎用 `openai_compatible` provider では、明示的な thinking ポリシーがある場合にのみ、iac-code は DashScope ネイティブの thinking wire format に切り替えます。 ## ツール権限設定 diff --git a/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/command-line-options.md b/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/command-line-options.md index 4f6bee21..1acec8fc 100644 --- a/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/command-line-options.md +++ b/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/command-line-options.md @@ -15,6 +15,7 @@ As opções de linha de comando alteram como o IaC Code é iniciado. Use-as ante | `-p `, `--prompt ` | Executar um único prompt e sair. Ativa o modo não interativo. Use `--prompt -` para ler o prompt da entrada padrão. | | `--output-format ` | Definir o formato de saída para o modo não interativo. Os valores suportados são `text`, `json` e `stream-json`. O padrão é `text`. | | `--max-turns ` | Limitar o número máximo de turnos do agente no modo não interativo. O padrão é `100`. | +| `--thinking-enabled`, `--no-thinking-enabled` | Controla se solicitações nao interativas de execucao unica habilitam thinking explicitamente. O padrão é `--thinking-enabled`; use `--no-thinking-enabled` para enviar `thinking_enabled=false` nesta execução sem reescrever `settings.yml`. | | `-d`, `--debug` | Ativar o registro de depuração para a execução atual. No modo interativo, use `/debug` para inspecionar ou alterar o registro de depuração após a inicialização. | | `-r `, `--resume ` | Retomar uma sessão anterior por ID exato, prefixo único de ID ou nome único de sessão. Sessões resolvidas em outro projeto imprimem um comando `cd ... && iac-code --resume ` em vez de trocar o projeto atual em tempo real. | | `-c`, `--continue` | Retomar a sessão mais recente. Não pode ser usado junto com `--resume`. | @@ -55,6 +56,12 @@ Executar um prompt único: iac-code --prompt "Create an OSS Bucket" ``` +Executar um prompt único com thinking desabilitado para esta solicitação: + +```bash +iac-code --prompt "Create an OSS Bucket" --no-thinking-enabled +``` + Ler o prompt da entrada padrão: ```bash diff --git a/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/commands.md b/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/commands.md index 6b48aa06..7c4f6d34 100644 --- a/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/commands.md +++ b/website/i18n/pt/docusaurus-plugin-content-docs/current/cli/commands.md @@ -18,6 +18,7 @@ O texto apos o nome do comando e passado como argumentos. Na tabela abaixo, `.models.` su Para Alibaba Cloud DashScope e DashScope Token Plan, o IaC Code tem um `thinkingBudget=8192` integrado para `glm-5.2` e `kimi-k2.7-code`. Quando `maxCompletionTokens` não é definido, o limite da requisição é calculado como o limite normal de tokens de resposta mais o thinking budget efetivo. +No REPL interativo, `/thinking_enabled on` ou `/thinking_enabled off` permite persistir o valor `thinkingEnabled` no nível do provedor. Executar `/thinking_enabled` sem argumento abre o seletor Habilitar/Desabilitar quando há uma UI de console disponível. Execuções headless de uma única vez podem substituir thinking para essa solicitação com `--thinking-enabled` ou `--no-thinking-enabled`, sem reescrever `settings.yml`. + Requisições A2A podem substituir essas configurações por um único turno de mensagem por meio de `message.metadata.iac_code.thinking` ou dos flags `--thinking-enabled`, `--thinking-effort` e `--thinking-budget` de `iac-code a2a-client call`. Se nenhum metadado explícito de thinking A2A for enviado, o runtime usa as configurações acima e os padrões normais do provedor. Para provedores genéricos `openai_compatible` com base URL em modo compatível do DashScope, o iac-code muda para o formato wire nativo de thinking do DashScope somente quando uma política explícita de thinking está presente. ## Configuração de permissões de ferramentas diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/command-line-options.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/command-line-options.md index e980cfb1..0de4be01 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/command-line-options.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/command-line-options.md @@ -15,6 +15,7 @@ description: IaC Code 启动选项和一次性执行参数参考。 | `-p `, `--prompt ` | 执行单条提示词并退出。这会进入非交互模式。使用 `--prompt -` 可以从标准输入读取提示词。 | | `--output-format ` | 设置非交互模式的输出格式。支持 `text`、`json` 和 `stream-json`,默认值为 `text`。 | | `--max-turns ` | 限制非交互模式中的最大代理轮次,默认值为 `100`。 | +| `--thinking-enabled`, `--no-thinking-enabled` | 控制一次性非交互请求是否显式启用 thinking。默认值为 `--thinking-enabled`;使用 `--no-thinking-enabled` 会在本次运行中发送 `thinking_enabled=false`,但不会改写 `settings.yml`。 | | `-d`, `--debug` | 为本次运行启用调试日志。交互模式启动后,可以使用 `/debug` 查看或调整调试日志。 | | `-r `, `--resume ` | 按精确会话 ID、唯一 ID 前缀或唯一会话名称恢复历史会话。解析到跨项目会话时,会打印 `cd ... && iac-code --resume ` 命令,而不是直接热切换当前项目。 | | `-c`, `--continue` | 恢复最近一次会话。不能与 `--resume` 同时使用。 | @@ -55,6 +56,12 @@ iac-code --model qwen3.6-plus iac-code --prompt "创建一个 OSS Bucket" ``` +执行一次性提示词,并只在本次请求中关闭 thinking: + +```bash +iac-code --prompt "创建一个 OSS Bucket" --no-thinking-enabled +``` + 从标准输入读取提示词: ```bash diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/commands.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/commands.md index e0028b65..74fb4341 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/commands.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/cli/commands.md @@ -18,6 +18,7 @@ Slash 命令用于在交互式会话中控制 IaC Code。输入 `/` 可以查看 | `/compact` | 将当前对话总结压缩,以降低上下文占用,同时保留最近几轮内容。长会话后仍想继续工作时使用。如果对话为空或太短,命令会提示没有可压缩内容。 | | `/debug [on\|off\|status]` | 查看或调整当前会话的运行时 debug 日志。`/debug` 和 `/debug status` 会显示日志是否开启;开启时还会显示日志文件路径。`/debug on` 开启当前会话日志,`/debug off` 关闭日志。 | | `/effort [level]` | 在当前模型支持 effort 控制时,查看或切换 thinking effort。带 `level` 时,如果该值对当前模型有效就会直接应用;不带参数时,在 REPL 中打开交互式选择器,在无控制台 UI 的场景中显示当前 effort。 | +| `/thinking_enabled [on\|off]` | 查看或切换当前 provider 是否请求 thinking。带 `on` 或 `off` 时,会为当前 provider 持久化 `thinkingEnabled` 并重新配置正在运行的 REPL provider;不带参数时,在 REPL 中打开“启用/禁用”交互式选择器,在无控制台 UI 的场景中显示当前值。 | | `/exit` | 退出交互式 REPL。别名:`/quit`、`/q`。 | | `/help` | 在 REPL 中显示可用命令和常用快捷键。别名:`/?`。 | | `/memory` | 打开记忆选择器。可以编辑项目或用户的 `AGENTS.md` 文件,切换 auto-memory,并在 auto-memory 开启时打开项目 auto-memory 文件夹。 | diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md index 3fc9b499..6cb12a18 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/configuration/runtime-configuration.md @@ -93,6 +93,8 @@ providers: 对阿里云百炼 DashScope 和 DashScope Token Plan,IaC Code 为 `glm-5.2` 和 `kimi-k2.7-code` 内置了 `thinkingBudget=8192`。未设置 `maxCompletionTokens` 时,请求上限会按普通回答 token 上限加上有效 thinking budget 计算。 +交互式 REPL 用户可以通过 `/thinking_enabled on` 或 `/thinking_enabled off` 持久化 provider 级 `thinkingEnabled` 值;直接运行 `/thinking_enabled` 时,如果有控制台 UI,会打开“启用/禁用”选择器。一次性 headless 运行可以用 `--thinking-enabled` 或 `--no-thinking-enabled` 只覆盖本次请求的 thinking 行为,不会改写 `settings.yml`。 + A2A 请求可以通过 `message.metadata.iac_code.thinking` 或 `iac-code a2a-client call` 的 `--thinking-enabled`、`--thinking-effort`、`--thinking-budget` flag 在单次 message turn 覆盖这些设置。如果没有发送显式 A2A thinking metadata,runtime 会使用上述配置和 provider 自身默认行为。对于 base URL 指向 DashScope compatible-mode 的通用 `openai_compatible` provider,只有存在显式 thinking 策略时,iac-code 才会切换到 DashScope 原生 thinking wire format。 ## 工具权限配置