Skip to content

Commit 04aeebd

Browse files
committed
feat: compatible with .env and venv config.yaml
(cherry picked from commit 7d166ef6b41ea89d4fc8d65d574c901d2d76ae80)
1 parent f80ef95 commit 04aeebd

File tree

8 files changed

+160
-18
lines changed

8 files changed

+160
-18
lines changed

agentkit/apps/agent_server_app/middleware.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919

2020
from agentkit.apps.agent_server_app.telemetry import telemetry
2121

22-
_EXCLUDED_HEADERS = {
23-
"authorization",
24-
"token"
25-
}
22+
_EXCLUDED_HEADERS = {"authorization", "token"}
2623

2724

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

4945
# Currently unable to retrieve user_id and session_id from headers; keep logic for future use
@@ -75,5 +71,5 @@ async def send_wrapper(message):
7571
try:
7672
await self.app(scope, receive, send_wrapper)
7773
except Exception as e:
78-
telemetry.trace_agent_server_finish(path=path,func_result="", exception=e)
74+
telemetry.trace_agent_server_finish(path=path, func_result="", exception=e)
7975
raise

agentkit/apps/agent_server_app/telemetry.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,11 @@ def trace_agent_server_finish(
108108
attributes["error_type"] = exception.__class__.__name__
109109

110110
# only record invoke request latency metrics
111-
if hasattr(span, "start_time") and self.latency_histogram and path in _INVOKE_PATH:
111+
if (
112+
hasattr(span, "start_time")
113+
and self.latency_histogram
114+
and path in _INVOKE_PATH
115+
):
112116
duration = (time.time_ns() - span.start_time) / 1e9 # type: ignore
113117
self.latency_histogram.record(duration, attributes)
114118
span.end()

agentkit/toolkit/config/utils.py

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,17 @@
1414

1515
"""Configuration utility functions."""
1616

17-
from typing import Dict, Any
17+
from pathlib import Path
18+
from typing import Dict, Any, Optional
19+
import logging
20+
21+
from dotenv import load_dotenv, dotenv_values
22+
from yaml import safe_load, YAMLError
1823

1924
from .constants import AUTO_CREATE_VE
25+
from .config import CommonConfig
26+
27+
logger = logging.getLogger(__name__)
2028

2129

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

2937

38+
def load_dotenv_file(project_dir: Path) -> Dict[str, str]:
39+
"""Load environment variables from .env file (standard dotenv format).
40+
41+
Args:
42+
project_dir: Project directory containing .env file
43+
44+
Returns:
45+
Dictionary of environment variables from .env file
46+
"""
47+
env_file_path = project_dir / ".env"
48+
if not env_file_path.exists():
49+
return {}
50+
51+
# Load .env into environment temporarily to get the values
52+
load_dotenv(env_file_path)
53+
env_values = dotenv_values(env_file_path)
54+
return {k: str(v) for k, v in env_values.items() if v is not None}
55+
56+
57+
def load_veadk_yaml_file(project_dir: Path) -> Dict[str, str]:
58+
"""Load and flatten veADK's config.yaml file.
59+
60+
Args:
61+
project_dir: Project directory containing config.yaml file
62+
63+
Returns:
64+
Dictionary of flattened environment variables from config.yaml
65+
"""
66+
config_yaml_path = project_dir / "config.yaml"
67+
if not config_yaml_path.exists():
68+
return {}
69+
70+
try:
71+
with open(config_yaml_path, "r", encoding="utf-8") as yaml_file:
72+
config_dict = safe_load(yaml_file) or {}
73+
74+
# Flatten nested dictionary structure like veADK does
75+
flattened_config = flatten_dict(config_dict)
76+
# Convert to uppercase keys like veADK does
77+
return {k.upper(): str(v) for k, v in flattened_config.items() if v is not None}
78+
except (FileNotFoundError, PermissionError) as e:
79+
logger.warning(f"Cannot read config.yaml: {e}")
80+
return {}
81+
except (YAMLError, ValueError) as e:
82+
logger.warning(f"Invalid YAML format in config.yaml: {e}")
83+
return {}
84+
85+
86+
def load_compat_config_files(project_dir: Optional[Path] = None) -> Dict[str, str]:
87+
"""Load compatibility configuration files (.env and veADK config.yaml).
88+
89+
This function loads external configuration files for veADK compatibility:
90+
1. Load standard .env file if exists (higher priority)
91+
2. Load veADK config.yaml file if exists and flatten nested structure (lower priority)
92+
93+
Args:
94+
project_dir: Project directory to search for files. If None, uses current working directory.
95+
96+
Returns:
97+
Dictionary of environment variables from .env file and veADK config.yaml
98+
"""
99+
if project_dir is None:
100+
project_dir = Path.cwd()
101+
102+
veadk_envs = {}
103+
veadk_envs.update(load_veadk_yaml_file(project_dir))
104+
veadk_envs.update(load_dotenv_file(project_dir))
105+
106+
return veadk_envs
107+
108+
109+
def flatten_dict(
110+
d: Dict[str, Any], parent_key: str = "", sep: str = "_"
111+
) -> Dict[str, str]:
112+
"""Flatten a nested dictionary like veADK does.
113+
114+
Input: {"model": {"name": "doubao"}}
115+
Output: {"MODEL_NAME": "doubao"}
116+
117+
Args:
118+
d: Dictionary to flatten
119+
parent_key: Parent key prefix
120+
sep: Separator to use
121+
122+
Returns:
123+
Flattened dictionary with string values
124+
"""
125+
items = []
126+
for k, v in d.items():
127+
new_key = f"{parent_key}{sep}{k}" if parent_key else k
128+
if isinstance(v, dict):
129+
items.extend(flatten_dict(v, new_key, sep=sep).items())
130+
else:
131+
items.append((new_key.upper(), str(v)))
132+
return dict(items)
133+
134+
30135
def merge_runtime_envs(
31-
common_config: Any, strategy_config: Dict[str, Any]
136+
common_config: CommonConfig,
137+
strategy_config: Dict[str, Any],
138+
project_dir: Optional[Path] = None,
32139
) -> Dict[str, str]:
33-
"""Merge application-level and strategy-level environment variables.
140+
"""Merge environment variables from multiple sources with veADK compatibility.
34141
35-
Strategy-level variables override application-level ones with the same name.
142+
Priority order (highest to lowest):
143+
1. Strategy-level runtime_envs (from agentkit.yaml launch_types.*.runtime_envs)
144+
2. Common-level runtime_envs (from agentkit.yaml common.runtime_envs)
145+
3. .env file environment variables (standard dotenv format)
146+
4. config.yaml file environment variables (veADK style, flattened)
36147
37148
Args:
38149
common_config: CommonConfig instance
39150
strategy_config: Strategy configuration dict
151+
project_dir: Project directory for loading veADK files and .env file
40152
41153
Returns:
42154
Merged environment variables dict
43155
"""
44156
merged_envs = {}
45157

158+
# Load veADK environment files first (lowest priority)
159+
veadk_envs = load_compat_config_files(project_dir)
160+
if veadk_envs:
161+
merged_envs.update(veadk_envs)
162+
163+
# Add common-level runtime_envs (medium priority)
46164
app_level_envs = getattr(common_config, "runtime_envs", {})
47165
if isinstance(app_level_envs, dict):
48166
merged_envs.update(app_level_envs)
49167

168+
# Add strategy-level runtime_envs (highest priority)
50169
strategy_level_envs = strategy_config.get("runtime_envs", {})
51170
if isinstance(strategy_level_envs, dict):
52171
merged_envs.update(strategy_level_envs)

agentkit/toolkit/strategies/cloud_strategy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,16 @@ def _to_runner_config(
312312
Convert VeAgentkitConfig to VeAgentkitRunnerConfig.
313313
314314
Centralizes configuration mapping to keep orchestration logic clear.
315-
Merges environment variables from common and strategy configs.
315+
Merges environment variables from common and strategy configs with veADK compatibility.
316316
"""
317-
merged_envs = merge_runtime_envs(common_config, strategy_config.to_dict())
317+
# Get project directory from config manager if available
318+
project_dir = None
319+
if self.config_manager:
320+
project_dir = self.config_manager.get_project_dir()
321+
322+
merged_envs = merge_runtime_envs(
323+
common_config, strategy_config.to_dict(), project_dir
324+
)
318325

319326
return VeAgentkitRunnerConfig(
320327
common_config=common_config,

agentkit/toolkit/strategies/hybrid_strategy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,9 +479,16 @@ def _to_runner_config(
479479
self, common_config: CommonConfig, strategy_config: HybridStrategyConfig
480480
) -> VeAgentkitRunnerConfig:
481481
"""
482-
Convert HybridStrategyConfig to VeAgentkitRunnerConfig.
482+
Convert HybridStrategyConfig to VeAgentkitRunnerConfig with veADK compatibility.
483483
"""
484-
merged_envs = merge_runtime_envs(common_config, strategy_config.to_dict())
484+
# Get project directory from config manager if available
485+
project_dir = None
486+
if self.config_manager:
487+
project_dir = self.config_manager.get_project_dir()
488+
489+
merged_envs = merge_runtime_envs(
490+
common_config, strategy_config.to_dict(), project_dir
491+
)
485492

486493
return VeAgentkitRunnerConfig(
487494
common_config=common_config,

agentkit/toolkit/strategies/local_strategy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,16 @@ def _to_runner_config(
261261
Convert configuration to runner format.
262262
263263
Centralizes configuration mapping and merges environment variables from
264-
both application and strategy levels.
264+
both application and strategy levels with veADK compatibility.
265265
"""
266-
merged_envs = merge_runtime_envs(common_config, strategy_config.to_dict())
266+
# Get project directory from config manager if available
267+
project_dir = None
268+
if self.config_manager:
269+
project_dir = self.config_manager.get_project_dir()
270+
271+
merged_envs = merge_runtime_envs(
272+
common_config, strategy_config.to_dict(), project_dir
273+
)
267274

268275
return LocalDockerRunnerConfig(
269276
common_config=common_config,

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies = [
3434
"requests>=2.32.5",
3535
"uvicorn>=0.37.0",
3636
"pyyaml",
37+
"python-dotenv>=1.1.0",
3738
"prompt_toolkit",
3839
"typer",
3940
"rich",

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pydantic>=2.11.9
1010
requests>=2.32.5
1111
uvicorn>=0.37.0
1212
pyyaml>=6.0.2
13+
python-dotenv>=1.1.0
1314
prompt_toolkit
1415
typer>=0.19.2
1516
rich

0 commit comments

Comments
 (0)