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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions agentkit/apps/agent_server_app/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@

from agentkit.apps.agent_server_app.telemetry import telemetry

_EXCLUDED_HEADERS = {
"authorization",
"token"
}
_EXCLUDED_HEADERS = {"authorization", "token"}


class AgentkitTelemetryHTTPMiddleware:
Expand All @@ -42,8 +39,7 @@ async def __call__(self, scope, receive, send):
ctx = trace.set_span_in_context(span)
context_api.attach(ctx)
headers = {
k: v for k, v in headers.items()
if k.lower() not in _EXCLUDED_HEADERS
k: v for k, v in headers.items() if k.lower() not in _EXCLUDED_HEADERS
}

# Currently unable to retrieve user_id and session_id from headers; keep logic for future use
Expand Down Expand Up @@ -75,5 +71,5 @@ async def send_wrapper(message):
try:
await self.app(scope, receive, send_wrapper)
except Exception as e:
telemetry.trace_agent_server_finish(path=path,func_result="", exception=e)
telemetry.trace_agent_server_finish(path=path, func_result="", exception=e)
raise
6 changes: 5 additions & 1 deletion agentkit/apps/agent_server_app/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ def trace_agent_server_finish(
attributes["error_type"] = exception.__class__.__name__

# only record invoke request latency metrics
if hasattr(span, "start_time") and self.latency_histogram and path in _INVOKE_PATH:
if (
hasattr(span, "start_time")
and self.latency_histogram
and path in _INVOKE_PATH
):
duration = (time.time_ns() - span.start_time) / 1e9 # type: ignore
self.latency_histogram.record(duration, attributes)
span.end()
Expand Down
127 changes: 123 additions & 4 deletions agentkit/toolkit/config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@

"""Configuration utility functions."""

from typing import Dict, Any
from pathlib import Path
from typing import Dict, Any, Optional
import logging

from dotenv import load_dotenv, dotenv_values
from yaml import safe_load, YAMLError

from .constants import AUTO_CREATE_VE
from .config import CommonConfig

logger = logging.getLogger(__name__)


def is_invalid_config(s: str) -> bool:
Expand All @@ -27,26 +35,137 @@ def is_valid_config(s: str) -> bool:
return not is_invalid_config(s)


def load_dotenv_file(project_dir: Path) -> Dict[str, str]:
"""Load environment variables from .env file (standard dotenv format).

Args:
project_dir: Project directory containing .env file

Returns:
Dictionary of environment variables from .env file
"""
env_file_path = project_dir / ".env"
if not env_file_path.exists():
return {}

# Load .env into environment temporarily to get the values
load_dotenv(env_file_path)
env_values = dotenv_values(env_file_path)
return {k: str(v) for k, v in env_values.items() if v is not None}


def load_veadk_yaml_file(project_dir: Path) -> Dict[str, str]:
"""Load and flatten veADK's config.yaml file.

Args:
project_dir: Project directory containing config.yaml file

Returns:
Dictionary of flattened environment variables from config.yaml
"""
config_yaml_path = project_dir / "config.yaml"
if not config_yaml_path.exists():
return {}

try:
with open(config_yaml_path, "r", encoding="utf-8") as yaml_file:
config_dict = safe_load(yaml_file) or {}

# Flatten nested dictionary structure like veADK does
flattened_config = flatten_dict(config_dict)
# Convert to uppercase keys like veADK does
return {k.upper(): str(v) for k, v in flattened_config.items() if v is not None}
except (FileNotFoundError, PermissionError) as e:
logger.warning(f"Cannot read config.yaml: {e}")
return {}
except (YAMLError, ValueError) as e:
logger.warning(f"Invalid YAML format in config.yaml: {e}")
return {}


def load_compat_config_files(project_dir: Optional[Path] = None) -> Dict[str, str]:
"""Load compatibility configuration files (.env and veADK config.yaml).

This function loads external configuration files for veADK compatibility:
1. Load standard .env file if exists (higher priority)
2. Load veADK config.yaml file if exists and flatten nested structure (lower priority)

Args:
project_dir: Project directory to search for files. If None, uses current working directory.

Returns:
Dictionary of environment variables from .env file and veADK config.yaml
"""
if project_dir is None:
project_dir = Path.cwd()

veadk_envs = {}
veadk_envs.update(load_veadk_yaml_file(project_dir))
veadk_envs.update(load_dotenv_file(project_dir))

return veadk_envs


def flatten_dict(
d: Dict[str, Any], parent_key: str = "", sep: str = "_"
) -> Dict[str, str]:
"""Flatten a nested dictionary like veADK does.

Input: {"model": {"name": "doubao"}}
Output: {"MODEL_NAME": "doubao"}

Args:
d: Dictionary to flatten
parent_key: Parent key prefix
sep: Separator to use

Returns:
Flattened dictionary with string values
"""
items = []
for k, v in d.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
items.extend(flatten_dict(v, new_key, sep=sep).items())
else:
items.append((new_key.upper(), str(v)))
return dict(items)


def merge_runtime_envs(
common_config: Any, strategy_config: Dict[str, Any]
common_config: CommonConfig,
strategy_config: Dict[str, Any],
project_dir: Optional[Path] = None,
) -> Dict[str, str]:
"""Merge application-level and strategy-level environment variables.
"""Merge environment variables from multiple sources with veADK compatibility.

Strategy-level variables override application-level ones with the same name.
Priority order (highest to lowest):
1. Strategy-level runtime_envs (from agentkit.yaml launch_types.*.runtime_envs)
2. Common-level runtime_envs (from agentkit.yaml common.runtime_envs)
3. .env file environment variables (standard dotenv format)
4. config.yaml file environment variables (veADK style, flattened)

Args:
common_config: CommonConfig instance
strategy_config: Strategy configuration dict
project_dir: Project directory for loading veADK files and .env file

Returns:
Merged environment variables dict
"""
merged_envs = {}

# Load veADK environment files first (lowest priority)
veadk_envs = load_compat_config_files(project_dir)
if veadk_envs:
merged_envs.update(veadk_envs)

# Add common-level runtime_envs (medium priority)
app_level_envs = getattr(common_config, "runtime_envs", {})
if isinstance(app_level_envs, dict):
merged_envs.update(app_level_envs)

# Add strategy-level runtime_envs (highest priority)
strategy_level_envs = strategy_config.get("runtime_envs", {})
if isinstance(strategy_level_envs, dict):
merged_envs.update(strategy_level_envs)
Expand Down
11 changes: 9 additions & 2 deletions agentkit/toolkit/strategies/cloud_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,16 @@ def _to_runner_config(
Convert VeAgentkitConfig to VeAgentkitRunnerConfig.

Centralizes configuration mapping to keep orchestration logic clear.
Merges environment variables from common and strategy configs.
Merges environment variables from common and strategy configs with veADK compatibility.
"""
merged_envs = merge_runtime_envs(common_config, strategy_config.to_dict())
# Get project directory from config manager if available
project_dir = None
if self.config_manager:
project_dir = self.config_manager.get_project_dir()

merged_envs = merge_runtime_envs(
common_config, strategy_config.to_dict(), project_dir
)

return VeAgentkitRunnerConfig(
common_config=common_config,
Expand Down
11 changes: 9 additions & 2 deletions agentkit/toolkit/strategies/hybrid_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,9 +479,16 @@ def _to_runner_config(
self, common_config: CommonConfig, strategy_config: HybridStrategyConfig
) -> VeAgentkitRunnerConfig:
"""
Convert HybridStrategyConfig to VeAgentkitRunnerConfig.
Convert HybridStrategyConfig to VeAgentkitRunnerConfig with veADK compatibility.
"""
merged_envs = merge_runtime_envs(common_config, strategy_config.to_dict())
# Get project directory from config manager if available
project_dir = None
if self.config_manager:
project_dir = self.config_manager.get_project_dir()

merged_envs = merge_runtime_envs(
common_config, strategy_config.to_dict(), project_dir
)

return VeAgentkitRunnerConfig(
common_config=common_config,
Expand Down
11 changes: 9 additions & 2 deletions agentkit/toolkit/strategies/local_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,16 @@ def _to_runner_config(
Convert configuration to runner format.

Centralizes configuration mapping and merges environment variables from
both application and strategy levels.
both application and strategy levels with veADK compatibility.
"""
merged_envs = merge_runtime_envs(common_config, strategy_config.to_dict())
# Get project directory from config manager if available
project_dir = None
if self.config_manager:
project_dir = self.config_manager.get_project_dir()

merged_envs = merge_runtime_envs(
common_config, strategy_config.to_dict(), project_dir
)

return LocalDockerRunnerConfig(
common_config=common_config,
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies = [
"requests>=2.32.5",
"uvicorn>=0.37.0",
"pyyaml",
"python-dotenv>=1.1.0",
"prompt_toolkit",
"typer",
"rich",
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pydantic>=2.11.9
requests>=2.32.5
uvicorn>=0.37.0
pyyaml>=6.0.2
python-dotenv>=1.1.0
prompt_toolkit
typer>=0.19.2
rich
Expand Down