langgenius / langgenius/dify

Automatic metadata filter crashes for time-typed fields (LLM returns date string instead of Unix timestamp)

Open
#41,597 2 comments 1 reaction 0 assignees View on GitHub
Dominant language
TypeScript
Stars
156k
Forks
24.6k
Avg merge
22h 9m
Merged PRs (30d)
610

Description

### Self Checks

- [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general).
- [x] I have searched for existing issues, including closed ones.
- [x] I confirm that I am using English to submit this report.
- [x] Please do not modify this template and fill in all the required fields.

### Dify version

1.16.1 (reproduced there; the same code is still present on `main` @ 6bc7f01261, unfixed)

### Cloud or Self Hosted

Self Hosted (Docker)

### Steps to reproduce

1. Create a Knowledge Base and enable **Automatic** metadata filtering (`metadata_model_config.mode`) instead of manual filtering.
2. Add a metadata field whose type is **`time`** (e.g. `release_date`), and give at least one indexed document a value for it.
3. Ask a question that references a date for that field, e.g. *"What documents were published after 2024-01-01?"*.
4. Observe the API logs / response.

Root cause: `DatasetRetrieval._automatic_metadata_filter_func` (`api/core/rag/retrieval/dataset_retrieval.py`) builds the LLM prompt from `all_metadata_fields`, which is just a list of **field names** — the field's `type` ("string" | "number" | "time") is looked up (`metadata_field.type`) but never passed along:

```python
all_metadata_fields = [metadata_field.name for metadata_field in metadata_fields]
...
prompt_messages, stop = self._get_prompt_template(
model_config=model_config,
mode=metadata_model_config.mode,
metadata_fields=all_metadata_fields,
query=query or "",
)
```

`METADATA_FILTER_SYSTEM_PROMPT` / `METADATA_FILTER_SYSTEM_PROMPT_CHAT` (`api/core/rag/retrieval/template_prompts.py`) likewise only ever show the model a bare list of field names, with no instruction about what format a `time`-typed value should be returned in. Since the model has no way to know the field is a timestamp, it naturally extracts the literal date mentioned in the query (`"2024-01-01"`) as the `metadata_field_value`.

That raw date string is then used downstream in a numeric comparison against the field (`CAST(doc_metadata->>field AS FLOAT) > :value` for `time`/`number` fields, since time metadata is stored/compared as a Unix timestamp), which fails because `"2024-01-01"` is not a valid float literal.

### ✔️ Expected Behavior

For a `time`-typed metadata field, the automatic filter either:
- converts the extracted value to a Unix timestamp before executing the query, or
- instructs the LLM (via the prompt) to emit the value as a Unix timestamp in the first place,

so the query executes normally and returns the expected filtered results.

### ❌ Actual Behavior

The automatic metadata filter crashes with a `psycopg2.errors.InvalidTextRepresentation` (invalid input syntax for type double precision: "2024-01-01") when the query mentions a date and the target metadata field is of type `time`, because the LLM-extracted value is a literal date string rather than a Unix timestamp, and nothing along the path casts or instructs it to be one.

We reproduced and fixed this locally (patch attached below for reference) by passing each field's `type` alongside its `name` in the prompt, and adding explicit value-format rules + a matching few-shot example instructing the model to always emit `time`-typed values as Unix timestamps:

```diff
- all_metadata_fields = [metadata_field.name for metadata_field in metadata_fields]
+ metadata_field_info = [
+ {"name": metadata_field.name, "type": metadata_field.type} for metadata_field in metadata_fields
+ ]
+ all_metadata_fields = [field["name"] for field in metadata_field_info]
```

```diff
- The input text is in the variable input_text. Metadata are specified as a list in the variable metadata_fields.
+ The input text is in the variable input_text. Metadata fields are specified as a list in the variable metadata_fields, where each item has a "name" and a "type" ("string", "number", or "time").
+ ### Value rules
+ - For fields of type "time", metadata_field_value MUST be a Unix timestamp in seconds (UTC midnight for date-only mentions), never a date string such as "2024-01-01".
+ - For fields of type "number", metadata_field_value MUST be a plain number.
+ - For fields of type "string", metadata_field_value is the extracted text.
```

Happy to open a PR with the full patch (touches `api/core/rag/retrieval/dataset_retrieval.py` and `api/core/rag/retrieval/template_prompts.py`, including updated few-shot examples) if that's useful.

Contributor guide

Open the contributing guide

Research direction

Start in api/core/rag/retrieval/dataset_retrieval.py at DatasetRetrieval._automatic_metadata_filter_func, then inspect the metadata filter prompts and few-shot examples in api/core/rag/retrieval/template_prompts.py. Reproduce the automatic filter with a time metadata field and a date query; done means the extracted value uses the required timestamp format and the filtered query returns results without the database error.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
ai, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.