signalwire-agents 0.1.0
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.
- package/CLAUDE.md +112 -0
- package/README.md +417 -0
- package/dist/AgentBase.d.ts +481 -0
- package/dist/AgentBase.js +1155 -0
- package/dist/AgentBase.js.map +1 -0
- package/dist/AgentServer.d.ts +60 -0
- package/dist/AgentServer.js +128 -0
- package/dist/AgentServer.js.map +1 -0
- package/dist/AuthHandler.d.ts +56 -0
- package/dist/AuthHandler.js +119 -0
- package/dist/AuthHandler.js.map +1 -0
- package/dist/ConfigLoader.d.ts +68 -0
- package/dist/ConfigLoader.js +150 -0
- package/dist/ConfigLoader.js.map +1 -0
- package/dist/ContextBuilder.d.ts +431 -0
- package/dist/ContextBuilder.js +751 -0
- package/dist/ContextBuilder.js.map +1 -0
- package/dist/DataMap.d.ts +179 -0
- package/dist/DataMap.js +321 -0
- package/dist/DataMap.js.map +1 -0
- package/dist/Logger.d.ts +91 -0
- package/dist/Logger.js +191 -0
- package/dist/Logger.js.map +1 -0
- package/dist/PomBuilder.d.ts +144 -0
- package/dist/PomBuilder.js +249 -0
- package/dist/PomBuilder.js.map +1 -0
- package/dist/PromptManager.d.ts +85 -0
- package/dist/PromptManager.js +108 -0
- package/dist/PromptManager.js.map +1 -0
- package/dist/SchemaUtils.d.ts +42 -0
- package/dist/SchemaUtils.js +148 -0
- package/dist/SchemaUtils.js.map +1 -0
- package/dist/ServerlessAdapter.d.ts +100 -0
- package/dist/ServerlessAdapter.js +166 -0
- package/dist/ServerlessAdapter.js.map +1 -0
- package/dist/SessionManager.d.ts +86 -0
- package/dist/SessionManager.js +155 -0
- package/dist/SessionManager.js.map +1 -0
- package/dist/SslConfig.d.ts +77 -0
- package/dist/SslConfig.js +100 -0
- package/dist/SslConfig.js.map +1 -0
- package/dist/SwaigFunction.d.ts +88 -0
- package/dist/SwaigFunction.js +125 -0
- package/dist/SwaigFunction.js.map +1 -0
- package/dist/SwaigFunctionResult.d.ts +442 -0
- package/dist/SwaigFunctionResult.js +735 -0
- package/dist/SwaigFunctionResult.js.map +1 -0
- package/dist/SwmlBuilder.d.ts +37 -0
- package/dist/SwmlBuilder.js +55 -0
- package/dist/SwmlBuilder.js.map +1 -0
- package/dist/cli/agent-loader.d.ts +22 -0
- package/dist/cli/agent-loader.js +106 -0
- package/dist/cli/agent-loader.js.map +1 -0
- package/dist/cli/mock-data.d.ts +37 -0
- package/dist/cli/mock-data.js +79 -0
- package/dist/cli/mock-data.js.map +1 -0
- package/dist/cli/swaig-test.d.ts +32 -0
- package/dist/cli/swaig-test.js +405 -0
- package/dist/cli/swaig-test.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/prefabs/ConciergeAgent.d.ts +64 -0
- package/dist/prefabs/ConciergeAgent.js +188 -0
- package/dist/prefabs/ConciergeAgent.js.map +1 -0
- package/dist/prefabs/FAQBotAgent.d.ts +68 -0
- package/dist/prefabs/FAQBotAgent.js +206 -0
- package/dist/prefabs/FAQBotAgent.js.map +1 -0
- package/dist/prefabs/InfoGathererAgent.d.ts +73 -0
- package/dist/prefabs/InfoGathererAgent.js +202 -0
- package/dist/prefabs/InfoGathererAgent.js.map +1 -0
- package/dist/prefabs/ReceptionistAgent.d.ts +65 -0
- package/dist/prefabs/ReceptionistAgent.js +205 -0
- package/dist/prefabs/ReceptionistAgent.js.map +1 -0
- package/dist/prefabs/SurveyAgent.d.ts +84 -0
- package/dist/prefabs/SurveyAgent.js +324 -0
- package/dist/prefabs/SurveyAgent.js.map +1 -0
- package/dist/prefabs/index.d.ts +13 -0
- package/dist/prefabs/index.js +14 -0
- package/dist/prefabs/index.js.map +1 -0
- package/dist/skills/SkillBase.d.ts +134 -0
- package/dist/skills/SkillBase.js +105 -0
- package/dist/skills/SkillBase.js.map +1 -0
- package/dist/skills/SkillManager.d.ts +83 -0
- package/dist/skills/SkillManager.js +159 -0
- package/dist/skills/SkillManager.js.map +1 -0
- package/dist/skills/SkillRegistry.d.ts +105 -0
- package/dist/skills/SkillRegistry.js +195 -0
- package/dist/skills/SkillRegistry.js.map +1 -0
- package/dist/skills/builtin/api_ninjas_trivia.d.ts +35 -0
- package/dist/skills/builtin/api_ninjas_trivia.js +161 -0
- package/dist/skills/builtin/api_ninjas_trivia.js.map +1 -0
- package/dist/skills/builtin/claude_skills.d.ts +34 -0
- package/dist/skills/builtin/claude_skills.js +156 -0
- package/dist/skills/builtin/claude_skills.js.map +1 -0
- package/dist/skills/builtin/custom_skills.d.ts +59 -0
- package/dist/skills/builtin/custom_skills.js +231 -0
- package/dist/skills/builtin/custom_skills.js.map +1 -0
- package/dist/skills/builtin/datasphere.d.ts +34 -0
- package/dist/skills/builtin/datasphere.js +163 -0
- package/dist/skills/builtin/datasphere.js.map +1 -0
- package/dist/skills/builtin/datasphere_serverless.d.ts +54 -0
- package/dist/skills/builtin/datasphere_serverless.js +155 -0
- package/dist/skills/builtin/datasphere_serverless.js.map +1 -0
- package/dist/skills/builtin/datetime.d.ts +31 -0
- package/dist/skills/builtin/datetime.js +95 -0
- package/dist/skills/builtin/datetime.js.map +1 -0
- package/dist/skills/builtin/google_maps.d.ts +33 -0
- package/dist/skills/builtin/google_maps.js +233 -0
- package/dist/skills/builtin/google_maps.js.map +1 -0
- package/dist/skills/builtin/index.d.ts +28 -0
- package/dist/skills/builtin/index.js +75 -0
- package/dist/skills/builtin/index.js.map +1 -0
- package/dist/skills/builtin/info_gatherer.d.ts +52 -0
- package/dist/skills/builtin/info_gatherer.js +237 -0
- package/dist/skills/builtin/info_gatherer.js.map +1 -0
- package/dist/skills/builtin/joke.d.ts +32 -0
- package/dist/skills/builtin/joke.js +142 -0
- package/dist/skills/builtin/joke.js.map +1 -0
- package/dist/skills/builtin/math.d.ts +33 -0
- package/dist/skills/builtin/math.js +120 -0
- package/dist/skills/builtin/math.js.map +1 -0
- package/dist/skills/builtin/mcp_gateway.d.ts +35 -0
- package/dist/skills/builtin/mcp_gateway.js +85 -0
- package/dist/skills/builtin/mcp_gateway.js.map +1 -0
- package/dist/skills/builtin/native_vector_search.d.ts +44 -0
- package/dist/skills/builtin/native_vector_search.js +248 -0
- package/dist/skills/builtin/native_vector_search.js.map +1 -0
- package/dist/skills/builtin/play_background_file.d.ts +34 -0
- package/dist/skills/builtin/play_background_file.js +135 -0
- package/dist/skills/builtin/play_background_file.js.map +1 -0
- package/dist/skills/builtin/spider.d.ts +34 -0
- package/dist/skills/builtin/spider.js +164 -0
- package/dist/skills/builtin/spider.js.map +1 -0
- package/dist/skills/builtin/swml_transfer.d.ts +44 -0
- package/dist/skills/builtin/swml_transfer.js +204 -0
- package/dist/skills/builtin/swml_transfer.js.map +1 -0
- package/dist/skills/builtin/weather_api.d.ts +34 -0
- package/dist/skills/builtin/weather_api.js +136 -0
- package/dist/skills/builtin/weather_api.js.map +1 -0
- package/dist/skills/builtin/web_search.d.ts +34 -0
- package/dist/skills/builtin/web_search.js +141 -0
- package/dist/skills/builtin/web_search.js.map +1 -0
- package/dist/skills/builtin/wikipedia_search.d.ts +34 -0
- package/dist/skills/builtin/wikipedia_search.js +166 -0
- package/dist/skills/builtin/wikipedia_search.js.map +1 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.js +10 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/docs/agent-guide.md +869 -0
- package/docs/api-reference.md +2419 -0
- package/docs/architecture.md +843 -0
- package/docs/cli-guide.md +579 -0
- package/docs/configuration.md +422 -0
- package/docs/contexts-guide.md +787 -0
- package/docs/datamap-guide.md +1141 -0
- package/docs/prefabs-guide.md +830 -0
- package/docs/security.md +436 -0
- package/docs/serverless-guide.md +513 -0
- package/docs/skills-guide.md +845 -0
- package/docs/swaig-reference.md +1627 -0
- package/examples/call-flow.ts +60 -0
- package/examples/contexts-steps.ts +62 -0
- package/examples/datamap-tools.ts +52 -0
- package/examples/dynamic-config.ts +51 -0
- package/examples/multi-agent.ts +67 -0
- package/examples/pom-prompt.ts +68 -0
- package/examples/simple-agent.ts +33 -0
- package/package.json +35 -0
- package/src/AgentBase.ts +1279 -0
- package/src/AgentServer.ts +149 -0
- package/src/AuthHandler.ts +141 -0
- package/src/ConfigLoader.ts +165 -0
- package/src/ContextBuilder.ts +796 -0
- package/src/DataMap.ts +365 -0
- package/src/Logger.ts +197 -0
- package/src/PomBuilder.ts +298 -0
- package/src/PromptManager.ts +133 -0
- package/src/SchemaUtils.ts +170 -0
- package/src/ServerlessAdapter.ts +218 -0
- package/src/SessionManager.ts +161 -0
- package/src/SslConfig.ts +118 -0
- package/src/SwaigFunction.ts +168 -0
- package/src/SwaigFunctionResult.ts +864 -0
- package/src/SwmlBuilder.ts +62 -0
- package/src/cli/agent-loader.ts +122 -0
- package/src/cli/mock-data.ts +110 -0
- package/src/cli/swaig-test.ts +440 -0
- package/src/index.ts +99 -0
- package/src/prefabs/ConciergeAgent.ts +251 -0
- package/src/prefabs/FAQBotAgent.ts +263 -0
- package/src/prefabs/InfoGathererAgent.ts +276 -0
- package/src/prefabs/ReceptionistAgent.ts +277 -0
- package/src/prefabs/SurveyAgent.ts +413 -0
- package/src/prefabs/index.ts +23 -0
- package/src/skills/SkillBase.ts +184 -0
- package/src/skills/SkillManager.ts +177 -0
- package/src/skills/SkillRegistry.ts +231 -0
- package/src/skills/builtin/api_ninjas_trivia.ts +230 -0
- package/src/skills/builtin/claude_skills.ts +239 -0
- package/src/skills/builtin/custom_skills.ts +326 -0
- package/src/skills/builtin/datasphere.ts +225 -0
- package/src/skills/builtin/datasphere_serverless.ts +194 -0
- package/src/skills/builtin/datetime.ts +115 -0
- package/src/skills/builtin/google_maps.ts +347 -0
- package/src/skills/builtin/index.ts +79 -0
- package/src/skills/builtin/info_gatherer.ts +331 -0
- package/src/skills/builtin/joke.ts +180 -0
- package/src/skills/builtin/math.ts +150 -0
- package/src/skills/builtin/mcp_gateway.ts +99 -0
- package/src/skills/builtin/native_vector_search.ts +327 -0
- package/src/skills/builtin/play_background_file.ts +189 -0
- package/src/skills/builtin/spider.ts +232 -0
- package/src/skills/builtin/swml_transfer.ts +285 -0
- package/src/skills/builtin/weather_api.ts +193 -0
- package/src/skills/builtin/web_search.ts +212 -0
- package/src/skills/builtin/wikipedia_search.ts +248 -0
- package/src/skills/index.ts +12 -0
- package/src/types.ts +99 -0
- package/tests/AgentBase.test.ts +693 -0
- package/tests/AgentServer.test.ts +102 -0
- package/tests/AuthHandler.test.ts +121 -0
- package/tests/ConfigLoader.test.ts +121 -0
- package/tests/DataMap.test.ts +286 -0
- package/tests/Logger.test.ts +218 -0
- package/tests/PomBuilder.test.ts +164 -0
- package/tests/ProxyDetection.test.ts +185 -0
- package/tests/SchemaUtils.test.ts +106 -0
- package/tests/ServerlessAdapter.test.ts +200 -0
- package/tests/SessionManager.test.ts +139 -0
- package/tests/SslConfig.test.ts +97 -0
- package/tests/SwaigFunctionResult.test.ts +298 -0
- package/tests/cli/mock-data.test.ts +94 -0
- package/tests/cli/swaig-test.test.ts +96 -0
- package/tests/prefabs/prefabs.test.ts +854 -0
- package/tests/setup.ts +6 -0
- package/tests/skills/SkillBase.test.ts +118 -0
- package/tests/skills/SkillManager.test.ts +164 -0
- package/tests/skills/SkillRegistry.test.ts +124 -0
- package/tests/skills/builtin.test.ts +924 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
# SignalWire AI Agents TypeScript SDK -- Agent Guide
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Introduction](#introduction)
|
|
6
|
+
- [Creating an Agent](#creating-an-agent)
|
|
7
|
+
- [Prompts](#prompts)
|
|
8
|
+
- [Raw Text Prompts](#raw-text-prompts)
|
|
9
|
+
- [Prompt Object Model (POM)](#prompt-object-model-pom)
|
|
10
|
+
- [Declarative PROMPT_SECTIONS](#declarative-prompt_sections)
|
|
11
|
+
- [Tools (SWAIG Functions)](#tools-swaig-functions)
|
|
12
|
+
- [Defining Tools](#defining-tools)
|
|
13
|
+
- [Tool Parameters](#tool-parameters)
|
|
14
|
+
- [Secure Tools](#secure-tools)
|
|
15
|
+
- [SwaigFunctionResult](#swaigfunctionresult)
|
|
16
|
+
- [DataMap (Server-Side Tools)](#datamap-server-side-tools)
|
|
17
|
+
- [Speech and Languages](#speech-and-languages)
|
|
18
|
+
- [Hints](#hints)
|
|
19
|
+
- [Languages](#languages)
|
|
20
|
+
- [Pronunciation](#pronunciation)
|
|
21
|
+
- [AI Parameters](#ai-parameters)
|
|
22
|
+
- [Global Data](#global-data)
|
|
23
|
+
- [Call Flow (5 Phases)](#call-flow-5-phases)
|
|
24
|
+
- [Dynamic Configuration](#dynamic-configuration)
|
|
25
|
+
- [Post-Prompt and Summaries](#post-prompt-and-summaries)
|
|
26
|
+
- [Multi-Agent Server](#multi-agent-server)
|
|
27
|
+
- [Subclassing AgentBase](#subclassing-agentbase)
|
|
28
|
+
- [HTTP Endpoints](#http-endpoints)
|
|
29
|
+
- [Environment Variables](#environment-variables)
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Introduction
|
|
34
|
+
|
|
35
|
+
The SignalWire AI Agents TypeScript SDK lets you build AI-powered voice agents as HTTP microservices. Each agent is a lightweight HTTP server built on the [Hono](https://hono.dev/) framework that serves **SWML** (SignalWire Markup Language) documents and handles **SWAIG** (SignalWire AI Gateway) function callbacks.
|
|
36
|
+
|
|
37
|
+
When SignalWire receives an inbound call (or an outbound call is placed), it requests a SWML document from your agent's HTTP endpoint. The SWML document describes the entire call flow: answering, playing audio, launching the AI engine with a prompt, registering tools the AI can invoke, and handling post-call summaries. The agent SDK automates all of this -- you configure the prompt, define tools, and the SDK handles SWML rendering, webhook routing, authentication, and session management.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
Caller --> SignalWire Platform --> GET/POST your-agent/ --> SWML document
|
|
41
|
+
POST your-agent/swaig --> tool execution
|
|
42
|
+
POST your-agent/post_prompt --> call summary
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install signalwire-ai-agents
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Quick Start
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { AgentBase, SwaigFunctionResult } from 'signalwire-ai-agents';
|
|
55
|
+
|
|
56
|
+
const agent = new AgentBase({ name: 'my-agent' });
|
|
57
|
+
|
|
58
|
+
agent.setPromptText('You are a helpful customer service agent for Acme Corp.');
|
|
59
|
+
|
|
60
|
+
agent.defineTool({
|
|
61
|
+
name: 'check_order_status',
|
|
62
|
+
description: 'Look up the status of a customer order by order number.',
|
|
63
|
+
parameters: {
|
|
64
|
+
order_id: { type: 'string', description: 'The order number to look up' },
|
|
65
|
+
},
|
|
66
|
+
handler: async (args) => {
|
|
67
|
+
const status = await lookupOrder(args.order_id as string);
|
|
68
|
+
return new SwaigFunctionResult(`Order ${args.order_id} is ${status}.`);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
agent.serve();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Creating an Agent
|
|
78
|
+
|
|
79
|
+
The `AgentBase` constructor accepts an `AgentOptions` object:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const agent = new AgentBase({
|
|
83
|
+
name: 'support-bot',
|
|
84
|
+
route: '/',
|
|
85
|
+
host: '0.0.0.0',
|
|
86
|
+
port: 3000,
|
|
87
|
+
basicAuth: ['admin', 's3cretP@ss'],
|
|
88
|
+
usePom: true,
|
|
89
|
+
tokenExpirySecs: 3600,
|
|
90
|
+
autoAnswer: true,
|
|
91
|
+
recordCall: false,
|
|
92
|
+
recordFormat: 'mp4',
|
|
93
|
+
recordStereo: true,
|
|
94
|
+
nativeFunctions: [],
|
|
95
|
+
suppressLogs: false,
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### AgentOptions Reference
|
|
100
|
+
|
|
101
|
+
| Option | Type | Default | Description |
|
|
102
|
+
|--------|------|---------|-------------|
|
|
103
|
+
| `name` | `string` | **(required)** | Display name; also used as the default basic-auth username when credentials are auto-generated. |
|
|
104
|
+
| `route` | `string` | `"/"` | HTTP route path this agent listens on. |
|
|
105
|
+
| `host` | `string` | `"0.0.0.0"` | Hostname the server binds to. |
|
|
106
|
+
| `port` | `number` | `PORT` env or `3000` | Port number for the HTTP server. |
|
|
107
|
+
| `basicAuth` | `[string, string]` | auto-generated | Explicit `[username, password]` pair; if omitted, reads from env vars or generates random credentials. |
|
|
108
|
+
| `usePom` | `boolean` | `true` | Use Prompt Object Model for structured prompts. |
|
|
109
|
+
| `tokenExpirySecs` | `number` | `3600` | Session token validity in seconds for secure tools. |
|
|
110
|
+
| `autoAnswer` | `boolean` | `true` | Automatically insert an `answer` verb in the SWML call flow. |
|
|
111
|
+
| `recordCall` | `boolean` | `false` | Whether to record the call. |
|
|
112
|
+
| `recordFormat` | `string` | `"mp4"` | Recording format (e.g., `"mp4"`, `"wav"`). |
|
|
113
|
+
| `recordStereo` | `boolean` | `true` | Whether to record in stereo. |
|
|
114
|
+
| `defaultWebhookUrl` | `string` | `null` | Override the default SWAIG function webhook URL. |
|
|
115
|
+
| `nativeFunctions` | `string[]` | `[]` | List of built-in platform function names to include. |
|
|
116
|
+
| `agentId` | `string` | random hex | Unique identifier for this agent instance. |
|
|
117
|
+
| `suppressLogs` | `boolean` | `false` | Suppress all log output. |
|
|
118
|
+
|
|
119
|
+
### Basic Auth
|
|
120
|
+
|
|
121
|
+
Authentication credentials are resolved in this order:
|
|
122
|
+
|
|
123
|
+
1. **Provided** -- `basicAuth` option in the constructor.
|
|
124
|
+
2. **Environment** -- `SWML_BASIC_AUTH_USER` and `SWML_BASIC_AUTH_PASSWORD` environment variables.
|
|
125
|
+
3. **Generated** -- A random password is generated, using the agent name as the username. Credentials are logged at startup so you can retrieve them.
|
|
126
|
+
|
|
127
|
+
### Starting the Server
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Start listening
|
|
131
|
+
await agent.serve();
|
|
132
|
+
// or equivalently:
|
|
133
|
+
await agent.run();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The server logs its address and auth credentials at startup:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Agent 'support-bot' running at http://0.0.0.0:3000/
|
|
140
|
+
Auth: support-bot:**** (source: generated)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Prompts
|
|
146
|
+
|
|
147
|
+
The SDK provides two ways to define the system prompt sent to the AI: **raw text** and the **Prompt Object Model (POM)**.
|
|
148
|
+
|
|
149
|
+
### Raw Text Prompts
|
|
150
|
+
|
|
151
|
+
The simplest approach -- pass a complete prompt string:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
agent.setPromptText(`
|
|
155
|
+
You are a friendly receptionist for Dr. Smith's office.
|
|
156
|
+
You help patients schedule appointments and answer basic questions.
|
|
157
|
+
Always be polite and professional.
|
|
158
|
+
`);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
When raw text is set, it is used verbatim as the AI's system prompt. This bypasses POM rendering.
|
|
162
|
+
|
|
163
|
+
### Prompt Object Model (POM)
|
|
164
|
+
|
|
165
|
+
POM allows you to build structured prompts from titled sections, body text, bullet points, and nested subsections. The SDK renders them as Markdown headings and lists. POM is enabled by default (`usePom: true`).
|
|
166
|
+
|
|
167
|
+
#### Adding Sections
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
agent.promptAddSection('Personality', {
|
|
171
|
+
body: 'You are a cheerful and knowledgeable tech support agent.',
|
|
172
|
+
bullets: [
|
|
173
|
+
'Always greet the customer warmly',
|
|
174
|
+
'Ask clarifying questions before troubleshooting',
|
|
175
|
+
'Offer to escalate if you cannot resolve the issue',
|
|
176
|
+
],
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### Appending to Existing Sections
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
agent.promptAddToSection('Personality', {
|
|
184
|
+
bullet: 'Never make promises about timelines',
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
agent.promptAddToSection('Personality', {
|
|
188
|
+
bullets: ['Speak in simple, non-technical language', 'Confirm understanding before proceeding'],
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
agent.promptAddToSection('Personality', {
|
|
192
|
+
body: 'Remember to always thank the customer at the end of the call.',
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Subsections
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
agent.promptAddSubsection('Personality', 'Tone', {
|
|
200
|
+
body: 'Maintain a warm but professional tone throughout the conversation.',
|
|
201
|
+
bullets: ['Avoid slang', 'Do not use jargon'],
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Checking for Sections
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
if (!agent.promptHasSection('Escalation')) {
|
|
209
|
+
agent.promptAddSection('Escalation', {
|
|
210
|
+
body: 'If the issue cannot be resolved, transfer to a human agent.',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Numbered Sections and Bullets
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
agent.promptAddSection('Troubleshooting Steps', {
|
|
219
|
+
numbered: true,
|
|
220
|
+
numberedBullets: true,
|
|
221
|
+
bullets: [
|
|
222
|
+
'Ask the customer to restart the device',
|
|
223
|
+
'Check if the firmware is up to date',
|
|
224
|
+
'Try a factory reset',
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Retrieving the Rendered Prompt
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const renderedPrompt: string = agent.getPrompt();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Declarative PROMPT_SECTIONS
|
|
236
|
+
|
|
237
|
+
When subclassing `AgentBase`, you can define prompt sections declaratively as a static property. They are automatically applied in the constructor:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
class SupportAgent extends AgentBase {
|
|
241
|
+
static PROMPT_SECTIONS = [
|
|
242
|
+
{
|
|
243
|
+
title: 'Role',
|
|
244
|
+
body: 'You are a technical support agent for CloudCo.',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
title: 'Guidelines',
|
|
248
|
+
bullets: [
|
|
249
|
+
'Be concise and helpful',
|
|
250
|
+
'Verify the customer identity before sharing account details',
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
constructor() {
|
|
256
|
+
super({ name: 'support-agent' });
|
|
257
|
+
this.defineTools();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Tools (SWAIG Functions)
|
|
265
|
+
|
|
266
|
+
Tools (also called SWAIG functions) are actions the AI can invoke during a conversation. When the AI decides to call a tool, SignalWire sends a POST request to your agent's `/swaig` endpoint with the function name and arguments.
|
|
267
|
+
|
|
268
|
+
### Defining Tools
|
|
269
|
+
|
|
270
|
+
Use `defineTool()` to register a tool:
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
agent.defineTool({
|
|
274
|
+
name: 'get_weather',
|
|
275
|
+
description: 'Get the current weather for a city.',
|
|
276
|
+
parameters: {
|
|
277
|
+
city: { type: 'string', description: 'City name' },
|
|
278
|
+
units: { type: 'string', description: 'Temperature units', enum: ['celsius', 'fahrenheit'] },
|
|
279
|
+
},
|
|
280
|
+
required: ['city'],
|
|
281
|
+
handler: async (args, rawData) => {
|
|
282
|
+
const weather = await fetchWeather(args.city as string, args.units as string);
|
|
283
|
+
return new SwaigFunctionResult(`The weather in ${args.city} is ${weather.temp} degrees and ${weather.condition}.`);
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The `handler` function receives two arguments:
|
|
289
|
+
|
|
290
|
+
| Argument | Type | Description |
|
|
291
|
+
|----------|------|-------------|
|
|
292
|
+
| `args` | `Record<string, unknown>` | Parsed arguments extracted by the AI from the user's speech. |
|
|
293
|
+
| `rawData` | `Record<string, unknown>` | The full raw SWAIG request payload from SignalWire. |
|
|
294
|
+
|
|
295
|
+
The handler can return:
|
|
296
|
+
|
|
297
|
+
- A `SwaigFunctionResult` instance (recommended)
|
|
298
|
+
- A plain object with a `response` key
|
|
299
|
+
- A plain string (wrapped automatically)
|
|
300
|
+
|
|
301
|
+
### Tool Parameters
|
|
302
|
+
|
|
303
|
+
Parameters follow JSON Schema conventions. You can provide them in shorthand (just the properties) or full schema format:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// Shorthand -- properties only, SDK wraps in { type: 'object', properties: ... }
|
|
307
|
+
parameters: {
|
|
308
|
+
name: { type: 'string', description: 'Customer name' },
|
|
309
|
+
email: { type: 'string', description: 'Email address' },
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Full schema
|
|
313
|
+
parameters: {
|
|
314
|
+
type: 'object',
|
|
315
|
+
properties: {
|
|
316
|
+
name: { type: 'string', description: 'Customer name' },
|
|
317
|
+
email: { type: 'string', description: 'Email address' },
|
|
318
|
+
},
|
|
319
|
+
required: ['name'],
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Secure Tools
|
|
324
|
+
|
|
325
|
+
Secure tools require a valid HMAC session token for each invocation. The token is bound to the call ID and function name, preventing replay attacks:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
agent.defineTool({
|
|
329
|
+
name: 'transfer_funds',
|
|
330
|
+
description: 'Transfer money between accounts.',
|
|
331
|
+
secure: true,
|
|
332
|
+
parameters: {
|
|
333
|
+
amount: { type: 'number', description: 'Amount to transfer' },
|
|
334
|
+
to_account: { type: 'string', description: 'Destination account ID' },
|
|
335
|
+
},
|
|
336
|
+
handler: async (args) => {
|
|
337
|
+
// This handler only executes if the token is valid
|
|
338
|
+
await doTransfer(args.amount as number, args.to_account as string);
|
|
339
|
+
return new SwaigFunctionResult('Transfer completed successfully.');
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
When `secure: true` is set, the SWML document includes a `__token` query parameter in the tool's webhook URL. On each `/swaig` request, the SDK validates the token's HMAC signature, call ID binding, and expiry before executing the handler.
|
|
345
|
+
|
|
346
|
+
### SwaigFunctionResult
|
|
347
|
+
|
|
348
|
+
`SwaigFunctionResult` is a fluent builder for tool responses. It carries response text and an ordered list of actions:
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
import { SwaigFunctionResult } from 'signalwire-ai-agents';
|
|
352
|
+
|
|
353
|
+
// Simple text response
|
|
354
|
+
const result = new SwaigFunctionResult('The order has been placed.');
|
|
355
|
+
|
|
356
|
+
// Response with actions
|
|
357
|
+
const result = new SwaigFunctionResult('Transferring you now.')
|
|
358
|
+
.connect('+15551234567') // Transfer the call
|
|
359
|
+
.hangup(); // Then hang up
|
|
360
|
+
|
|
361
|
+
// Update global data from within a tool
|
|
362
|
+
const result = new SwaigFunctionResult('Account verified.')
|
|
363
|
+
.updateGlobalData({ verified: true, customer_id: '12345' });
|
|
364
|
+
|
|
365
|
+
// Context switch
|
|
366
|
+
const result = new SwaigFunctionResult('Let me transfer you to billing.')
|
|
367
|
+
.switchContext({ systemPrompt: 'You are now a billing specialist.' });
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Key `SwaigFunctionResult` methods:
|
|
371
|
+
|
|
372
|
+
| Method | Description |
|
|
373
|
+
|--------|-------------|
|
|
374
|
+
| `setResponse(text)` | Set the response text. |
|
|
375
|
+
| `connect(destination, final?, from?)` | Transfer the call to a destination. |
|
|
376
|
+
| `hangup()` | Hang up the call. |
|
|
377
|
+
| `hold(timeout?)` | Place the call on hold (0-900 seconds). |
|
|
378
|
+
| `say(text)` | Speak text via TTS. |
|
|
379
|
+
| `stop()` | Stop the AI session. |
|
|
380
|
+
| `updateGlobalData(data)` | Merge data into the shared global store. |
|
|
381
|
+
| `setMetadata(data)` | Set call metadata. |
|
|
382
|
+
| `switchContext(opts)` | Switch the AI context with optional prompt changes. |
|
|
383
|
+
| `toggleFunctions(toggles)` | Enable/disable specific SWAIG functions. |
|
|
384
|
+
| `sendSms(opts)` | Send an SMS or MMS. |
|
|
385
|
+
| `playBackgroundFile(url, wait?)` | Play background audio. |
|
|
386
|
+
| `waitForUser(opts?)` | Wait for user input before continuing. |
|
|
387
|
+
| `addDynamicHints(hints)` | Add speech recognition hints at runtime. |
|
|
388
|
+
| `executeSwml(swml, transfer?)` | Execute arbitrary SWML content. |
|
|
389
|
+
| `pay(opts)` | Initiate a payment collection flow. |
|
|
390
|
+
|
|
391
|
+
Serialization fallback: if both `response` and `action` are empty, `toDict()` returns `{ response: "Action completed." }`.
|
|
392
|
+
|
|
393
|
+
### DataMap (Server-Side Tools)
|
|
394
|
+
|
|
395
|
+
DataMap tools execute entirely on the SignalWire platform without requiring a webhook. They are useful for simple API calls and pattern-matching:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
import { DataMap, SwaigFunctionResult } from 'signalwire-ai-agents';
|
|
399
|
+
|
|
400
|
+
const weatherTool = new DataMap('get_weather')
|
|
401
|
+
.purpose('Look up weather for a city')
|
|
402
|
+
.parameter('city', 'string', 'City name', { required: true })
|
|
403
|
+
.webhook('GET', 'https://api.weather.com/v1/current?q=${args.city}', {
|
|
404
|
+
headers: { 'Authorization': 'Bearer ${ENV.WEATHER_API_KEY}' },
|
|
405
|
+
})
|
|
406
|
+
.output(new SwaigFunctionResult('The weather is ${temp}F and ${condition}.'))
|
|
407
|
+
.enableEnvExpansion();
|
|
408
|
+
|
|
409
|
+
weatherTool.registerWithAgent(agent);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Convenience helpers:
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import { createSimpleApiTool, createExpressionTool } from 'signalwire-ai-agents';
|
|
416
|
+
|
|
417
|
+
const tool = createSimpleApiTool({
|
|
418
|
+
name: 'joke',
|
|
419
|
+
url: 'https://api.example.com/joke',
|
|
420
|
+
responseTemplate: 'Here is a joke: ${joke}',
|
|
421
|
+
});
|
|
422
|
+
tool.registerWithAgent(agent);
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Speech and Languages
|
|
428
|
+
|
|
429
|
+
### Hints
|
|
430
|
+
|
|
431
|
+
Speech recognition hints improve transcription accuracy for domain-specific vocabulary:
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
// Single hint
|
|
435
|
+
agent.addHint('SignalWire');
|
|
436
|
+
|
|
437
|
+
// Multiple hints
|
|
438
|
+
agent.addHints(['HIPAA', 'deductible', 'copay', 'pre-authorization']);
|
|
439
|
+
|
|
440
|
+
// Pattern hint (find and replace in transcription)
|
|
441
|
+
agent.addPatternHint({
|
|
442
|
+
pattern: 'signal wire',
|
|
443
|
+
replace: 'SignalWire',
|
|
444
|
+
ignoreCase: true,
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Languages
|
|
449
|
+
|
|
450
|
+
Configure supported languages with voice and filler phrases:
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
agent.addLanguage({
|
|
454
|
+
name: 'English',
|
|
455
|
+
code: 'en-US',
|
|
456
|
+
voice: 'en-US-Neural2-F',
|
|
457
|
+
engine: 'google',
|
|
458
|
+
speechModel: 'enhanced',
|
|
459
|
+
fillers: { thinking: ['Let me check on that...', 'One moment please...'] },
|
|
460
|
+
functionFillers: { get_weather: { 'en-US': ['Checking the forecast...'] } },
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
agent.addLanguage({
|
|
464
|
+
name: 'Spanish',
|
|
465
|
+
code: 'es-MX',
|
|
466
|
+
voice: 'es-MX-Neural2-A',
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Replace all languages at once:
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
agent.setLanguages([
|
|
474
|
+
{ name: 'English', code: 'en-US', voice: 'en-US-Neural2-F' },
|
|
475
|
+
{ name: 'French', code: 'fr-FR', voice: 'fr-FR-Neural2-A' },
|
|
476
|
+
]);
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Pronunciation
|
|
480
|
+
|
|
481
|
+
Override how the TTS engine pronounces specific words:
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
agent.addPronunciation({
|
|
485
|
+
replace: 'SQL',
|
|
486
|
+
with: 'sequel',
|
|
487
|
+
ignoreCase: true,
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
agent.addPronunciation({
|
|
491
|
+
replace: 'API',
|
|
492
|
+
with: 'A P I',
|
|
493
|
+
});
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## AI Parameters
|
|
499
|
+
|
|
500
|
+
Control the AI model's behavior with parameters like temperature and top_p:
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
// Set a single parameter
|
|
504
|
+
agent.setParam('temperature', 0.7);
|
|
505
|
+
agent.setParam('top_p', 0.9);
|
|
506
|
+
|
|
507
|
+
// Set multiple parameters at once
|
|
508
|
+
agent.setParams({
|
|
509
|
+
temperature: 0.3,
|
|
510
|
+
top_p: 0.85,
|
|
511
|
+
confidence: 0.5,
|
|
512
|
+
barge_confidence: 0.3,
|
|
513
|
+
presence_penalty: 0.2,
|
|
514
|
+
frequency_penalty: 0.5,
|
|
515
|
+
});
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Prompt-Specific LLM Parameters
|
|
519
|
+
|
|
520
|
+
Set LLM parameters that apply specifically to the main prompt or post-prompt:
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
// Main prompt LLM params (e.g., model selection, temperature override)
|
|
524
|
+
agent.setPromptLlmParams({
|
|
525
|
+
model: 'gpt-4',
|
|
526
|
+
temperature: 0.2,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Post-prompt LLM params
|
|
530
|
+
agent.setPostPromptLlmParams({
|
|
531
|
+
model: 'gpt-3.5-turbo',
|
|
532
|
+
temperature: 0.1,
|
|
533
|
+
});
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Global Data
|
|
539
|
+
|
|
540
|
+
Global data is a key-value store shared across all tool invocations within a call. Tools can read from it and update it:
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
// Replace the entire global data object
|
|
544
|
+
agent.setGlobalData({
|
|
545
|
+
company: 'Acme Corp',
|
|
546
|
+
support_hours: '9am-5pm EST',
|
|
547
|
+
max_refund: 500,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Merge additional entries into existing global data
|
|
551
|
+
agent.updateGlobalData({
|
|
552
|
+
promo_code: 'SUMMER2025',
|
|
553
|
+
});
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Within a tool handler, you can update global data dynamically:
|
|
557
|
+
|
|
558
|
+
```typescript
|
|
559
|
+
handler: async (args) => {
|
|
560
|
+
return new SwaigFunctionResult('Customer verified.')
|
|
561
|
+
.updateGlobalData({ customer_verified: true, customer_name: args.name });
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Call Flow (5 Phases)
|
|
568
|
+
|
|
569
|
+
The SWML document is assembled from five sequential phases. Each phase adds verbs to the document's `main` section:
|
|
570
|
+
|
|
571
|
+
| Phase | Method | Description |
|
|
572
|
+
|-------|--------|-------------|
|
|
573
|
+
| **1. Pre-Answer** | `addPreAnswerVerb(verb, config)` | Verbs executed before answering the call (e.g., playing a ring tone). |
|
|
574
|
+
| **2. Answer** | `addAnswerVerb(config?)` | The `answer` verb that picks up the call. Automatic when `autoAnswer: true`. |
|
|
575
|
+
| **3. Post-Answer** | `addPostAnswerVerb(verb, config)` | Verbs after answering but before the AI starts (e.g., `record_call`, `play`). |
|
|
576
|
+
| **4. AI** | *(automatic)* | The `ai` verb containing the prompt, tools, hints, languages, and all AI configuration. |
|
|
577
|
+
| **5. Post-AI** | `addPostAiVerb(verb, config)` | Verbs executed after the AI session ends (e.g., `hangup`, `connect`). |
|
|
578
|
+
|
|
579
|
+
### Example
|
|
580
|
+
|
|
581
|
+
```typescript
|
|
582
|
+
const agent = new AgentBase({ name: 'ivr-bot', autoAnswer: true, recordCall: true });
|
|
583
|
+
|
|
584
|
+
// Phase 1: Play hold music before answering
|
|
585
|
+
agent.addPreAnswerVerb('play', {
|
|
586
|
+
url: 'https://cdn.example.com/hold-music.mp3',
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// Phase 3: Play a greeting after answering, before AI starts
|
|
590
|
+
agent.addPostAnswerVerb('play', {
|
|
591
|
+
url: 'https://cdn.example.com/greeting.wav',
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Phase 5: Hang up after the AI session ends
|
|
595
|
+
agent.addPostAiVerb('hangup', {});
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Note: When `recordCall: true`, a `record_call` verb is automatically inserted at the start of Phase 3.
|
|
599
|
+
|
|
600
|
+
### Clearing Verbs
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
agent.clearPreAnswerVerbs();
|
|
604
|
+
agent.clearPostAnswerVerbs();
|
|
605
|
+
agent.clearPostAiVerbs();
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## Dynamic Configuration
|
|
611
|
+
|
|
612
|
+
Dynamic configuration lets you modify the agent's settings per-request. On each SWML request, the SDK creates an **ephemeral copy** of the agent, passes it to your callback, and renders SWML from the modified copy. The original agent is never mutated.
|
|
613
|
+
|
|
614
|
+
```typescript
|
|
615
|
+
agent.setDynamicConfigCallback(async (queryParams, bodyParams, headers, ephemeralAgent) => {
|
|
616
|
+
const agent = ephemeralAgent as AgentBase;
|
|
617
|
+
|
|
618
|
+
// Customize based on query parameters
|
|
619
|
+
const lang = queryParams['lang'];
|
|
620
|
+
if (lang === 'es') {
|
|
621
|
+
agent.setPromptText('Eres un asistente de servicio al cliente amable.');
|
|
622
|
+
agent.addLanguage({ name: 'Spanish', code: 'es-MX', voice: 'es-MX-Neural2-A' });
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Customize based on request headers
|
|
626
|
+
const tier = headers['x-customer-tier'];
|
|
627
|
+
if (tier === 'premium') {
|
|
628
|
+
agent.updateGlobalData({ premium: true, max_refund: 1000 });
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Customize based on body data
|
|
632
|
+
const callerId = bodyParams['caller_id_number'] as string;
|
|
633
|
+
if (callerId) {
|
|
634
|
+
const customer = await lookupCustomer(callerId);
|
|
635
|
+
agent.updateGlobalData({ customer_name: customer.name });
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
The callback signature:
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
type DynamicConfigCallback = (
|
|
644
|
+
queryParams: Record<string, string>, // URL query parameters
|
|
645
|
+
bodyParams: Record<string, unknown>, // Parsed request body
|
|
646
|
+
headers: Record<string, string>, // HTTP headers
|
|
647
|
+
agent: unknown, // Ephemeral AgentBase copy (cast as needed)
|
|
648
|
+
) => void | Promise<void>;
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
### SWAIG Query Parameters
|
|
652
|
+
|
|
653
|
+
Append custom query parameters to all SWAIG webhook URLs:
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
agent.addSwaigQueryParams({ tenant: 'acme', region: 'us-east' });
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
## Post-Prompt and Summaries
|
|
662
|
+
|
|
663
|
+
The **post-prompt** is a separate prompt evaluated after the call ends. It is typically used to generate a structured summary of the conversation.
|
|
664
|
+
|
|
665
|
+
### Setting the Post-Prompt
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
agent.setPostPrompt(`
|
|
669
|
+
Summarize this conversation as JSON with the following fields:
|
|
670
|
+
- "resolution": whether the issue was resolved (true/false)
|
|
671
|
+
- "category": the type of issue (billing, technical, general)
|
|
672
|
+
- "summary": a one-sentence summary of the call
|
|
673
|
+
`);
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Handling Summaries
|
|
677
|
+
|
|
678
|
+
Override the `onSummary` lifecycle hook to process the summary when it arrives:
|
|
679
|
+
|
|
680
|
+
```typescript
|
|
681
|
+
class MyAgent extends AgentBase {
|
|
682
|
+
constructor() {
|
|
683
|
+
super({ name: 'my-agent' });
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
async onSummary(summary: Record<string, unknown> | null, rawData: Record<string, unknown>): Promise<void> {
|
|
687
|
+
if (summary) {
|
|
688
|
+
console.log('Call summary:', summary);
|
|
689
|
+
await saveToDatabase(summary);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
The SDK extracts the summary from the post-prompt payload by checking `body.summary`, then `body.post_prompt_data.parsed[0]`, then parsing `body.post_prompt_data.raw` as JSON.
|
|
696
|
+
|
|
697
|
+
### Post-Prompt LLM Parameters
|
|
698
|
+
|
|
699
|
+
You can use a different (often cheaper/faster) model for the post-prompt evaluation:
|
|
700
|
+
|
|
701
|
+
```typescript
|
|
702
|
+
agent.setPostPromptLlmParams({
|
|
703
|
+
model: 'gpt-3.5-turbo',
|
|
704
|
+
temperature: 0.0,
|
|
705
|
+
});
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Multi-Agent Server
|
|
711
|
+
|
|
712
|
+
`AgentServer` hosts multiple `AgentBase` instances on a single HTTP server, each mounted at its own route prefix:
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
import { AgentBase, AgentServer } from 'signalwire-ai-agents';
|
|
716
|
+
|
|
717
|
+
const salesAgent = new AgentBase({ name: 'sales', route: '/sales' });
|
|
718
|
+
salesAgent.setPromptText('You are a sales representative.');
|
|
719
|
+
|
|
720
|
+
const supportAgent = new AgentBase({ name: 'support', route: '/support' });
|
|
721
|
+
supportAgent.setPromptText('You are a technical support agent.');
|
|
722
|
+
|
|
723
|
+
const server = new AgentServer({ host: '0.0.0.0', port: 4000 });
|
|
724
|
+
server.register(salesAgent);
|
|
725
|
+
server.register(supportAgent);
|
|
726
|
+
|
|
727
|
+
await server.run();
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
### AgentServer Features
|
|
731
|
+
|
|
732
|
+
- **Agent listing**: If no agent is mounted at `/`, a GET to `/` returns a JSON listing of all registered agents and their routes.
|
|
733
|
+
- **Health endpoints**: `/health` and `/ready` return `{ "status": "ok" }` and `{ "status": "ready" }` respectively.
|
|
734
|
+
- **Security headers**: `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, and `Referrer-Policy` are set on all responses.
|
|
735
|
+
- **CORS**: Enabled globally with `origin: *`.
|
|
736
|
+
|
|
737
|
+
### API
|
|
738
|
+
|
|
739
|
+
| Method | Description |
|
|
740
|
+
|--------|-------------|
|
|
741
|
+
| `register(agent, route?)` | Mount an agent at a route prefix. Throws if the route is already in use. |
|
|
742
|
+
| `unregister(route)` | Remove an agent registration. |
|
|
743
|
+
| `getAgents()` | Returns a `Map<string, AgentBase>` of all registered agents. |
|
|
744
|
+
| `getAgent(route)` | Look up an agent by route. |
|
|
745
|
+
| `getApp()` | Returns the underlying Hono application. |
|
|
746
|
+
| `run(host?, port?)` | Start the HTTP server. |
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
## Subclassing AgentBase
|
|
751
|
+
|
|
752
|
+
For reusable, self-contained agents, extend `AgentBase`:
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
import { AgentBase, SwaigFunctionResult } from 'signalwire-ai-agents';
|
|
756
|
+
|
|
757
|
+
class RestaurantBot extends AgentBase {
|
|
758
|
+
// Declarative prompt sections
|
|
759
|
+
static PROMPT_SECTIONS = [
|
|
760
|
+
{
|
|
761
|
+
title: 'Role',
|
|
762
|
+
body: 'You are a helpful restaurant reservation assistant for Bella Italia.',
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
title: 'Guidelines',
|
|
766
|
+
bullets: [
|
|
767
|
+
'Be warm and inviting',
|
|
768
|
+
'Always confirm the reservation details before finalizing',
|
|
769
|
+
'Mention daily specials when relevant',
|
|
770
|
+
],
|
|
771
|
+
},
|
|
772
|
+
];
|
|
773
|
+
|
|
774
|
+
constructor() {
|
|
775
|
+
super({ name: 'restaurant-bot', route: '/restaurant' });
|
|
776
|
+
|
|
777
|
+
this.addHints(['Bella Italia', 'risotto', 'tiramisu']);
|
|
778
|
+
this.setParams({ temperature: 0.6 });
|
|
779
|
+
|
|
780
|
+
// Must be called explicitly at the end of the constructor
|
|
781
|
+
this.defineTools();
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Override to register tools
|
|
785
|
+
protected defineTools(): void {
|
|
786
|
+
this.defineTool({
|
|
787
|
+
name: 'make_reservation',
|
|
788
|
+
description: 'Create a restaurant reservation.',
|
|
789
|
+
parameters: {
|
|
790
|
+
date: { type: 'string', description: 'Reservation date (YYYY-MM-DD)' },
|
|
791
|
+
time: { type: 'string', description: 'Reservation time (HH:MM)' },
|
|
792
|
+
party_size: { type: 'number', description: 'Number of guests' },
|
|
793
|
+
name: { type: 'string', description: 'Name for the reservation' },
|
|
794
|
+
},
|
|
795
|
+
required: ['date', 'time', 'party_size', 'name'],
|
|
796
|
+
handler: async (args) => {
|
|
797
|
+
const id = await createReservation(args);
|
|
798
|
+
return new SwaigFunctionResult(
|
|
799
|
+
`Reservation confirmed for ${args.party_size} on ${args.date} at ${args.time}. ` +
|
|
800
|
+
`Confirmation number: ${id}.`
|
|
801
|
+
);
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Override the post-prompt summary hook
|
|
807
|
+
async onSummary(summary: Record<string, unknown> | null): Promise<void> {
|
|
808
|
+
if (summary) {
|
|
809
|
+
await logReservationSummary(summary);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Override pre-execution hook for all tools
|
|
814
|
+
async onFunctionCall(name: string, args: Record<string, unknown>): Promise<void> {
|
|
815
|
+
console.log(`Tool invoked: ${name}`, args);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// Override basic auth validation
|
|
819
|
+
validateBasicAuth(username: string, password: string): boolean {
|
|
820
|
+
return username === 'admin' && password === process.env.ADMIN_PASSWORD!;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### Extension Points
|
|
826
|
+
|
|
827
|
+
| Method / Property | Purpose |
|
|
828
|
+
|-------------------|---------|
|
|
829
|
+
| `static PROMPT_SECTIONS` | Declarative prompt sections applied in the constructor. |
|
|
830
|
+
| `defineTools()` | Override to register tools. Call explicitly at the end of your constructor. |
|
|
831
|
+
| `onSummary(summary, rawData)` | Called when a post-prompt summary is received. |
|
|
832
|
+
| `onFunctionCall(name, args, rawData)` | Called before each tool handler executes. |
|
|
833
|
+
| `onSwmlRequest(rawData)` | Called on every SWML request before rendering. |
|
|
834
|
+
| `onDebugEvent(event)` | Called when a debug event webhook is received. |
|
|
835
|
+
| `validateBasicAuth(username, password)` | Override to add custom auth validation logic. |
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
## HTTP Endpoints
|
|
840
|
+
|
|
841
|
+
Each agent exposes the following HTTP endpoints (all relative to the agent's `route`):
|
|
842
|
+
|
|
843
|
+
| Method | Path | Auth | Description |
|
|
844
|
+
|--------|------|------|-------------|
|
|
845
|
+
| GET / POST | `/` | Basic Auth | Returns the rendered SWML document. Triggers proxy detection and dynamic config callback. |
|
|
846
|
+
| GET / POST | `/swaig` | Basic Auth | SWAIG function dispatcher. Receives `{ "function": "name", "argument": {...} }`, validates tokens for secure tools, calls `onFunctionCall`, executes the handler. |
|
|
847
|
+
| GET / POST | `/post_prompt` | Basic Auth | Receives post-prompt summary data. Extracts the summary and calls `onSummary`. |
|
|
848
|
+
| POST | `/debug_events` | Basic Auth | Receives debug event webhooks (when `enableDebugEvents()` is active). |
|
|
849
|
+
| GET | `/health` | None | Returns `{ "status": "ok" }`. |
|
|
850
|
+
| GET | `/ready` | None | Returns `{ "status": "ready" }`. |
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## Environment Variables
|
|
855
|
+
|
|
856
|
+
| Variable | Default | Description |
|
|
857
|
+
|----------|---------|-------------|
|
|
858
|
+
| `PORT` | `3000` | HTTP server port (used when `port` is not specified in `AgentOptions`). |
|
|
859
|
+
| `SWML_BASIC_AUTH_USER` | *(none)* | Basic auth username. Used when `basicAuth` is not provided in options. |
|
|
860
|
+
| `SWML_BASIC_AUTH_PASSWORD` | *(none)* | Basic auth password. Used when `basicAuth` is not provided in options. |
|
|
861
|
+
| `SWML_PROXY_URL_BASE` | *(none)* | External-facing base URL for webhook URLs (e.g., `https://agents.example.com`). Takes highest priority for proxy detection. |
|
|
862
|
+
| `SWML_PROXY_DEBUG` | `"false"` | Set to `"true"` to enable verbose logging of proxy/header detection. |
|
|
863
|
+
| `SWML_CORS_ORIGINS` | `"*"` | Comma-separated list of allowed CORS origins. Defaults to allow all. |
|
|
864
|
+
| `SWML_ALLOWED_HOSTS` | *(none)* | Comma-separated list of allowed `Host` header values. Requests from other hosts receive 403. |
|
|
865
|
+
| `SWML_MAX_REQUEST_SIZE` | `1048576` | Maximum request body size in bytes (default 1 MB). Requests exceeding this receive 413. |
|
|
866
|
+
| `SWML_RATE_LIMIT` | *(none)* | Maximum requests per minute per IP address. When exceeded, requests receive 429. |
|
|
867
|
+
| `SWML_REQUEST_TIMEOUT` | `30000` | Request timeout in milliseconds. |
|
|
868
|
+
| `SIGNALWIRE_LOG_LEVEL` | `"info"` | Logging level: `"debug"`, `"info"`, `"warn"`, `"error"`. |
|
|
869
|
+
| `SIGNALWIRE_LOG_MODE` | `"pretty"` | Log output format: `"pretty"` for human-readable, `"json"` for structured JSON. |
|