# Labels

> Tag what you add with your own name/value pairs, then keep any recall to them.

Give what you add labels of your own, such as the channel it came from or the account it is about. Then keep
any recall to them: context, ask, search, the list of memories and a brief all take a filter.

```python
mem.memories.add(email.body, title=email.subject, labels={"channel": "email", "account": "lumen"})
mem.context("When does the renewal come up?", labels={"account": "lumen"})
```

```ts
await mem.memories.add({ text: email.body, title: email.subject, labels: { channel: "email", account: "lumen" } });
await mem.context("When does the renewal come up?", { labels: { account: "lumen" } });
```

```bash
curl https://api.geniffy.com/v1/memories \
  -H "Authorization: Bearer $GENIFFY_API_KEY" \
  -H "X-Geniffy-Space: customer_1042" \
  -H "Content-Type: application/json" \
  -d '{"text": "The Lumen renewal comes up in March.", "labels": {"channel": "email", "account": "lumen"}}'
```

## Filters

- Every name in a filter must match: `{"channel": "email", "account": "lumen"}` keeps to sources that carry
  both.
- A list of values is any one of them: `{"channel": ["email", "chat"]}`.
- A memory counts when any source that said it carries the labels. If an unlabelled note and a labelled
  email both said it, the filter finds it.
- In a query string, a filter is `label=name:value`, repeated for another name or another value:
  `GET /v1/memories?label=account:lumen&label=channel:email`.

```python
mem.search("pricing", labels={"channel": ["email", "chat"]})
mem.ask("Who signs the renewal?", labels={"account": "lumen"})
mem.memories.list(labels={"account": "lumen"})
mem.brief(labels={"account": "lumen"})
```

```ts
await mem.search("pricing", { labels: { channel: ["email", "chat"] } });
await mem.ask("Who signs the renewal?", { labels: { account: "lumen" } });
await mem.memories.list({ labels: { account: "lumen" } });
await mem.brief(undefined, { labels: { account: "lumen" } });
```

```bash
curl https://api.geniffy.com/v1/context \
  -H "Authorization: Bearer $GENIFFY_API_KEY" \
  -H "X-Geniffy-Space: customer_1042" \
  -H "Content-Type: application/json" \
  -d '{"question": "When does the renewal come up?", "labels": {"account": "lumen"}}'
```

When nothing carrying the labels bears on the question, context says so in one sentence, as it does
without a filter, so your model says it doesn't know instead of reaching for something outside them.

Every memory that comes back names the labels of the source it came from, as `source.labels`, so you can
show where each one came from or sort them yourself.

## Changing labels

Labels belong to a source. To change them, send the source again under [your own id](https://docs.geniffy.com/add-memories/your-own-ids):

- **New labels:** they replace the old ones at once. Nothing is read or learned again.
- **No labels:** the source keeps the ones it has, and a part it gains when it grows carries them too.
- **`{}`:** clears them.

Without an `external_id`, sending a source again adds a second one, so a source added that way keeps the
labels it was added with. To change them, delete it and add it again. `GET /v1/sources` and
`GET /v1/sources/{id}` show each source's `labels`.

## Forget everything with a label

When your user disconnects a data source, delete everything you added from it in one call, if you added it
under its label:

```python
mem.sources.delete_labelled({"channel": "gmail"})      # how many sources went
```

```ts
await mem.sources.deleteLabelled({ channel: "gmail" });
```

```bash
curl -X DELETE "https://api.geniffy.com/v1/sources?label=channel:gmail" \
  -H "Authorization: Bearer $GENIFFY_API_KEY" \
  -H "X-Geniffy-Space: customer_1042"
```

Each source goes with every memory only it taught; a memory another source also said stays. One call
deletes up to 100 sources and answers `more: true` while others carry the labels, and the SDKs call again for
you. To see what would go first, `GET /v1/sources?label=channel:gmail` lists them, and so does
`sources.list(labels=...)`.

At the end of a sync that read everything from a data source, pass the ids of what it still has as `keep`,
and every other source with the labels, such as what is gone from the data source, is deleted:

```python
mem.sources.delete_labelled({"channel": "notion"}, keep=seen)   # seen: the external_ids this sync added
```

```ts
await mem.sources.deleteLabelled({ channel: "notion" }, { keep: seen });
```

The SDKs read the whole list first, then delete the rest one by one. [Gmail](https://docs.geniffy.com/integrations/gmail),
[Google Drive](https://docs.geniffy.com/integrations/google-drive), [Notion](https://docs.geniffy.com/integrations/notion) and [OneDrive](https://docs.geniffy.com/integrations/onedrive)
each show a full sync.

## Labels or spaces?

A [space](https://docs.geniffy.com/keys-and-spaces) is one of your users: a memory of its own that nothing else can read. Labels sort
what one user's memory holds. Use a space for who it belongs to, and labels for what it is: the channel, the
account, the project. A label never lets one space read another.

## The rules

- Up to 20 labels on a source.
- A name is up to 64 letters, digits, dots, dashes or underscores, starting with a letter or digit.
- A value is 1 to 200 characters; spaces at either end are trimmed.
- A filter holds up to 20 names, each with up to 50 values.
- Anything else is refused with `bad_labels`, before anything is stored.

Files take labels too, as a JSON form field: `labels={"channel": "drive"}`. So does each item of a
[batch](https://docs.geniffy.com/add-memories/many-at-once).

Source: https://docs.geniffy.com/add-memories/labels
