aigency 0.0.1rc15474277__py3-none-any.whl → 0.0.1rc62012314__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aigency/agents/client.py +78 -0
- aigency/agents/communicator.py +157 -0
- aigency/agents/executor.py +58 -2
- aigency/agents/generator.py +144 -22
- aigency/schemas/agent/agent.py +50 -0
- aigency/schemas/agent/model.py +46 -0
- aigency/schemas/agent/remote_agent.py +41 -0
- aigency/schemas/agent/skills.py +42 -0
- aigency/schemas/agent/tools.py +126 -0
- aigency/schemas/aigency_config.py +48 -0
- aigency/schemas/metadata/metadata.py +38 -0
- aigency/schemas/observability/observability.py +45 -0
- aigency/schemas/observability/phoenix.py +36 -0
- aigency/schemas/service/capabilities.py +32 -0
- aigency/schemas/service/interface.py +37 -0
- aigency/schemas/service/service.py +42 -0
- aigency/tools/generator.py +62 -15
- aigency/utils/config_service.py +116 -23
- aigency/utils/logger.py +135 -46
- aigency/utils/singleton.py +62 -1
- aigency/utils/utils.py +63 -6
- aigency-0.0.1rc62012314.dist-info/METADATA +267 -0
- aigency-0.0.1rc62012314.dist-info/RECORD +26 -0
- aigency/models/config.py +0 -69
- aigency/models/core.py +0 -28
- aigency/models/tools.py +0 -39
- aigency-0.0.1rc15474277.dist-info/METADATA +0 -267
- aigency-0.0.1rc15474277.dist-info/RECORD +0 -15
- {aigency-0.0.1rc15474277.dist-info → aigency-0.0.1rc62012314.dist-info}/WHEEL +0 -0
- {aigency-0.0.1rc15474277.dist-info → aigency-0.0.1rc62012314.dist-info}/top_level.txt +0 -0
aigency/utils/config_service.py
CHANGED
@@ -1,4 +1,24 @@
|
|
1
|
-
"""Configuration service for loading and parsing agent configurations.
|
1
|
+
"""Configuration service for loading and parsing agent configurations.
|
2
|
+
|
3
|
+
This module provides comprehensive configuration management for Aigency agents,
|
4
|
+
including YAML file loading, environment-specific configuration merging, and
|
5
|
+
validation through Pydantic models. It supports hierarchical configuration
|
6
|
+
structures and environment-based overrides.
|
7
|
+
|
8
|
+
The ConfigService class handles the complete configuration lifecycle from file
|
9
|
+
loading through parsing and validation, ensuring that agent configurations
|
10
|
+
are properly structured and ready for use by the agent system.
|
11
|
+
|
12
|
+
Example:
|
13
|
+
Loading and parsing agent configuration:
|
14
|
+
|
15
|
+
>>> service = ConfigService("config.yaml", environment="production")
|
16
|
+
>>> config = service.config
|
17
|
+
>>> agent_name = config.metadata.name
|
18
|
+
|
19
|
+
Attributes:
|
20
|
+
logger: Module-level logger instance for configuration events.
|
21
|
+
"""
|
2
22
|
|
3
23
|
import os
|
4
24
|
from pathlib import Path
|
@@ -6,63 +26,136 @@ from typing import Any, Dict, Optional
|
|
6
26
|
|
7
27
|
import yaml
|
8
28
|
|
9
|
-
from aigency.
|
29
|
+
from aigency.schemas.aigency_config import AigencyConfig
|
10
30
|
from aigency.utils.logger import get_logger
|
11
31
|
|
12
32
|
|
13
33
|
logger = get_logger()
|
14
34
|
|
35
|
+
|
15
36
|
class ConfigService:
|
16
|
-
"""Service for loading and managing agent configurations.
|
37
|
+
"""Service for loading and managing agent configurations.
|
38
|
+
|
39
|
+
This service handles loading YAML configuration files, merging environment-specific
|
40
|
+
configurations, and parsing them into AigencyConfig objects.
|
41
|
+
|
42
|
+
Attributes:
|
43
|
+
config_file (str): Path to the main configuration file.
|
44
|
+
environment (str | None): Environment name for environment-specific configs.
|
45
|
+
config (AigencyConfig): Parsed configuration object.
|
46
|
+
"""
|
47
|
+
|
17
48
|
def __init__(self, config_file: str, environment: Optional[str] = None):
|
49
|
+
"""Initialize the configuration service.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
config_file (str): Path to the main configuration file.
|
53
|
+
environment (str, optional): Environment name for environment-specific
|
54
|
+
configs. Defaults to None.
|
55
|
+
"""
|
18
56
|
self.config_file = config_file
|
19
|
-
self.environment = environment or os.getenv(
|
57
|
+
self.environment = environment or os.getenv("ENVIRONMENT", None)
|
20
58
|
self.config = self._load_and_parse()
|
21
59
|
|
22
|
-
def _load_and_parse(self) ->
|
23
|
-
"""
|
60
|
+
def _load_and_parse(self) -> AigencyConfig:
|
61
|
+
"""Load YAMLs, merge them and parse according to AigencyConfig.
|
62
|
+
|
63
|
+
Loads the main configuration file and optionally merges it with
|
64
|
+
environment-specific configuration if available.
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
AigencyConfig: Parsed and validated configuration object.
|
68
|
+
"""
|
24
69
|
|
25
70
|
logger.info(f"Loading configuration from {self.config_file}")
|
26
71
|
config = self._load_yaml(self.config_file)
|
27
72
|
|
28
73
|
if self.environment is not None:
|
29
|
-
logger.info(
|
74
|
+
logger.info(
|
75
|
+
f"Environment '{self.environment}' detected, loading environment-specific configuration"
|
76
|
+
)
|
30
77
|
env_config = self._load_env_config()
|
31
78
|
if env_config:
|
32
|
-
logger.info(
|
79
|
+
logger.info(
|
80
|
+
f"Successfully loaded environment configuration with {len(env_config)} keys: {list(env_config.keys())}"
|
81
|
+
)
|
33
82
|
config = self._merge_configs(config, env_config)
|
34
|
-
logger.debug(
|
83
|
+
logger.debug(
|
84
|
+
f"Configuration merged successfully for environment '{self.environment}'"
|
85
|
+
)
|
35
86
|
else:
|
36
|
-
logger.warning(
|
37
|
-
|
38
|
-
|
87
|
+
logger.warning(
|
88
|
+
f"No environment-specific configuration found for '{self.environment}', using base configuration only"
|
89
|
+
)
|
90
|
+
|
91
|
+
return AigencyConfig(**config)
|
39
92
|
|
40
93
|
def _load_yaml(self, file_path: str) -> Dict[str, Any]:
|
41
|
-
"""
|
94
|
+
"""Load a YAML file.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
file_path (str): Path to the YAML file to load.
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
Dict[str, Any]: Parsed YAML content as dictionary.
|
101
|
+
|
102
|
+
Raises:
|
103
|
+
FileNotFoundError: If the configuration file is not found.
|
104
|
+
ValueError: If there's an error parsing the YAML file.
|
105
|
+
"""
|
42
106
|
try:
|
43
107
|
with open(file_path, "r", encoding="utf-8") as file:
|
44
108
|
return yaml.safe_load(file) or {}
|
45
109
|
except FileNotFoundError:
|
46
|
-
raise FileNotFoundError(f"
|
110
|
+
raise FileNotFoundError(f"Configuration file not found: {file_path}")
|
47
111
|
except yaml.YAMLError as e:
|
48
|
-
raise ValueError(f"Error
|
112
|
+
raise ValueError(f"Error parsing YAML {file_path}: {e}")
|
49
113
|
|
50
114
|
def _load_env_config(self) -> Optional[Dict[str, Any]]:
|
51
|
-
"""
|
115
|
+
"""Load environment-specific configuration.
|
116
|
+
|
117
|
+
Attempts to load a configuration file with environment-specific naming
|
118
|
+
pattern (e.g., config.dev.yaml for 'dev' environment).
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
Dict[str, Any] | None: Environment configuration dictionary or None
|
122
|
+
if no environment-specific file exists.
|
123
|
+
"""
|
52
124
|
config_path = Path(self.config_file)
|
53
|
-
env_file =
|
54
|
-
|
125
|
+
env_file = (
|
126
|
+
config_path.parent
|
127
|
+
/ f"{config_path.stem}.{self.environment}{config_path.suffix}"
|
128
|
+
)
|
129
|
+
|
55
130
|
return self._load_yaml(str(env_file)) if env_file.exists() else None
|
56
131
|
|
57
|
-
def _merge_configs(
|
58
|
-
|
132
|
+
def _merge_configs(
|
133
|
+
self, base: Dict[str, Any], env: Optional[Dict[str, Any]]
|
134
|
+
) -> Dict[str, Any]:
|
135
|
+
"""Merge base configuration with environment configuration.
|
136
|
+
|
137
|
+
Recursively merges environment-specific configuration into the base
|
138
|
+
configuration, with environment values taking precedence.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
base (Dict[str, Any]): Base configuration dictionary.
|
142
|
+
env (Dict[str, Any] | None): Environment-specific configuration
|
143
|
+
dictionary.
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
Dict[str, Any]: Merged configuration dictionary.
|
147
|
+
"""
|
59
148
|
if not env:
|
60
149
|
return base
|
61
|
-
|
150
|
+
|
62
151
|
result = base.copy()
|
63
152
|
for key, value in env.items():
|
64
|
-
if
|
153
|
+
if (
|
154
|
+
key in result
|
155
|
+
and isinstance(result[key], dict)
|
156
|
+
and isinstance(value, dict)
|
157
|
+
):
|
65
158
|
result[key] = self._merge_configs(result[key], value)
|
66
159
|
else:
|
67
160
|
result[key] = value
|
68
|
-
return result
|
161
|
+
return result
|
aigency/utils/logger.py
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
"""Singleton logger implementation for consistent application-wide logging.
|
2
|
+
|
3
|
+
This module provides a centralized logging system using the Singleton pattern to
|
4
|
+
ensure consistent logging behavior across the entire Aigency application. It supports
|
5
|
+
configurable log levels, multiple output handlers, and dynamic configuration updates.
|
6
|
+
|
7
|
+
The Logger class extends the Singleton base class to guarantee only one logger
|
8
|
+
instance exists throughout the application lifecycle, preventing configuration
|
9
|
+
conflicts and ensuring unified log formatting and output destinations.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
Basic logger usage:
|
13
|
+
|
14
|
+
>>> logger = get_logger({"log_level": "DEBUG", "log_file": "app.log"})
|
15
|
+
>>> logger.info("Application started")
|
16
|
+
>>> logger.error("An error occurred", exc_info=True)
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
None: This module contains only class definitions and utility functions.
|
20
|
+
"""
|
21
|
+
|
1
22
|
import logging
|
2
23
|
import sys
|
3
24
|
from typing import Optional, Dict, Any
|
@@ -5,97 +26,165 @@ from aigency.utils.singleton import Singleton
|
|
5
26
|
|
6
27
|
|
7
28
|
class Logger(Singleton):
|
29
|
+
"""Singleton logger class for consistent logging across the application.
|
30
|
+
|
31
|
+
This class provides a centralized logging mechanism that ensures only one
|
32
|
+
logger instance exists throughout the application lifecycle.
|
33
|
+
|
34
|
+
Attributes:
|
35
|
+
config (Dict[str, Any]): Configuration dictionary for logger settings.
|
36
|
+
_logger (logging.Logger): Internal logger instance.
|
37
|
+
_initialized (bool): Flag to track initialization state.
|
38
|
+
"""
|
39
|
+
|
8
40
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
9
|
-
|
10
|
-
|
11
|
-
|
41
|
+
"""Initialize the logger with optional configuration.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
config (Dict[str, Any], optional): Dictionary containing logger
|
45
|
+
configuration. Defaults to None.
|
46
|
+
"""
|
47
|
+
if hasattr(self, "_initialized"):
|
48
|
+
# If already initialized and new config is passed, update
|
49
|
+
if config and config != getattr(self, "config", {}):
|
12
50
|
self.config.update(config)
|
13
51
|
self._setup_logger()
|
14
52
|
return
|
15
|
-
|
53
|
+
|
16
54
|
self._initialized = True
|
17
55
|
self.config = config or {}
|
18
56
|
self._logger = None
|
19
57
|
self._setup_logger()
|
20
|
-
|
58
|
+
|
21
59
|
def _setup_logger(self):
|
22
|
-
"""
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
60
|
+
"""Configure the logger with the provided configuration.
|
61
|
+
|
62
|
+
Sets up the internal logger instance with handlers, formatters, and
|
63
|
+
log levels based on the configuration dictionary.
|
64
|
+
"""
|
65
|
+
# Get logger configuration
|
66
|
+
log_level = self.config.get("log_level", "INFO").upper()
|
67
|
+
log_format = self.config.get(
|
68
|
+
"log_format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
69
|
+
)
|
70
|
+
log_file = self.config.get("log_file")
|
71
|
+
logger_name = self.config.get("logger_name", "aigency")
|
72
|
+
|
73
|
+
# Create logger
|
30
74
|
self._logger = logging.getLogger(logger_name)
|
31
75
|
self._logger.setLevel(getattr(logging, log_level, logging.INFO))
|
32
|
-
|
33
|
-
#
|
76
|
+
|
77
|
+
# Avoid duplicating handlers if they already exist
|
34
78
|
if self._logger.handlers:
|
35
79
|
return
|
36
|
-
|
37
|
-
#
|
80
|
+
|
81
|
+
# Create formatter
|
38
82
|
formatter = logging.Formatter(log_format)
|
39
|
-
|
40
|
-
#
|
83
|
+
|
84
|
+
# Console handler
|
41
85
|
console_handler = logging.StreamHandler(sys.stdout)
|
42
86
|
console_handler.setLevel(getattr(logging, log_level, logging.INFO))
|
43
87
|
console_handler.setFormatter(formatter)
|
44
88
|
self._logger.addHandler(console_handler)
|
45
|
-
|
46
|
-
#
|
89
|
+
|
90
|
+
# File handler if specified
|
47
91
|
if log_file:
|
48
92
|
file_handler = logging.FileHandler(log_file)
|
49
93
|
file_handler.setLevel(getattr(logging, log_level, logging.INFO))
|
50
94
|
file_handler.setFormatter(formatter)
|
51
95
|
self._logger.addHandler(file_handler)
|
52
|
-
|
96
|
+
|
53
97
|
def debug(self, message: str, *args, **kwargs):
|
54
|
-
"""Log a debug message
|
98
|
+
"""Log a debug message.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
message (str): The message to log.
|
102
|
+
*args: Variable length argument list.
|
103
|
+
**kwargs: Arbitrary keyword arguments.
|
104
|
+
"""
|
55
105
|
self._logger.debug(message, *args, **kwargs)
|
56
|
-
|
106
|
+
|
57
107
|
def info(self, message: str, *args, **kwargs):
|
58
|
-
"""Log an info message
|
108
|
+
"""Log an info message.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
message (str): The message to log.
|
112
|
+
*args: Variable length argument list.
|
113
|
+
**kwargs: Arbitrary keyword arguments.
|
114
|
+
"""
|
59
115
|
self._logger.info(message, *args, **kwargs)
|
60
|
-
|
116
|
+
|
61
117
|
def warning(self, message: str, *args, **kwargs):
|
62
|
-
"""Log a warning message
|
118
|
+
"""Log a warning message.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
message (str): The message to log.
|
122
|
+
*args: Variable length argument list.
|
123
|
+
**kwargs: Arbitrary keyword arguments.
|
124
|
+
"""
|
63
125
|
self._logger.warning(message, *args, **kwargs)
|
64
|
-
|
126
|
+
|
65
127
|
def error(self, message: str, *args, **kwargs):
|
66
|
-
"""Log an error message
|
128
|
+
"""Log an error message.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
message (str): The message to log.
|
132
|
+
*args: Variable length argument list.
|
133
|
+
**kwargs: Arbitrary keyword arguments.
|
134
|
+
"""
|
67
135
|
self._logger.error(message, *args, **kwargs)
|
68
|
-
|
136
|
+
|
69
137
|
def critical(self, message: str, *args, **kwargs):
|
70
|
-
"""Log a critical message
|
138
|
+
"""Log a critical message.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
message (str): The message to log.
|
142
|
+
*args: Variable length argument list.
|
143
|
+
**kwargs: Arbitrary keyword arguments.
|
144
|
+
"""
|
71
145
|
self._logger.critical(message, *args, **kwargs)
|
72
|
-
|
146
|
+
|
73
147
|
def exception(self, message: str, *args, **kwargs):
|
74
|
-
"""Log an exception with traceback
|
148
|
+
"""Log an exception with traceback.
|
149
|
+
|
150
|
+
Args:
|
151
|
+
message (str): The message to log.
|
152
|
+
*args: Variable length argument list.
|
153
|
+
**kwargs: Arbitrary keyword arguments.
|
154
|
+
"""
|
75
155
|
self._logger.exception(message, *args, **kwargs)
|
76
|
-
|
156
|
+
|
77
157
|
def set_level(self, level: str):
|
78
|
-
"""
|
158
|
+
"""Change the logging level dynamically.
|
159
|
+
|
160
|
+
Args:
|
161
|
+
level (str): The new logging level as a string.
|
162
|
+
"""
|
79
163
|
log_level = level.upper()
|
80
164
|
self._logger.setLevel(getattr(logging, log_level, logging.INFO))
|
81
165
|
for handler in self._logger.handlers:
|
82
166
|
handler.setLevel(getattr(logging, log_level, logging.INFO))
|
83
|
-
|
167
|
+
|
84
168
|
def get_logger(self):
|
85
|
-
"""
|
169
|
+
"""Get the internal logger instance.
|
170
|
+
|
171
|
+
Returns:
|
172
|
+
logging.Logger: The internal logging.Logger instance.
|
173
|
+
"""
|
86
174
|
return self._logger
|
87
175
|
|
88
176
|
|
89
|
-
#
|
177
|
+
# Convenience function to get the logger instance
|
90
178
|
def get_logger(config: Optional[Dict[str, Any]] = None) -> Logger:
|
91
|
-
"""
|
92
|
-
|
93
|
-
|
94
|
-
|
179
|
+
"""Get the singleton logger instance.
|
180
|
+
|
181
|
+
If this is the first call and config is provided, that configuration is used.
|
182
|
+
|
95
183
|
Args:
|
96
|
-
config
|
97
|
-
|
184
|
+
config (Dict[str, Any], optional): Optional configuration for the logger
|
185
|
+
(only used on first call). Defaults to None.
|
186
|
+
|
98
187
|
Returns:
|
99
|
-
|
188
|
+
Logger: Singleton Logger instance.
|
100
189
|
"""
|
101
|
-
return Logger(config)
|
190
|
+
return Logger(config)
|
aigency/utils/singleton.py
CHANGED
@@ -1,7 +1,54 @@
|
|
1
|
+
"""Singleton pattern implementation using metaclasses.
|
2
|
+
|
3
|
+
This module provides a robust implementation of the Singleton design pattern using
|
4
|
+
Python metaclasses. It ensures that classes inheriting from the Singleton base class
|
5
|
+
can have only one instance throughout the application lifecycle, which is useful
|
6
|
+
for shared resources like loggers, configuration managers, and database connections.
|
7
|
+
|
8
|
+
The implementation uses a metaclass approach to control instance creation at the
|
9
|
+
class level, providing thread-safe singleton behavior without requiring explicit
|
10
|
+
synchronization in the client code.
|
11
|
+
|
12
|
+
Example:
|
13
|
+
Creating singleton classes:
|
14
|
+
|
15
|
+
>>> class DatabaseManager(Singleton):
|
16
|
+
... def __init__(self):
|
17
|
+
... self.connection = "db_connection"
|
18
|
+
>>>
|
19
|
+
>>> db1 = DatabaseManager()
|
20
|
+
>>> db2 = DatabaseManager()
|
21
|
+
>>> db1 is db2
|
22
|
+
True
|
23
|
+
|
24
|
+
Attributes:
|
25
|
+
None: This module contains only class definitions.
|
26
|
+
"""
|
27
|
+
|
28
|
+
|
1
29
|
class SingletonMeta(type):
|
30
|
+
"""Metaclass that implements the Singleton pattern.
|
31
|
+
|
32
|
+
This metaclass ensures that only one instance of a class can exist.
|
33
|
+
When a class uses this metaclass, subsequent instantiation attempts
|
34
|
+
will return the same instance.
|
35
|
+
|
36
|
+
Attributes:
|
37
|
+
_instances (dict): Dictionary storing singleton instances by class.
|
38
|
+
"""
|
39
|
+
|
2
40
|
_instances = {}
|
3
41
|
|
4
42
|
def __call__(cls, *args, **kwargs):
|
43
|
+
"""Control instance creation to ensure singleton behavior.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
*args: Variable length argument list for class instantiation.
|
47
|
+
**kwargs: Arbitrary keyword arguments for class instantiation.
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
object: The singleton instance of the class.
|
51
|
+
"""
|
5
52
|
if cls not in cls._instances:
|
6
53
|
instance = super().__call__(*args, **kwargs)
|
7
54
|
cls._instances[cls] = instance
|
@@ -9,4 +56,18 @@ class SingletonMeta(type):
|
|
9
56
|
|
10
57
|
|
11
58
|
class Singleton(metaclass=SingletonMeta):
|
12
|
-
|
59
|
+
"""Base class for implementing singleton pattern.
|
60
|
+
|
61
|
+
Classes that inherit from this base class will automatically
|
62
|
+
follow the singleton pattern, ensuring only one instance exists.
|
63
|
+
|
64
|
+
Example:
|
65
|
+
>>> class MyClass(Singleton):
|
66
|
+
... pass
|
67
|
+
>>> obj1 = MyClass()
|
68
|
+
>>> obj2 = MyClass()
|
69
|
+
>>> obj1 is obj2
|
70
|
+
True
|
71
|
+
"""
|
72
|
+
|
73
|
+
pass
|
aigency/utils/utils.py
CHANGED
@@ -1,4 +1,23 @@
|
|
1
|
-
"""Utility functions for type conversions and environment variable handling.
|
1
|
+
"""Utility functions for type conversions and environment variable handling.
|
2
|
+
|
3
|
+
This module provides essential utility functions for the Aigency framework, including
|
4
|
+
type conversions between A2A and Google GenAI formats, environment variable expansion,
|
5
|
+
URL generation, and safe asynchronous execution helpers.
|
6
|
+
|
7
|
+
The utilities handle common operations needed across the framework, such as converting
|
8
|
+
message parts between different protocol formats, managing environment variables in
|
9
|
+
configurations, and safely running coroutines in various asyncio contexts.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
Converting between A2A and GenAI formats:
|
13
|
+
|
14
|
+
>>> a2a_part = TextPart(text="Hello world")
|
15
|
+
>>> genai_part = convert_a2a_part_to_genai(a2a_part)
|
16
|
+
>>> back_to_a2a = convert_genai_part_to_a2a(genai_part)
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
logger: Module-level logger instance for utility operations.
|
20
|
+
"""
|
2
21
|
|
3
22
|
import asyncio
|
4
23
|
import os
|
@@ -11,6 +30,7 @@ from aigency.utils.logger import get_logger
|
|
11
30
|
|
12
31
|
logger = get_logger()
|
13
32
|
|
33
|
+
|
14
34
|
def convert_a2a_part_to_genai(part: Part) -> types.Part:
|
15
35
|
"""Convert a single A2A Part type into a Google Gen AI Part type.
|
16
36
|
|
@@ -54,10 +74,19 @@ def convert_genai_part_to_a2a(part: types.Part) -> Part:
|
|
54
74
|
)
|
55
75
|
raise ValueError(f"Unsupported part type: {part}")
|
56
76
|
|
77
|
+
|
57
78
|
def expand_env_vars(env_dict):
|
58
|
-
"""
|
59
|
-
|
60
|
-
|
79
|
+
"""Expand dictionary values using environment variables.
|
80
|
+
|
81
|
+
Expands values in the dictionary using environment variables only if the value
|
82
|
+
is an existing environment variable key. If the variable doesn't exist in the
|
83
|
+
environment, leaves the literal value.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
env_dict (dict): Dictionary with potential environment variable references.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
dict: Dictionary with expanded environment variable values.
|
61
90
|
"""
|
62
91
|
result = {}
|
63
92
|
for k, v in env_dict.items():
|
@@ -67,8 +96,36 @@ def expand_env_vars(env_dict):
|
|
67
96
|
logger.warning(f"Environment variable {v} not found")
|
68
97
|
return result
|
69
98
|
|
99
|
+
|
100
|
+
def generate_url(host: str, port: int, path: str = "") -> str:
|
101
|
+
"""Generate a URL from host, port, and path components.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
host (str): Hostname or IP address.
|
105
|
+
port (int): Port number.
|
106
|
+
path (str, optional): URL path. Defaults to "".
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
str: Complete URL in the format http://host:port/path.
|
110
|
+
"""
|
111
|
+
return f"http://{host}:{port}{path}"
|
112
|
+
|
113
|
+
|
70
114
|
def safe_async_run(coro):
|
71
|
-
"""Simple wrapper to safely run async code.
|
115
|
+
"""Simple wrapper to safely run async code.
|
116
|
+
|
117
|
+
This function handles different asyncio event loop scenarios to safely
|
118
|
+
execute coroutines, including cases where a loop is already running.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
coro: The coroutine to execute.
|
122
|
+
|
123
|
+
Returns:
|
124
|
+
Any: The result of the coroutine execution.
|
125
|
+
|
126
|
+
Raises:
|
127
|
+
Exception: Any exception raised by the coroutine.
|
128
|
+
"""
|
72
129
|
try:
|
73
130
|
loop = asyncio.get_event_loop()
|
74
131
|
if loop.is_running():
|
@@ -93,4 +150,4 @@ def safe_async_run(coro):
|
|
93
150
|
else:
|
94
151
|
return loop.run_until_complete(coro)
|
95
152
|
except RuntimeError:
|
96
|
-
return asyncio.run(coro)
|
153
|
+
return asyncio.run(coro)
|