---
sidebar:
  hidden: true
title: roboto.ai.agent_thread.feedback
---
## Module Contents

### AdminUpdateFeedbackRequest

```python
class roboto.ai.agent_thread.feedback.AdminUpdateFeedbackRequest(/, **data: Any)
```

`from roboto.ai.agent_thread import AdminUpdateFeedbackRequest`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L121-L144)

Bases: `pydantic.BaseModel`

Triage fields editable by Roboto admins.

Fields omitted from the request (`NotSet`) are left unchanged. Fields explicitly set to `None` clear the column back to `NULL`. Fields set to a value overwrite the column.

Setting `resolved` to `True` additionally stamps `resolved_at` and `resolved_by` server-side; setting it back to `False` clears them.

**Parameters**

- **data** (`Any`)

**Attributes**

- **AdminUpdateFeedbackRequest.admin_label** (`str | None | roboto.sentinels.NotSetType`)
- **AdminUpdateFeedbackRequest.admin_note** (`str | None | roboto.sentinels.NotSetType`)
- **AdminUpdateFeedbackRequest.model_config**: Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- **AdminUpdateFeedbackRequest.resolved** (`bool | roboto.sentinels.NotSetType`)

### FeedbackCategory

```python
class roboto.ai.agent_thread.feedback.FeedbackCategory
```

`from roboto.ai.agent_thread import FeedbackCategory`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L23-L46)

Bases: `roboto.compat.StrEnum`

Taxonomy of feedback categories.

Which categories are valid depends on `sentiment` (see [`category_is_valid_for_sentiment()`](/reference/python-sdk/roboto/ai/agent_thread/feedback#roboto.ai.agent_thread.feedback.category_is_valid_for_sentiment)). `OTHER` is always permitted; when `OTHER` is among the selected categories, `notes` is required.

**Attributes**

- **FeedbackCategory.CORRECT** = `'correct'`
- **FeedbackCategory.FORMATTING** = `'formatting'`
- **FeedbackCategory.GOOD_TOOL_USE** = `'good_tool_use'`
- **FeedbackCategory.HELPFUL** = `'helpful'`
- **FeedbackCategory.INCOMPLETE** = `'incomplete'`
- **FeedbackCategory.INCORRECT** = `'incorrect'`
- **FeedbackCategory.OTHER** = `'other'`
- **FeedbackCategory.REFUSED_VALID_REQUEST** = `'refused_valid_request'`
- **FeedbackCategory.SLOW** = `'slow'`
- **FeedbackCategory.TOOL_FAILURE** = `'tool_failure'`
- **FeedbackCategory.UNSAFE** = `'unsafe'`

### FeedbackSentiment

```python
class roboto.ai.agent_thread.feedback.FeedbackSentiment
```

`from roboto.ai.agent_thread import FeedbackSentiment`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L16-L20)

Bases: `roboto.compat.StrEnum`

Overall rating direction for a piece of AI-chat feedback.

**Attributes**

- **FeedbackSentiment.NEGATIVE** = `'negative'`
- **FeedbackSentiment.POSITIVE** = `'positive'`

### NEGATIVE_CATEGORIES

```python
roboto.ai.agent_thread.feedback.NEGATIVE_CATEGORIES: frozenset[FeedbackCategory]
```

`from roboto.ai.agent_thread import NEGATIVE_CATEGORIES`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L49-L59)

### POSITIVE_CATEGORIES

```python
roboto.ai.agent_thread.feedback.POSITIVE_CATEGORIES: frozenset[FeedbackCategory]
```

`from roboto.ai.agent_thread import POSITIVE_CATEGORIES`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L61-L67)

### SubmitFeedbackRequest

```python
class roboto.ai.agent_thread.feedback.SubmitFeedbackRequest(/, **data: Any)
```

`from roboto.ai.agent_thread import SubmitFeedbackRequest`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L82-L118)

Bases: `pydantic.BaseModel`

Request body for submitting feedback on an assistant message.

One row per (session, message, user); resubmitting replaces the previous sentiment/categories/notes rather than adding a new row.

**Parameters**

- **data** (`Any`)

**Attributes**

- **SubmitFeedbackRequest.categories** (`list[FeedbackCategory]`) = `None`: Categories describing what was good or bad. Semantically a *set*: duplicates are dropped and the persisted order is enum-value sort, not request order.
- **SubmitFeedbackRequest.notes** (`str | None`) = `None`: Free-text notes. Whitespace-only input is normalised to `None` before persistence. Required when `FeedbackCategory.OTHER` is selected.
- **SubmitFeedbackRequest.sentiment** (`FeedbackSentiment`): Overall rating direction.

### UserFeedbackRecord

```python
class roboto.ai.agent_thread.feedback.UserFeedbackRecord(/, **data: Any)
```

`from roboto.ai.agent_thread import UserFeedbackRecord`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L206-L267)

Bases: `pydantic.BaseModel`

Customer-facing projection of `AgentFeedbackRecord`.

Excludes every admin-only column (`admin_label`, `admin_note`, `resolved`, `resolved_by`, `resolved_at`) so routes that serve an end user never return internal triage state. Use this as the return type of any non-admin endpoint that surfaces feedback, including the response to the submitter's own submit call.

The admin endpoints continue to return `AgentFeedbackRecord` directly.

**Parameters**

- **data** (`Any`)

**Attributes**

- **UserFeedbackRecord.categories** (`list[FeedbackCategory]`) = `None`: Categories describing the feedback. May be empty.
- **UserFeedbackRecord.created** (`datetime.datetime`): When this feedback was first submitted.
- **UserFeedbackRecord.created_by** (`str`): User id of the submitter.
- **UserFeedbackRecord.feedback_id** (`str`): Unique identifier for this feedback entry.
- **UserFeedbackRecord.message_sequence_num** (`int`): Zero-indexed position of the assistant message within the session.
- **UserFeedbackRecord.modified** (`datetime.datetime`): When the submitter last updated this feedback.
- **UserFeedbackRecord.modified_by** (`str`): User id of the submitter's last edit.
- **UserFeedbackRecord.notes** (`str | None`) = `None`: Free-text notes from the submitter, if any.
- **UserFeedbackRecord.org_id** (`str`): Org the session belonged to at the time of submission.
- **UserFeedbackRecord.sentiment** (`FeedbackSentiment`): Overall rating direction.
- **UserFeedbackRecord.thread_id** (`str`): Session the feedback was submitted against.

#### UserFeedbackRecord.from_admin_record()

```python
@classmethod
def from_admin_record(record: AgentFeedbackRecord) -> UserFeedbackRecord
```

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L253-L267)

Project an `AgentFeedbackRecord` down to the user-facing shape.

Use at the boundary of any non-admin route that materialises a full admin record from persistence — the projection guarantees no admin triage column accidentally escapes to a customer response.

The projection is driven by `cls.model_fields` rather than a hand list of columns: a new submitter-controlled column added to both records flows through automatically, and a new admin-only column on `AgentFeedbackRecord` is silently dropped here (which is what we want for privacy). The matching test asserts the admin-only field set has not drifted unexpectedly.

**Parameters**

- **record** (`AgentFeedbackRecord`)

**Returns**

- `UserFeedbackRecord`

### category_is_valid_for_sentiment()

```python
def roboto.ai.agent_thread.feedback.category_is_valid_for_sentiment(
    category: FeedbackCategory,
    sentiment: FeedbackSentiment,
) -> bool
```

`from roboto.ai.agent_thread import category_is_valid_for_sentiment`

[Source](https://github.com/roboto-ai/roboto-python-sdk/blob/main/src/roboto/ai/agent_thread/feedback.py#L70-L79)

Report whether `category` is a permitted choice under `sentiment`.

`FeedbackCategory.OTHER` is always permitted.

**Parameters**

- **category** (`FeedbackCategory`)
- **sentiment** (`FeedbackSentiment`)

**Returns**

- `bool`
