signalwire-agents 0.1.20__py3-none-any.whl → 0.1.23__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.
Files changed (37) hide show
  1. signalwire_agents/__init__.py +1 -1
  2. signalwire_agents/agent_server.py +50 -11
  3. signalwire_agents/core/__init__.py +2 -2
  4. signalwire_agents/core/agent/__init__.py +14 -0
  5. signalwire_agents/core/agent/config/__init__.py +14 -0
  6. signalwire_agents/core/agent/config/ephemeral.py +176 -0
  7. signalwire_agents/core/agent/deployment/__init__.py +0 -0
  8. signalwire_agents/core/agent/deployment/handlers/__init__.py +0 -0
  9. signalwire_agents/core/agent/prompt/__init__.py +14 -0
  10. signalwire_agents/core/agent/prompt/manager.py +288 -0
  11. signalwire_agents/core/agent/routing/__init__.py +0 -0
  12. signalwire_agents/core/agent/security/__init__.py +0 -0
  13. signalwire_agents/core/agent/swml/__init__.py +0 -0
  14. signalwire_agents/core/agent/tools/__init__.py +15 -0
  15. signalwire_agents/core/agent/tools/decorator.py +95 -0
  16. signalwire_agents/core/agent/tools/registry.py +192 -0
  17. signalwire_agents/core/agent_base.py +131 -413
  18. signalwire_agents/core/data_map.py +3 -15
  19. signalwire_agents/core/skill_manager.py +0 -17
  20. signalwire_agents/core/swaig_function.py +0 -2
  21. signalwire_agents/core/swml_builder.py +207 -11
  22. signalwire_agents/core/swml_renderer.py +123 -312
  23. signalwire_agents/core/swml_service.py +25 -94
  24. signalwire_agents/search/index_builder.py +1 -1
  25. signalwire_agents/skills/api_ninjas_trivia/__init__.py +3 -0
  26. signalwire_agents/skills/api_ninjas_trivia/skill.py +192 -0
  27. signalwire_agents/skills/play_background_file/__init__.py +3 -0
  28. signalwire_agents/skills/play_background_file/skill.py +197 -0
  29. signalwire_agents/skills/weather_api/__init__.py +3 -0
  30. signalwire_agents/skills/weather_api/skill.py +154 -0
  31. {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/METADATA +5 -8
  32. {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/RECORD +37 -18
  33. {signalwire_agents-0.1.20.data → signalwire_agents-0.1.23.data}/data/schema.json +0 -0
  34. {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/WHEEL +0 -0
  35. {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/entry_points.txt +0 -0
  36. {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/licenses/LICENSE +0 -0
  37. {signalwire_agents-0.1.20.dist-info → signalwire_agents-0.1.23.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,192 @@
1
+ """
2
+ API Ninjas Trivia Skill
3
+
4
+ A configurable skill for getting trivia questions from API Ninjas with customizable
5
+ categories and multiple tool instances.
6
+ """
7
+
8
+ from typing import Dict, Any, List
9
+ from signalwire_agents.core import SwaigFunctionResult
10
+ from signalwire_agents.core.skill_base import SkillBase
11
+
12
+
13
+ class ApiNinjasTriviaSkill(SkillBase):
14
+ """
15
+ Skill for getting trivia questions from API Ninjas with configurable categories.
16
+
17
+ Supports multiple instances with different tool names and category combinations.
18
+ Uses DataMap for serverless execution with dynamic enum generation.
19
+
20
+ Configuration:
21
+ - tool_name: Custom name for the generated SWAIG function
22
+ - api_key: API Ninjas API key
23
+ - categories: Array of category strings to enable
24
+
25
+ Available categories:
26
+ - artliterature: Art and Literature
27
+ - language: Language
28
+ - sciencenature: Science and Nature
29
+ - general: General Knowledge
30
+ - fooddrink: Food and Drink
31
+ - peopleplaces: People and Places
32
+ - geography: Geography
33
+ - historyholidays: History and Holidays
34
+ - entertainment: Entertainment
35
+ - toysgames: Toys and Games
36
+ - music: Music
37
+ - mathematics: Mathematics
38
+ - religionmythology: Religion and Mythology
39
+ - sportsleisure: Sports and Leisure
40
+
41
+ Example:
42
+ agent.add_skill("api_ninjas_trivia", {
43
+ "tool_name": "get_science_trivia",
44
+ "api_key": "your_api_key",
45
+ "categories": ["sciencenature", "mathematics", "general"]
46
+ })
47
+ """
48
+
49
+ SKILL_NAME = "api_ninjas_trivia"
50
+ SKILL_DESCRIPTION = "Get trivia questions from API Ninjas"
51
+ SUPPORTS_MULTIPLE_INSTANCES = True
52
+ REQUIRED_ENV_VARS = [] # API key can be passed via params
53
+
54
+ # Valid API Ninjas trivia categories with human-readable descriptions
55
+ VALID_CATEGORIES = {
56
+ "artliterature": "Art and Literature",
57
+ "language": "Language",
58
+ "sciencenature": "Science and Nature",
59
+ "general": "General Knowledge",
60
+ "fooddrink": "Food and Drink",
61
+ "peopleplaces": "People and Places",
62
+ "geography": "Geography",
63
+ "historyholidays": "History and Holidays",
64
+ "entertainment": "Entertainment",
65
+ "toysgames": "Toys and Games",
66
+ "music": "Music",
67
+ "mathematics": "Mathematics",
68
+ "religionmythology": "Religion and Mythology",
69
+ "sportsleisure": "Sports and Leisure"
70
+ }
71
+
72
+ def __init__(self, agent, params: Dict[str, Any] = None):
73
+ """
74
+ Initialize the skill with configuration parameters.
75
+
76
+ Args:
77
+ agent: The agent instance this skill belongs to
78
+ params: Configuration dictionary containing:
79
+ - tool_name: Custom tool name (default: "get_trivia")
80
+ - api_key: API Ninjas API key (required)
81
+ - categories: Array of category strings (default: all categories)
82
+ """
83
+ super().__init__(agent, params)
84
+
85
+ # Extract configuration
86
+ self.tool_name = self.params.get('tool_name', 'get_trivia')
87
+ self.api_key = self.params.get('api_key')
88
+ self.categories = self.params.get('categories', list(self.VALID_CATEGORIES.keys()))
89
+
90
+ # Validate configuration
91
+ self._validate_config()
92
+
93
+ def _validate_config(self):
94
+ """Validate the skill configuration."""
95
+ # Validate API key
96
+ if not self.api_key or not isinstance(self.api_key, str):
97
+ raise ValueError("api_key parameter is required and must be a non-empty string")
98
+
99
+ # Validate categories
100
+ if not isinstance(self.categories, list) or len(self.categories) == 0:
101
+ raise ValueError("categories parameter must be a non-empty list")
102
+
103
+ # Validate each category
104
+ for i, category in enumerate(self.categories):
105
+ if not isinstance(category, str):
106
+ raise ValueError(f"Category {i} must be a string")
107
+ if category not in self.VALID_CATEGORIES:
108
+ valid_cats = ', '.join(self.VALID_CATEGORIES.keys())
109
+ raise ValueError(f"Category '{category}' is not valid. Valid categories: {valid_cats}")
110
+
111
+ def setup(self) -> bool:
112
+ """
113
+ Setup the skill - validates API key is available.
114
+
115
+ Returns:
116
+ True if setup successful
117
+ """
118
+ # API key validation already done in _validate_config
119
+ return True
120
+
121
+ def register_tools(self) -> None:
122
+ """Register SWAIG tools with the agent"""
123
+ tools = self.get_tools()
124
+ for tool in tools:
125
+ # Merge any swaig_fields from params into the tool
126
+ if self.swaig_fields:
127
+ tool.update(self.swaig_fields)
128
+ self.agent.register_swaig_function(tool)
129
+
130
+ def get_instance_key(self) -> str:
131
+ """
132
+ Generate a unique instance key for this skill configuration.
133
+
134
+ Returns:
135
+ Unique key combining skill name and tool name
136
+ """
137
+ return f"{self.SKILL_NAME}_{self.tool_name}"
138
+
139
+ def get_tools(self) -> List[Dict[str, Any]]:
140
+ """
141
+ Generate the SWAIG tool with DataMap webhook.
142
+
143
+ Returns:
144
+ List containing the generated tool configuration
145
+ """
146
+ # Build enum values and descriptions
147
+ enum_values = []
148
+ descriptions = []
149
+
150
+ for category in self.categories:
151
+ enum_values.append(category)
152
+ descriptions.append(f"{category}: {self.VALID_CATEGORIES[category]}")
153
+
154
+ # Build parameter description
155
+ description = "Category for trivia question. Options: " + "; ".join(descriptions)
156
+
157
+ # Create the tool configuration with DataMap webhook
158
+ tool = {
159
+ "function": self.tool_name,
160
+ "description": f"Get trivia questions for {self.tool_name.replace('_', ' ')}",
161
+ "parameters": {
162
+ "type": "object",
163
+ "properties": {
164
+ "category": {
165
+ "type": "string",
166
+ "description": description,
167
+ "enum": enum_values
168
+ }
169
+ },
170
+ "required": ["category"]
171
+ },
172
+ "data_map": {
173
+ "webhooks": [
174
+ {
175
+ "url": "https://api.api-ninjas.com/v1/trivia?category=%{args.category}",
176
+ "method": "GET",
177
+ "headers": {
178
+ "X-Api-Key": self.api_key
179
+ },
180
+ "output": SwaigFunctionResult(
181
+ "Category %{array[0].category} question: %{array[0].question} Answer: %{array[0].answer}, be sure to give the user time to answer before saying the answer."
182
+ ).to_dict()
183
+ }
184
+ ],
185
+ "error_keys": ["error"],
186
+ "output": SwaigFunctionResult(
187
+ "Sorry, I cannot get trivia questions right now. Please try again later."
188
+ ).to_dict()
189
+ }
190
+ }
191
+
192
+ return [tool]
@@ -0,0 +1,3 @@
1
+ from .skill import PlayBackgroundFileSkill
2
+
3
+ __all__ = ['PlayBackgroundFileSkill']
@@ -0,0 +1,197 @@
1
+ """
2
+ Play Background File Skill
3
+
4
+ A configurable skill for managing background file playback with custom tool names
5
+ and multiple file collections. Supports both audio and video files.
6
+ """
7
+
8
+ from typing import Dict, Any, List
9
+ from signalwire_agents.core import SwaigFunctionResult
10
+ from signalwire_agents.core.skill_base import SkillBase
11
+
12
+
13
+ class PlayBackgroundFileSkill(SkillBase):
14
+ """
15
+ Skill for playing background files (audio/video) with configurable tool names.
16
+
17
+ Supports multiple instances with different tool names and file collections.
18
+ Uses DataMap for serverless execution with dynamic enum generation.
19
+
20
+ Configuration:
21
+ - tool_name: Custom name for the generated SWAIG function
22
+ - files: Array of file objects with key, description, url, and optional wait
23
+
24
+ Example:
25
+ agent.add_skill("play_background_file", {
26
+ "tool_name": "play_testimonial",
27
+ "files": [
28
+ {
29
+ "key": "massey",
30
+ "description": "Customer success story from Massey Energy",
31
+ "url": "https://example.com/massey.mp4",
32
+ "wait": True
33
+ }
34
+ ]
35
+ })
36
+ """
37
+
38
+ SKILL_NAME = "play_background_file"
39
+ SKILL_DESCRIPTION = "Control background file playback"
40
+ SUPPORTS_MULTIPLE_INSTANCES = True
41
+
42
+ def __init__(self, agent, params: Dict[str, Any] = None):
43
+ """
44
+ Initialize the skill with configuration parameters.
45
+
46
+ Args:
47
+ agent: The agent instance this skill belongs to
48
+ params: Configuration dictionary containing:
49
+ - tool_name: Custom tool name (default: "play_background_file")
50
+ - files: Array of file objects with key, description, url, wait
51
+ """
52
+ super().__init__(agent, params)
53
+
54
+ # Extract configuration
55
+ self.tool_name = self.params.get('tool_name', 'play_background_file')
56
+ self.files = self.params.get('files', [])
57
+
58
+ # Validate configuration
59
+ self._validate_config()
60
+
61
+ def _validate_config(self):
62
+ """Validate the skill configuration."""
63
+ if not isinstance(self.files, list) or len(self.files) == 0:
64
+ raise ValueError("files parameter must be a non-empty list")
65
+
66
+ for i, file_obj in enumerate(self.files):
67
+ if not isinstance(file_obj, dict):
68
+ raise ValueError(f"File {i} must be a dictionary")
69
+
70
+ required_fields = ['key', 'description', 'url']
71
+ for field in required_fields:
72
+ if field not in file_obj:
73
+ raise ValueError(f"File {i} missing required field: {field}")
74
+ if not isinstance(file_obj[field], str) or not file_obj[field].strip():
75
+ raise ValueError(f"File {i} field '{field}' must be a non-empty string")
76
+
77
+ # Validate optional wait field
78
+ if 'wait' in file_obj and not isinstance(file_obj['wait'], bool):
79
+ raise ValueError(f"File {i} field 'wait' must be a boolean")
80
+
81
+ # Validate key format (alphanumeric, underscores, hyphens)
82
+ key = file_obj['key']
83
+ if not key.replace('_', '').replace('-', '').isalnum():
84
+ raise ValueError(f"File {i} key '{key}' must contain only alphanumeric characters, underscores, and hyphens")
85
+
86
+ def get_instance_key(self) -> str:
87
+ """
88
+ Generate a unique instance key for this skill configuration.
89
+
90
+ Returns:
91
+ Unique key combining skill name and tool name
92
+ """
93
+ return f"{self.SKILL_NAME}_{self.tool_name}"
94
+
95
+ def setup(self) -> bool:
96
+ """
97
+ Setup the skill - no external dependencies needed.
98
+
99
+ Returns:
100
+ True if setup successful
101
+ """
102
+ return True
103
+
104
+ def register_tools(self) -> None:
105
+ """Register SWAIG tools with the agent"""
106
+ tools = self.get_tools()
107
+ for tool in tools:
108
+ # Merge any swaig_fields from params into the tool
109
+ if self.swaig_fields:
110
+ tool.update(self.swaig_fields)
111
+ self.agent.register_swaig_function(tool)
112
+
113
+ def get_tools(self) -> List[Dict[str, Any]]:
114
+ """
115
+ Generate the SWAIG tool with DataMap expressions.
116
+
117
+ Returns:
118
+ List containing the generated tool configuration
119
+ """
120
+ # Build enum values and descriptions
121
+ enum_values = []
122
+ descriptions = []
123
+
124
+ for file_obj in self.files:
125
+ enum_values.append(f"start_{file_obj['key']}")
126
+ descriptions.append(f"start_{file_obj['key']}: {file_obj['description']}")
127
+
128
+ enum_values.append("stop")
129
+ descriptions.append("stop: Stop any currently playing background file")
130
+
131
+ # Build parameter description
132
+ description = "Action to perform. Options: " + "; ".join(descriptions)
133
+
134
+ # Create the tool configuration
135
+ tool = {
136
+ "function": self.tool_name,
137
+ "description": f"Control background file playback for {self.tool_name.replace('_', ' ')}",
138
+ "parameters": {
139
+ "type": "object",
140
+ "properties": {
141
+ "action": {
142
+ "type": "string",
143
+ "description": description,
144
+ "enum": enum_values
145
+ }
146
+ },
147
+ "required": ["action"]
148
+ },
149
+ "wait_for_fillers": True,
150
+ "skip_fillers": True,
151
+ "data_map": {
152
+ "expressions": self._build_expressions()
153
+ }
154
+ }
155
+
156
+ return [tool]
157
+
158
+ def _build_expressions(self) -> List[Dict[str, Any]]:
159
+ """
160
+ Build DataMap expressions for file playback and stop actions.
161
+
162
+ Returns:
163
+ List of expression configurations
164
+ """
165
+ expressions = []
166
+
167
+ # Add expressions for each file
168
+ for file_obj in self.files:
169
+ key = file_obj['key']
170
+ url = file_obj['url']
171
+ wait = file_obj.get('wait', False)
172
+ description = file_obj['description']
173
+
174
+ # Create the result with appropriate response
175
+ result = SwaigFunctionResult(
176
+ f"Tell the user you are now going to play {description} for them.",
177
+ post_process=True
178
+ ).play_background_file(url, wait=wait)
179
+
180
+ expressions.append({
181
+ "string": "${args.action}",
182
+ "pattern": f"/start_{key}/i",
183
+ "output": result.to_dict()
184
+ })
185
+
186
+ # Add stop expression
187
+ stop_result = SwaigFunctionResult(
188
+ "Tell the user you have stopped the background file playback."
189
+ ).stop_background_file()
190
+
191
+ expressions.append({
192
+ "string": "${args.action}",
193
+ "pattern": "/stop/i",
194
+ "output": stop_result.to_dict()
195
+ })
196
+
197
+ return expressions
@@ -0,0 +1,3 @@
1
+ from .skill import WeatherApiSkill
2
+
3
+ __all__ = ['WeatherApiSkill']
@@ -0,0 +1,154 @@
1
+ """
2
+ Weather API Skill
3
+
4
+ A configurable skill for getting weather information from WeatherAPI.com with customizable
5
+ temperature units and TTS-friendly responses.
6
+ """
7
+
8
+ from typing import Dict, Any, List
9
+ from signalwire_agents.core import SwaigFunctionResult
10
+ from signalwire_agents.core.skill_base import SkillBase
11
+
12
+
13
+ class WeatherApiSkill(SkillBase):
14
+ """
15
+ Skill for getting weather information from WeatherAPI.com.
16
+
17
+ Provides current weather data with configurable temperature units and
18
+ TTS-optimized natural language responses.
19
+
20
+ Configuration:
21
+ - tool_name: Custom name for the generated SWAIG function
22
+ - api_key: WeatherAPI.com API key
23
+ - temperature_unit: "fahrenheit" or "celsius" for temperature display
24
+
25
+ Example:
26
+ agent.add_skill("weather_api", {
27
+ "tool_name": "get_weather",
28
+ "api_key": "your_weatherapi_key",
29
+ "temperature_unit": "fahrenheit"
30
+ })
31
+ """
32
+
33
+ SKILL_NAME = "weather_api"
34
+ SKILL_DESCRIPTION = "Get current weather information from WeatherAPI.com"
35
+ SUPPORTS_MULTIPLE_INSTANCES = False
36
+ REQUIRED_ENV_VARS = [] # API key can be passed via params
37
+
38
+ def __init__(self, agent, params: Dict[str, Any] = None):
39
+ """
40
+ Initialize the skill with configuration parameters.
41
+
42
+ Args:
43
+ agent: The agent instance this skill belongs to
44
+ params: Configuration dictionary containing:
45
+ - tool_name: Custom tool name (default: "get_weather")
46
+ - api_key: WeatherAPI.com API key (required)
47
+ - temperature_unit: "fahrenheit" or "celsius" (default: "fahrenheit")
48
+ """
49
+ super().__init__(agent, params)
50
+
51
+ # Extract configuration
52
+ self.tool_name = self.params.get('tool_name', 'get_weather')
53
+ self.api_key = self.params.get('api_key')
54
+ self.temperature_unit = self.params.get('temperature_unit', 'fahrenheit')
55
+
56
+ # Validate configuration
57
+ self._validate_config()
58
+
59
+ def _validate_config(self):
60
+ """Validate the skill configuration."""
61
+ # Validate API key
62
+ if not self.api_key or not isinstance(self.api_key, str):
63
+ raise ValueError("api_key parameter is required and must be a non-empty string")
64
+
65
+ # Validate temperature unit
66
+ if self.temperature_unit not in ['fahrenheit', 'celsius']:
67
+ raise ValueError("temperature_unit must be either 'fahrenheit' or 'celsius'")
68
+
69
+ def setup(self) -> bool:
70
+ """
71
+ Setup the skill - validates API key is available.
72
+
73
+ Returns:
74
+ True if setup successful
75
+ """
76
+ # API key validation already done in _validate_config
77
+ return True
78
+
79
+ def register_tools(self) -> None:
80
+ """Register SWAIG tools with the agent"""
81
+ tools = self.get_tools()
82
+ for tool in tools:
83
+ # Merge any swaig_fields from params into the tool
84
+ if self.swaig_fields:
85
+ tool.update(self.swaig_fields)
86
+ self.agent.register_swaig_function(tool)
87
+
88
+ def get_tools(self) -> List[Dict[str, Any]]:
89
+ """
90
+ Generate the SWAIG tool with DataMap webhook.
91
+
92
+ Returns:
93
+ List containing the generated tool configuration
94
+ """
95
+ # Determine temperature fields based on unit
96
+ if self.temperature_unit == 'fahrenheit':
97
+ temp_field = 'temp_f'
98
+ feels_like_field = 'feelslike_f'
99
+ unit_name = 'Fahrenheit'
100
+ else:
101
+ temp_field = 'temp_c'
102
+ feels_like_field = 'feelslike_c'
103
+ unit_name = 'Celsius'
104
+
105
+ # Create TTS-friendly response instruction
106
+ response_instruction = (
107
+ f"Tell the user the current weather conditions. "
108
+ f"Express all temperatures in {unit_name} using natural language numbers "
109
+ f"without abbreviations or symbols for clear text-to-speech pronunciation. "
110
+ f"For example, say 'seventy two degrees {unit_name}' instead of '72F' or '72°F'. "
111
+ f"Include the condition, current temperature, wind direction and speed, "
112
+ f"cloud coverage percentage, and what the temperature feels like."
113
+ )
114
+
115
+ # Build the weather data template
116
+ weather_template = (
117
+ f"{response_instruction} "
118
+ f"Current conditions: ${{current.condition.text}}. "
119
+ f"Temperature: ${{current.{temp_field}}} degrees {unit_name}. "
120
+ f"Wind: ${{current.wind_dir}} at ${{current.wind_mph}} miles per hour. "
121
+ f"Cloud coverage: ${{current.cloud}} percent. "
122
+ f"Feels like: ${{current.{feels_like_field}}} degrees {unit_name}."
123
+ )
124
+
125
+ # Create the tool configuration with DataMap webhook
126
+ tool = {
127
+ "function": self.tool_name,
128
+ "description": f"Get current weather information for any location",
129
+ "parameters": {
130
+ "type": "object",
131
+ "properties": {
132
+ "location": {
133
+ "type": "string",
134
+ "description": "The city, state, country, or location to get weather for"
135
+ }
136
+ },
137
+ "required": ["location"]
138
+ },
139
+ "data_map": {
140
+ "webhooks": [
141
+ {
142
+ "url": f"https://api.weatherapi.com/v1/current.json?key={self.api_key}&q=%{{args.location}}&aqi=no",
143
+ "method": "GET",
144
+ "output": SwaigFunctionResult(weather_template).to_dict()
145
+ }
146
+ ],
147
+ "error_keys": ["error"],
148
+ "output": SwaigFunctionResult(
149
+ "Sorry, I cannot get weather information right now. Please try again later or check if the location name is correct."
150
+ ).to_dict()
151
+ }
152
+ }
153
+
154
+ return [tool]
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire_agents
3
- Version: 0.1.20
3
+ Version: 0.1.23
4
4
  Summary: SignalWire AI Agents SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
- Project-URL: Homepage, https://github.com/signalwire/signalwire-ai-agents
7
- Classifier: Development Status :: 3 - Alpha
6
+ Project-URL: Homepage, https://github.com/signalwire/signalwire-agents
7
+ Classifier: Development Status :: 4 - Beta
8
8
  Classifier: Intended Audience :: Developers
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
@@ -176,7 +176,7 @@ agent.serve()
176
176
  - **Dependency validation**: Clear error messages for missing requirements
177
177
  - **Modular architecture**: Skills are self-contained and reusable
178
178
 
179
- For detailed documentation, see [Skills System README](docs/SKILLS_SYSTEM_README.md).
179
+ For detailed documentation, see [Skills System README](docs/skills_system.md).
180
180
 
181
181
  ## DataMap Tools
182
182
 
@@ -572,10 +572,7 @@ class SimpleAgent(AgentBase):
572
572
  self.prompt_add_section("Goal", body="Help users with basic questions.")
573
573
  self.prompt_add_section("Instructions", bullets=["Be concise and clear."])
574
574
 
575
- # Alternative using convenience methods:
576
- # self.setPersonality("You are a helpful assistant.")
577
- # self.setGoal("Help users with basic questions.")
578
- # self.setInstructions(["Be concise and clear."])
575
+ # Note: Use prompt_add_section() for all prompt configuration
579
576
 
580
577
  @AgentBase.tool(
581
578
  name="get_time",