mirror of
https://github.com/langgenius/dify.git
synced 2026-11-12 14:21:29 +08:00
Track backend integration, Ruff cleanup and migration reconciliation in ENG-1271. Retain required copy contracts without implementing frontend interactions.
99 lines
4.1 KiB
Python
99 lines
4.1 KiB
Python
"""Test-data form fields and replies for the Test Run effect disclosure (PRD §9.1, 超出边界确认卡).
|
|
|
|
The disclosure travels with the existing test-data form: one checkbox per side
|
|
effect names the node, its target and account, unticked by default so the
|
|
default stays Mock. Read and model calls and destructive steps are listed in
|
|
the reply that carries the form.
|
|
"""
|
|
|
|
from collections.abc import Mapping, Sequence
|
|
|
|
from core.dify_builder.contract import FormField
|
|
from core.dify_builder.models import NodeOutput
|
|
from runtime_contracts.builder.copy_ids import BuilderCopyId
|
|
from runtime_contracts.builder.presentation import BuilderText, copy_text, text_parts
|
|
from services.dify_builder.effect_disclosure import EffectDisclosure, confirm_field_key
|
|
from services.dify_builder.execution_messages import code_risk_unavailable_notice
|
|
|
|
|
|
def effect_confirmation_fields(disclosure: EffectDisclosure) -> list[FormField]:
|
|
fields: list[FormField] = []
|
|
for item in disclosure.confirmable:
|
|
details: list[BuilderText] = []
|
|
if item.account:
|
|
details.append(copy_text(BuilderCopyId.FORMS_TEST_DATA_EFFECT_ACCOUNT, account=item.account))
|
|
if item.object:
|
|
details.append(item.object)
|
|
if item.risk_notice:
|
|
details.append(item.risk_notice)
|
|
hint = text_parts(*_joined(details, " · ")) if details else None
|
|
fields.append(
|
|
FormField(
|
|
key=confirm_field_key(item.node_id),
|
|
label=copy_text(
|
|
BuilderCopyId.FORMS_TEST_DATA_CONFIRM_EFFECT, node=item.title, target=item.target or item.node_type
|
|
),
|
|
type="checkbox",
|
|
default=False,
|
|
hint=hint,
|
|
)
|
|
)
|
|
return fields
|
|
|
|
|
|
def effect_confirmation_values(disclosure: EffectDisclosure, confirmation: Mapping[str, str]) -> dict[str, bool]:
|
|
"""Keep a still-valid earlier confirmation ticked when the form is shown again."""
|
|
return {
|
|
confirm_field_key(item.node_id): confirmation.get(item.node_id) == item.signature
|
|
for item in disclosure.confirmable
|
|
}
|
|
|
|
|
|
def effects_note(disclosure: EffectDisclosure, *, reply_language: str = "") -> BuilderText | None:
|
|
"""The reply lines that accompany the form; ``None`` when the run touches nothing outside Dify."""
|
|
reads = disclosure.by_tier("read")
|
|
destructive = disclosure.by_tier("destructive")
|
|
code = tuple(item for item in disclosure.items if item.node_type == "code" and item.risk_notice)
|
|
if not (reads or destructive or disclosure.confirmable or code):
|
|
return None
|
|
lines: list[BuilderText] = [copy_text(BuilderCopyId.TEST_EFFECTS_INTRO)]
|
|
lines += [
|
|
copy_text(BuilderCopyId.TEST_EFFECT_CALLS, node=item.title, target=item.target or item.node_type)
|
|
for item in reads
|
|
]
|
|
lines += [
|
|
copy_text(BuilderCopyId.TEST_EFFECT_ALWAYS_MOCKED, node=item.title, target=item.target or item.node_type)
|
|
for item in destructive
|
|
]
|
|
for item in code:
|
|
notice = (
|
|
code_risk_unavailable_notice(reply_language)
|
|
if item.assessment_status == "unavailable"
|
|
else item.risk_notice
|
|
)
|
|
lines.append(f"{item.title}: {notice}")
|
|
if disclosure.confirmable:
|
|
lines.append(copy_text(BuilderCopyId.TEST_EFFECT_CONFIRM_REQUIRED))
|
|
return text_parts(*_joined(lines, "\n"))
|
|
|
|
|
|
def with_effects_note(reply: BuilderText, note: BuilderText | None) -> BuilderText:
|
|
return text_parts(reply, "\n\n", note) if note is not None else reply
|
|
|
|
|
|
def mocked_note(simulated_node_ids: Sequence[str], per_node: Sequence[NodeOutput]) -> BuilderText | None:
|
|
if not simulated_node_ids:
|
|
return None
|
|
titles = {node.node_id: node.title for node in per_node if node.title}
|
|
names = ", ".join(titles.get(node_id, node_id) for node_id in simulated_node_ids)
|
|
return copy_text(BuilderCopyId.TEST_MOCKED_NOTE, nodes=names)
|
|
|
|
|
|
def _joined(parts: Sequence[BuilderText], separator: str) -> list[BuilderText]:
|
|
joined: list[BuilderText] = []
|
|
for index, part in enumerate(parts):
|
|
if index:
|
|
joined.append(separator)
|
|
joined.append(part)
|
|
return joined
|