Skip to content

Commit dbd94cd

Browse files
RiskeyLlaipz8200claude
authored
docs: sync release/1.14.1 into main (#777)
* docs: add Hidden & Pre-Filled section to User Input page (#773) * docs: add Hidden & Pre-Filled section to User Input page * translate: add Hidden & Pre-Filled section in user-input (zh, ja) * docs: add macro translation principles to zh formatting guide * fix: correct Variables anchor in zh/ja translations * docs: update docker compose environment docs (#774) * docs: update docker compose environment docs * chore: retrigger documentation analysis * docs: refine env-split deployment docs and translate zh/ja * docs: update aws premium env guidance * docs: refine aws premium env guidance and translate zh/ja --------- Co-authored-by: RiskeyL <7a8y@163.com> * Docs/1.14.1 check (#775) * docs: sync knowledge api spec for 1.14.1 * chore: tighten release-sync and api-reference skills * docs: update education discount steps * docs: relax over-applied zh translation rules * docs: sync env vars for 1.14.1 * chore: env-vars verifier supports multi-file layout * docs: cover editable class labels in question classifier; sync question classifier zh/ja docs * chore: sync termbase_i18n with current glossary * docs: update collaboration docs for websocket service split; sync zh/ja translation * docs: human input updates for 1.14.1; sync zh/ja * docs: add end user term to glossary * chore: align ja api spec human input terminology with glossary * fix: enforce at-least-one tag in unbinding request schema * Revert "fix: enforce at-least-one tag in unbinding request schema" This reverts commit b392b14. Mintlify renders schema-level `anyOf` as unlabeled "Option 1" / "Option 2" tabs in the request body section, which is more confusing than the loose-schema-with-prose-rule it replaced. The SDK-generator benefit isn't worth the rendered-docs regression for a deprecated compat path. Keep the contract in the parameter descriptions only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add "Agent app" glossary entry to disambiguate from Agent node --------- Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6d24394 commit dbd94cd

43 files changed

Lines changed: 1355 additions & 491 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/dify-docs-api-reference/SKILL.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,21 @@ Pattern: `{verb}{AppType}{Resource}`
143143
- **Descriptions must add value**: `"Session identifier."` is a label, not a description. Instead: `"The \`user\` identifier provided in API requests."`.
144144
- **Nullable/conditional fields**: Explain when present or `null`.
145145

146+
#### Endpoint vs parameter descriptions
147+
148+
| Field | Scope |
149+
|---|---|
150+
| Endpoint `description` | What the endpoint does, plus any whole-API surprise (cascading delete, long-poll duration). One sentence when possible. |
151+
| Parameter `description` | Field meaning, valid values, deprecation, normalization, and rules about when to use it vs an alternative. |
152+
153+
If the endpoint description explains a parameter, move it down.
154+
155+
❌ "Remove one or more tag bindings from a knowledge base. Provide tag IDs in `tag_ids`. The legacy `tag_id` field is still accepted for single-tag requests and is normalized into `tag_ids` server-side; supply at least one of the two."
156+
157+
✅ Endpoint: "Remove one or more tags from a knowledge base."
158+
`tag_ids`: "Tag IDs to unbind. Required unless the legacy `tag_id` is provided."
159+
`tag_id`: "Legacy single-tag form. Normalized into `tag_ids` server-side. Use `tag_ids` for new integrations."
160+
146161
### Cross-API Links
147162

148163
When a description mentions another endpoint, add a markdown link. Pattern: `/api-reference/{category}/{endpoint-name}` (kebab-case from endpoint summary).

.claude/skills/dify-docs-env-vars/SKILL.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,21 @@ Read these shared guides:
1717
2. `writing-guides/formatting-guide.md`
1818
3. `writing-guides/glossary.md`
1919

20-
## Source of Truth: `docker/.env.example`
20+
## Source of Truth: `docker/.env.example` + `docker/envs/**/*.env.example`
2121

22-
The doc mirrors `docker/.env.example`, the supported self-host knob surface.
22+
After Dify PR #31586, the supported self-host knob surface is split across:
23+
24+
- `docker/.env.example` — essential startup values
25+
- `docker/envs/**/*.env.example` — categorized optional vars (core-services, databases, infrastructure, security, vectorstores, middleware)
26+
27+
The verifier reads both. Pass `--env-example docker/.env.example --env-example docker/envs` (the second arg is a directory; the verifier globs `**/*.env.example` recursively).
2328

2429
| Var location | Action |
2530
|---|---|
26-
| In `.env.example`, uncommented | Document. |
27-
| In `.env.example`, commented (`#FOO=bar`) | Document; add to **Verifier false positives** in `ignored-vars.md` (the verifier can't parse defaults from comments). |
28-
| Only in `api/configs/` Pydantic, not in `.env.example` | **Don't document.** Upstream-deferred; file a PR adding it to `.env.example` first. |
31+
| In any `.env.example` file, uncommented | Document. |
32+
| In any `.env.example` file, commented (`#FOO=bar`) | Document; add to **Verifier false positives** in `ignored-vars.md` (the verifier can't parse defaults from comments). |
33+
| Only in `api/configs/` Pydantic, not in any `.env.example` | **Don't document.** Upstream-deferred; file a PR adding it to the appropriate `.env.example` file first. |
34+
| Removed from `.env.example` because the code no longer reads it | **Remove from docs.** Documenting unreferenced vars implies they still take effect. Discoverability for upgraders belongs in upstream Dify release notes, not this docs site. |
2935

3036
The verifier's "extra in docs" signal is not an escape hatch. Never suppress it for Pydantic-only vars via `ignored-vars.md`.
3137

.claude/skills/dify-docs-env-vars/verify-env-docs.py

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
#!/usr/bin/env python3
22
"""
3-
Verify Dify environment variable documentation against .env.example.
3+
Verify Dify environment variable documentation against .env.example sources.
44
5-
Parses both files, extracts variable names and defaults, and reports
6-
discrepancies between what the documentation says and what .env.example defines.
5+
Parses one or more env-example files plus the docs MDX, extracts variable names
6+
and defaults, and reports discrepancies.
7+
8+
After Dify PR #31586, env vars were split across `docker/.env.example` and
9+
`docker/envs/**/*.env.example`. The verifier accepts either a single file or
10+
a directory; passing a directory globs `**/*.env.example` recursively.
711
812
Usage:
9-
python3 verify-env-docs.py [--env-example PATH] [--docs PATH]
13+
python3 verify-env-docs.py --env-example PATH [--env-example PATH ...] --docs PATH
1014
11-
Both arguments are required (no defaults).
15+
`--env-example` may be repeated and may point to either a file or a directory.
1216
"""
1317

1418
import argparse
@@ -17,8 +21,8 @@
1721
from pathlib import Path
1822

1923

20-
def parse_env_example(path: str) -> dict[str, str]:
21-
"""Parse .env.example and return {VARIABLE_NAME: default_value}."""
24+
def parse_env_file(path: Path) -> dict[str, str]:
25+
"""Parse a single env-example file and return {VARIABLE_NAME: default_value}."""
2226
variables = {}
2327
with open(path, encoding="utf-8") as f:
2428
for line in f:
@@ -35,6 +39,52 @@ def parse_env_example(path: str) -> dict[str, str]:
3539
return variables
3640

3741

42+
def collect_env_files(sources: list[str]) -> list[Path]:
43+
"""Resolve each source (file or directory) into a list of env-example files.
44+
45+
Files are returned as-is. Directories are scanned recursively for any
46+
file whose name ends with `.env.example` (matches both `.env.example`
47+
and `<name>.env.example`).
48+
"""
49+
files: list[Path] = []
50+
seen: set[Path] = set()
51+
for source in sources:
52+
p = Path(source)
53+
if not p.exists():
54+
print(f"ERROR: env source not found: {source}", file=sys.stderr)
55+
sys.exit(1)
56+
if p.is_dir():
57+
for f in sorted(p.rglob("*.env.example")):
58+
if f not in seen:
59+
files.append(f)
60+
seen.add(f)
61+
# Also catch the bare `.env.example` filename, which rglob's
62+
# `*.env.example` pattern does include via the leading wildcard,
63+
# but be explicit for clarity.
64+
for f in sorted(p.rglob(".env.example")):
65+
if f not in seen:
66+
files.append(f)
67+
seen.add(f)
68+
else:
69+
if p not in seen:
70+
files.append(p)
71+
seen.add(p)
72+
return files
73+
74+
75+
def parse_env_example(sources: list[str]) -> tuple[dict[str, str], list[Path]]:
76+
"""Parse all env-example files reachable from the given sources.
77+
78+
Later files override earlier ones for duplicate keys. Returns the merged
79+
variable map plus the list of files actually parsed (for diagnostics).
80+
"""
81+
files = collect_env_files(sources)
82+
merged: dict[str, str] = {}
83+
for f in files:
84+
merged.update(parse_env_file(f))
85+
return merged, files
86+
87+
3888
def parse_ignored_vars(path: str) -> set[str]:
3989
"""Parse the ignored-vars markdown file and return the set of ignored names.
4090
@@ -183,7 +233,13 @@ def main():
183233
parser.add_argument(
184234
"--env-example",
185235
required=True,
186-
help="Path to .env.example file (e.g., /path/to/dify/docker/.env.example)",
236+
action="append",
237+
help=(
238+
"Path to a .env.example file or to a directory containing them. "
239+
"May be repeated. When given a directory, the verifier globs "
240+
"**/*.env.example recursively. Pass both `docker/.env.example` and "
241+
"`docker/envs/` to capture the post-PR-#31586 layout."
242+
),
187243
)
188244
parser.add_argument(
189245
"--docs",
@@ -197,18 +253,17 @@ def main():
197253
)
198254
args = parser.parse_args()
199255

200-
if not Path(args.env_example).exists():
201-
print(f"ERROR: .env.example not found at {args.env_example}")
202-
sys.exit(1)
203256
if not Path(args.docs).exists():
204257
print(f"ERROR: Documentation not found at {args.docs}")
205258
sys.exit(1)
206259

207-
env_vars = parse_env_example(args.env_example)
260+
env_vars, env_files = parse_env_example(args.env_example)
208261
doc_vars = parse_mdx_docs(args.docs)
209262
ignored = parse_ignored_vars(args.ignored)
210263

211-
print(f"Parsed {len(env_vars)} variables from .env.example")
264+
print(f"Parsed {len(env_vars)} variables from {len(env_files)} env-example file(s):")
265+
for f in env_files:
266+
print(f" - {f}")
212267
print(f"Parsed {len(doc_vars)} variables from documentation")
213268
print(f"Loaded {len(ignored)} ignored variables from {args.ignored}")
214269
print()
@@ -223,12 +278,12 @@ def main():
223278
print(f" {name}={env_vars[name]}")
224279
print()
225280

226-
# --- Check 2: Variables in docs but not in .env.example ---
281+
# --- Check 2: Variables in docs but not in any env-example source ---
227282
extra_in_docs = sorted(
228283
(set(doc_vars.keys()) - set(env_vars.keys())) - ignored
229284
)
230285
if extra_in_docs:
231-
print(f"=== IN DOCS BUT NOT IN .env.example ({len(extra_in_docs)}) ===")
286+
print(f"=== IN DOCS BUT NOT IN ANY .env.example ({len(extra_in_docs)}) ===")
232287
for name in extra_in_docs:
233288
print(f" {name} (doc default: {doc_vars[name]!r})")
234289
print()

.claude/skills/dify-docs-release-sync/SKILL.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,23 @@ git log <from>..<to> --oneline --grep="(#"
5353

5454
This captures every change between the two versions, regardless of whether PRs were tagged to a milestone.
5555

56+
**Do not pre-filter the diff to a hand-picked path list.** Run `git diff --stat` over the full change set, then categorize in 1.2. Pre-filtering hides files like new `docker/dify-compose*` scripts, `docker/.env.default`, `docker/README.md`, or root `README.md` that drive deployment-doc updates.
57+
58+
### 1.1a Cross-check existing docs PRs
59+
60+
Before generating the report, check whether dify-docs already has open or recently-merged PRs covering the same source PRs. This prevents duplicate work and reveals doc paths you might miss:
61+
62+
```bash
63+
# Search by source-PR number(s) you plan to flag
64+
gh pr list --repo langgenius/dify-docs --state all --search "#<dify-PR-number>" \
65+
--json number,title,state,files
66+
# Or scan recent docs PRs for the release window
67+
gh pr list --repo langgenius/dify-docs --state all --limit 30 \
68+
--json number,title,state,mergedAt,files
69+
```
70+
71+
If a docs PR already covers an item, mark it **Already addressed (PR #N)** in the report and exclude it from execution.
72+
5673
For context on specific changes, fetch the relevant PR details:
5774
```bash
5875
# Extract PR numbers from commit messages
@@ -110,8 +127,8 @@ Read the PR description for context. Map changed source paths to likely doc area
110127
| dify | `api/core/agent/` | `en/use-dify/build-apps/agent.mdx` |
111128
| dify | `api/core/app/` | `en/use-dify/build-apps/` |
112129
| dify | `web/app/components/` | UI-related docs (check PR description for specifics) |
113-
| dify | `docker/`, deployment configs | `en/getting-started/install/` |
114-
| dify | `api/configs/` | Configuration/environment variable docs |
130+
| dify | `docker/.env.example`, `docker/docker-compose.yaml`, `docker/docker-compose-template.yaml`, `api/configs/` | `en/self-host/configuration/environments.mdx` (env var docs) |
131+
| dify | `docker/README.md`, `docker/dify-compose*`, `docker/.env.default`, root `README.md`, any new file under `docker/` | `en/self-host/quick-start/docker-compose.mdx` and `en/self-host/quick-start/faqs.mdx` (deployment workflow docs) |
115132
| graphon | `src/graphon/nodes/` (built-in nodes: llm, code, http_request, if_else, loop, iteration, parameter_extractor, document_extractor, list_operator, variable_aggregator/assigner, question_classifier, template_transform, tool, start/end/answer, human_input) | `en/use-dify/workflow/nodes/` |
116133
| graphon | `src/graphon/model_runtime/` | `en/use-dify/model-providers/` |
117134
| graphon | `src/graphon/graph_engine/`, `src/graphon/runtime/` | workflow engine behavior, execution semantics |
@@ -296,3 +313,6 @@ For each affected variable group, use `dify-docs-env-vars` skill:
296313
| Skipping Pydantic model changes | A model change may affect multiple endpoints — trace which controllers use it |
297314
| Forgetting to checkout target version | Audit against the target release code, not whatever is currently checked out |
298315
| Manually translating after EN fixes | Translation is automatic on PR push — never run manual translation scripts. **Exception**: `environments.mdx` is in the ignore list and must be translated manually. |
316+
| Pre-filtering the diff to a hand-picked path list | Run the full `git diff --stat` first; categorize after. Pre-filtering hides deployment scripts, new docker files, and root README changes. |
317+
| Trusting the `chore:` / `fix:` prefix as a no-doc-impact signal | Read the PR title and body. "chore: easier and simpler deploy" is a deployment workflow change. Prefix is not a category. |
318+
| Skipping the dify-docs PR cross-check | Always run `gh pr list --repo langgenius/dify-docs` against the source PR numbers before reporting. Avoids duplicate work and surfaces doc paths the heuristic mapping missed. |

en/api-reference/openapi_knowledge.json

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4544,7 +4544,7 @@
45444544
"Tags"
45454545
],
45464546
"summary": "Delete Tag Binding",
4547-
"description": "Remove a tag binding from a knowledge base.",
4547+
"description": "Remove one or more tags from a knowledge base.",
45484548
"operationId": "unbindTagFromDataset",
45494549
"requestBody": {
45504550
"required": true,
@@ -4553,13 +4553,21 @@
45534553
"schema": {
45544554
"type": "object",
45554555
"required": [
4556-
"tag_id",
45574556
"target_id"
45584557
],
45594558
"properties": {
4559+
"tag_ids": {
4560+
"type": "array",
4561+
"items": {
4562+
"type": "string"
4563+
},
4564+
"minItems": 1,
4565+
"description": "Tag IDs to unbind. Required unless the legacy `tag_id` is provided."
4566+
},
45604567
"tag_id": {
45614568
"type": "string",
4562-
"description": "Tag ID to unbind."
4569+
"deprecated": true,
4570+
"description": "Legacy single-tag form. Normalized into `tag_ids` server-side. Use `tag_ids` for new integrations."
45634571
},
45644572
"target_id": {
45654573
"type": "string",
@@ -6871,6 +6879,83 @@
68716879
}
68726880
}
68736881
}
6882+
},
6883+
"metadata_filtering_conditions": {
6884+
"type": "object",
6885+
"nullable": true,
6886+
"description": "Restrict retrieval to chunks whose document metadata matches the given conditions. Conditions are evaluated server-side against document metadata fields.",
6887+
"properties": {
6888+
"logical_operator": {
6889+
"type": "string",
6890+
"enum": [
6891+
"and",
6892+
"or"
6893+
],
6894+
"default": "and",
6895+
"nullable": true,
6896+
"description": "How to combine multiple conditions."
6897+
},
6898+
"conditions": {
6899+
"type": "array",
6900+
"nullable": true,
6901+
"description": "List of metadata conditions to evaluate.",
6902+
"items": {
6903+
"type": "object",
6904+
"required": [
6905+
"name",
6906+
"comparison_operator"
6907+
],
6908+
"properties": {
6909+
"name": {
6910+
"type": "string",
6911+
"description": "Metadata field name to compare against."
6912+
},
6913+
"comparison_operator": {
6914+
"type": "string",
6915+
"description": "Comparison to apply. String operators (`contains`, `not contains`, `start with`, `end with`, `is`, `is not`, `empty`, `not empty`, `in`, `not in`) act on string or array metadata. Numeric operators (`=`, `≠`, `>`, `<`, `≥`, `≤`) act on numeric metadata. Time operators (`before`, `after`) act on time metadata.",
6916+
"enum": [
6917+
"contains",
6918+
"not contains",
6919+
"start with",
6920+
"end with",
6921+
"is",
6922+
"is not",
6923+
"empty",
6924+
"not empty",
6925+
"in",
6926+
"not in",
6927+
"=",
6928+
"",
6929+
">",
6930+
"<",
6931+
"",
6932+
"",
6933+
"before",
6934+
"after"
6935+
]
6936+
},
6937+
"value": {
6938+
"nullable": true,
6939+
"description": "Value to compare against. Type depends on `comparison_operator`: string for most string operators, array of strings for `in` and `not in`, number for numeric operators, and omitted for `empty` and `not empty`.",
6940+
"oneOf": [
6941+
{
6942+
"type": "string"
6943+
},
6944+
{
6945+
"type": "array",
6946+
"items": {
6947+
"type": "string"
6948+
}
6949+
},
6950+
{
6951+
"type": "number"
6952+
}
6953+
]
6954+
}
6955+
}
6956+
}
6957+
}
6958+
}
68746959
}
68756960
}
68766961
}

en/self-host/advanced-deployments/local-source-code.mdx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@ A series of middlewares for storage (e.g. PostgreSQL / Redis / Weaviate (if not
3131
```bash
3232
cd docker
3333

34-
cp middleware.env.example middleware.env
34+
cp envs/middleware.env.example middleware.env
3535

36-
# change the profile to mysql if you are not using postgresql
37-
# change the profile to other vector database if you are not using weaviate
38-
docker compose -f docker-compose.middleware.yaml --profile postgresql --profile weaviate -p dify up -d
36+
# Change DB_TYPE or COMPOSE_PROFILES in middleware.env if you are not using PostgreSQL and Weaviate.
37+
docker compose --env-file middleware.env -f docker-compose.middleware.yaml -p dify up -d
3938
```
4039

4140
---

0 commit comments

Comments
 (0)