# Generate the complete OpenAPI document
open_api = construct_base_open_api()
open_api = construct_open_api_with_schema_class(open_api)

if __name__ == "__main__":
    with open("openapi.yaml", "w") as file:
        file.write(
            yaml.dump(
                open_api.model_dump(
                    by_alias=True, mode="json", exclude_none=True, exclude_unset=True,
                ),
                sort_keys=False,
            )
        )
```

This code defines:

1. Response models that wrap our Pydantic models (like `PetsResponse`) for consistent API responses
2. A function that builds the OpenAPI document with four endpoints:
   - `GET /pets`: Lists all pets
   - `POST /pets`: Creates a new pet
   - `GET /pets/{pet_id}`: Gets a pet by ID
   - `GET /owners`: Lists all owners
3. Each endpoint includes:
   - An `operationId` for SDK generation
   - A description of what the endpoint does
   - Request parameters/body (where applicable)
   - Response schemas that reference our Pydantic models

When run, this generates an `openapi.yaml` file with the full API specification:

```yaml filename="openapi.yaml"
openapi: 3.1.0
info:
  title: Pet Sitter API
  version: 0.0.1
servers:
  - url: http://127.0.0.1:4010
    description: Local prism server
paths:
  /pets:
    get:
      description: List all pets
      operationId: listPets
      responses:
        "200":
          description: A list of pets
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PetsResponse"
    post:
      description: Create a pet
      operationId: createPet
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Pet"
      responses:
        "201":
          description: Pet created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
  /pets/{pet_id}:
    get:
      description: Get a pet by ID
      operationId: getPetById
      parameters:
        - name: pet_id
          in: path
          description: ID of pet to return
          required: true
          schema:
            type: integer
            format: int64
          examples:
            "1":
              value: 1
      responses:
        "200":
          description: A pet
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Pet"
  /owners:
    get:
      description: List all owners
      operationId: listOwners
      responses:
        "200":
          description: A list of owners
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OwnersResponse"
components:
  schemas:
    # Schemas for our models are included here
    # (Pet, Owner, PetType, PetsResponse, OwnersResponse)
```

The generated OpenAPI document includes all the components from our Pydantic models, along with the API endpoints we defined. The schemas include all the titles, descriptions, examples, and other details we added to our Pydantic models.

## Generating an SDK from the OpenAPI document

Now that we have a complete OpenAPI document with paths and operations, we can use it to generate an SDK client for our API.

### Prerequisites for SDK generation

Install Speakeasy by following the [Speakeasy installation instructions](/docs/speakeasy-reference/cli/getting-started#install)

On macOS, you can install Speakeasy using Homebrew:

```bash filename="Terminal"
brew install speakeasy-api/tap/speakeasy
```

Authenticate with Speakeasy using the following command:

```bash filename="Terminal"
speakeasy auth login
```

### Generating an SDK using Speakeasy

Run the following command to generate an SDK from the `openapi.yaml` file:

```bash filename="Terminal"
speakeasy quickstart
```

Follow the onscreen prompts to provide the necessary configuration details for your new SDK, such as the name, schema location, and output path. Enter `openapi.yaml` when prompted for the OpenAPI document location and select TypeScript when prompted for which language you would like to generate.

Speakeasy [validates](/docs/sdks/core-concepts#validation) the OpenAPI document to check that it's ready for code generation. Validation issues will be printed in the terminal. The generated SDK will be saved as a folder in your project.

![Speakeasy quickstart command output](/assets/openapi/speakeasy-quickstart-output.png)

Speakeasy also suggests improvements for your SDK using [Speakeasy Suggest](/docs/prep-openapi/maintenance), which is an AI-powered tool in Speakeasy Studio. You can view the suggestions in Speakeasy Studio:

![Speakeasy Studio suggestions](/assets/openapi/hono/speakeasy-studio-suggestions.png)

### Adding Speakeasy extensions to the OpenAPI document

Speakeasy uses [OpenAPI extensions](/openapi/extensions) to provide additional information for generating SDKs.

We can add extensions using [OpenAPI overlays](/openapi/overlays), which are YAML files that [Speakeasy lays on top of the OpenAPI document](/docs/prep-openapi/overlays/create-overlays).

We can use overlays alongside [OpenAPI transformations](/docs/prep-openapi/transformations) to improve the OpenAPI document for SDK generation.

Transformations are predefined functions that allow you to remove unused components, filter operations, and format your OpenAPI document. Unlike overlays, transformations directly modify the OpenAPI document itself.

Note that for Speakeasy OpenAPI extensions, you can also add extensions directly to the OpenAPI document using the `x-` prefix.

For example, you can add the [`x-speakeasy-retries`](/docs/customize/runtime/retries) extension to have Speakeasy generate retry logic in the SDK.

Import the `Dict` and `Any` types from the `typing` module in `api.py`, and `ConfigDict` from `pydantic`.

We'll use these types to define the `x-speakeasy-retries` extension in the OpenAPI document.

```python filename="api.py" 
from typing import List, Dict, Any
import yaml
from pydantic import BaseModel, Field, ConfigDict
from openapi_pydantic.v3 import OpenAPI, Info, PathItem, Operation
from openapi_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
from models import Pet, Owner