import { CodeTabs } from '../views/docs/CodeTabs';
import { Callout, Prose, NextLink } from '../views/docs/prose';

## Inbound w/ Form Filling

In this example, we'll build an inbound voice agent for a fictional restaurant. Callers can join the waitlist by providing their name, party size, and a callback number — which the agent collects conversationally using a structured task.

### Define the Agent

`guava.Agent` is our starting point for building Guava agents. We'll create one with a name and purpose scoped to the restaurant.

export const AGENT_PY = `import guava

agent = guava.Agent(
    name="Mia",
    organization="Thai Palace",
    purpose="Helping callers join the restaurant waitlist",
)`;

export const AGENT_TS = `import * as guava from "@guava-ai/guava-sdk";

const agent = new guava.Agent({
  name: "Mia",
  organization: "Thai Palace",
  purpose: "Helping callers join the restaurant waitlist",
});`;

<CodeTabs
  python={{ code: AGENT_PY, filename: "restaurant_waitlist.py" }}
  typescript={{ code: AGENT_TS, filename: "restaurant-waitlist.ts" }}
/>

### Accept or reject the call

`on_call_received` fires before the call starts and gives you a chance to accept or reject based on caller info. Here we accept all calls.

export const ACCEPT_PY = `@agent.on_call_received
def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
    return guava.AcceptCall()`;

export const ACCEPT_TS = `agent.onCallReceived(async (_callInfo: guava.CallInfo) => {
  return { action: "accept" };
});`;

<CodeTabs
  python={{ code: ACCEPT_PY, filename: "restaurant_waitlist.py" }}
  typescript={{ code: ACCEPT_TS, filename: "restaurant-waitlist.ts" }}
/>

<Callout>
  If you don't register <code>on_call_received</code>, Guava accepts all calls by default. Implement it only when you need to screen callers or look up information based off the incoming phone number.
</Callout>

### Set up the form

`on_call_start` fires at the beginning of every accepted call. We use `set_task` to hand the agent a structured checklist of fields to collect. The agent gathers each piece of information conversationally — it knows when all fields are filled and automatically moves on.

export const TASK_PY = `@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
    call.set_task(
        "waitlist",
        objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
        checklist=[
            guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
            guava.Field(key="party_size", field_type="integer", description="Number of people"),
            guava.Field(
                key="phone_number",
                field_type="text",
                description="Phone number to text when the table is ready",
            ),
            "Read the phone number back to the caller to confirm.",
        ],
    )`;

export const TASK_TS = `agent.onCallStart(async (call: guava.Call) => {
  await call.setTask({
    taskId: "waitlist",
    objective: "You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
    checklist: [
      guava.Field({ key: "caller_name", fieldType: "text", description: "Name for the waitlist" }),
      guava.Field({ key: "party_size", fieldType: "integer", description: "Number of people" }),
      guava.Field({
        key: "phone_number",
        fieldType: "text",
        description: "Phone number to text when the table is ready",
      }),
      "Read the phone number back to the caller to confirm.",
    ],
  });
});`;

<CodeTabs
  python={{ code: TASK_PY, filename: "restaurant_waitlist.py" }}
  typescript={{ code: TASK_TS, filename: "restaurant-waitlist.ts" }}
/>

<Callout>
  The checklist can mix <code>Field</code> objects (typed, named values the agent extracts) with plain strings (freeform instructions the agent follows). Fields are retrievable later via <code>get_field()</code>.
</Callout>

### Handle task completion

`on_task_complete` fires once every field in the checklist is collected. This is the right place to save the data to your backend, trigger a notification, or hang up.

export const COMPLETE_PY = `@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call) -> None:
    logger.info(
        "Added %s, party of %d, to waitlist.",
        call.get_field("caller_name"),
        call.get_field("party_size"),
    )
    call.hangup("Thank the caller and let them know we'll text when their table is ready.")`;

export const COMPLETE_TS = `agent.onTaskComplete("waitlist", async (call: guava.Call) => {
  logger.info(
    "Added %s, party of %d, to waitlist.",
    await call.getField("caller_name"),
    await call.getField("party_size"),
  );
  await call.hangup("Thank the caller and let them know we'll text when their table is ready.");
});`;

<CodeTabs
  python={{ code: COMPLETE_PY, filename: "restaurant_waitlist.py" }}
  typescript={{ code: COMPLETE_TS, filename: "restaurant-waitlist.ts" }}
/>

### Start the agent

Attach the agent to a phone number to start receiving inbound calls.

export const RUN_PY = `# Run this to attach your agent to a phone number. Call your agent's number to talk to it.
agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])

# Run this to receive a WebRTC link where you can talk to your agent in the browser.
agent.listen_webrtc()

# Run this to talk to your agent using your local audio device.
agent.call_local()`;

export const RUN_TS = `// Run this to attach your agent to a phone number. Call your agent's number to talk to it.
agent.listenPhone(process.env.GUAVA_AGENT_NUMBER!);`;

<CodeTabs
  python={{ code: RUN_PY, filename: "restaurant_waitlist.py" }}
  typescript={{ code: RUN_TS, filename: "restaurant-waitlist.ts" }}
/>

### Complete example

export const FULL_PY = `import os
import guava
import logging
import argparse
from guava import logging_utils

logger = logging.getLogger("thai_palace")

agent = guava.Agent(
    name="Mia",
    organization="Thai Palace",
    purpose="Helping callers join the restaurant waitlist",
)


@agent.on_call_received
def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
    return guava.AcceptCall()


@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
    call.set_task(
        "waitlist",
        objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
        checklist=[
            guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
            guava.Field(key="party_size", field_type="integer", description="Number of people"),
            guava.Field(
                key="phone_number",
                field_type="text",
                description="Phone number to text when the table is ready",
            ),
            "Read the phone number back to the caller to confirm.",
        ],
    )


@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call) -> None:
    logger.info(
        "Added %s, party of %d, to waitlist.",
        call.get_field("caller_name"),
        call.get_field("party_size"),
    )
    call.hangup("Thank the caller and let them know we'll text when their table is ready.")


if __name__ == "__main__":
    logging_utils.configure_logging()

    parser = argparse.ArgumentParser()
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("--phone", action="store_true", help="Listen for phone calls.")
    group.add_argument("--webrtc", action="store_true", help="Create on a WebRTC code.")
    group.add_argument("--local", action="store_true", help="Start a local call.")
    args = parser.parse_args()

    if args.phone:
        agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])
    elif args.webrtc:
        agent.listen_webrtc()
    else:
        agent.call_local()`;

export const FULL_TS = `import * as guava from "@guava-ai/guava-sdk";
import { getDefaultLogger } from "@guava-ai/guava-sdk";

const logger = getDefaultLogger();

const agent = new guava.Agent({
  name: "Mia",
  organization: "Thai Palace",
  purpose: "Helping callers join the restaurant waitlist",
});

agent.onCallReceived(async (_callInfo: guava.CallInfo) => {
  return { action: "accept" };
});

agent.onCallStart(async (call: guava.Call) => {
  await call.setTask({
    taskId: "waitlist",
    objective: "You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
    checklist: [
      guava.Field({ key: "caller_name", fieldType: "text", description: "Name for the waitlist" }),
      guava.Field({ key: "party_size", fieldType: "integer", description: "Number of people" }),
      guava.Field({
        key: "phone_number",
        fieldType: "text",
        description: "Phone number to text when the table is ready",
      }),
      "Read the phone number back to the caller to confirm.",
    ],
  });
});

agent.onTaskComplete("waitlist", async (call: guava.Call) => {
  logger.info(
    "Added %s, party of %d, to waitlist.",
    await call.getField("caller_name"),
    await call.getField("party_size"),
  );
  await call.hangup("Thank the caller and let them know we'll text when their table is ready.");
});

agent.listenPhone(process.env.GUAVA_AGENT_NUMBER!);`;

<CodeTabs
  python={{ code: FULL_PY, filename: "restaurant_waitlist.py" }}
  typescript={{ code: FULL_TS, filename: "restaurant-waitlist.ts" }}
/>
