File Coverage

blib/lib/SignalWire/Agents/Skills/SkillBase.pm
Criterion Covered Total %
statement 35 40 87.5
branch 6 8 75.0
condition n/a
subroutine 14 16 87.5
pod 0 11 0.0
total 55 75 73.3


line stmt bran cond sub pod time code
1             package SignalWire::Agents::Skills::SkillBase;
2             # Copyright (c) 2025 SignalWire
3             # Licensed under the MIT License.
4              
5 19     19   358257 use strict;
  19         48  
  19         800  
6 19     19   101 use warnings;
  19         39  
  19         1217  
7 19     19   740 use Moo;
  19         10155  
  19         137  
8 19     19   9476 use Carp qw(croak);
  19         47  
  19         16977  
9              
10             # Required class-level constants (subclasses override via 'has' or '+')
11             has skill_name => (is => 'ro', required => 1);
12             has skill_description => (is => 'ro', required => 1);
13             has skill_version => (is => 'ro', default => sub { '1.0.0' });
14              
15             has supports_multiple_instances => (is => 'ro', default => sub { 0 });
16             has required_packages => (is => 'ro', default => sub { [] });
17             has required_env_vars => (is => 'ro', default => sub { [] });
18              
19             # The agent this skill is attached to
20             has agent => (is => 'ro', required => 1, weak_ref => 1);
21             # Config params passed at registration
22             has params => (is => 'rw', default => sub { {} });
23              
24             # Extra SWAIG fields to merge into tool definitions
25             has swaig_fields => (is => 'rw', default => sub { {} });
26              
27             sub BUILD {
28 131     131 0 938 my ($self) = @_;
29             # Extract swaig_fields from params if present
30 131 100       1713 if (exists $self->params->{swaig_fields}) {
31 1         15 $self->swaig_fields(delete $self->params->{swaig_fields});
32             }
33             }
34              
35             # --- Abstract interface (subclasses must override) ---
36              
37             sub setup {
38 0     0 0 0 my ($self) = @_;
39 0         0 croak(ref($self) . " must implement setup()");
40             }
41              
42             sub register_tools {
43 0     0 0 0 my ($self) = @_;
44 0         0 croak(ref($self) . " must implement register_tools()");
45             }
46              
47             # --- Default implementations ---
48              
49             sub define_tool {
50 66     66 0 345 my ($self, %opts) = @_;
51             # Merge swaig_fields into the tool definition
52 66         179 my %merged = (%{ $self->swaig_fields }, %opts);
  66         488  
53 66         464 return $self->agent->define_tool(%merged);
54             }
55              
56             sub get_hints {
57 9     9 0 26 return [];
58             }
59              
60             sub get_global_data {
61 10     10 0 27 return {};
62             }
63              
64             sub get_prompt_sections {
65 23     23 0 166 my ($self) = @_;
66 23 100       149 return [] if $self->params->{skip_prompt};
67 21         98 return $self->_get_prompt_sections;
68             }
69              
70             sub _get_prompt_sections {
71 2     2   5 return [];
72             }
73              
74       1 0   sub cleanup {
75             # no-op by default
76             }
77              
78             sub validate_env_vars {
79 10     10 0 30 my ($self) = @_;
80 10         22 for my $var (@{ $self->required_env_vars }) {
  10         51  
81 0 0       0 return 0 unless $ENV{$var};
82             }
83 10         40 return 1;
84             }
85              
86             sub get_parameter_schema {
87             return {
88 15     15 0 308 swaig_fields => { type => 'object', description => 'Additional SWAIG fields' },
89             skip_prompt => { type => 'boolean', description => 'Skip injecting prompt sections', default => 0 },
90             tool_name => { type => 'string', description => 'Override the default tool name' },
91             };
92             }
93              
94             sub get_instance_key {
95 14     14 0 59 my ($self) = @_;
96 14         51 my $base = $self->skill_name;
97 14 100       60 if ($self->params->{tool_name}) {
98 4         31 return $base . ':' . $self->params->{tool_name};
99             }
100 10         38 return $base;
101             }
102              
103             1;