1
1
"""Experimental agent configuration utilities.
2
2
3
3
This module provides utilities for creating agents from configuration files or dictionaries.
4
+
5
+ Note: Configuration-based agent setup only works for tools that don't require code-based
6
+ instantiation. For tools that need constructor arguments or complex setup, use the
7
+ programmatic approach after creating the agent:
8
+
9
+ agent = config_to_agent("config.json")
10
+ # Add tools that need code-based instantiation
11
+ agent.process_tools([ToolWithConfigArg(HttpsConnection("localhost"))])
4
12
"""
5
13
14
+ import importlib
6
15
import json
16
+ import os
7
17
from pathlib import Path
8
18
9
19
import jsonschema
10
20
from jsonschema import ValidationError
11
21
12
22
from ..agent import Agent
13
23
24
+ # JSON Schema for agent configuration
25
+ AGENT_CONFIG_SCHEMA = {
26
+ "$schema" : "http://json-schema.org/draft-07/schema#" ,
27
+ "title" : "Agent Configuration" ,
28
+ "description" : "Configuration schema for creating agents" ,
29
+ "type" : "object" ,
30
+ "properties" : {
31
+ "name" : {
32
+ "description" : "Name of the agent" ,
33
+ "type" : ["string" , "null" ],
34
+ "default" : None
35
+ },
36
+ "model" : {
37
+ "description" : "The model ID to use for this agent. If not specified, uses the default model." ,
38
+ "type" : ["string" , "null" ],
39
+ "default" : None
40
+ },
41
+ "prompt" : {
42
+ "description" : "The system prompt for the agent. Provides high level context to the agent." ,
43
+ "type" : ["string" , "null" ],
44
+ "default" : None
45
+ },
46
+ "tools" : {
47
+ "description" : "List of tools the agent can use. Can be file paths, Python module names, or @tool annotated functions in files." ,
48
+ "type" : "array" ,
49
+ "items" : {
50
+ "type" : "string"
51
+ },
52
+ "default" : []
53
+ }
54
+ },
55
+ "additionalProperties" : False
56
+ }
14
57
15
- def _load_schema () -> dict :
16
- """Load the agent configuration schema from file."""
17
- schema_path = Path (__file__ ).parent / "schemas" / "agent-config-v1.json"
18
- with open (schema_path , 'r' ) as f :
19
- return json .load (f )
58
+ # Pre-compile validator for better performance
59
+ _VALIDATOR = jsonschema .Draft7Validator (AGENT_CONFIG_SCHEMA )
20
60
21
61
22
- # Pre-compile validator for better performance
23
- _VALIDATOR = jsonschema .Draft7Validator (_load_schema ())
62
+ def _is_filepath (tool_path : str ) -> bool :
63
+ """Check if the tool string is a file path."""
64
+ return os .path .exists (tool_path ) or tool_path .endswith ('.py' )
65
+
66
+
67
+ def _validate_tools (tools : list [str ]) -> None :
68
+ """Validate that tools can be loaded as files or modules."""
69
+ for tool in tools :
70
+ if _is_filepath (tool ):
71
+ # File path - will be handled by Agent's tool loading
72
+ continue
73
+
74
+ try :
75
+ # Try to import as module
76
+ importlib .import_module (tool )
77
+ except ImportError :
78
+ # Not a file and not a module - check if it might be a function reference
79
+ if '.' in tool :
80
+ module_path , func_name = tool .rsplit ('.' , 1 )
81
+ try :
82
+ module = importlib .import_module (module_path )
83
+ if not hasattr (module , func_name ):
84
+ raise ValueError (
85
+ f"Tool '{ tool } ' not found. The configured tool is not annotated with @tool, "
86
+ f"and is not a module or file. To properly import this tool, you must annotate it with @tool."
87
+ )
88
+ except ImportError :
89
+ raise ValueError (
90
+ f"Tool '{ tool } ' not found. The configured tool is not annotated with @tool, "
91
+ f"and is not a module or file. To properly import this tool, you must annotate it with @tool."
92
+ )
93
+ else :
94
+ raise ValueError (
95
+ f"Tool '{ tool } ' not found. The configured tool is not annotated with @tool, "
96
+ f"and is not a module or file. To properly import this tool, you must annotate it with @tool."
97
+ )
24
98
25
99
26
100
def config_to_agent (config : str | dict [str , any ], ** kwargs ) -> Agent :
27
101
"""Create an Agent from a configuration file or dictionary.
28
102
103
+ This function supports tools that can be loaded declaratively (file paths, module names,
104
+ or @tool annotated functions). For tools requiring code-based instantiation with constructor
105
+ arguments, add them programmatically after creating the agent:
106
+
107
+ agent = config_to_agent("config.json")
108
+ agent.process_tools([ToolWithConfigArg(HttpsConnection("localhost"))])
109
+
29
110
Args:
30
111
config: Either a file path (with optional file:// prefix) or a configuration dictionary
31
112
**kwargs: Additional keyword arguments to pass to the Agent constructor
@@ -36,7 +117,7 @@ def config_to_agent(config: str | dict[str, any], **kwargs) -> Agent:
36
117
Raises:
37
118
FileNotFoundError: If the configuration file doesn't exist
38
119
json.JSONDecodeError: If the configuration file contains invalid JSON
39
- ValueError: If the configuration is invalid
120
+ ValueError: If the configuration is invalid or tools cannot be loaded
40
121
41
122
Examples:
42
123
Create agent from file:
@@ -78,6 +159,10 @@ def config_to_agent(config: str | dict[str, any], **kwargs) -> Agent:
78
159
error_path = " -> " .join (str (p ) for p in e .absolute_path ) if e .absolute_path else "root"
79
160
raise ValueError (f"Configuration validation error at { error_path } : { e .message } " ) from e
80
161
162
+ # Validate tools can be loaded
163
+ if "tools" in config_dict and config_dict ["tools" ]:
164
+ _validate_tools (config_dict ["tools" ])
165
+
81
166
# Prepare Agent constructor arguments
82
167
agent_kwargs = {}
83
168
0 commit comments