mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 15:19:46 +08:00
* checkout api handlers
* add missing table
* update to use "version" terminology for flows instead of outdated "history" verbage
* Fix provider account id mapping
* recover a little todo comment
* Add flow version migration and minor exception handling, etc
1. deployments.py — Added AuthenticationError import + handling (401) in all 9 error handler blocks. Added update_deployment_db import + call to persist name changes to local DB after adapter update succeeds.
2. flow_version/exceptions.py — Added FlowVersionDeployedError for blocking deletion of deployed versions.
3. flow_version/crud.py —
- Added has_deployment_attachments() helper that checks if a flow version has any deployment attachments
- delete_flow_version_entry() now raises FlowVersionDeployedError if the version is attached to deployments
- Pruning now excludes deployed versions via a NOT IN subquery on FlowVersionDeplottachment, preventing provider-side snapshot orphaning
4. flow_version/__init__.py — Exports FlowVersionDeployedError
5. flow_version.py (API route) — Imported FlowVersionDeployedError, added it to _translate_version_error as 409 Conflict
6. models/__init__.py — Registered FlowVersionDeploymentAttachment for alembic/SQLModel metadata detection
7. Migration c0d2ce43b315 — Creates flow_version_deployment_attachment table with all columns, FKs (CASCADE), and indexes. Single head, chains off fc7f696a57bf.
* [autofix.ci] apply automated fixes
* address bugs and inconsistencies
* rename column to "provider_snapshot_id"
* harden deployment API: fail loudly on invalid state instead of silently passing
Replace silent fallbacks, warning logs, and dropped values with hard
failures (422/500/502) across the deployment orchestration layer. Adds
logging to bare exception handlers and wraps materialize_snapshots in
the adapter error handler.
* [autofix.ci] apply automated fixes
* add materialize_snapshots to deployment service protocol
Promotes materialize_snapshots from duck-typed getattr usage to a
first-class method on DeploymentServiceProtocol, BaseDeploymentService,
and the no-op DeploymentService stub. Introduces MaterializeSnapshotsResult
schema for typed return values.
Updates deployments.py to call the method through the protocol instead of
getattr, giving static analysis coverage over the contract.
Documents the snapshot abstraction across BaseFlowArtifact, SnapshotItem,
MaterializeSnapshotsResult, and FlowVersionDeploymentAttachment.provider_snapshot_id
— explaining that snapshots are immutable, provider-owned copies of flow data
with opaque provider-assigned identifiers (e.g. wxO tool ID, K8s ConfigMap
name, S3 key).
* deployment sync: extract helpers, server-side type filter, orphan detection
- Extract _fetch_provider_resource_keys helper for provider validation
- Rename _sync_page_with_provider → _list_deployments_synced
- Match resource keys by provider ID only (not name)
- Restore server-side deployment_type filter with guard against
false deletions (skip rows whose type doesn't match the filter
instead of deleting them)
- Add orphan/divergence logging for post-create, post-update,
post-duplicate DB write failures
- Return 422 on invalid UUID in update remove list (was silently ignored)
- Handle NotImplementedError → 501 from provider adapters
- Convert attachment IntegrityError to ValueError with descriptive message
- Add tests for sync helpers (15 cases)
* rebase on release-1.9.0 and align with lfx/services
* refactor(deployments): align provider mapper routing and WXO update payload mapping
Align deployment mapper resolution with adapter-type/provider-key routing and
refactor PATCH update handling to use mapper resolve/shape contracts end-to-end.
Map Langflow flow_version_id references at the API boundary into provider update
operations for Watsonx bind/unbind/remove paths, with expanded mapper tests.
* patch down-revision
* first pass with formalized boundary rules
* fix(deployments): harden watsonx payload boundary contracts
Enforce fail-fast payload slot parsing for required adapter results, split execution create/status slot contracts, and route execution-create mapping through deployment mappers.
Require watsonx flow artifact source_ref and move update reconciliation output to provider_result to keep mapper/adapter boundaries explicit and typed.
* refactor(deployments): modularize watsonx orchestrate create/update flow
Extract create/update logic into dedicated core modules with shared helpers to tighten deployment boundary contracts.
Align backend/lfx payload schema mapping and expand e2e/unit coverage for response mapping and update schema behavior.
* api impl for wxo-specific create payload
* further refactoring. add rollback of existing tools (undo new app bindings) in the create path
* add todo in execution.py
* refactor(deployments): replace snapshot_id/reference_id with source_ref-correlated tool refs
Introduce WatsonxToolRefBinding to correlate source_ref (flow version id)
with provider tool_id across all operation types. This replaces the prior
reference_id and snapshot_id fields with a unified structure that carries
provenance through create, bind, unbind, and remove_tool operations.
Key changes:
- Flatten API operation payloads: hoist flow_version_id onto operations,
remove nested WatsonxApiUpdateToolReference wrapper
- Replace tools.existing_ids with inline tool_id_with_ref on bind operations
- Rename WatsonxCreateSnapshotBinding to WatsonxToolRefBinding (input) and
WatsonxResultToolRefBinding (output, with created flag)
- Add created_app_ids to update results for connection tracking
- Raise HTTPException on contract violations in _to_api_tool_app_bindings
instead of silently dropping unmappable bindings
- Add schema-level validation for conflicting source_ref on same tool_id
- E2E: cache tool_id→source_ref from create results, use helpers to build
refs with distinct source_ref vs tool_id values
* Enforce DeploymentType enum and add description column
Make deployment_type a required column backed by a SQLAlchemy
TypeDecorator that validates on write (rejects None and invalid strings)
and coerces to DeploymentType on read. Add nullable description column
to the deployment model and surface it through the API.
Key changes:
- Add _DeploymentTypeColumn TypeDecorator for enum round-trip fidelity
- Make deployment_type non-optional in Deployment model, DeploymentRead,
CRUD functions, and API layer
- Add description (Text, nullable) to Deployment model and fold its
migration into the existing c0d2ce43b315 revision
- Remove _resolve_deployment_type helper — TypeDecorator handles coercion
- Remove DeploymentType fallbacks and backward-compat shims from API
endpoints, base mapper, and watsonx orchestrate mapper
- Document cross-package coupling: DeploymentType is owned by lfx but
persisted by langflow; member values must never be removed
- Fix pre-existing bug: provider_account_id → deployment_provider_account_id
in get_deployment_status endpoint
Note: deployment_type is nullable=True at the DB level to satisfy the
EXPAND-phase migration validator; NOT NULL is enforced at the application
layer by the _DeploymentTypeColumn TypeDecorator.
* refactor(deployments): extract route helpers, harden sync and error handling
Move bulk of deployment route logic into mappers/helpers layer to slim
down deployments.py and enforce clearer boundary between routes, mappers,
and adapters (documented in DEPLOYMENT_BOUNDARY_RULES.md).
Key changes:
- Extract ~700 lines from deployments.py into helpers.py (pagination,
adapter/mapper resolution, attachment management, snapshot sync,
rollback, response shaping)
- Add read-path snapshot-level sync: get_deployment and
list_deployments_synced verify provider_snapshot_ids against the
provider and prune stale attachments, with graceful fallback on error
- Add compensating rollback for create (rollback_provider_create) and
update (rollback_provider_update) when DB commit fails after provider
mutation, using mapper-driven payload reconstruction
- Introduce handle_adapter_errors() context manager centralising
DeploymentServiceError → HTTP status mapping via
http_status_for_deployment_error; sanitise 500 detail to avoid
leaking internals
- Add DeploymentNotConfiguredError → 503 mapping
- Add util_snapshot_ids_to_verify and resolve_rollback_update to base
mapper with WxO overrides for provider-specific snapshot ID extraction
and put_tools-based rollback payloads
- Add put_tools field to WatsonxDeploymentUpdatePayload for full tool
list replacement; early-return in build_provider_update_plan and
validate_operation_references when put_tools is set
- Extract verify_tools_by_ids into core/tools.py helper
- Harden resource_name_prefix with strip_whitespace + min_length=1
- Deduplicate snapshot_ids before provider calls
- Add deterministic order_by(created_at) to attachment CRUD queries
- Add exc_info=True to all best-effort rollback/compensate error logs
- Add session.rollback() in get_deployment snapshot sync error path
- Warn when list_snapshots receives both deployment_ids and snapshot_ids
- Add E2E scenarios for empty snapshot list, mixed snapshot IDs, tools
endpoint, and deployment re-list after update
Tests:
- Add test_deployment_route_handlers.py covering stale-row delete +
commit, non-404 adapter errors, handle_adapter_errors wiring,
snapshot sync (happy path, skip, error fallback), project-scoped
flow version validation for create and update
- Expand test_deployment_sync.py with snapshot-phase tests, rollback
tests, pagination guard, and project-scoped validation
- Add deployment_type assertion to response mapping test
- Add DeploymentNotConfiguredError and bare DeploymentServiceError cases
to exception mapping tests
- Add put_tools schema and update plan tests
* remove pompous performance commentary
* fix(deployments): remove upsert behavior and fail fast on duplicate name
The create_deployment endpoint previously performed a
get_deployment_by_resource_key lookup before inserting the DB row,
silently tolerating duplicates. Since the provider adapter returns a
fresh resource ID on every create, this lookup could never legitimately
match — and if it did, it would mask a data inconsistency bug.
Changes:
- Remove the get-or-create (upsert) pattern; go straight to
create_deployment_db and let the unique constraint surface conflicts.
- Add deployment_name_exists CRUD function and an early 409 guard so
duplicate names are rejected before any provider call, avoiding
costly provider-side rollback for a locally-checkable condition.
- Update existing route-handler tests to reflect the removed lookup.
- Add tests for deployment_name_exists and the 409 duplicate-name path.
* feat: convert provider_key and deployment_type columns to DB-level enums
Replace plain string columns with SQLAlchemy Enum types backed by
Postgres/SQLite enum constraints, enforcing valid values at the DB
layer rather than only in application code.
Migration follows expand-contract pattern (add enum column, backfill,
drop old string column, rename) with index ops outside batch context
to avoid SQLite column-lookup issues. Upgrade and downgrade are fully
atomic.
- Add DeploymentProviderKey enum as single source of truth for
provider identifiers; remove magic strings and _DeploymentTypeColumn
TypeDecorator
- Make provider_key immutable after creation (remove from update
request schema and API handler)
- Fix pre-existing test gap: add missing deployment_type argument to
all TestDeploymentCRUD create_deployment() calls
* feat(flow-versions): add deployment awareness and sync-on-read
Add is_deployed field to flow version responses and provider-verified
sync to the list/get read paths, matching the existing deployment
endpoint sync pattern.
API changes:
- list_flow_versions returns is_deployed per entry and accepts optional
deployment_ids filter to scope by specific deployments
- get_single_flow_version returns is_deployed on the full response
- Both endpoints now use write sessions and run best-effort
snapshot-level sync before returning results
Sync-on-read (helpers.sync_flow_version_attachments):
- Queries all attachments for a flow's versions joined with deployment
and provider account info in a single query
- Groups by provider, resolves adapter/mapper per group, verifies
provider_snapshot_ids via list_snapshots, and prunes stale attachment
rows through the existing sync_attachment_snapshot_ids helper
- Per-provider error handling so one provider outage doesn't block the
response
New CRUD: list_attachments_for_flow_with_provider_info joins
FlowVersionDeploymentAttachment -> Deployment -> DeploymentProviderAccount
to avoid N+1 queries during sync grouping.
Tests: 50 passing (11 new) covering is_deployed indicator, deployment_ids
filtering, stale attachment pruning on list/get, and sync failure
resilience.
* Add user id authentication to a few missing endpoints
* [autofix.ci] apply automated fixes
* Update tests
Removes some mock tests that did nothing
Adds sqlite for true testing of db accessors
Reduces Mock objects usage
* refactor(deployments): enforce ownership boundaries on execution responses
Move provider-owned execution identifiers (execution_id, agent_id,
status, timestamps, errors) out of the top-level API response and into
provider_data, keeping only Langflow-owned fields (deployment_id) at the
top level. This prevents future collisions if Langflow introduces its
own execution tracking.
Key changes:
- Remove execution_id from _ExecutionResponseBase; provider's opaque
run identifier now lives exclusively inside provider_data
- Rename WatsonxExecutionResultData → WatsonxAgentExecutionResultData
(adapter layer) and split the API-layer class into a private base
(_WatsonxApiAgentExecutionResultBase) with dedicated
WatsonxApiAgentExecutionCreateResultData and
WatsonxApiAgentExecutionStatusResultData subclasses
- Translate WXO run_id → execution_id at the adapter boundary
(create_agent_run_result / get_agent_run).
- Collapse util_execution_id + util_execution_deployment_resource_key
into a single util_resource_key_from_execution that trusts the
adapter-provided result.deployment_id directly
- Remove build_orchestrate_runs_query and extra payload fields
(thread_id, llm_params, guardrails, etc.) unused in MVP
- Simplify WxOClient.post_run signature (drop query_suffix)
- Exclude provider_data from flow tool artifact to avoid unexpected
top-level keys in the WxO tool runtime
- Document ownership boundary rules in DEPLOYMENT_BOUNDARY_RULES.md §14
- Add E2E polling for terminal execution status, input format variants,
and missing-deployment negative test
- Expand unit tests for renamed schemas, field mapping, passthrough
validation, and simplified payload builder
* feat: add name column to deployment_provider_account
Add a required, user-chosen display name to provider accounts
(e.g. "staging", "prod") that is unique within a given provider_key.
Includes model, CRUD, API schema/route, mapper, migration with
backfill, and tests.
* fix: replace hand-written migration with Alembic-generated revision
Replace the manually authored migration (b4e6f8a2c1d3) with an
Alembic-generated one (8255e9fc18d9) for the deployment_provider_account
name column.
Fix SQLite compatibility: sa.func.concat() generates a concat() function
call which does not exist in SQLite. Use sa.literal().concat() instead,
which produces the || operator and works on both PostgreSQL and SQLite.
* remove bogus unverified math from migration file
* feat: verify provider credentials before account creation
Add a verify_credentials step to the provider account creation flow
that validates API keys against the provider before persisting them.
This prevents storing invalid or revoked credentials and gives users
immediate feedback.
Key changes:
- Add verify_credentials to the deployment adapter interface (base,
service, protocol) with WXO implementation that obtains a token
via the IBM authenticator
- Add SSRF-hardened URL validation for provider_url (HTTPS-only,
private IP blocklist, localhost rejection, normalization)
- Introduce ValidatedUrl/ValidatedUrlOptional annotated types in
the API schema layer
- Refactor raise_for_status_and_detail to accept an optional cause
parameter for explicit exception chain control
- Use ResourceNotFoundError (parent) instead of DeploymentNotFoundError
in raise_for_status_and_detail for provider-agnostic 404 mapping
- Narrow get_authenticator return type to concrete union
* use whitelist only for valid urls
* feat: BREAKING: move credentials into provider_data and centralize update logic in mapper
- Replace top-level `api_key: SecretStr` with opaque `provider_data: dict` in API schemas;
mapper extracts credentials via `resolve_credential_fields` for DB storage
- Add `resolve_provider_account_update` to base mapper so routes delegate
full update-kwargs assembly (including cross-field logic) to the mapper
- WXO mapper override re-derives `provider_tenant_id` when `provider_url` changes
- Add tenant extraction utilities and `validate_tenant_url_consistency` in
`deployment_provider_account/utils.py` as single source of truth
- Add `model_validator` on `DeploymentProviderAccount` for defense-in-depth
tenant/URL consistency checks
- Rename `DEPLOYMENT_BOUNDARY_RULES.md` → `RULES.md`; document DB-direction
mapper contract, credential flow, and update assembly
- Update all tests for new `provider_data` shape, mapper update methods,
tenant extraction, and model-level consistency validation
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* fix(deployments): Harden provider account validation and WXO rollback
Use provider_url when resolving WXO credentials and scope provider account names per user within each provider. Re-verify provider account updates before persisting them and return 4xx responses for account conflicts instead of surfacing 500s.
Block deleting provider accounts that still own deployments and extend create rollback so failed WXO writes clean up provider-side resources. Add route, schema, mapper, sync, CRUD, and WXO tests to cover the new behavior.
* [autofix.ci] apply automated fixes
* update url validator docs; remove outdated reference to private url blacklist logic
* fix(deployments): Isolate snapshot sync writes
Use nested transactions around best-effort snapshot cleanup so provider sync failures cannot leak partial attachment deletes into the outer request transaction.
Persist explicit description clears during deployment PATCH requests, and add regression tests for both deployment sync paths and the route handler update flow.
* fix(deployments): Harden delete cleanup and wxO create tests
Treat missing provider agents as stale local cleanup so delete can
finish instead of leaving orphaned deployment rows behind.
Commit local delete operations eagerly, retry once on commit failure,
and move wxO create-path tests toward fake client objects so the real
service logic is exercised without external calls.
* new head
* fix(deployments): stop prefixing wxo raw connection app_ids
Preserve caller app_ids for newly created wxo connections while keeping lf_ prefixing for tool/deployment naming, centralize resource_name_prefix validation, and update mapper/service schema tests and docs to reflect the new behavior.
* fix(deployments): Harden provider account cleanup
Reconcile stale deployment rows before provider-account deletion so
out-of-band provider removals do not leave account cleanup blocked.
* fix: restore py310 compatibility and align payload slot tests
Import Self from typing_extensions in the deployment provider account model to keep backend imports working on Python 3.10, and update payload formalization tests to assert strict rejection of cross-model BaseModel inputs.
* fix(deployments): resolve mypy typing regressions across deployment flows
Normalize SQLModel query expressions for typed SQL operators, make deployment-related model IDs non-nullable where appropriate, and tighten provider mapper/update typing guards. Update deployment tests to align with stricter type contracts.
* fix(deployments): restore py310 test compatibility and migration idempotency
Replace Python 3.11-only UTC imports with timezone.utc in deployment-related tests and the watsonx e2e helper, and make the provider-account name migration safe to re-run when the column and unique constraint are created in separate steps.
* fix(deployments): restore provider account route ownership helpers
Reuse the shared provider-account lookup in update/delete routes so route tests keep the expected ownership path, and use the explicit-field helper when deciding whether to reverify credentials.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* [autofix.ci] apply automated fixes (attempt 3/3)
* fix(deployments): enforce deployment guard constraints with clean API errors
Add DB trigger guards to block deleting deployed flow versions, deleting projects with deployments, and moving deployed flows across projects. Translate trigger violations to HTTP 409 with sanitized details, add best-effort pre-operation deployment/snapshot sync to reduce stale-state false positives, and cover guard parsing/delete flush behavior with tests.
* checkout latest crud and helpers files
* make helper for try catch sync logic
* add gaurd to prevent moving deployments across projects
* prevent moving deployments across provider accounts
* add live sqlite tests
* recover lost changes
* recover feature flag setup from release-1.9.0
* fix: address review findings for deployment guard branch
- Add comment in delete_user explaining why best-effort provider sync
is intentionally skipped (too expensive; DB trigger is authoritative)
- Add docstring to _clean_guard_detail for maintainability
- Add happy-path trigger tests verifying operations succeed when no
deployment conflicts exist (flow version delete, project delete,
flow move, deployment rename)
- Fix list_attachments_for_flow_with_provider_info callers still using
the old flow_id= parameter (now flow_ids=)
- Fix pre-existing broken test fixture (missing name in
DeploymentProviderAccount)
* feat: add cross-project attachment guard and harden test suite
Add a new DB trigger (prevent_cross_project_attachment) that blocks
attaching a flow version to a deployment in a different project, for
both PostgreSQL and SQLite.
Refactor trigger tests into a unified dual-dialect test file
(test_deployment_guard_triggers.py) that runs against both SQLite
(always) and PostgreSQL (when TEST_DEPLOYMENT_GUARD_PG_URL is set).
Validated with both asyncpg and psycopg drivers.
Additional improvements:
- test_downgrade_removes_triggers now asserts the guard is active
before downgrade, preventing false-positive passes
- Endpoint test uses statement inspection instead of fragile
call-count gating
- Add exception parser coverage for CROSS_PROJECT_ATTACHMENT,
implicit __context__ chains, and cyclic exception chains
- Add endpoint tests for cascade_delete_flow, update_flow,
delete_multiple_flows, and global exception handler
* fix(deployments): make provider account identity immutable
Add DB deployment guard triggers to prevent updating provider account identity fields
(provider_key, provider_tenant_id, provider_url) once created, and preserve guard
error translation in the provider-account PATCH route. Extend trigger and route
tests to cover blocked updates and same-value no-op updates.
* refactor: extract sync logic into dedicated module and fix sync correctness issues
- Move deployment/attachment sync functions from helpers.py to new sync.py
module, reducing helpers.py size and improving separation of concerns
- Fix fetch_provider_resource_keys to use deployment_ids instead of
provider_params (pre-existing bug: wrong adapter parameter)
- Add empty-list guard to fetch_provider_resource_keys to avoid
unnecessary provider calls
- Wrap snapshot sync in savepoint for proper fault isolation
- Update imports across flows.py, projects.py, flow_version.py,
deployments.py to reference new sync module
- Update unit tests to target refactored module paths and add coverage
for bug fixes
* refactor: implement binding-aware deployment sync and optimistic guard retries
Add provider binding contract and mapper extraction path:
- add ProviderSnapshotBinding in deployments/contracts.py
- add BaseDeploymentMapper.extract_snapshot_bindings() default no-op
- implement Watsonx mapper extract_snapshot_bindings() with strict provider_data parsing via PayloadSlot (snapshot_ids required)
Replace attachment snapshot verification loop with binding-aware set-based cleanup:
- add delete_unbound_attachments(user_id, deployment_ids, bindings) in flow_version_deployment_attachment/crud.py
- uses CTE/anti-join to delete local attachments not present in provider bindings
- when bindings are empty, delete all attachments for provided deployment_ids
- add count_attachments_by_deployment_ids() grouped recount helper
- update list_deployments_synced() in deployments/helpers.py to:
- collect bindings from fetch_provider_resource_keys() provider_view during batch sync
- run one savepoint-wrapped delete_unbound_attachments() for accepted deployments
- run one grouped recount and patch attached_count from DB
- remove list_attachments_by_deployment_ids + snapshot-provider phase
- enforce two sync rounds max (initial pass + one refill)
Consolidate provider-side sync plumbing:
- change fetch_provider_resource_keys() in deployments/sync.py to return (known_resource_keys, provider_view)
- document tuple contents in function docstring
- remove now-unused sync_provider_attachment_snapshots() helper
- keep fetch_provider_snapshot_keys()/sync_attachment_snapshot_ids() for single-deployment read path
Scope read sync by provider account and conditionally expose deployment status:
- update deployment CRUD listing helpers to accept provider_account_id filter
- in flow_version API, add deployment_provider_account_id query param
- only perform deployment sync/is_deployed calculation when provider id is explicitly passed
- make FlowVersion.is_deployed optional (default None) so field can be omitted when not requested
Switch mutating APIs to optimistic catch-sync-retry pattern:
- add _retry_on_deployment_guard() in flows.py and projects.py
- replace pre-sync mutation flow with guarded retry on DeploymentGuardError
- for project create/update, retry only guarded flow-move DB operations
- preserve side-effect safety and improve stale-state recovery behavior
Tests:
- add unit tests for deployment guard retry helper (test_deployment_guard_retry.py)
- add unit tests for delete_unbound_attachments behavior, including empty bindings delete-all case (test_flow_version_deployment_attachment_crud.py)
- update deployment sync tests for tuple return, binding extraction, consolidated phase-2 cleanup/recount, and two-round refill behavior
- update flow_version tests for provider-scoped sync and conditional is_deployed field presence
* fix: extract tool_ids from provider data
* dont use stale rolled-back OORM Flow instance
* refactor(deployment): demote delete triggers and standardize guard handling
- Edit the existing deployment guard migration to remove DB delete triggers for flow version and project deletion in both PostgreSQL and SQLite.
- Keep core DB invariants (flow move, deployment project move, deployment provider account move, provider account identity immutability, and cross-project attachment).
- Add a new DB invariant trigger to enforce deployment.resource_key immutability.
- Rewrite trigger error text to technical/operator-focused language and remove ambiguous “detach” wording.
- Add migration comments documenting the current global provider identity guard and the future provider-specific migration path.
- Add app-level guard helpers in deployment/guards.py:
- check_flow_has_deployed_versions
- check_project_has_deployments
- Wire check_flow_has_deployed_versions into cascade_delete_flow before destructive deletes.
- Wire check_project_has_deployments into delete_project before deleting the folder row.
- Keep explicit DeploymentGuardError passthrough in cascade_delete_flow and retain parse fallback for non-guard exceptions.
- Refactor DeploymentGuardError to carry:
- code
- technical_detail
- detail (API-friendly message)
- Add friendly guard-detail mapping by guard code.
- Update parse_deployment_guard_error to parse DEPLOYMENT_GUARD:<CODE>:<DETAIL> and preserve raw technical detail from chained exceptions.
- Simplify delete_user to DB-cascade delete + flush only.
- Remove deployment guard translation/parsing logic from delete_user.
- Keep an explicit endpoint comment documenting the intentional no-provider-teardown trade-off.
- Update flow_version deletion message to: “Remove its deployment attachment rows first.”
- Update tests to match the new guard contract and behavior:
- Remove stale delete_user guard-translation endpoint test.
- Update delete endpoint tests to assert app-level guard behavior and new friendly details.
- Update trigger tests to reflect removed delete triggers and added resource_key trigger coverage.
- Update trigger downgrade assertions for the revised trigger set.
- Expand exception tests for code mapping, technical-detail preservation, unknown-code fallback, and new guard codes.
- Add unit tests for deployment guard helper functions.
* surface better msg when project deletion attempted
* refactor(deployment): rename flow guard code and align guard messages
Rename FLOW_VERSION_DEPLOYED to FLOW_HAS_DEPLOYED_VERSIONS across deployment guard checks, project-level remapping, and tests.
Update friendly guard wording for flow/project/move/cross-project cases, and convert pytest match patterns to raw escaped regex strings to satisfy Ruff.
* add docs to migration file
* update docs in migration
* update docs
* refactor(flow-version): scope deployment status to list endpoint
Keep deployment status provider-scoped on version listing only, remove single-version sync/status behavior, and omit is_deployed from snapshot creation responses while aligning docs and tests.
* refactor(deployment): centralize guard retry helpers and align sync error handling
Move flow/project deployment-guard retry wrappers into deployment sync helpers while preserving upstream route logic, and align provider sync error mapping with deployment domain exceptions. Tighten wxo snapshot binding validation and update docs/tests for guard messaging and sync behavior.
* refactor(deployment): enforce snapshot ID invariants and improve delete error UX
- add shared non-empty string guard (`require_non_empty`) in database utils and use it in deployment attachment CRUD write paths
- enforce required `provider_snapshot_id` on attachment model and add Pydantic + SQLAlchemy validators as safety nets for non-CRUD writes
- introduce deployment attachment key schemas (`DeploymentAttachmentKey`, `DeploymentAttachmentKeyBatch`) with deduplication support
- add batch stale-row cleanup (`delete_deployment_attachments_by_keys`) using a VALUES CTE to delete exact deployment/flow-version pairs
- remove mapper-level `util_snapshot_ids_to_verify` hook from base + watsonx mapper and replace with sync utilities:
- `extract_verified_snapshot_ids`
- `extract_verified_provider_snapshot_ids`
- update sync and helper flows to use verified snapshot IDs, stricter provider ID validation, and safer corrected-count handling
- align provider ID handling so both deployment and snapshot sync paths reject blank provider IDs consistently
- tighten adapter/provider-key validation and enforce required snapshot binding during attach flow-version operations
- update deployment routes/helpers to new sync signatures and behavior
- remove legacy null/blank snapshot safety-net filters from attachment queries/delete-unbound reconciliation (intentional invariant enforcement)
- add broad backend test coverage for:
- rejecting empty/blank snapshot IDs on create/update
- exact-key batch deletion behavior
- cartesian cross-delete regression prevention
- updated route/sync expectations after helper refactor
- improve flow delete UX in the frontend list view by surfacing backend `detail` via `getAxiosErrorMessage` instead of a generic "Please try again"
* update logic and remove stale comment
* fix(deployment): handle orphaned deployment attachments and enforce SQLite FK constraints
Enable PRAGMA foreign_keys=ON for SQLite to enforce referential integrity
that was previously silently ignored, and add application-level cleanup for
orphaned FlowVersionDeploymentAttachment rows left behind by environments
where cascades never fired.
- Enable foreign_keys pragma in lfx SQLite settings
- Update deployment guards to detect and prune orphan attachments (missing
deployment parent) before blocking on live ones, with structured logging
- Update flow_version and deployment CRUD queries to join on Deployment so
is_deployed status and attachment counts reflect only live attachments
- Document has_deployment_attachments write side-effect (orphan pruning)
- Wrap pre-sync orphan cleanup in try/except so failures don't abort the
sync process
- Explicitly delete spans and traces in cascade_delete_flow to avoid FK
violations from the span.trace_id constraint (which lacks ON DELETE
CASCADE in the DDL)
- Add in-memory SQLite integration tests covering guard pruning, orphan
cleanup, version pruning, deployment counts, and is_deployed status
- Add unit tests for sync exception-handling paths
* refactor(deployment): replace DB trigger guards with ORM-layer preflight checks
Remove the unshipped trigger migration (97c9a98c9c01) and enforce
deployment invariants at the ORM/service layer instead:
- Add orm_guards.py with preflight checks for all six guard codes
(FLOW_DEPLOYED_IN_PROJECT, DEPLOYMENT_PROJECT_MOVE, DEPLOYMENT_TYPE_UPDATE,
DEPLOYMENT_RESOURCE_KEY_UPDATE, DEPLOYMENT_PROVIDER_ACCOUNT_MOVE,
DEPLOYMENT_PROVIDER_ACCOUNT_IDENTITY_UPDATE, CROSS_PROJECT_ATTACHMENT)
- Wire guards as pure prefix blocks into deployment, provider-account,
and attachment CRUD functions — existing write logic unchanged
- Add preflight validation before bulk update(Flow) statements in
projects.py and folder/utils.py — existing statements unchanged
- Add ensure_flow_move_allowed to flows_helpers.py for ORM-instance
update paths (_patch_flow, _update_existing_flow, _validate_and_assign_folder)
- Optimize batch flow-move guard to one query per source folder
- Simplify parse_deployment_guard_error to isinstance-only chain walk
now that guards are raised explicitly as DeploymentGuardError
- Document retry helper contract: operations must enforce guards internally
- Replace trigger-centric tests with ORM/service-level guard tests
- Add project API guard integration tests for flow-move blocking
* fix(deployment): prune attachment rows before deployment deletes
Explicitly delete flow-version deployment attachments before deployment row deletes
to prevent orphaned attachments when FK cascades are disabled (e.g. SQLite
foreign_keys=OFF). Keep delete-by-resource-key and delete-by-id behavior intact
while documenting guard None-scope behavior and adding coverage for missing-row
and FK-disabled cleanup paths.
* recover lost tests
* misc
* remove deployment guard error extraction from lfx session scope
* extract re-raise logic into a helper
* fix: stabilize deployment guard retries and guard diagnostics
- scope WxO provider-client ContextVar state to each deployment provider scope via provider_clients_memoization_scope
- compose WxO scope lifecycle into deployment_provider_scope and document forward multi-adapter dispatch options
- add successful provider resource-key sync debug logging and remove stray print-based sync error debug
- log DeploymentGuardError details during flow cascade delete and in deployment guard exception re-raise helper
- fix folder defaulting guard precheck query to use Flow.folder_id.is_(None) so NULL-folder flows are included
- deduplicate require_non_empty by importing from langflow.services.database.utils in deployment mappers
- remove deprecated duplicate helper module at api/v1/mappers/deployments/util.py and keep package export wired to canonical helper
- add regression test for default-folder guard precheck covering NULL-folder flow handling
* add tests for provider scope context
* fix: improve deployment guard logging and async guard handling
Log DeploymentGuardError directly in flow/project endpoint guard catch blocks with operation context, and use the async guard helper for generic exception paths that need guard parsing. Add tests for async guard helper logging and raise behavior.
* less verbose name for scope
* harden delete_unbound_attachments by passing provider account id to it to scope deletion
* fix: return dense deployment attachment counts and simplify snapshot sync
Make `count_attachments_by_deployment_ids` return a dense mapping for all
requested deployment ids, including zero for ids with no countable attachments.
Update deployment listing code to rely on direct key access for corrected counts,
remove redundant `verified_snapshot_ids` plumbing from snapshot sync call sites,
keep local snapshot id integrity checks in sync logic, and align backend tests
plus log messaging with the new count contract.
* guard context scope
* test: expand deployment guard and sync regression coverage
Add comprehensive regression coverage for deployment guard and deployment-sync behavior across database and API test suites.
- verify deployment deletes remove linked attachment rows
- cover batched move guards and immutable deployment/provider fields
- validate cascade flow deletion and user-scoped orphan cleanup behavior
- exercise retry helper edge cases for flow/project guard retries
- align sync helper tests with stricter provider-id/error handling and dedupe/early-return paths
- add API 409 guard-path tests for deployed flow/project delete and move scenarios
* fix: align flow-version list semantics with provider-scoped deployment status
- remove `deployment_ids` handling from `GET /flows/{flow_id}/versions/` and keep plain list mode as versions-only
- enforce provider-account ownership for `deployment_provider_id` via `get_owned_provider_account_or_404` (404 on unknown/foreign ids)
- add explicit feature-flag guard for provider-scoped mode:
`Cannot use deployment_provider_id: the wxo_deployments feature flag is disabled`
- remove `has_providers` / `count_provider_accounts` branching and drive behavior directly from request params
- replace `get_flow_version_list` with `get_flow_versions_with_provider_status` and scope `is_deployed` by:
`Deployment.deployment_provider_account_id == provider_account_id`
- keep best-effort `sync_flow_version_attachments` before provider-scoped reads
- update API tests:
- delete deployment_ids endpoint tests
- add provider-scoped status regression coverage (true under provider A, false under provider B)
- add provider-id feature-flag rejection test (400)
- add unknown and foreign provider-id ownership tests (404)
- simplify feature-disabled plain-list test to assert `is_deployed` remains omitted
- migrate CRUD/in-memory tests off removed `get_flow_version_list` to `get_flow_versions_with_provider_status`
- update stale flow export TODO comments referencing the removed function
* fix: batch stale deployment cleanup per provider sync group
Replace per-deployment stale deletes in deployment sync with a single batched delete per provider group.
Add a bulk deployment-delete CRUD helper and regression coverage for grouped stale-delete behavior plus multi-deployment attachment cleanup.
* test: expand deployment guard and provider account update coverage
Add direct ORM guard coverage for noop immutable updates and attachment project matching. Update in-memory deployment tests to cover provider account API key rotation and align stale fixtures with current model constraints.
* fix: make deployment GET sync binding-aware and harden guard/prune behavior
- Align SQLite defaults in `lfx/services/settings/base.py`:
- remove forced `foreign_keys=ON` pragma from default sqlite pragmas
- Replace deployment GET snapshot-id verification with binding-aware reconciliation in `api/v1/deployments.py`:
- resolve and use the concrete deployment mapper in GET
- call mapper-provided `extract_snapshot_bindings_for_get(...)`
- prune detached links via `delete_unbound_attachments(...)` inside `session.begin_nested()`
- fall back safely to unverified attachment counts when sync is unsupported or fails
- change provider_data response shaping to `mapper.shape_deployment_get_data(...)` to hide internal fields
- Extend mapper contracts in `api/v1/mappers/deployments/base.py`:
- add `extract_snapshot_bindings_for_get(...)` (explicitly raises `NotImplementedError` by default)
- add `shape_deployment_get_data(...)` (explicitly raises `NotImplementedError` by default)
- make GET sync/shaping behavior explicit per provider instead of accidental passthrough
- Implement wxO-specific GET sync/shaping in `watsonx_orchestrate/mapper.py`:
- parse/validate `provider_data.tool_ids` for binding extraction
- emit `ProviderSnapshotBinding(resource_key, snapshot_id)` for each tool id
- validate/shape GET `provider_data` to expose only `{"llm": ...}` to API clients
- raise clear 500/value errors for malformed adapter payload contracts
- Update wxO adapter GET payload in `watsonx_orchestrate/service.py`:
- always include `tool_ids` and derived `environment`
- include `llm` when available
- remove `or None` fallback so mapper receives consistent structure
- Harden flow-version pruning in `services/database/models/flow_version/crud.py`:
- resolve concrete version IDs to prune first
- delete `FlowVersionDeploymentAttachment` children before deleting `FlowVersion` rows
- prevent stale doubly-orphan attachment rows when SQLite runs with foreign keys disabled
- Simplify guard exception semantics in `deployment/exceptions.py`:
- stop walking chained exceptions; only treat explicit `DeploymentGuardError` instances as guard failures
- add optional remap hook to `araise_if_deployment_guard_error_or_skip(...)`
- add `remap_flow_guard_for_project_delete(...)` to convert flow guard code to project guard code where needed
- Remove duplicated per-route guard catch blocks and centralize behavior:
- `api/utils/flow_utils.py`, `api/v1/flows.py`, and `api/v1/projects.py` now rely on shared async guard helper
- project delete path now remaps `FLOW_HAS_DEPLOYED_VERSIONS` into `PROJECT_HAS_DEPLOYMENTS` via shared remap function
- Keep multi-delete flow route behavior consistent in `api/v1/flows.py`:
- run guard remap/log helper in broad exception path before generic logging and 500 handling
- Update deployment exception tests in `test_exceptions.py`:
- remove chain-parsing expectations
- validate direct guard-raise semantics
- add remap function coverage
- Expand flow version pruning tests:
- `test_crud.py`: verify delete ordering (attachments first, versions second) and no-op behavior when nothing is pruned
- `test_in_memory.py`: add broad regression class proving orphan attachment cleanup, live deployment preservation, cross-flow/user isolation, and repeated-cycle convergence
- Expand deployment API/mapper/sync test coverage:
- `test_deployment_mapper_base.py`: assert new base mapper GET shaper raises until implemented
- `test_deployment_route_handlers.py`: migrate assertions to binding-aware sync behavior, unsupported-provider fallback, sync-failure fallback, and provider_data sanitization
- `test_deployment_sync.py`: add wxO mapper tests for `extract_snapshot_bindings_for_get(...)` and GET data shaping requirements
- `test_watsonx_orchestrate.py`: validate wxO GET provider_data now includes `tool_ids`/`environment` defaults and feature-flag-scoped provider context behavior
* remove redunant description
* remove agent environment from payload for get()
* fix(deployment): prune orphan attachments before reading in flow guard
The cascade flow delete path called `check_flow_has_deployed_versions`,
which issued a SELECT before any write. On SQLite that left the
connection holding only a SHARED lock; the subsequent cascade DELETEs
then had to upgrade SHARED -> RESERVED, which doesn't busy-wait when
another connection already holds RESERVED, surfacing intermittently
in CI as `OperationalError: database is locked`.
Reorder the guard to issue a single DELETE-with-scalar-subquery for
orphan attachments first, so the connection acquires the writer lock
up front. The live-attachment SELECT runs afterwards on the same
session. As a side benefit, orphan pruning now also runs when live
attachments coexist (previously the early raise short-circuited it,
though SAVEPOINT rollback in the retry wrapper still discards the
prune in that failure path).
Log the raw driver-reported `rowcount` verbatim on every prune for
debuggability, and update guard tests for the new DELETE-then-SELECT
ordering.
* add log that pruning might be rolled back
* fix(deployment): set provider scope when reconciling before provider-account delete
The DELETE /api/v1/deployments/providers/{id} handler called
list_deployments_synced without entering deployment_provider_scope, so the
WxO adapter raised CredentialResolutionError ("Deployment account context is
not available for adapter resolution"). Reconciliation was silently dropped
and the stale local count blocked every delete with a 409.
Wrap the reconciliation call in deployment_provider_scope(provider_account.id)
to match every other adapter call site in this module, so stale local
deployment rows can actually be pruned and the provider account can be
removed when the provider no longer owns any resources.
Add three regression tests in TestProviderAccountRoutes:
- assert deployment_provider_scope is active (with the right provider id)
during reconciliation, and that the context does not leak afterwards
- assert that a CredentialResolutionError from reconciliation falls back to
the local count and returns 409 without deleting the provider row
- assert that reconciliation is skipped entirely when no local deployments
exist for the provider
---------
Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>