mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(fal): extend whitespace-only FAL_KEY handling to all call sites
Follow-up to PR #2504. The original fix covered the two direct FAL_KEY checks in image_generation_tool but left four other call sites intact, including the managed-gateway gate where a whitespace-only FAL_KEY falsely claimed 'user has direct FAL' and *skipped* the Nous managed gateway fallback entirely. Introduce fal_key_is_configured() in tools/tool_backend_helpers.py as a single source of truth (consults os.environ, falls back to .env for CLI-setup paths) and route every FAL_KEY presence check through it: - tools/image_generation_tool.py : _resolve_managed_fal_gateway, image_generate_tool's upfront check, check_fal_api_key - hermes_cli/nous_subscription.py : direct_fal detection, selected toolset gating, tools_ready map - hermes_cli/tools_config.py : image_gen needs-setup check Verified by extending tests/tools/test_image_generation_env.py and by E2E exercising whitespace + managed-gateway composition directly.
This commit is contained in:
parent
77061ac995
commit
2e722ee29a
4 changed files with 35 additions and 13 deletions
|
|
@ -10,6 +10,7 @@ from hermes_cli.auth import get_nous_auth_status
|
||||||
from hermes_cli.config import get_env_value, load_config
|
from hermes_cli.config import get_env_value, load_config
|
||||||
from tools.managed_tool_gateway import is_managed_tool_gateway_ready
|
from tools.managed_tool_gateway import is_managed_tool_gateway_ready
|
||||||
from tools.tool_backend_helpers import (
|
from tools.tool_backend_helpers import (
|
||||||
|
fal_key_is_configured,
|
||||||
has_direct_modal_credentials,
|
has_direct_modal_credentials,
|
||||||
managed_nous_tools_enabled,
|
managed_nous_tools_enabled,
|
||||||
normalize_browser_cloud_provider,
|
normalize_browser_cloud_provider,
|
||||||
|
|
@ -271,7 +272,7 @@ def get_nous_subscription_features(
|
||||||
direct_firecrawl = bool(get_env_value("FIRECRAWL_API_KEY") or get_env_value("FIRECRAWL_API_URL"))
|
direct_firecrawl = bool(get_env_value("FIRECRAWL_API_KEY") or get_env_value("FIRECRAWL_API_URL"))
|
||||||
direct_parallel = bool(get_env_value("PARALLEL_API_KEY"))
|
direct_parallel = bool(get_env_value("PARALLEL_API_KEY"))
|
||||||
direct_tavily = bool(get_env_value("TAVILY_API_KEY"))
|
direct_tavily = bool(get_env_value("TAVILY_API_KEY"))
|
||||||
direct_fal = bool(get_env_value("FAL_KEY"))
|
direct_fal = fal_key_is_configured()
|
||||||
direct_openai_tts = bool(resolve_openai_audio_api_key())
|
direct_openai_tts = bool(resolve_openai_audio_api_key())
|
||||||
direct_elevenlabs = bool(get_env_value("ELEVENLABS_API_KEY"))
|
direct_elevenlabs = bool(get_env_value("ELEVENLABS_API_KEY"))
|
||||||
direct_camofox = bool(get_env_value("CAMOFOX_URL"))
|
direct_camofox = bool(get_env_value("CAMOFOX_URL"))
|
||||||
|
|
@ -520,7 +521,7 @@ def apply_nous_managed_defaults(
|
||||||
browser_cfg["cloud_provider"] = "browser-use"
|
browser_cfg["cloud_provider"] = "browser-use"
|
||||||
changed.add("browser")
|
changed.add("browser")
|
||||||
|
|
||||||
if "image_gen" in selected_toolsets and not get_env_value("FAL_KEY"):
|
if "image_gen" in selected_toolsets and not fal_key_is_configured():
|
||||||
changed.add("image_gen")
|
changed.add("image_gen")
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
|
|
@ -548,7 +549,7 @@ def _get_gateway_direct_credentials() -> Dict[str, bool]:
|
||||||
or get_env_value("TAVILY_API_KEY")
|
or get_env_value("TAVILY_API_KEY")
|
||||||
or get_env_value("EXA_API_KEY")
|
or get_env_value("EXA_API_KEY")
|
||||||
),
|
),
|
||||||
"image_gen": bool(get_env_value("FAL_KEY")),
|
"image_gen": fal_key_is_configured(),
|
||||||
"tts": bool(
|
"tts": bool(
|
||||||
resolve_openai_audio_api_key()
|
resolve_openai_audio_api_key()
|
||||||
or get_env_value("ELEVENLABS_API_KEY")
|
or get_env_value("ELEVENLABS_API_KEY")
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ from hermes_cli.nous_subscription import (
|
||||||
apply_nous_managed_defaults,
|
apply_nous_managed_defaults,
|
||||||
get_nous_subscription_features,
|
get_nous_subscription_features,
|
||||||
)
|
)
|
||||||
from tools.tool_backend_helpers import managed_nous_tools_enabled
|
from tools.tool_backend_helpers import fal_key_is_configured, managed_nous_tools_enabled
|
||||||
from utils import base_url_hostname
|
from utils import base_url_hostname
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -876,7 +876,7 @@ def _toolset_needs_configuration_prompt(ts_key: str, config: dict) -> bool:
|
||||||
browser_cfg = config.get("browser", {})
|
browser_cfg = config.get("browser", {})
|
||||||
return not isinstance(browser_cfg, dict) or "cloud_provider" not in browser_cfg
|
return not isinstance(browser_cfg, dict) or "cloud_provider" not in browser_cfg
|
||||||
if ts_key == "image_gen":
|
if ts_key == "image_gen":
|
||||||
return not get_env_value("FAL_KEY")
|
return not fal_key_is_configured()
|
||||||
|
|
||||||
return not _toolset_has_keys(ts_key, config)
|
return not _toolset_has_keys(ts_key, config)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,11 @@ import fal_client
|
||||||
|
|
||||||
from tools.debug_helpers import DebugSession
|
from tools.debug_helpers import DebugSession
|
||||||
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
||||||
from tools.tool_backend_helpers import managed_nous_tools_enabled, prefers_gateway
|
from tools.tool_backend_helpers import (
|
||||||
|
fal_key_is_configured,
|
||||||
|
managed_nous_tools_enabled,
|
||||||
|
prefers_gateway,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -286,7 +290,7 @@ _managed_fal_client_lock = threading.Lock()
|
||||||
def _resolve_managed_fal_gateway():
|
def _resolve_managed_fal_gateway():
|
||||||
"""Return managed fal-queue gateway config when the user prefers the gateway
|
"""Return managed fal-queue gateway config when the user prefers the gateway
|
||||||
or direct FAL credentials are absent."""
|
or direct FAL credentials are absent."""
|
||||||
if os.getenv("FAL_KEY") and not prefers_gateway("image_gen"):
|
if fal_key_is_configured() and not prefers_gateway("image_gen"):
|
||||||
return None
|
return None
|
||||||
return resolve_managed_tool_gateway("fal-queue")
|
return resolve_managed_tool_gateway("fal-queue")
|
||||||
|
|
||||||
|
|
@ -623,9 +627,7 @@ def image_generate_tool(
|
||||||
if not prompt or not isinstance(prompt, str) or len(prompt.strip()) == 0:
|
if not prompt or not isinstance(prompt, str) or len(prompt.strip()) == 0:
|
||||||
raise ValueError("Prompt is required and must be a non-empty string")
|
raise ValueError("Prompt is required and must be a non-empty string")
|
||||||
|
|
||||||
fal_key_value = os.getenv("FAL_KEY")
|
if not (fal_key_is_configured() or _resolve_managed_fal_gateway()):
|
||||||
fal_key_set = bool(fal_key_value and fal_key_value.strip())
|
|
||||||
if not (fal_key_set or _resolve_managed_fal_gateway()):
|
|
||||||
message = "FAL_KEY environment variable not set"
|
message = "FAL_KEY environment variable not set"
|
||||||
if managed_nous_tools_enabled():
|
if managed_nous_tools_enabled():
|
||||||
message += " and managed FAL gateway is unavailable"
|
message += " and managed FAL gateway is unavailable"
|
||||||
|
|
@ -736,9 +738,7 @@ def image_generate_tool(
|
||||||
|
|
||||||
def check_fal_api_key() -> bool:
|
def check_fal_api_key() -> bool:
|
||||||
"""True if the FAL.ai API key (direct or managed gateway) is available."""
|
"""True if the FAL.ai API key (direct or managed gateway) is available."""
|
||||||
fal_key_value = os.getenv("FAL_KEY")
|
return bool(fal_key_is_configured() or _resolve_managed_fal_gateway())
|
||||||
fal_key_set = bool(fal_key_value and fal_key_value.strip())
|
|
||||||
return bool(fal_key_set or _resolve_managed_fal_gateway())
|
|
||||||
|
|
||||||
|
|
||||||
def check_image_generation_requirements() -> bool:
|
def check_image_generation_requirements() -> bool:
|
||||||
|
|
|
||||||
|
|
@ -119,3 +119,24 @@ def prefers_gateway(config_section: str) -> bool:
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def fal_key_is_configured() -> bool:
|
||||||
|
"""Return True when FAL_KEY is set to a non-whitespace value.
|
||||||
|
|
||||||
|
Consults both ``os.environ`` and ``~/.hermes/.env`` (via
|
||||||
|
``hermes_cli.config.get_env_value`` when available) so tool-side
|
||||||
|
checks and CLI setup-time checks agree. A whitespace-only value
|
||||||
|
is treated as unset everywhere.
|
||||||
|
"""
|
||||||
|
value = os.getenv("FAL_KEY")
|
||||||
|
if value is None:
|
||||||
|
# Fall back to the .env file for CLI paths that may run before
|
||||||
|
# dotenv is loaded into os.environ.
|
||||||
|
try:
|
||||||
|
from hermes_cli.config import get_env_value
|
||||||
|
|
||||||
|
value = get_env_value("FAL_KEY")
|
||||||
|
except Exception:
|
||||||
|
value = None
|
||||||
|
return bool(value and value.strip())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue