openai/chatkit-python

Public

mirrored from https://github.com/openai/chatkit-pythonAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2e1209a2c4f4957e7f783eceeb79655c9e38d036

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

docs/guides/add-features/create-custom-forms.md

69lines · modepreview

# Create custom forms

Wrap widgets that collect user input in a `Form` to have their values automatically injected into every action triggered inside that form. The form values arrive in the action payload, keyed by each field’s `name`.

- `<Select name="title" />` → `action.payload["title"]`
- `<Select name="todo.title" />` → `action.payload["todo"]["title"]`

```jsx
<Form
  direction="col"
  onSubmitAction={{
    type: "update_todo",
    payload: { id: todo.id },
  }}
>
  <Title value="Edit Todo" />
  <Text value="Title" color="secondary" size="sm" />
  <Text
    value={todo.title}
    editable={{ name: "title", required: true }}
  />
  <Text value="Description" color="secondary" size="sm" />
  <Text
    value={todo.description}
    editable={{ name: "description" }}
  />
  <Button label="Save" submit />
</Form>
```

On the server, read the form values from the action payload. Any action originating from inside the form will include the latest field values.

```python
from collections.abc import AsyncIterator
from chatkit.server import ChatKitServer
from chatkit.types import Action, ThreadMetadata, ThreadStreamEvent, WidgetItem


class MyChatKitServer(ChatKitServer[RequestContext]):
    async def action(
        self,
        thread: ThreadMetadata,
        action: Action[str, Any],
        sender: WidgetItem | None,
        context: RequestContext,
    ) -> AsyncIterator[ThreadStreamEvent]:
        if action.type == "update_todo":
            todo_id = action.payload["id"]
            # Any action that originates from within the Form will
            # include title and description
            title = action.payload["title"]
            description = action.payload["description"]

            # ...
```

### Validation

`Form` uses basic native form validation; it enforces `required` and `pattern` on configured fields and blocks submission when any field is invalid.

We may add new validation modes with better UX, more expressive validation, and custom error display. Until then, widgets are not a great medium for complex forms with tricky validation. If you need this, a better pattern is to use client-side action handling to trigger a modal, show a custom form there, then pass the result back into ChatKit with `sendCustomAction`.

### Treating `Card` as a `Form`

You can pass `asForm=True` to `Card` and it will behave as a `Form`, running validation and passing collected fields to the Card’s `confirm` action.

### Payload key collisions

If there is a naming collision with some other existing pre-defined key on your payload, the form value will be ignored. This is probably a bug, so we’ll emit an `error` event when we see this.