mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
Spec N4 has the build-complete card headline read "<App name> is ready", with the annotation 名字只在回复句里念一次;记录卡上不出现 -- said once, in that sentence, and never repeated on the completion receipt. We said it nowhere: the card was titled "Build plan" and the engine had no app name to say. DifyBuilderContext now carries app_name, seeded at session creation through a get_app_name_fn seam alongside the existing revision/authorize ones. Env.rename_app returns the name the database actually took rather than None, so the refine records what landed instead of what it proposed -- the service normalizes after the engine hands off, and the two can differ. The headline is interpolated, so it gets a strings.py template or it would survive localization untranslated. That pattern matches almost anything ending in "is ready", so it sits last and every more specific template gets first refusal. The receipt already omitted the name; there is now a test pinning that. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
67 lines
2.6 KiB
Python
67 lines
2.6 KiB
Python
"""Store the Builder's proposed app name (spec N1).
|
|
|
|
The seam between the model's proposal and the app row. Strictly best-effort: a
|
|
build that is going fine must not fail because a cosmetic rename did, so every
|
|
failure is logged and swallowed and the app keeps its derived name.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
from core.dify_builder import naming
|
|
from extensions.ext_database import db
|
|
from models.model import App
|
|
from services.app_service import AppService
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def rename_app_from_proposal(*, app_id: str, tenant_id: str, account_id: str, proposed: str) -> str:
|
|
"""Store ``proposed`` as the app's name if usable; return what the app is now called.
|
|
|
|
Returns "" when nothing was stored, so the caller never records a name the
|
|
database does not have. Never raises. Opens its own session: this runs in
|
|
the Celery advance task, outside any Flask request.
|
|
"""
|
|
name = naming.normalize_proposed_name(proposed)
|
|
if not name:
|
|
return ""
|
|
try:
|
|
factory = sessionmaker(bind=db.engine, expire_on_commit=False)
|
|
with factory() as session:
|
|
app = _load_app(session, app_id=app_id, tenant_id=tenant_id)
|
|
if app is None:
|
|
return ""
|
|
if app.name == name:
|
|
return name
|
|
AppService().rename_app(app, name, account_id=account_id, session=session)
|
|
return name
|
|
except Exception:
|
|
# The app keeps its derived name; the card keeps its generic title.
|
|
logger.warning("dify_builder: renaming app %s failed; keeping the derived name", app_id, exc_info=True)
|
|
return ""
|
|
|
|
|
|
def current_app_name(*, app_id: str, tenant_id: str) -> str:
|
|
"""The app's stored name, or "" if it cannot be read. Never raises.
|
|
|
|
Seeds the Builder context so the build-complete card can name the app
|
|
(spec N4) without the engine reaching for the database itself.
|
|
"""
|
|
try:
|
|
factory = sessionmaker(bind=db.engine, expire_on_commit=False)
|
|
with factory() as session:
|
|
app = _load_app(session, app_id=app_id, tenant_id=tenant_id)
|
|
return app.name if app is not None else ""
|
|
except Exception:
|
|
logger.warning("dify_builder: reading the name of app %s failed", app_id, exc_info=True)
|
|
return ""
|
|
|
|
|
|
def _load_app(session: Session, *, app_id: str, tenant_id: str) -> App | None:
|
|
# Tenant-scoped: the actor's tenant is the only authority this path carries,
|
|
# so a cross-tenant id must find nothing rather than rename.
|
|
return session.scalar(select(App).where(App.id == app_id, App.tenant_id == tenant_id))
|