> ## Documentation Index
> Fetch the complete documentation index at: https://docs.transluce.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Labels, General Labels, Tags & Comments

> Annotate Docent objects with structured labels, tags, and comments

Docent provides several annotation mechanisms:

* **Label sets** — structured annotations on agent runs, validated against a JSON schema
* **General label sets** — structured annotations on agent runs, transcripts, transcript slices, or reading results
* **Tags** — lightweight string annotations on agent runs
* **Comments** — free-text notes on agent runs

See [Labeling Agent Runs](/analysis/labeling) for a tutorial.

## Label Sets

### Create a Label Set

Define a new label set with a JSON schema that all labels must conform to.

```python theme={null}
from docent import Docent

client = Docent()

label_set_id = client.create_label_set(
    "my-collection-id",
    name="Quality Assessment",
    description="Human quality ratings for agent responses",
    label_schema={
        "type": "object",
        "properties": {
            "quality": {"type": "string", "enum": ["good", "acceptable", "poor"]},
            "notes": {"type": "string"},
        },
        "required": ["quality"],
    },
)
```

#### Parameters

<ParamField body="collection_id" type="str" required>
  ID of the collection.
</ParamField>

<ParamField body="name" type="str" required>
  Display name for the label set.
</ParamField>

<ParamField body="label_schema" type="dict" required>
  JSON schema for validating labels. Must be a valid JSON Schema object.
</ParamField>

<ParamField body="description" type="str | None">
  Optional description.
</ParamField>

#### Returns

<ResponseField name="label_set_id" type="str">
  ID of the created label set.
</ResponseField>

### List Label Sets

```python theme={null}
label_sets = client.get_label_sets("my-collection-id")
for ls in label_sets:
    print(f"{ls['id']}: {ls['name']}")
```

### Add a Label

```python theme={null}
from docent.data_models.judge import Label

label = Label(
    label_set_id=label_set_id,
    agent_run_id="run-id-123",
    label_value={"quality": "good", "notes": "Clear and helpful response"},
)
result = client.add_label("my-collection-id", label)
```

### Add Multiple Labels

```python theme={null}
labels = [
    Label(
        label_set_id=label_set_id,
        agent_run_id=run_id,
        label_value={"quality": "good"},
    )
    for run_id in run_ids
]
result = client.add_labels("my-collection-id", labels)
```

### Get Labels

```python theme={null}
labels = client.get_labels("my-collection-id", label_set_id)

# Only get labels that pass schema validation (including required fields)
valid_labels = client.get_labels(
    "my-collection-id",
    label_set_id,
    filter_valid_labels=True,
)
```

#### Parameters

<ParamField body="collection_id" type="str" required>
  ID of the collection.
</ParamField>

<ParamField body="label_set_id" type="str" required>
  ID of the label set.
</ParamField>

<ParamField body="filter_valid_labels" type="bool" default="False">
  If `True`, only return labels that fully match the label set schema including
  required fields. Default returns all labels.
</ParamField>

### Update a Label

Update an existing label's `label_value`. The server validates the updated value against
the label set schema, but does not enforce top-level `required` fields for regular labels.
It also verifies that the label belongs to the given label set.

```python theme={null}
updated = client.update_label(
    "my-collection-id",
    label_set_id,
    label_id="label-123",
    label_value={"quality": "acceptable", "notes": "Resolved after review"},
)
print(updated["id"])
```

#### Parameters

<ParamField body="collection_id" type="str" required>
  ID of the collection.
</ParamField>

<ParamField body="label_set_id" type="str" required>
  ID of the label set that owns the label.
</ParamField>

<ParamField body="label_id" type="str" required>
  ID of the label to update.
</ParamField>

<ParamField body="label_value" type="dict" required>
  New label value. It must conform to the label set's JSON schema validation rules,
  except top-level `required` fields are not enforced for regular labels.
</ParamField>

#### Returns

<ResponseField name="label" type="dict">
  Updated label object.
</ResponseField>

### Delete Labels

Preview or delete specific labels from a label set. Deletion defaults to a dry run so
you can inspect the returned labels before mutating data.

```python theme={null}
preview = client.delete_labels(
    "my-collection-id",
    label_set_id,
    ["label-123", "label-456"],
)

for label in preview.labels:
    print(label.id, label.label_value)

# Perform the deletion after reviewing the preview.
result = client.delete_labels(
    "my-collection-id",
    label_set_id,
    ["label-123", "label-456"],
    dry_run=False,
)
print(result.message)
```

#### Parameters

<ParamField body="collection_id" type="str" required>
  ID of the collection.
</ParamField>

<ParamField body="label_set_id" type="str" required>
  ID of the label set that owns all labels being deleted.
</ParamField>

<ParamField body="label_ids" type="list[str]" required>
  Non-empty list of unique label IDs.
</ParamField>

<ParamField body="dry_run" type="bool" default="True">
  If `True`, return the labels that would be deleted without deleting them.
  Pass `False` to perform the deletion.
</ParamField>

#### Returns

<ResponseField name="result" type="DeleteLabelsResult">
  Structured deletion preview or result.

  <Expandable title="DeleteLabelsResult fields">
    <ResponseField name="dry_run" type="bool">Whether this call was a dry run.</ResponseField>
    <ResponseField name="deleted" type="bool">Whether labels were actually deleted.</ResponseField>
    <ResponseField name="collection_id" type="str">Collection ID.</ResponseField>
    <ResponseField name="label_set_id" type="str">Label set ID.</ResponseField>
    <ResponseField name="label_ids" type="list[str]">Requested label IDs.</ResponseField>
    <ResponseField name="count" type="int">Number of matched labels.</ResponseField>
    <ResponseField name="labels" type="list[DeletedLabelPreview]">Preview records for the matched labels.</ResponseField>
    <ResponseField name="message" type="str">Server message describing the result.</ResponseField>
  </Expandable>
</ResponseField>

`DeleteLabelsResult` and `DeletedLabelPreview` are exported from `docent.sdk` for typing:

```python theme={null}
from docent.sdk import DeletedLabelPreview, DeleteLabelsResult
```

`DeletedLabelPreview` records include:

<ResponseField name="id" type="str">Label ID.</ResponseField>
<ResponseField name="label_set_id" type="str">Label set ID.</ResponseField>
<ResponseField name="label_value" type="dict">Stored label value.</ResponseField>
<ResponseField name="agent_run_id" type="str | None">Agent run ID for regular labels.</ResponseField>
<ResponseField name="target" type="LabelTarget | None">General-label target, when deleting general labels.</ResponseField>

#### Errors

* **`ValueError`** — `label_ids` is empty or contains duplicates
* **`HTTPError (404)`** — Collection, label set, or label not found

***

## General Labels

General labels use the same schema validation model as label sets, but they can
target several Docent object types instead of only one agent run.

### Create a General Label Set

```python theme={null}
general_label_set = client.create_general_label_set(
    "my-collection-id",
    name="Review Findings",
    description="Annotations over runs, transcript slices, and reading results",
    label_schema={
        "type": "object",
        "properties": {
            "severity": {"type": "string", "enum": ["low", "medium", "high"]},
            "summary": {"type": "string"},
        },
        "required": ["severity", "summary"],
    },
    metadata={"source": "human-review"},
)
```

### List and Fetch General Label Sets

```python theme={null}
general_label_sets = client.get_general_label_sets("my-collection-id")
general_label_set = client.get_general_label_set(
    "my-collection-id",
    general_label_set_id,
)
```

### Create a General Label

```python theme={null}
general_label = client.create_general_label(
    "my-collection-id",
    general_label_set_id,
    target={
        "items": [
            {
                "object_type": "transcript_slice",
                "transcript_id": "transcript-123",
                "transcript_slice_start_idx": 4,
                "transcript_slice_end_idx": 8,
            }
        ]
    },
    label_value={
        "severity": "medium",
        "summary": "The answer misses a constraint from the prompt.",
    },
)
```

`target.items` can contain `agent_run`, `transcript`, `transcript_slice`, or
`reading_result` targets.

### Get a General Label

```python theme={null}
general_label = client.get_general_label(
    "my-collection-id",
    label_id="label-123",
)
print(general_label.label_value)
```

### Update a General Label

Update a general label's `label_value` and optionally replace its metadata. The
server verifies that the label belongs to the supplied general label set.

```python theme={null}
updated = client.update_general_label(
    "my-collection-id",
    general_label_set_id,
    label_id="label-123",
    label_value={
        "severity": "high",
        "summary": "The response violates the core task requirement.",
    },
    metadata={"reviewed_by": "human-reviewer"},
)
print(updated.updated_at)
```

#### Parameters

<ParamField body="collection_id" type="str" required>
  ID of the collection.
</ParamField>

<ParamField body="general_label_set_id" type="str" required>
  ID of the general label set that owns the label.
</ParamField>

<ParamField body="label_id" type="str" required>
  ID of the general label to update.
</ParamField>

<ParamField body="label_value" type="dict" required>
  New label value. It must conform to the general label set's JSON schema.
</ParamField>

<ParamField body="metadata" type="dict | None">
  Optional metadata to store on the label. If omitted, existing metadata is unchanged.
</ParamField>

#### Returns

<ResponseField name="general_label" type="GeneralLabel">
  Updated general label.
</ResponseField>

### Delete General Labels

Preview or delete specific general labels from a general label set. Like
`delete_labels`, this method defaults to a dry run.

```python theme={null}
preview = client.delete_general_labels(
    "my-collection-id",
    general_label_set_id,
    ["label-123", "label-456"],
)

for label in preview.labels:
    print(label.id, label.target)

result = client.delete_general_labels(
    "my-collection-id",
    general_label_set_id,
    ["label-123", "label-456"],
    dry_run=False,
)
print(result.message)
```

#### Parameters

<ParamField body="collection_id" type="str" required>
  ID of the collection.
</ParamField>

<ParamField body="general_label_set_id" type="str" required>
  ID of the general label set that owns all labels being deleted.
</ParamField>

<ParamField body="label_ids" type="list[str]" required>
  Non-empty list of unique general label IDs.
</ParamField>

<ParamField body="dry_run" type="bool" default="True">
  If `True`, return the general labels that would be deleted without deleting
  them. Pass `False` to perform the deletion.
</ParamField>

#### Returns

<ResponseField name="result" type="DeleteLabelsResult">
  Structured deletion preview or result. See `Delete Labels` for the result fields.
</ResponseField>

#### Errors

* **`ValueError`** — `label_ids` is empty or contains duplicates
* **`HTTPError (404)`** — Collection, general label set, or general label not found

***

## Tags

Lightweight string annotations on agent runs.

### Add a Tag

```python theme={null}
client.tag_transcript("my-collection-id", "run-id-123", "needs-review")
```

### Get Tags

```python theme={null}
# All tags in a collection
all_tags = client.get_tags("my-collection-id")

# Filter by value
review_tags = client.get_tags("my-collection-id", value="needs-review")

# Tags for a specific run
run_tags = client.get_tags_for_agent_run("my-collection-id", "run-id-123")
```

### Delete a Tag

```python theme={null}
client.delete_tag("my-collection-id", tag_id="tag-456")
```

***

## Comments

Free-text notes on agent runs.

### Get Comments

```python theme={null}
# All comments in a collection
comments = client.get_comments("my-collection-id")

# Comments for a specific run
run_comments = client.get_comments_for_agent_run("my-collection-id", "run-id-123")
for c in run_comments:
    print(f"{c.get('created_by')}: {c.get('text')}")
```
