Automatic metadata filter crashes for time-typed fields (LLM returns date string instead of Unix timestamp)
- 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
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