signalwire-agents 0.1.17__tar.gz → 0.1.18__tar.gz
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.
- {signalwire_agents-0.1.17/signalwire_agents.egg-info → signalwire_agents-0.1.18}/PKG-INFO +43 -30
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/README.md +42 -29
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/pyproject.toml +1 -1
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/__init__.py +1 -1
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/cli/build_search.py +152 -4
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/agent_base.py +77 -4
- signalwire_agents-0.1.18/signalwire_agents/core/contexts.py +622 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/swml_handler.py +27 -21
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/web_search/skill.py +1 -1
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18/signalwire_agents.egg-info}/PKG-INFO +43 -30
- signalwire_agents-0.1.17/signalwire_agents/core/contexts.py +0 -289
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/LICENSE +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/schema.json +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/setup.cfg +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/setup.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/agent_server.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/cli/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/cli/test_swaig.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/data_map.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/function_result.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/logging_config.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/pom_builder.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/security/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/security/session_manager.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/skill_base.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/skill_manager.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/state/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/state/file_state_manager.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/state/state_manager.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/swaig_function.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/swml_builder.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/swml_renderer.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/core/swml_service.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/prefabs/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/prefabs/concierge.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/prefabs/faq_bot.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/prefabs/info_gatherer.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/prefabs/receptionist.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/prefabs/survey.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/schema.json +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/search/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/search/document_processor.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/search/index_builder.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/search/query_processor.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/search/search_engine.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/search/search_service.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/datasphere/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/datasphere/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/datasphere_serverless/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/datasphere_serverless/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/datetime/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/datetime/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/joke/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/joke/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/math/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/math/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/native_vector_search/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/native_vector_search/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/registry.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/web_search/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/wikipedia_search/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/skills/wikipedia_search/skill.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/utils/__init__.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/utils/pom_utils.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/utils/schema_utils.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/utils/token_generators.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents/utils/validators.py +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents.egg-info/SOURCES.txt +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents.egg-info/dependency_links.txt +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents.egg-info/entry_points.txt +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents.egg-info/requires.txt +0 -0
- {signalwire_agents-0.1.17 → signalwire_agents-0.1.18}/signalwire_agents.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: signalwire_agents
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.18
|
4
4
|
Summary: SignalWire AI Agents SDK
|
5
5
|
Author-email: SignalWire Team <info@signalwire.com>
|
6
6
|
Project-URL: Homepage, https://github.com/signalwire/signalwire-ai-agents
|
@@ -287,7 +287,7 @@ For detailed documentation, see [DataMap Guide](docs/datamap_guide.md).
|
|
287
287
|
|
288
288
|
## Contexts and Steps
|
289
289
|
|
290
|
-
The SignalWire Agents SDK provides a powerful
|
290
|
+
The SignalWire Agents SDK provides a powerful enhancement to traditional prompts through the **Contexts and Steps** system. This feature allows you to add structured, workflow-driven AI interactions on top of your base prompt, with explicit navigation control and step-by-step guidance.
|
291
291
|
|
292
292
|
### Why Use Contexts and Steps?
|
293
293
|
|
@@ -296,7 +296,7 @@ The SignalWire Agents SDK provides a powerful alternative to traditional Prompt
|
|
296
296
|
- **Completion Criteria**: Set specific criteria for step completion and progression
|
297
297
|
- **Function Restrictions**: Limit which AI tools are available in each step
|
298
298
|
- **Workflow Isolation**: Create separate contexts for different conversation flows
|
299
|
-
- **
|
299
|
+
- **Enhanced Base Prompts**: Adds structured workflows on top of your existing prompt foundation
|
300
300
|
|
301
301
|
### Basic Usage
|
302
302
|
|
@@ -307,30 +307,34 @@ class WorkflowAgent(AgentBase):
|
|
307
307
|
def __init__(self):
|
308
308
|
super().__init__(name="Workflow Assistant", route="/workflow")
|
309
309
|
|
310
|
-
#
|
310
|
+
# Set base prompt (required even when using contexts)
|
311
|
+
self.prompt_add_section("Role", "You are a helpful workflow assistant.")
|
312
|
+
self.prompt_add_section("Instructions", "Guide users through structured processes step by step.")
|
313
|
+
|
314
|
+
# Define contexts and steps (adds structured workflow to base prompt)
|
311
315
|
contexts = self.define_contexts()
|
312
316
|
|
313
317
|
# Create a single context named "default" (required for single context)
|
314
|
-
context = contexts.
|
318
|
+
context = contexts.add_context("default")
|
315
319
|
|
316
320
|
# Add step-by-step workflow
|
317
|
-
context.
|
321
|
+
context.add_step("greeting") \
|
318
322
|
.set_text("Welcome! I'm here to help you complete your application. Let's start with your personal information.") \
|
319
323
|
.set_step_criteria("User has provided their name and confirmed they want to continue") \
|
320
324
|
.set_valid_steps(["personal_info"]) # Can only go to personal_info step
|
321
325
|
|
322
|
-
context.
|
326
|
+
context.add_step("personal_info") \
|
323
327
|
.add_section("Instructions", "Collect the user's personal information") \
|
324
328
|
.add_bullets(["Ask for full name", "Ask for email address", "Ask for phone number"]) \
|
325
329
|
.set_step_criteria("All personal information has been collected and confirmed") \
|
326
330
|
.set_valid_steps(["review", "personal_info"]) # Can stay or move to review
|
327
331
|
|
328
|
-
context.
|
332
|
+
context.add_step("review") \
|
329
333
|
.set_text("Let me review the information you've provided. Please confirm if everything is correct.") \
|
330
334
|
.set_step_criteria("User has confirmed or requested changes") \
|
331
335
|
.set_valid_steps(["personal_info", "complete"]) # Can go back or complete
|
332
336
|
|
333
|
-
context.
|
337
|
+
context.add_step("complete") \
|
334
338
|
.set_text("Thank you! Your application has been submitted successfully.") \
|
335
339
|
.set_step_criteria("Application processing is complete")
|
336
340
|
# No valid_steps = end of workflow
|
@@ -346,22 +350,27 @@ class MultiContextAgent(AgentBase):
|
|
346
350
|
def __init__(self):
|
347
351
|
super().__init__(name="Multi-Context Agent", route="/multi-context")
|
348
352
|
|
349
|
-
#
|
353
|
+
# Set base prompt (required)
|
354
|
+
self.prompt_add_section("Role", "You are a versatile AI assistant.")
|
355
|
+
self.prompt_add_section("Capabilities", "You can help with calculations and provide time information.")
|
356
|
+
|
357
|
+
# Add skills
|
350
358
|
self.add_skill("datetime")
|
351
359
|
self.add_skill("math")
|
352
360
|
|
361
|
+
# Define contexts for different service modes
|
353
362
|
contexts = self.define_contexts()
|
354
363
|
|
355
364
|
# Main conversation context
|
356
|
-
main_context = contexts.
|
357
|
-
main_context.
|
365
|
+
main_context = contexts.add_context("main")
|
366
|
+
main_context.add_step("welcome") \
|
358
367
|
.set_text("Welcome! I can help with calculations or provide date/time info. What would you like to do?") \
|
359
368
|
.set_step_criteria("User has chosen a service type") \
|
360
369
|
.set_valid_contexts(["calculator", "datetime_info"]) # Can switch contexts
|
361
370
|
|
362
371
|
# Calculator context with function restrictions
|
363
|
-
calc_context = contexts.
|
364
|
-
calc_context.
|
372
|
+
calc_context = contexts.add_context("calculator")
|
373
|
+
calc_context.add_step("math_mode") \
|
365
374
|
.add_section("Role", "You are a mathematical assistant") \
|
366
375
|
.add_section("Instructions", "Help users with calculations") \
|
367
376
|
.set_functions(["math"]) # Only math function available \
|
@@ -369,8 +378,8 @@ class MultiContextAgent(AgentBase):
|
|
369
378
|
.set_valid_contexts(["main"]) # Can return to main
|
370
379
|
|
371
380
|
# DateTime context
|
372
|
-
datetime_context = contexts.
|
373
|
-
datetime_context.
|
381
|
+
datetime_context = contexts.add_context("datetime_info")
|
382
|
+
datetime_context.add_step("time_mode") \
|
374
383
|
.set_text("I can provide current date and time information. What would you like to know?") \
|
375
384
|
.set_functions(["datetime"]) # Only datetime function available \
|
376
385
|
.set_step_criteria("Date/time information has been provided") \
|
@@ -380,7 +389,7 @@ class MultiContextAgent(AgentBase):
|
|
380
389
|
### Context and Step Methods
|
381
390
|
|
382
391
|
#### Context Methods
|
383
|
-
- `
|
392
|
+
- `add_step(name)`: Create a new step in this context
|
384
393
|
- `set_valid_contexts(contexts)`: Control which contexts can be accessed from this context
|
385
394
|
|
386
395
|
#### Step Methods
|
@@ -406,41 +415,45 @@ class SupportAgent(AgentBase):
|
|
406
415
|
def __init__(self):
|
407
416
|
super().__init__(name="Customer Support", route="/support")
|
408
417
|
|
418
|
+
# Set base prompt (required)
|
419
|
+
self.prompt_add_section("Role", "You are a professional customer support representative.")
|
420
|
+
self.prompt_add_section("Goal", "Provide excellent customer service using structured workflows.")
|
421
|
+
|
409
422
|
# Add skills for enhanced capabilities
|
410
423
|
self.add_skill("datetime")
|
411
424
|
self.add_skill("web_search", {"api_key": "your-key", "search_engine_id": "your-id"})
|
412
425
|
|
426
|
+
# Define support workflow contexts
|
413
427
|
contexts = self.define_contexts()
|
414
428
|
|
415
429
|
# Triage context
|
416
|
-
triage = contexts.
|
417
|
-
triage.
|
418
|
-
.add_section("
|
419
|
-
.
|
420
|
-
.add_bullets(["Be empathetic and professional", "Ask clarifying questions", "Categorize the issue"]) \
|
430
|
+
triage = contexts.add_context("triage")
|
431
|
+
triage.add_step("initial_greeting") \
|
432
|
+
.add_section("Current Task", "Understand the customer's issue and route them appropriately") \
|
433
|
+
.add_bullets("Questions to Ask", ["What problem are you experiencing?", "How urgent is this issue?", "Have you tried any troubleshooting steps?"]) \
|
421
434
|
.set_step_criteria("Issue type has been identified") \
|
422
435
|
.set_valid_contexts(["technical_support", "billing_support", "general_inquiry"])
|
423
436
|
|
424
437
|
# Technical support context
|
425
|
-
tech = contexts.
|
426
|
-
tech.
|
427
|
-
.add_section("
|
428
|
-
.add_section("
|
438
|
+
tech = contexts.add_context("technical_support")
|
439
|
+
tech.add_step("technical_diagnosis") \
|
440
|
+
.add_section("Current Task", "Help diagnose and resolve technical issues") \
|
441
|
+
.add_section("Available Tools", "Use web search to find solutions and datetime to check service windows") \
|
429
442
|
.set_functions(["web_search", "datetime"]) # Can search for solutions and check times \
|
430
443
|
.set_step_criteria("Technical issue is resolved or escalated") \
|
431
444
|
.set_valid_contexts(["triage"]) # Can return to triage
|
432
445
|
|
433
446
|
# Billing support context
|
434
|
-
billing = contexts.
|
435
|
-
billing.
|
447
|
+
billing = contexts.add_context("billing_support")
|
448
|
+
billing.add_step("billing_assistance") \
|
436
449
|
.set_text("I'll help you with your billing inquiry. Please provide your account details.") \
|
437
450
|
.set_functions("none") # No external tools for sensitive billing info \
|
438
451
|
.set_step_criteria("Billing issue is addressed") \
|
439
452
|
.set_valid_contexts(["triage"])
|
440
453
|
|
441
454
|
# General inquiry context
|
442
|
-
general = contexts.
|
443
|
-
general.
|
455
|
+
general = contexts.add_context("general_inquiry")
|
456
|
+
general.add_step("general_help") \
|
444
457
|
.set_text("I'm here to help with general questions. What can I assist you with?") \
|
445
458
|
.set_functions(["web_search", "datetime"]) # Full access to search and time \
|
446
459
|
.set_step_criteria("Inquiry has been answered") \
|
@@ -221,7 +221,7 @@ For detailed documentation, see [DataMap Guide](docs/datamap_guide.md).
|
|
221
221
|
|
222
222
|
## Contexts and Steps
|
223
223
|
|
224
|
-
The SignalWire Agents SDK provides a powerful
|
224
|
+
The SignalWire Agents SDK provides a powerful enhancement to traditional prompts through the **Contexts and Steps** system. This feature allows you to add structured, workflow-driven AI interactions on top of your base prompt, with explicit navigation control and step-by-step guidance.
|
225
225
|
|
226
226
|
### Why Use Contexts and Steps?
|
227
227
|
|
@@ -230,7 +230,7 @@ The SignalWire Agents SDK provides a powerful alternative to traditional Prompt
|
|
230
230
|
- **Completion Criteria**: Set specific criteria for step completion and progression
|
231
231
|
- **Function Restrictions**: Limit which AI tools are available in each step
|
232
232
|
- **Workflow Isolation**: Create separate contexts for different conversation flows
|
233
|
-
- **
|
233
|
+
- **Enhanced Base Prompts**: Adds structured workflows on top of your existing prompt foundation
|
234
234
|
|
235
235
|
### Basic Usage
|
236
236
|
|
@@ -241,30 +241,34 @@ class WorkflowAgent(AgentBase):
|
|
241
241
|
def __init__(self):
|
242
242
|
super().__init__(name="Workflow Assistant", route="/workflow")
|
243
243
|
|
244
|
-
#
|
244
|
+
# Set base prompt (required even when using contexts)
|
245
|
+
self.prompt_add_section("Role", "You are a helpful workflow assistant.")
|
246
|
+
self.prompt_add_section("Instructions", "Guide users through structured processes step by step.")
|
247
|
+
|
248
|
+
# Define contexts and steps (adds structured workflow to base prompt)
|
245
249
|
contexts = self.define_contexts()
|
246
250
|
|
247
251
|
# Create a single context named "default" (required for single context)
|
248
|
-
context = contexts.
|
252
|
+
context = contexts.add_context("default")
|
249
253
|
|
250
254
|
# Add step-by-step workflow
|
251
|
-
context.
|
255
|
+
context.add_step("greeting") \
|
252
256
|
.set_text("Welcome! I'm here to help you complete your application. Let's start with your personal information.") \
|
253
257
|
.set_step_criteria("User has provided their name and confirmed they want to continue") \
|
254
258
|
.set_valid_steps(["personal_info"]) # Can only go to personal_info step
|
255
259
|
|
256
|
-
context.
|
260
|
+
context.add_step("personal_info") \
|
257
261
|
.add_section("Instructions", "Collect the user's personal information") \
|
258
262
|
.add_bullets(["Ask for full name", "Ask for email address", "Ask for phone number"]) \
|
259
263
|
.set_step_criteria("All personal information has been collected and confirmed") \
|
260
264
|
.set_valid_steps(["review", "personal_info"]) # Can stay or move to review
|
261
265
|
|
262
|
-
context.
|
266
|
+
context.add_step("review") \
|
263
267
|
.set_text("Let me review the information you've provided. Please confirm if everything is correct.") \
|
264
268
|
.set_step_criteria("User has confirmed or requested changes") \
|
265
269
|
.set_valid_steps(["personal_info", "complete"]) # Can go back or complete
|
266
270
|
|
267
|
-
context.
|
271
|
+
context.add_step("complete") \
|
268
272
|
.set_text("Thank you! Your application has been submitted successfully.") \
|
269
273
|
.set_step_criteria("Application processing is complete")
|
270
274
|
# No valid_steps = end of workflow
|
@@ -280,22 +284,27 @@ class MultiContextAgent(AgentBase):
|
|
280
284
|
def __init__(self):
|
281
285
|
super().__init__(name="Multi-Context Agent", route="/multi-context")
|
282
286
|
|
283
|
-
#
|
287
|
+
# Set base prompt (required)
|
288
|
+
self.prompt_add_section("Role", "You are a versatile AI assistant.")
|
289
|
+
self.prompt_add_section("Capabilities", "You can help with calculations and provide time information.")
|
290
|
+
|
291
|
+
# Add skills
|
284
292
|
self.add_skill("datetime")
|
285
293
|
self.add_skill("math")
|
286
294
|
|
295
|
+
# Define contexts for different service modes
|
287
296
|
contexts = self.define_contexts()
|
288
297
|
|
289
298
|
# Main conversation context
|
290
|
-
main_context = contexts.
|
291
|
-
main_context.
|
299
|
+
main_context = contexts.add_context("main")
|
300
|
+
main_context.add_step("welcome") \
|
292
301
|
.set_text("Welcome! I can help with calculations or provide date/time info. What would you like to do?") \
|
293
302
|
.set_step_criteria("User has chosen a service type") \
|
294
303
|
.set_valid_contexts(["calculator", "datetime_info"]) # Can switch contexts
|
295
304
|
|
296
305
|
# Calculator context with function restrictions
|
297
|
-
calc_context = contexts.
|
298
|
-
calc_context.
|
306
|
+
calc_context = contexts.add_context("calculator")
|
307
|
+
calc_context.add_step("math_mode") \
|
299
308
|
.add_section("Role", "You are a mathematical assistant") \
|
300
309
|
.add_section("Instructions", "Help users with calculations") \
|
301
310
|
.set_functions(["math"]) # Only math function available \
|
@@ -303,8 +312,8 @@ class MultiContextAgent(AgentBase):
|
|
303
312
|
.set_valid_contexts(["main"]) # Can return to main
|
304
313
|
|
305
314
|
# DateTime context
|
306
|
-
datetime_context = contexts.
|
307
|
-
datetime_context.
|
315
|
+
datetime_context = contexts.add_context("datetime_info")
|
316
|
+
datetime_context.add_step("time_mode") \
|
308
317
|
.set_text("I can provide current date and time information. What would you like to know?") \
|
309
318
|
.set_functions(["datetime"]) # Only datetime function available \
|
310
319
|
.set_step_criteria("Date/time information has been provided") \
|
@@ -314,7 +323,7 @@ class MultiContextAgent(AgentBase):
|
|
314
323
|
### Context and Step Methods
|
315
324
|
|
316
325
|
#### Context Methods
|
317
|
-
- `
|
326
|
+
- `add_step(name)`: Create a new step in this context
|
318
327
|
- `set_valid_contexts(contexts)`: Control which contexts can be accessed from this context
|
319
328
|
|
320
329
|
#### Step Methods
|
@@ -340,41 +349,45 @@ class SupportAgent(AgentBase):
|
|
340
349
|
def __init__(self):
|
341
350
|
super().__init__(name="Customer Support", route="/support")
|
342
351
|
|
352
|
+
# Set base prompt (required)
|
353
|
+
self.prompt_add_section("Role", "You are a professional customer support representative.")
|
354
|
+
self.prompt_add_section("Goal", "Provide excellent customer service using structured workflows.")
|
355
|
+
|
343
356
|
# Add skills for enhanced capabilities
|
344
357
|
self.add_skill("datetime")
|
345
358
|
self.add_skill("web_search", {"api_key": "your-key", "search_engine_id": "your-id"})
|
346
359
|
|
360
|
+
# Define support workflow contexts
|
347
361
|
contexts = self.define_contexts()
|
348
362
|
|
349
363
|
# Triage context
|
350
|
-
triage = contexts.
|
351
|
-
triage.
|
352
|
-
.add_section("
|
353
|
-
.
|
354
|
-
.add_bullets(["Be empathetic and professional", "Ask clarifying questions", "Categorize the issue"]) \
|
364
|
+
triage = contexts.add_context("triage")
|
365
|
+
triage.add_step("initial_greeting") \
|
366
|
+
.add_section("Current Task", "Understand the customer's issue and route them appropriately") \
|
367
|
+
.add_bullets("Questions to Ask", ["What problem are you experiencing?", "How urgent is this issue?", "Have you tried any troubleshooting steps?"]) \
|
355
368
|
.set_step_criteria("Issue type has been identified") \
|
356
369
|
.set_valid_contexts(["technical_support", "billing_support", "general_inquiry"])
|
357
370
|
|
358
371
|
# Technical support context
|
359
|
-
tech = contexts.
|
360
|
-
tech.
|
361
|
-
.add_section("
|
362
|
-
.add_section("
|
372
|
+
tech = contexts.add_context("technical_support")
|
373
|
+
tech.add_step("technical_diagnosis") \
|
374
|
+
.add_section("Current Task", "Help diagnose and resolve technical issues") \
|
375
|
+
.add_section("Available Tools", "Use web search to find solutions and datetime to check service windows") \
|
363
376
|
.set_functions(["web_search", "datetime"]) # Can search for solutions and check times \
|
364
377
|
.set_step_criteria("Technical issue is resolved or escalated") \
|
365
378
|
.set_valid_contexts(["triage"]) # Can return to triage
|
366
379
|
|
367
380
|
# Billing support context
|
368
|
-
billing = contexts.
|
369
|
-
billing.
|
381
|
+
billing = contexts.add_context("billing_support")
|
382
|
+
billing.add_step("billing_assistance") \
|
370
383
|
.set_text("I'll help you with your billing inquiry. Please provide your account details.") \
|
371
384
|
.set_functions("none") # No external tools for sensitive billing info \
|
372
385
|
.set_step_criteria("Billing issue is addressed") \
|
373
386
|
.set_valid_contexts(["triage"])
|
374
387
|
|
375
388
|
# General inquiry context
|
376
|
-
general = contexts.
|
377
|
-
general.
|
389
|
+
general = contexts.add_context("general_inquiry")
|
390
|
+
general.add_step("general_help") \
|
378
391
|
.set_text("I'm here to help with general questions. What can I assist you with?") \
|
379
392
|
.set_functions(["web_search", "datetime"]) # Full access to search and time \
|
380
393
|
.set_step_criteria("Inquiry has been answered") \
|
@@ -18,7 +18,7 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
|
|
18
18
|
from .core.logging_config import configure_logging
|
19
19
|
configure_logging()
|
20
20
|
|
21
|
-
__version__ = "0.1.
|
21
|
+
__version__ = "0.1.18"
|
22
22
|
|
23
23
|
# Import core classes for easier access
|
24
24
|
from .core.agent_base import AgentBase
|
@@ -85,6 +85,10 @@ Examples:
|
|
85
85
|
sw-search search ./docs.swsearch "how to create an agent"
|
86
86
|
sw-search search ./docs.swsearch "API reference" --count 3 --verbose
|
87
87
|
sw-search search ./docs.swsearch "configuration" --tags documentation --json
|
88
|
+
|
89
|
+
# Search via remote API
|
90
|
+
sw-search remote http://localhost:8001 "how to create an agent" --index-name docs
|
91
|
+
sw-search remote localhost:8001 "API reference" --index-name docs --count 3 --verbose
|
88
92
|
"""
|
89
93
|
)
|
90
94
|
|
@@ -263,7 +267,7 @@ Examples:
|
|
263
267
|
|
264
268
|
try:
|
265
269
|
# Create index builder - import only when actually needed
|
266
|
-
from
|
270
|
+
from signalwire_agents.search.index_builder import IndexBuilder
|
267
271
|
builder = IndexBuilder(
|
268
272
|
model_name=args.model,
|
269
273
|
chunking_strategy=args.chunking_strategy,
|
@@ -328,7 +332,7 @@ def validate_command():
|
|
328
332
|
sys.exit(1)
|
329
333
|
|
330
334
|
try:
|
331
|
-
from
|
335
|
+
from signalwire_agents.search.index_builder import IndexBuilder
|
332
336
|
builder = IndexBuilder()
|
333
337
|
|
334
338
|
validation = builder.validate_index(args.index_file)
|
@@ -376,8 +380,8 @@ def search_command():
|
|
376
380
|
try:
|
377
381
|
# Import search dependencies
|
378
382
|
try:
|
379
|
-
from
|
380
|
-
from
|
383
|
+
from signalwire_agents.search.search_engine import SearchEngine
|
384
|
+
from signalwire_agents.search.query_processor import preprocess_query
|
381
385
|
except ImportError as e:
|
382
386
|
print(f"Error: Search functionality not available. Install with: pip install signalwire-agents[search]")
|
383
387
|
print(f"Details: {e}")
|
@@ -478,6 +482,141 @@ def search_command():
|
|
478
482
|
traceback.print_exc()
|
479
483
|
sys.exit(1)
|
480
484
|
|
485
|
+
def remote_command():
|
486
|
+
"""Search via remote API endpoint"""
|
487
|
+
parser = argparse.ArgumentParser(description='Search via remote API endpoint')
|
488
|
+
parser.add_argument('endpoint', help='Remote API endpoint URL (e.g., http://localhost:8001)')
|
489
|
+
parser.add_argument('query', help='Search query')
|
490
|
+
parser.add_argument('--index-name', required=True, help='Name of the index to search')
|
491
|
+
parser.add_argument('--count', type=int, default=5, help='Number of results to return (default: 5)')
|
492
|
+
parser.add_argument('--distance-threshold', type=float, default=0.0, help='Minimum similarity score (default: 0.0)')
|
493
|
+
parser.add_argument('--tags', help='Comma-separated tags to filter by')
|
494
|
+
parser.add_argument('--verbose', action='store_true', help='Show detailed information')
|
495
|
+
parser.add_argument('--json', action='store_true', help='Output results as JSON')
|
496
|
+
parser.add_argument('--no-content', action='store_true', help='Hide content in results (show only metadata)')
|
497
|
+
parser.add_argument('--timeout', type=int, default=30, help='Request timeout in seconds (default: 30)')
|
498
|
+
|
499
|
+
args = parser.parse_args()
|
500
|
+
|
501
|
+
# Ensure endpoint starts with http:// or https://
|
502
|
+
endpoint = args.endpoint
|
503
|
+
if not endpoint.startswith(('http://', 'https://')):
|
504
|
+
endpoint = f"http://{endpoint}"
|
505
|
+
|
506
|
+
# Ensure endpoint ends with /search
|
507
|
+
if not endpoint.endswith('/search'):
|
508
|
+
if endpoint.endswith('/'):
|
509
|
+
endpoint += 'search'
|
510
|
+
else:
|
511
|
+
endpoint += '/search'
|
512
|
+
|
513
|
+
try:
|
514
|
+
import requests
|
515
|
+
except ImportError:
|
516
|
+
print("Error: requests library not available. Install with: pip install requests")
|
517
|
+
sys.exit(1)
|
518
|
+
|
519
|
+
# Prepare request payload
|
520
|
+
payload = {
|
521
|
+
'query': args.query,
|
522
|
+
'index_name': args.index_name,
|
523
|
+
'count': args.count,
|
524
|
+
'distance_threshold': args.distance_threshold
|
525
|
+
}
|
526
|
+
|
527
|
+
if args.tags:
|
528
|
+
payload['tags'] = [tag.strip() for tag in args.tags.split(',')]
|
529
|
+
|
530
|
+
if args.verbose:
|
531
|
+
print(f"Searching remote endpoint: {endpoint}")
|
532
|
+
print(f"Payload: {payload}")
|
533
|
+
print()
|
534
|
+
|
535
|
+
try:
|
536
|
+
# Make the API request
|
537
|
+
response = requests.post(
|
538
|
+
endpoint,
|
539
|
+
json=payload,
|
540
|
+
headers={'Content-Type': 'application/json'},
|
541
|
+
timeout=args.timeout
|
542
|
+
)
|
543
|
+
|
544
|
+
if response.status_code == 200:
|
545
|
+
result = response.json()
|
546
|
+
|
547
|
+
if args.json:
|
548
|
+
# Output raw JSON response
|
549
|
+
import json
|
550
|
+
print(json.dumps(result, indent=2))
|
551
|
+
else:
|
552
|
+
# Human-readable output
|
553
|
+
results = result.get('results', [])
|
554
|
+
if not results:
|
555
|
+
print(f"No results found for '{args.query}' in index '{args.index_name}'")
|
556
|
+
sys.exit(0)
|
557
|
+
|
558
|
+
print(f"Found {len(results)} result(s) for '{args.query}' in index '{args.index_name}':")
|
559
|
+
if result.get('enhanced_query') and result.get('enhanced_query') != args.query:
|
560
|
+
print(f"Enhanced query: '{result.get('enhanced_query')}'")
|
561
|
+
print("=" * 80)
|
562
|
+
|
563
|
+
for i, search_result in enumerate(results):
|
564
|
+
print(f"\n[{i+1}] Score: {search_result.get('score', 0):.4f}")
|
565
|
+
|
566
|
+
# Show metadata
|
567
|
+
metadata = search_result.get('metadata', {})
|
568
|
+
print(f"File: {metadata.get('filename', 'Unknown')}")
|
569
|
+
if metadata.get('section'):
|
570
|
+
print(f"Section: {metadata['section']}")
|
571
|
+
if metadata.get('line_start'):
|
572
|
+
print(f"Lines: {metadata['line_start']}-{metadata.get('line_end', metadata['line_start'])}")
|
573
|
+
if metadata.get('tags'):
|
574
|
+
print(f"Tags: {', '.join(metadata['tags'])}")
|
575
|
+
|
576
|
+
# Show content unless suppressed
|
577
|
+
if not args.no_content and 'content' in search_result:
|
578
|
+
content = search_result['content']
|
579
|
+
if len(content) > 500 and not args.verbose:
|
580
|
+
content = content[:500] + "..."
|
581
|
+
print(f"\nContent:\n{content}")
|
582
|
+
|
583
|
+
if i < len(results) - 1:
|
584
|
+
print("-" * 80)
|
585
|
+
|
586
|
+
elif response.status_code == 404:
|
587
|
+
try:
|
588
|
+
error_detail = response.json()
|
589
|
+
error_msg = error_detail.get('detail', 'Index not found')
|
590
|
+
except:
|
591
|
+
error_msg = 'Index not found'
|
592
|
+
print(f"Error: {error_msg}")
|
593
|
+
sys.exit(1)
|
594
|
+
else:
|
595
|
+
try:
|
596
|
+
error_detail = response.json()
|
597
|
+
error_msg = error_detail.get('detail', f'HTTP {response.status_code}')
|
598
|
+
except:
|
599
|
+
error_msg = f'HTTP {response.status_code}: {response.text}'
|
600
|
+
print(f"Error: {error_msg}")
|
601
|
+
sys.exit(1)
|
602
|
+
|
603
|
+
except requests.ConnectionError:
|
604
|
+
print(f"Error: Could not connect to {endpoint}")
|
605
|
+
print("Make sure the search server is running")
|
606
|
+
sys.exit(1)
|
607
|
+
except requests.Timeout:
|
608
|
+
print(f"Error: Request timed out after {args.timeout} seconds")
|
609
|
+
sys.exit(1)
|
610
|
+
except requests.RequestException as e:
|
611
|
+
print(f"Error making request: {e}")
|
612
|
+
sys.exit(1)
|
613
|
+
except Exception as e:
|
614
|
+
print(f"Error: {e}")
|
615
|
+
if args.verbose:
|
616
|
+
import traceback
|
617
|
+
traceback.print_exc()
|
618
|
+
sys.exit(1)
|
619
|
+
|
481
620
|
def console_entry_point():
|
482
621
|
"""Console script entry point for pip installation"""
|
483
622
|
import sys
|
@@ -594,6 +733,10 @@ Examples:
|
|
594
733
|
sw-search search ./docs.swsearch "how to create an agent"
|
595
734
|
sw-search search ./docs.swsearch "API reference" --count 3 --verbose
|
596
735
|
sw-search search ./docs.swsearch "configuration" --tags documentation --json
|
736
|
+
|
737
|
+
# Search via remote API
|
738
|
+
sw-search remote http://localhost:8001 "how to create an agent" --index-name docs
|
739
|
+
sw-search remote localhost:8001 "API reference" --index-name docs --count 3 --verbose
|
597
740
|
""")
|
598
741
|
return
|
599
742
|
|
@@ -609,6 +752,11 @@ Examples:
|
|
609
752
|
sys.argv.pop(1)
|
610
753
|
search_command()
|
611
754
|
return
|
755
|
+
elif sys.argv[1] == 'remote':
|
756
|
+
# Remove 'remote' from argv and call remote_command
|
757
|
+
sys.argv.pop(1)
|
758
|
+
remote_command()
|
759
|
+
return
|
612
760
|
|
613
761
|
# Regular build command
|
614
762
|
main()
|