File Coverage

blib/lib/SignalWire/Agents/Prefabs/FAQBot.pm
Criterion Covered Total %
statement 33 33 100.0
branch 10 10 100.0
condition 1 2 50.0
subroutine 6 6 100.0
pod 0 1 0.0
total 50 52 96.1


line stmt bran cond sub pod time code
1             package SignalWire::Agents::Prefabs::FAQBot;
2             # Copyright (c) 2025 SignalWire
3             # Licensed under the MIT License.
4              
5 2     2   326528 use strict;
  2         6  
  2         120  
6 2     2   14 use warnings;
  2         3  
  2         123  
7 2     2   618 use Moo;
  2         9847  
  2         76  
8 2     2   3193 use JSON qw(encode_json);
  2         13695  
  2         14  
9             extends 'SignalWire::Agents::Agent::AgentBase';
10              
11             has faqs => (is => 'ro', default => sub { [] });
12             has suggest_related => (is => 'ro', default => sub { 1 });
13             has persona => (is => 'ro', default => sub { 'You are a helpful FAQ bot that provides accurate answers to common questions.' });
14              
15             sub BUILD {
16 13     13 0 202 my ($self, $args) = @_;
17              
18 13 100       69 $self->name('faq_bot') if $self->name eq 'agent';
19 13 100       65 $self->route('/faq') if $self->route eq '/';
20 13         39 $self->use_pom(1);
21              
22 13         33 my $faqs = $self->faqs;
23              
24 13 100       76 $self->set_global_data({
25             faqs => $faqs,
26             suggest_related => $self->suggest_related ? JSON::true : JSON::false,
27             });
28              
29 13         61 $self->prompt_add_section(
30             'Personality',
31             $self->persona,
32             );
33              
34             # Build FAQ knowledge
35 13         18 my @faq_bullets;
36 13         28 for my $faq (@$faqs) {
37 15         56 push @faq_bullets, "Q: $faq->{question} A: $faq->{answer}";
38             }
39              
40             $self->prompt_add_section(
41 13         58 'FAQ Knowledge Base',
42             'You have knowledge of the following frequently asked questions.',
43             bullets => \@faq_bullets,
44             );
45              
46 13 100       31 if ($self->suggest_related) {
47 12         30 $self->prompt_add_section(
48             'Related Questions',
49             'When appropriate, suggest related questions the user might also be interested in.',
50             );
51             }
52              
53             # Register lookup tool
54             $self->define_tool(
55             name => 'lookup_faq',
56             description => 'Look up an FAQ answer by keyword matching',
57             parameters => {
58             type => 'object',
59             properties => {
60             query => { type => 'string', description => 'The question or keywords to search' },
61             },
62             required => ['query'],
63             },
64             handler => sub {
65 3     3   6 my ($a, $raw) = @_;
66 3         603 require SignalWire::Agents::SWAIG::FunctionResult;
67 3   50     17 my $query = lc($a->{query} // '');
68 3         6 for my $faq (@$faqs) {
69 3 100       13 if (index(lc($faq->{question}), $query) >= 0) {
70             return SignalWire::Agents::SWAIG::FunctionResult->new(
71             response => $faq->{answer},
72 2         45 );
73             }
74             }
75 1         20 return SignalWire::Agents::SWAIG::FunctionResult->new(
76             response => "No FAQ found matching: $a->{query}",
77             );
78             },
79 13         187 );
80             }
81              
82             1;