diff --git a/.claude/rules/mdx-components.md b/.claude/rules/mdx-components.md index 9b17e3c8e2..daad576201 100644 --- a/.claude/rules/mdx-components.md +++ b/.claude/rules/mdx-components.md @@ -43,6 +43,7 @@ Every component available on this site: Fern's full default library (description Custom components (registered in `docs.yml` → `fern/components/`): - `VoiceWidget` — interactive click-to-call demo. Human-only: wrap usages in ``. +- `SigmondWidget` — renders nothing itself; it wires every `[data-sigmond-launcher]` element on the page to the live Sigmond video call. Pair it with the clickable markup — `` or the homepage band — or the card renders and does nothing. Human-only: wrap the launcher markup *and* the `` element in ``. - `Skeleton` / `SkeletonText` — loading placeholders; registered, no MDX uses yet. Fern's Markdown export flattens an untitled callout into ordinary prose. Give a `Warning` a concise title when losing the warning context could change a reader's safe interpretation—for example, legal or compliance disclaimers, credential handling, network restrictions, or service availability. Do not add titles mechanically to every callout; the title must identify the specific hazard or constraint. diff --git a/fern/assets/images/sigmond-thumbnail.jpg b/fern/assets/images/sigmond-thumbnail.jpg new file mode 100644 index 0000000000..d30ed74129 Binary files /dev/null and b/fern/assets/images/sigmond-thumbnail.jpg differ diff --git a/fern/components/index.tsx b/fern/components/index.tsx index 289efdaf69..be70e4621d 100644 --- a/fern/components/index.tsx +++ b/fern/components/index.tsx @@ -6,3 +6,4 @@ // component's public surface (components + types) without enumerating them. export * from "./voice-widget/index"; export * from "./skeleton/index"; +export * from "./sigmond-card/index"; diff --git a/fern/components/sigmond-card/index.tsx b/fern/components/sigmond-card/index.tsx new file mode 100644 index 0000000000..05b270d9c1 --- /dev/null +++ b/fern/components/sigmond-card/index.tsx @@ -0,0 +1,154 @@ +import { useEffect } from "react"; + +const WIDGET_UMD_URL = + "https://cdn.signalwire.com/npm/@signalwire/address-widget@dev/dist/address-widget.umd.js"; + +const DEFAULT_TOKEN = + "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIiwidHlwIjoiU0FUIiwiY2giOiJwdWMuc2lnbmFsd2lyZS5jb20ifQ..IKPXZDR3ZYlpnUuw.yU8NBgPFQN1LH0Ws-Rs2ca-vAbUEP7Gvr0upOf-hDaTUfp-Aa5TALLMkJdUli8UX0ina34hng0G2XPLh7NZ2VZ5cphdd8AUCDKWkJ5HCI8538aNxPWcUWG25B-eELha7yEIrHZlO5BIxuyGVPskf0BIPGgiFSlctotyGB-VyYwSp_Uf6kdcwTCiEgra9LcyUMpNZyz5xH3p93xDU9bvzunFj50qIbfmQ2S0tDgEdduQWlTIfqUjRT6ePuwLGDBZYCCz9JtCQM0S2Fs7ZwvdSOzLFLqThkWnH3mSnkgKtkSTTXpnKUplPBJQaNRn0P4rJiwkk61BGyCJQ8Y8XYNeUHa-m1pRERQNweohQNBA0Ph3ie3vZLFJFjOeWuuztK8JSnfu4m9fxlkiA6icaHYuUm7KTSQ6n7oV5a2dkbLs7s51GRi7di0mm6Xw3jr2Zw1EGS8NgIrAU2HTv7stMAjpvDneYDfPaysCrmhCwu_TJgpG7PARiA4glKv9IuPeF5xpnymM5p0V2KL4HSEz-MJwziOwPpQRED_Brd2DiE0cz.CEYQUqZhcs5wKCtJQqUAMQ"; + +const DEFAULT_DESTINATION = "/public/sigmond"; +const DEFAULT_LABEL = "Talk with Sigmond"; +const DEFAULT_POSTER = "https://mcdn.signalwire.com/images/sigmond_still.png"; + +function pageSlug(): string { + if (typeof window === "undefined") return ""; + const segments = (window.location.pathname || "/").split("/").filter(Boolean); + if (segments.length === 0) return "home"; + return segments + .map((s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")) + .filter(Boolean) + .join("__"); +} + +type WidgetInstance = { open: () => Promise; close: () => Promise; theme?: string }; +type WidgetGlobal = { + mount: (target: Element | string, options: Record) => WidgetInstance; + unmount?: (widget: WidgetInstance) => Promise; +}; + +declare global { + interface Window { + SignalWireAddressWidget?: WidgetGlobal; + } +} + +let loaderPromise: Promise | null = null; +function loadWidgetGlobal(): Promise { + if (typeof window === "undefined") return Promise.reject(new Error("no window")); + if (window.SignalWireAddressWidget) return Promise.resolve(window.SignalWireAddressWidget); + if (!loaderPromise) { + loaderPromise = new Promise((resolve, reject) => { + const s = document.createElement("script"); + s.src = WIDGET_UMD_URL; + s.async = true; + s.onload = () => + window.SignalWireAddressWidget + ? resolve(window.SignalWireAddressWidget) + : reject(new Error("address-widget UMD loaded but global missing")); + s.onerror = () => { + loaderPromise = null; + reject(new Error("failed to load address-widget UMD")); + }; + document.head.appendChild(s); + }); + } + return loaderPromise; +} + +export interface SigmondWidgetProps { + token?: string; +} + +export function SigmondWidget({ token = DEFAULT_TOKEN }: SigmondWidgetProps) { + useEffect(() => { + const teardown: Array<() => void> = []; + + if (!document.head.querySelector("link[data-signalwire-address-fonts]")) { + const marker = document.createElement("link"); + marker.setAttribute("data-signalwire-address-fonts", "host"); + document.head.appendChild(marker); + } + + void loadWidgetGlobal().catch(() => {}); + + const launchers = Array.from( + document.querySelectorAll("[data-sigmond-launcher]"), + ).filter((el) => !el.dataset.sigmondWired); + + if (launchers.length === 0) { + console.warn( + "SigmondWidget: no [data-sigmond-launcher] element found on this page — the card will render but do nothing.", + ); + } + + for (const el of launchers) { + el.dataset.sigmondWired = "true"; + let widget: WidgetInstance | null = null; + let busy = false; + + const open = async () => { + if (busy) return; + busy = true; + el.classList.add("sigmond-card--opening"); + try { + const global = await loadWidgetGlobal(); + const theme = document.documentElement.classList.contains("dark") ? "dark" : "light"; + if (!widget) { + const host = document.createElement("div"); + host.className = "sigmond-widget-host"; + document.body.appendChild(host); + widget = global.mount(host, { + token, + destination: el.dataset.destination || DEFAULT_DESTINATION, + label: el.dataset.label || DEFAULT_LABEL, + poster: el.dataset.poster || DEFAULT_POSTER, + theme, + layout: "stacked", + video: true, + audio: true, + autoGainControl: false, + inputVolume: 125, + userVariables: { page_slug: pageSlug() }, + }); + const mounted = widget; + teardown.push(() => { + const unmount = window.SignalWireAddressWidget?.unmount; + const done = unmount + ? Promise.resolve(unmount(mounted)).catch((e) => + console.error("SigmondWidget: unmount failed:", e), + ) + : Promise.resolve(); + void done.then(() => host.remove()); + }); + } + widget.theme = theme; + void widget.open(); + } catch (e) { + console.error("SigmondWidget:", e); + } finally { + busy = false; + el.classList.remove("sigmond-card--opening"); + } + }; + + const onClick = () => void open(); + const onKey = (e: KeyboardEvent) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + void open(); + } + }; + el.addEventListener("click", onClick); + el.addEventListener("keydown", onKey); + teardown.push(() => { + el.removeEventListener("click", onClick); + el.removeEventListener("keydown", onKey); + delete el.dataset.sigmondWired; + }); + } + + return () => teardown.forEach((fn) => fn()); + }, [token]); + + return null; +} diff --git a/fern/components/sigmond-card/styles.css b/fern/components/sigmond-card/styles.css new file mode 100644 index 0000000000..4ffa3e49a6 --- /dev/null +++ b/fern/components/sigmond-card/styles.css @@ -0,0 +1,285 @@ +.sigmond-card-wrap { + margin: 1.5rem 0; +} + +.sigmond-card { + position: relative; + display: flex; + align-items: stretch; + gap: 1.25rem; + width: 100%; + text-align: left; + padding: 1rem; + border: none; + border-radius: var(--radius-xl); + background: var(--bg-surface-raised); + box-shadow: var(--card-shadow), var(--card-inset); + cursor: pointer; + color: inherit; + font: inherit; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.sigmond-card:hover { + transform: translateY(-3px); + box-shadow: 0 0 0 1.5px rgba(var(--sw-fuchsia-rgb), 0.15), + var(--card-hover-shadow), var(--card-inset-hover), + inset 0 -1px 0 var(--card-hover-border); +} + +.sigmond-card:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.sigmond-card--opening { + cursor: wait; +} + +.sigmond-card-media { + position: relative; + flex: 0 0 auto; + width: 160px; + height: 90px; + align-self: center; + border-radius: 0.6rem; + background-color: var(--grayscale-4); + overflow: hidden; + pointer-events: none; +} + +.sigmond-card-media * { + position: absolute !important; + inset: 0 !important; + width: 100% !important; + height: 100% !important; + max-width: none !important; + margin: 0 !important; + padding: 0 !important; + transform: none !important; + border-radius: inherit; +} + +.sigmond-card-media img { + object-fit: cover; + display: block; +} + +.sigmond-card-body { + display: flex; + flex-direction: column; + justify-content: center; + gap: 0.25rem; + min-width: 0; +} + +.sigmond-card-title { + font-size: 1rem; + font-weight: 700; + color: var(--grayscale-12); +} + +.sigmond-card-desc { + font-size: 0.875rem; + line-height: 1.45; + color: var(--grayscale-a11); +} + +.sigmond-card-cta { + display: inline-flex; + align-items: center; + gap: 0.35rem; + margin-top: 0.35rem; + font-size: 0.8125rem; + font-weight: 600; + color: var(--accent-a11); +} + +.sigmond-card-cta svg { + display: block; + width: 1em; + height: 1em; + flex: 0 0 auto; + color: currentColor; + fill: currentColor; +} + +/* --- Homepage below-the-fold band --- */ + +.sigmond-band { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.lp-page-container .sigmond-band h2 { + margin-bottom: 0; + font-size: clamp(1.25rem, 3.5vw, 1.75rem); + text-wrap: balance; +} + +.sigmond-band-eyebrow { + display: block; + margin-bottom: 0.6rem; + font-size: 0.6875rem; + font-weight: 700; + letter-spacing: var(--type-letter-spacing-eyebrow); + text-transform: uppercase; + color: var(--accent-a11); +} + +.sigmond-band-desc { + max-width: 36rem; + margin-top: 0.75rem; + font-size: 0.875rem; + line-height: 1.6; + color: var(--fg-muted); + text-wrap: balance; +} + +.sigmond-band-actions { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 0.75rem 1.5rem; + margin-top: 1.5rem; +} + +.sigmond-band-cta { + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; + gap: 0.5rem; + padding: 0.7rem 1.25rem; + border-radius: 999px; + background: var(--accent); + color: var(--accent-contrast); + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease; +} + +.sigmond-band-cta svg, +.sigmond-band-link svg { + display: block; + width: 1em; + height: 1em; + flex: 0 0 auto; + color: currentColor; + fill: currentColor; +} + +.sigmond-band-cta svg * { + fill: currentColor; +} + +.sigmond-band-cta:hover { + transform: translateY(-1px); + filter: brightness(1.08); + box-shadow: var(--card-hover-shadow); +} + +.sigmond-band-cta:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.sigmond-band-cta.sigmond-card--opening { + cursor: wait; +} + +.sigmond-band-sep { + width: 4px; + height: 4px; + border-radius: 999px; + background: var(--fg-subtle); + opacity: 0.6; +} + +.sigmond-band-link { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-size: 0.8125rem; + font-weight: 600; + color: var(--interactive-link-default); +} + +.sigmond-band-link:hover { + color: var(--interactive-link-hover); +} + +.sigmond-band-media { + flex: 0 0 auto; + width: clamp(120px, 22vw, 180px); + height: clamp(120px, 22vw, 180px); + margin-top: 2rem; + border-radius: 999px; + overflow: hidden; + border: 1px solid var(--border-default); + background-color: var(--bg-surface); + pointer-events: none; +} + +.sigmond-band-media img, +.sigmond-band-media span, +.sigmond-band-media div { + width: 100%; + height: 100%; + margin: 0; + border-radius: inherit; +} + +.sigmond-band-media img { + display: block; + object-fit: cover; +} + +@media (max-width: 640px) { + .sigmond-band-media { + margin-top: 1.5rem; + } + + .sigmond-band-actions { + margin-top: 1.25rem; + gap: 0.75rem; + } + + .sigmond-band-sep { + display: none; + } + + .sigmond-band-cta { + min-height: 48px; + padding: 0.75rem 1.5rem; + } +} + +.sigmond-widget-host { + position: absolute; + width: 0; + height: 0; +} + +.sigmond-widget-host signalwire-address::part(launcher) { + display: none !important; +} + +@media (max-width: 560px) { + .sigmond-card { + flex-direction: column; + align-items: stretch; + gap: 0.875rem; + } + + .sigmond-card-media { + width: 100%; + height: auto; + aspect-ratio: 16 / 9; + align-self: stretch; + } +} diff --git a/fern/docs.yml b/fern/docs.yml index 7ba739f4ad..63198bdc4d 100644 --- a/fern/docs.yml +++ b/fern/docs.yml @@ -184,6 +184,7 @@ css: - styles.css - components/skeleton/styles.css - components/voice-widget/styles.css + - components/sigmond-card/styles.css redirects: - source: /docs/agents-sdk diff --git a/fern/products/home/pages/welcome.mdx b/fern/products/home/pages/welcome.mdx index 49c0930867..0ea03c9d3a 100644 --- a/fern/products/home/pages/welcome.mdx +++ b/fern/products/home/pages/welcome.mdx @@ -7,6 +7,8 @@ hide-toc: true layout: custom --- +import { SigmondWidget } from "@/components/index"; + @@ -106,6 +108,31 @@ layout: custom + {/* Talk with Sigmond */} + + +
+ Try it now +

Sigmond runs on SignalWire

+

+ A voice and video AI agent built with the SignalWire SDKs, wired to a live knowledge base, and able to see what you show it. +

+
+ Sigmond, the SignalWire AI agent +
+
+ + + Call Sigmond + + + Build an agent like this +
+
+ + +
+ {/* Twilio Migration */}
diff --git a/fern/products/platform/pages/ai/overview.mdx b/fern/products/platform/pages/ai/overview.mdx index c4871d74fd..79b176cf91 100644 --- a/fern/products/platform/pages/ai/overview.mdx +++ b/fern/products/platform/pages/ai/overview.mdx @@ -8,6 +8,827 @@ description: SignalWire AI runs voice and text conversations while your code sup max-toc-depth: 3 --- +import { SigmondWidget } from "@/components/index"; + +SignalWire AI runs programmable voice agents on the same realtime platform that carries your calls. +Deploy a minimum viable product with no-code and low-code tools, then scale it with +[SWML](/docs/swml) or the [Server SDKs](/docs/server-sdks). + + + + + + + + + +## Quickstart + +Deploy a serverless AI agent and call it over the public switched telephone network (PSTN) in under 5 minutes. + + + +### Create a free account + +[Sign up for a SignalWire account](https://signalwire.com/signup), or log in if you already have one. + +### Create a SWML script + +From your [SignalWire Dashboard](https://my.signalwire.com), click **Script**, then **SWML script**. +Paste the following script into the Primary Script field, then select **Create**. + +This simple YAML/JSON document is a complete calling application! + +```yaml title="swml.yaml" +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: You are a knowledgeable developer. Have an open-ended discussion with the caller about SignalWire and programmable communications. +``` + +### Assign a phone number + +[Buy a phone number](https://my.signalwire.com?page=phone_numbers) using your $5 promotional credit. +Select the number from your **Phone Numbers** list and click **Edit Settings**. +Under **Inbound Call Settings**, choose **Assign Resource**, pick the SWML script you just created, +and click **Save**. + +### Give it a call + +Dial your newly configured AI agent over the PSTN from your cell phone or a VoIP dialer. + + + +## SWML + +SWML (SignalWire Markup Language) is a structured language for configuring and orchestrating +real-time communication applications using lightweight and readable JSON or YAML files. +It's the foundation for AI on the platform: the Server SDKs build agents by generating SWML, +and you can write it directly, deploying serverlessly in SignalWire's cloud or from your server. + +SWML's `ai` method integrates AI agents, which can interact with external APIs. + + + SWML AI method + + +## AI Agents + +Configure AI Agents right in your SignalWire Space with a streamlined, no-code user interface. + + + Getting started guide + + + +## Call Flow Builder + +Add AI Agents built in your SignalWire Space directly to drag-and-drop call flows. + + + Guide to the AI Agent node + + +## Server SDKs + +Build custom voice AI agents in the language of your choice. The SignalWire Server SDKs provide complete programmatic control for sophisticated voice applications. + + + + Build your first agent in 5 minutes + + + Progressive examples from simple to advanced + + + Agent archetypes to use directly or extend + + + +### Use cases + + + + +A basic AI-powered phone agent that can hold an open-ended conversation. + + + + +```python +from signalwire import AgentBase + +# Create an agent and assign a route +agent = AgentBase(name="My Assistant", route="/assistant") + +# Add some basic capabilities +agent.add_skill("datetime") # Current date/time info +agent.add_skill("math") # Mathematical calculations + +# Start the agent +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [Quickstart guide](/docs/server-sdks/guides/quickstart) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + post_prompt_url: https://example.com/my-post-prompt-url + params: + save_conversation: true + prompt: + text: | + You are a knowledgeable developer. + Have an open-ended discussion with the caller about SignalWire and programmable communications. +``` + +[SWML docs](/docs/swml) | [AI method reference](/docs/swml/reference/calling/ai) + + + + + + A simple AI Agent in the Call Flow Builder interface. + + +[Call Flow Builder docs](/docs/call-flow-builder) + + + + + + + +An AI agent that answers frequently asked questions about your business. + + + + +```python +from signalwire.prefabs import FAQBotAgent + +agent = FAQBotAgent( + faqs=[ + { + "question": "What are your hours?", + "answer": "We're open 9 AM to 5 PM, Monday to Friday." + }, + { + "question": "Where are you located?", + "answer": "123 Main Street, Downtown." + } + ] +) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [FAQ Bot prefab](/docs/server-sdks/guides/faq-bot) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are a helpful FAQ bot. Answer questions about our business using the available functions. + SWAIG: + defaults: + web_hook_url: https://example.com/faq-webhook + functions: + - function: search_faq + description: Search frequently asked questions + parameters: + type: object + properties: + query: + type: string + description: The question to search for +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +An agent that looks up customer accounts and transfers calls to human support when needed. + + + + +```python +from signalwire import AgentBase, FunctionResult + +agent = AgentBase(name="support") +agent.prompt_add_section("Role", "You are a helpful customer service agent.") + +@agent.tool(description="Look up customer account") +def lookup_account(account_id: str) -> FunctionResult: + # Simulate database lookup + customer = {"name": "John Doe", "status": "active"} + return FunctionResult(f"Account: {customer['name']}, Status: {customer['status']}") + +@agent.tool(description="Transfer to support") +def transfer_support() -> FunctionResult: + return FunctionResult("Connecting you to support.").connect("+15551234567") + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [Custom functions guide](/docs/server-sdks/guides/prompts-pom) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are a customer service agent. Help customers with their questions and account needs. + Use the available functions to look up information or transfer calls when needed. + SWAIG: + functions: + - function: lookup_account + description: Look up customer account information + parameters: + type: object + properties: + account_id: + type: string + description: Customer account ID + web_hook_url: https://example.com/account-lookup + - function: transfer_to_support + description: Transfer to human support agent + web_hook_url: https://example.com/transfer-support +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +A virtual concierge that helps guests with amenity information and service bookings. + + + + +```python +from signalwire.prefabs import ConciergeAgent + +agent = ConciergeAgent( + venue_name="Grand Hotel", + services=["room service", "spa bookings", "restaurant reservations"], + amenities={ + "pool": {"hours": "7 AM - 10 PM", "location": "2nd Floor"}, + "gym": {"hours": "24 hours", "location": "3rd Floor"} + } +) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [Concierge prefab](/docs/server-sdks/guides/concierge) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are a hotel concierge for Grand Hotel. Help guests with information about amenities, + services, and bookings. Use available functions to provide accurate information. + SWAIG: + functions: + - function: check_availability + description: Check availability for services + parameters: + type: object + properties: + service: + type: string + description: Service to check (spa, restaurant, etc.) + date: + type: string + description: Date for booking + web_hook_url: https://example.com/hotel-availability + - function: get_amenity_info + description: Get information about hotel amenities + parameters: + type: object + properties: + amenity: + type: string + description: Which amenity (pool, gym, spa, etc.) + web_hook_url: https://example.com/amenity-info +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +An agent that checks availability, books appointments, and sends SMS confirmations. + + + + +```python +from signalwire import AgentBase, FunctionResult +from datetime import datetime + +appointments = [] + +agent = AgentBase(name="scheduler", route="/scheduler") +agent.prompt_add_section("Role", "You help customers schedule appointments.") +agent.prompt_add_section("Guidelines", """ +- Collect customer name, date, and preferred time +- Confirm all details before booking +- Send SMS confirmation when booking is complete +""") +agent.add_language("English", "en-US", "rime.spore") + +@agent.tool(description="Check if a time slot is available") +def check_availability(date: str, time: str) -> FunctionResult: + for apt in appointments: + if apt["date"] == date and apt["time"] == time: + return FunctionResult(f"Sorry, {date} at {time} is not available.") + return FunctionResult(f"{date} at {time} is available.") + +@agent.tool(description="Book an appointment") +def book_appointment( + name: str, + phone: str, + date: str, + time: str +) -> FunctionResult: + appointments.append({ + "name": name, + "phone": phone, + "date": date, + "time": time, + "booked_at": datetime.now().isoformat() + }) + return ( + FunctionResult(f"Appointment booked for {name} on {date} at {time}.") + .send_sms( + to_number=phone, + from_number="+15559876543", + body=f"Your appointment is confirmed for {date} at {time}." + ) + ) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [InfoGatherer prefab](/docs/server-sdks/guides/info-gatherer) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are an appointment scheduling agent. Help customers book appointments, + check availability, and send confirmations. + SWAIG: + functions: + - function: check_availability + description: Check if a time slot is available + parameters: + type: object + properties: + date: + type: string + description: Date for appointment + time: + type: string + description: Preferred time + web_hook_url: https://example.com/check-availability + - function: book_appointment + description: Book an appointment + parameters: + type: object + properties: + name: + type: string + description: Customer name + phone: + type: string + description: Customer phone number + date: + type: string + description: Appointment date + time: + type: string + description: Appointment time + web_hook_url: https://example.com/book-appointment +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +An agent that conducts customer satisfaction surveys with different question types. + + + + +```python +from signalwire.prefabs import SurveyAgent + +agent = SurveyAgent( + survey_name="Customer Satisfaction Survey", + questions=[ + { + "id": "satisfaction", + "text": "How satisfied were you with our service?", + "type": "rating", + "scale": 5 + }, + { + "id": "recommend", + "text": "Would you recommend us to others?", + "type": "yes_no" + }, + { + "id": "comments", + "text": "Any additional comments?", + "type": "open_ended", + "required": False + } + ] +) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [Survey prefab](/docs/server-sdks/guides/survey) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are a survey agent. Conduct a customer satisfaction survey by asking + the provided questions and recording their responses. + SWAIG: + functions: + - function: record_response + description: Record survey response + parameters: + type: object + properties: + question_id: + type: string + description: ID of the question being answered + response: + type: string + description: The customer's response + web_hook_url: https://example.com/record-survey + - function: get_next_question + description: Get the next survey question + parameters: + type: object + properties: + current_id: + type: string + description: Current question ID + web_hook_url: https://example.com/next-question +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +A virtual receptionist that greets callers and routes them to the right department. + + + + +```python +from signalwire.prefabs import ReceptionistAgent + +agent = ReceptionistAgent( + departments=[ + { + "name": "sales", + "description": "Product inquiries, pricing, and purchasing", + "number": "+15551234567" + }, + { + "name": "support", + "description": "Technical help and troubleshooting", + "number": "+15551234568" + }, + { + "name": "billing", + "description": "Payment questions and account issues", + "number": "+15551234569" + } + ] +) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [Receptionist prefab](/docs/server-sdks/guides/receptionist) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are a receptionist. Greet callers and determine which department + they need based on their inquiry. Transfer them to the appropriate department. + SWAIG: + functions: + - function: route_to_department + description: Transfer caller to specific department + parameters: + type: object + properties: + department: + type: string + description: Target department (sales, support, billing) + web_hook_url: https://example.com/route-department + - function: get_department_info + description: Get information about available departments + parameters: + type: object + properties: + department: + type: string + description: Department name to lookup + web_hook_url: https://example.com/department-info +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +An agent that collects information from potential customers and qualifies them for the sales team. + + + + +```python +from signalwire.prefabs import InfoGathererAgent + +agent = InfoGathererAgent( + questions=[ + {"key_name": "name", "question_text": "What is your name?"}, + {"key_name": "company", "question_text": "What company are you with?"}, + {"key_name": "phone", "question_text": "What is your phone number?", "confirm": True}, + {"key_name": "budget", "question_text": "What is your budget range for this project?"}, + {"key_name": "timeline", "question_text": "What is your timeline for making a decision?"} + ], + name="lead-qualifier" +) + +agent.prompt_add_section( + "Role", + "You are qualifying leads for the sales team. Be friendly and professional." +) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [InfoGatherer prefab](/docs/server-sdks/guides/info-gatherer) + + + + +```yaml +version: 1.0.0 +sections: + main: + - ai: + prompt: + text: | + You are a lead qualification agent. Collect information from potential customers + and determine if they are qualified leads for our sales team. + SWAIG: + functions: + - function: collect_lead_info + description: Collect and qualify lead information + parameters: + type: object + properties: + name: + type: string + description: Contact name + company: + type: string + description: Company name + budget: + type: string + description: Project budget range + web_hook_url: https://example.com/lead-qualification + - function: schedule_followup + description: Schedule a follow-up call + parameters: + type: object + properties: + datetime: + type: string + description: When to schedule follow-up + web_hook_url: https://example.com/schedule-followup +``` + +[SWML docs](/docs/swml) | [SWAIG functions guide](/docs/swml/guides/swaig) + + + + + + + +An agent that records calls and provides real-time transcription. + + + + +```python +from signalwire import AgentBase, FunctionResult + +agent = AgentBase(name="transcription-agent") +agent.add_language("English", "en-US", "rime.spore") +agent.prompt_add_section("Role", "You are a helpful assistant. The call is being recorded for transcription.") +agent.set_params({"save_conversation": True}) + +@agent.tool(description="Start recording the call for transcription") +def start_recording() -> FunctionResult: + return ( + FunctionResult("Recording has started.") + .record_call( + control_id="transcription", + stereo=True, + format="wav" + ) + ) + +if __name__ == "__main__": + agent.run() +``` + +[Server SDK docs](/docs/server-sdks) | [Call recording guide](/docs/server-sdks/guides/call-recording) + + + + +```yaml +version: 1.0.0 +sections: + main: + - record_call: + stereo: true + format: "wav" + - ai: + prompt: + text: | + You are a transcription agent. Listen to the conversation and provide + real-time transcription of what is being said. + params: + save_conversation: true + attention_timeout: 30000 + SWAIG: + functions: + - function: get_transcript + description: Get current conversation transcript + parameters: + type: object + properties: + format: + type: string + description: Output format (text, json, etc.) + web_hook_url: https://example.com/get-transcript +``` + +[SWML docs](/docs/swml) | [Call recording guide](/docs/server-sdks/guides/call-recording) + + + + + + + + + + From beginner to expert level + + + Complete reference guide + + + +## How it works + +Think of the AI agent as the **front end** of the call. +Its prompt and voice handle the conversation: understanding the caller, collecting details, and speaking naturally. +Your backend remains the backend, owning the business logic: prices, lookups, bookings, and rules. + +Connecting the two is **SWAIG** (the SignalWire AI Gateway), which delivers the agent's tool calls +to your backend over HTTP. +The call itself never leaves SignalWire: the platform carries the audio, runs the speech models, +and holds the conversation state, so your code can run anywhere an HTTP endpoint can. +Because those models run inside the engine carrying the audio, no network hop sits between the +caller and the AI, which is what keeps replies quick and their timing steady from turn to turn. +[AI in the media path](/docs/platform/ai/capabilities#ai-in-the-media-path) walks through that +architecture. [addresses]: /docs/platform/addresses [analytics]: /docs/platform/ai/analytics [best-practices]: /docs/platform/ai/best-practices diff --git a/fern/snippets/common/talk-with-sigmond.mdx b/fern/snippets/common/talk-with-sigmond.mdx new file mode 100644 index 0000000000..5b8cf490b8 --- /dev/null +++ b/fern/snippets/common/talk-with-sigmond.mdx @@ -0,0 +1,12 @@ +
+
+ + Sigmond, the SignalWire AI agent + + + Call Sigmond + A voice and video AI agent built with the SignalWire SDKs, wired to a live knowledge base, and able to see what you show it. The same agent runs on voice, phone, WhatsApp, or SIP. + Start a video call + +
+