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


export const FIELD_SIG_PY = `guava.Field(
    # Identifier used to retrieve the value via get_field() after collection.
    key: str,

    # Natural-language instruction to the LLM about how to collect this value.
    # Use when you do not particularly care how the agent phrases its question.
    description: str = '',

    # Encourages the agent to ask for the field in a particular way. Use instead
    # of description when you want more control over the phrasing.
    question: str = '',

    # Controls parsing and validation. "calendar_slot" and "multiple_choice"
    # require either choices or searchable=True.
    field_type: Literal[
        'text', 'date', 'datetime', 'integer', 'multiple_choice', 'calendar_slot'
    ] = 'text',

    # If False, the agent can skip this field if the caller is unwilling to provide it.
    required: bool = True,

    # Static list of valid options for "calendar_slot" and "multiple_choice" fields.
    # Use when the list is small. Large lists should use searchable=True.
    choices: list[str] = [],

    # When True, enables dynamic search for "multiple_choice" and "calendar_slot"
    # fields. The agent searches for options matching the caller's query at runtime.
    searchable: bool = False,
)`;

export const FIELD_SIG_TS = `guava.Field({
  // Identifier used to retrieve the value via get_field() after collection.
  key: string,

  // Natural-language instruction to the LLM about how to collect this value.
  // Use when you do not particularly care how the agent phrases its question.
  description: string,

  // Controls parsing and validation. "calendar_slot" and "multiple_choice"
  // require either choices or choiceGenerator.
  fieldType: 'text' | 'date' | 'datetime' | 'integer' | 'multiple_choice' | 'calendar_slot',

  // If false, the agent can skip this field if the caller is unwilling to provide it.
  required?: boolean, // default: true

  // Static list of valid options for "calendar_slot" and "multiple_choice" fields.
  // Use when the list is small. Large lists should use choiceGenerator.
  choices?: string[], // default: []

  // Takes a query string and returns (matching, fallback) lists. Use for large
  // or dynamic option sets with "calendar_slot" and "multiple_choice".
  choiceGenerator?: ChoiceGenerator,

  // When true, enables dynamic search for "multiple_choice" and "calendar_slot"
  // fields. The agent searches for options matching the caller's query at runtime.
  searchable?: boolean, // default: false
})`;

## Field

A `Field` is a [Task](./tasks) checklist item instructing the Guava agent to collect structured data from the caller. The agent elicits the value through natural conversation, validates it against the specified type, and marks the checklist item complete when satisfied.


<CodeTabs
  python={{ code: FIELD_SIG_PY, filename: "signature" }}
  typescript={{ code: FIELD_SIG_TS, filename: "signature" }}
/>


### Basic Examples

export const FIELD_EX1_PY = `# Basic text field
field = guava.Field(
    key="caller_name",
    description="Get the caller's name",
)

# Integer field with question
field = guava.Field(
    key="caller_age",
    question="How old are you?",
    field_type="integer",
)

# Multiple choice with static choices
field = guava.Field(
    key="caller_preference",
    description="Get the caller's preferred fruit",
    field_type="multiple_choice",
    # Use searchable=True instead when there's a large number of choices
    choices=["apple", "banana", "orange"],
    required=False,
)`;

export const FIELD_EX1_TS = `// Basic text field
const field = guava.Field({
  key: "caller_name",
  description: "Get the caller's name",
});

// Integer field with question
const field = guava.Field({
  key: "caller_age",
  description: "How old are you?",
  fieldType: "integer",
});

// Multiple choice with static choices
const field = guava.Field({
  key: "caller_preference",
  description: "Get the caller's preferred fruit",
  fieldType: "multiple_choice",
  // Use searchable: true instead when there's a large number of choices
  choices: ["apple", "banana", "orange"],
  required: false,
});`;

<CodeTabs
  python={{ code: FIELD_EX1_PY, filename: "examples.py" }}
  typescript={{ code: FIELD_EX1_TS, filename: "examples.ts" }}
/>

### Search Fields

Some fields can have a very large set of valid options.
For example, a `destination_airport` field may include thousands of airports worldwide.
In other cases, options must be generated dynamically, such as an `appointment_time` field populated from a booking system.

This is where search fields come in handy. Set `searchable=True` on the field, then register an `@agent.on_search_query` handler.
When the agent needs options, it calls your handler with a natural-language query string.
Return two lists: a primary list of matches, and a fallback list shown only when no primary matches are found.

export const FIELD_EX4_PY = `field = guava.Field(
    key="airport",
    description="Find a suitable airport for the caller",
    field_type="multiple_choice",
    searchable=True,
)

@agent.on_search_query("airport")
def search_airports(call: guava.Call, query: str):
    matching_airports: list[str] = []
    other_airports: list[str] = []

    ...
    # Do some work to generate a few matching airport
    # options based on the caller's query.
    # 'query' will be human natural language
    # (e.g. "I need to fly out of an airport in
    # southern california")
    ...

    # The second list only becomes relevant if there
    # are no matches to the caller's query. It is used
    # to at least present something to the caller in
    # case there are no perfect matches.
    return matching_airports, other_airports`;

export const FIELD_EX4_TS = `const field = guava.Field({
  key: "airport",
  description: "Find a suitable airport for the caller",
  fieldType: "multiple_choice",
  searchable: true,
});

agent.onSearchQuery("airport", async (call, query) => {
  const matchingAirports: string[] = [];
  const otherAirports: string[] = [];

  // ...
  // Do some work to generate a few matching airport
  // options based on the caller's query.
  // 'query' will be human natural language
  // (e.g. "I need to fly out of an airport in
  // southern california")
  // ...

  // The second list only becomes relevant if there
  // are no matches to the caller's query. It is used
  // to at least present something to the caller in
  // case there are no perfect matches.
  return [matchingAirports, otherAirports];
})`;

<CodeTabs
  python={{ code: FIELD_EX4_PY, filename: "search_field.py" }}
  typescript={{ code: FIELD_EX4_TS, filename: "search_field.ts" }}
/>

### Field Types Reference

| Type | Example collected value | Return type from `get_field()` |
|------|------------------------|-------------------------------|
| `text` | `"I want to cancel my appointment"` | `str` |
| `date` | `{"year": 2024, "month": 3, "day": 15}` | `dict` with keys `year`, `month`, `day` (all `int`) |
| `integer` | `42` | `int` |
| `multiple_choice` | `"apple"` | `str` (guaranteed to be one of `choices` or returned by `choice_generator`) |
| `calendar_slot` | `"2022-12-31T17:30"` | ISO-8601 datetime `str` |

<Callout>
  <span className="text-primary font-semibold">Note:</span> The `choices` list for `calendar_slot` fields must be ISO-8601 datetimes (e.g. `"2022-12-31T17:30"`).
</Callout>

<NextLink section="say" label="Say" />
