openai/chatkit-python
Publicmirrored from https://github.com/openai/chatkit-pythonAvailable
docs/guides/add-features/create-custom-forms.md
69lines · modecode
| 1 | # Create custom forms |
| 2 | |
| 3 | 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`. |
| 4 | |
| 5 | - `<Select name="title" />` → `action.payload["title"]` |
| 6 | - `<Select name="todo.title" />` → `action.payload["todo"]["title"]` |
| 7 | |
| 8 | ```jsx |
| 9 | <Form |
| 10 | direction="col" |
| 11 | onSubmitAction={{ |
| 12 | type: "update_todo", |
| 13 | payload: { id: todo.id }, |
| 14 | }} |
| 15 | > |
| 16 | <Title value="Edit Todo" /> |
| 17 | <Text value="Title" color="secondary" size="sm" /> |
| 18 | <Text |
| 19 | value={todo.title} |
| 20 | editable={{ name: "title", required: true }} |
| 21 | /> |
| 22 | <Text value="Description" color="secondary" size="sm" /> |
| 23 | <Text |
| 24 | value={todo.description} |
| 25 | editable={{ name: "description" }} |
| 26 | /> |
| 27 | <Button label="Save" submit /> |
| 28 | </Form> |
| 29 | ``` |
| 30 | |
| 31 | On the server, read the form values from the action payload. Any action originating from inside the form will include the latest field values. |
| 32 | |
| 33 | ```python |
| 34 | from collections.abc import AsyncIterator |
| 35 | from chatkit.server import ChatKitServer |
| 36 | from chatkit.types import Action, ThreadMetadata, ThreadStreamEvent, WidgetItem |
| 37 | |
| 38 | |
| 39 | class MyChatKitServer(ChatKitServer[RequestContext]): |
| 40 | async def action( |
| 41 | self, |
| 42 | thread: ThreadMetadata, |
| 43 | action: Action[str, Any], |
| 44 | sender: WidgetItem | None, |
| 45 | context: RequestContext, |
| 46 | ) -> AsyncIterator[ThreadStreamEvent]: |
| 47 | if action.type == "update_todo": |
| 48 | todo_id = action.payload["id"] |
| 49 | # Any action that originates from within the Form will |
| 50 | # include title and description |
| 51 | title = action.payload["title"] |
| 52 | description = action.payload["description"] |
| 53 | |
| 54 | # ... |
| 55 | ``` |
| 56 | |
| 57 | ### Validation |
| 58 | |
| 59 | `Form` uses basic native form validation; it enforces `required` and `pattern` on configured fields and blocks submission when any field is invalid. |
| 60 | |
| 61 | 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`. |
| 62 | |
| 63 | ### Treating `Card` as a `Form` |
| 64 | |
| 65 | 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. |
| 66 | |
| 67 | ### Payload key collisions |
| 68 | |
| 69 | 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. |
| 70 | |