mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
feat(dify-builder): engine hooks for language detection + item localization (M2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e70c7e189
commit
e7c7c7684a
3 files changed
+79
No files matched your search
@@ -243,6 +243,7 @@ class DifyBuilderContext:
|
||||
|
||||
# -- Build fields (Slice 2, additive) --
|
||||
goal_text: str = ""
|
||||
reply_language: str = "" # detected user-input language (BCP-47); drives reply/localization
|
||||
requirements: dict[str, Any] = field(default_factory=dict)
|
||||
plan_items: list[str] = field(default_factory=list)
|
||||
plan_version_tag: str = ""
|
||||
|
||||
@@ -107,6 +107,10 @@ class Env:
|
||||
# the current operation and persisted with its assistant turn, independent
|
||||
# from observable execution progress.
|
||||
emit_reasoning: Callable[[ReasoningEventData], None] | None = None
|
||||
# Detects the user's input language (BCP-47) for a turn's user text. None -> no detection.
|
||||
detect_language: Callable[[str], str] | None = None
|
||||
# Localizes an about-to-be-committed item list into the given language. None -> no-op.
|
||||
localize_items: Callable[[list["ConversationItem"], str], list["ConversationItem"]] | None = None
|
||||
# Correlation metadata for the handler transition currently running.
|
||||
# The runner resets it before every independently committed step.
|
||||
session_id: str = ""
|
||||
@@ -180,6 +184,8 @@ class Runner:
|
||||
settled: bool,
|
||||
) -> None:
|
||||
at_version = session.version + 1
|
||||
if self._env.localize_items is not None and context.reply_language:
|
||||
items = self._env.localize_items(items, context.reply_language)
|
||||
for item in items:
|
||||
# Handler helpers own conversation sequence numbers; the runner is
|
||||
# the only layer that knows which CAS version will make them
|
||||
@@ -251,6 +257,13 @@ class Runner:
|
||||
s, fc = self._env.repo.get_session(session_id)
|
||||
self._env.begin_operation(s)
|
||||
|
||||
if self._env.detect_language is not None:
|
||||
user_text = turn.action.payload.get("text") if turn.action is not None else None
|
||||
if isinstance(user_text, str) and user_text.strip():
|
||||
fc.reply_language = self._env.detect_language(user_text)
|
||||
elif not fc.reply_language and fc.goal_text:
|
||||
fc.reply_language = self._env.detect_language(fc.goal_text)
|
||||
|
||||
action_kind = turn.action.kind if turn.action is not None else ""
|
||||
|
||||
# A retried message may arrive after its user bubble committed but the
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
"""Engine wiring: detect language from user input; localize items before commit."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from core.dify_builder.models import Action, Actor, DifyBuilderContext, EntryMode, Session, Turn
|
||||
from core.dify_builder.runner import Env, Runner
|
||||
from core.dify_builder.state import PcState
|
||||
from core.dify_builder.handlers_build import build_registry
|
||||
from core.dify_builder.handlers_edit import edit_registry
|
||||
from core.dify_builder.handlers_fix import fix_registry
|
||||
from tests.unit_tests.core.dify_builder.fakes import FakeDifyPort, InMemoryRepository
|
||||
from core.dify_builder.placeholder_agent import PlaceholderAgent
|
||||
|
||||
|
||||
def _env(**overrides):
|
||||
repo = InMemoryRepository()
|
||||
env = Env(dify=FakeDifyPort(), agent=PlaceholderAgent(), repo=repo, now=lambda: datetime.min, **overrides)
|
||||
return env, repo
|
||||
|
||||
|
||||
def test_context_has_reply_language_default():
|
||||
assert DifyBuilderContext().reply_language == ""
|
||||
|
||||
|
||||
def test_localize_items_called_on_commit_with_reply_language():
|
||||
seen = {}
|
||||
|
||||
def localize(items, language):
|
||||
seen["language"] = language
|
||||
for it in items:
|
||||
if it.payload.get("text") == "PING":
|
||||
it.payload["text"] = "PONG"
|
||||
return items
|
||||
|
||||
env, repo = _env(localize_items=localize)
|
||||
runner = Runner(env, fix_registry() | build_registry() | edit_registry())
|
||||
s = Session(app_id="app", tenant_id="t", owner_account_id="a",
|
||||
entry_mode=EntryMode.FIX, current_state=PcState.FIX_AWAIT_DECISION)
|
||||
fc = DifyBuilderContext(reply_language="zh-Hans")
|
||||
repo.create_session(s, fc, [])
|
||||
# a stop action produces a settled commit with no items -> localize still invoked
|
||||
runner.advance(s.id, Turn(action=Action(kind="stop", base_version=1), actor=Actor(account_id="a", tenant_id="t")))
|
||||
assert seen.get("language") == "zh-Hans"
|
||||
|
||||
|
||||
def test_detect_language_sets_reply_language_from_message_text():
|
||||
detected = []
|
||||
|
||||
def detect(text):
|
||||
detected.append(text)
|
||||
return "ja"
|
||||
|
||||
env, repo = _env(detect_language=detect)
|
||||
runner = Runner(env, fix_registry() | build_registry() | edit_registry())
|
||||
s = Session(app_id="app", tenant_id="t", owner_account_id="a",
|
||||
entry_mode=EntryMode.FIX, current_state=PcState.FIX_AWAIT_DECISION)
|
||||
repo.create_session(s, DifyBuilderContext(), [])
|
||||
runner.advance(
|
||||
s.id,
|
||||
Turn(action=Action(kind="message", payload={"text": "テスト", "client_turn_id": "t1"}, base_version=1),
|
||||
actor=Actor(account_id="a", tenant_id="t")),
|
||||
)
|
||||
_, fc = repo.get_session(s.id)
|
||||
assert fc.reply_language == "ja"
|
||||
assert "テスト" in detected
|
||||
Reference in new issue
Block a user