Skip to content

feat(openapi): add delete endpoints for models and model credentials - #45340

Open
mesrop2008 wants to merge 8 commits into
langgenius:mainfrom
mesrop2008:feat/openapi-delete-model-endpoints
Open

mesrop2008 wants to merge 8 commits into
langgenius:mainfrom
mesrop2008:feat/openapi-delete-model-endpoints

Conversation

@mesrop2008

Copy link
Copy Markdown
Contributor

Fixes #45219

Summary

This PR adds OpenAPI delete endpoints for self-hosted / custom models and individual model credentials under /openapi/v1/workspaces/{workspace_id}/model-providers/{provider}/...:

  1. DELETE /openapi/v1/workspaces/{workspace_id}/model-providers/{provider}/models (delete.model):

    • Deletes a custom model and its associated credentials from the workspace.
    • Query parameters: model (string) and model_type (ModelType).
    • Delegates to ModelProviderService().remove_model(...).
    • Returns 200 OK with {"result": "success"} (SimpleResultResponse).
  2. DELETE /openapi/v1/workspaces/{workspace_id}/model-providers/{provider}/models/credentials/{credential_id} (delete.model.credential):

    • Deletes a specific credential belonging to a model.
    • Path parameter: credential_id (string).
    • Query parameters: model (string) and model_type (ModelType).
    • Delegates to ModelProviderService().remove_model_credential(...).
    • Returns 200 OK with {"result": "success"} (SimpleResultResponse).

Motivation & Context

Previously, OpenAPI operations existed to inspect providers, list models, and create/update models and credentials, but no operations existed to delete them. This forced CLI and automated script users (such as difyctl setup/teardown pipelines) to fall back to the internal Console API (DELETE /console/api/workspaces/current/model-providers/{provider}/models) to clean up self-hosted models (e.g. langgenius/openai_api_compatible).

Because difyctl automatically builds its command tree from the OpenAPI catalog (GET /openapi/v1/_catalog), adding delete.model and delete.model.credential enables:

  • difyctl delete model --provider <id> --model <name> --model-type <type>
  • difyctl delete model credential --provider <id> --model <name> --model-type <type> --credential-id <id>

Both operations are marked destructive and protected by _MANAGE (RBACPermission.CREDENTIAL_MANAGE and privileged admin roles).

Testing & Verifications

1. Unit Tests

  • Added unit tests in api/tests/unit_tests/controllers/openapi/test_model_providers.py:
    • test_delete_model: Verifies parameter extraction, context binding, and delegation to ModelProviderService.remove_model.
    • test_delete_model_credential: Verifies parameter extraction, credential ID lookup, and delegation to ModelProviderService.remove_model_credential.
  • Registered routes and response status codes (200 OK, 401 Unauthorized, 403 Forbidden, 404 Not Found) across the OpenAPI auth matrix in api/tests/unit_tests/controllers/openapi/auth/test_requirements.py:
    • models.delete
    • models.credential.delete
  • Ran catalog and contract generation verification (test__catalog.py, test__contract.py).
  • Ran the complete OpenAPI test suite: 1,481 tests passed.

2. Live Database & Runtime Verification

  • Executed an end-to-end verification script against a running stack with real PostgreSQL (port 5433) and Redis (port 6380):
    1. Provisioned test workspace and owner account in PostgreSQL.
    2. Inserted a custom model record (ProviderModel) and multiple credentials (ProviderModelCredential) in the database.
    3. Dispatched a real HTTP DELETE .../models/credentials/{cred_id2} request with OAuth Bearer token and current catalog hash (X-Dify-Catalog).
      • Received 200 OK {"result": "success"}.
      • Confirmed via direct database query that cred_id2 was deleted from PostgreSQL while cred_id1 remained intact.
    4. Dispatched a real HTTP DELETE .../models request with Bearer auth and X-Dify-Catalog.
      • Received 200 OK {"result": "success"}.
      • Confirmed via direct database query that the ProviderModel record and all associated credentials were removed.

Screenshots

N/A (backend changes only)

Checklist

  • This change requires a documentation update, included: Dify Document
  • I understand that this PR may be closed in case there was no previous discussion or issues. (This doesn't apply to typos!)
  • I've verified the change and added or updated tests where meaningful regression risk justifies coverage.
  • I've updated the documentation accordingly.
  • I ran make lint && make type-check (backend) and vp staged (frontend) to appease the lint gods

@github-actions github-actions Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 82.86% 82.86% +0.00%
Strict coverage 82.51% 82.51% +0.00%
Typed symbols 85,418 85,436 +18
Untyped symbols 17,749 17,750 +1
Modules 4372 4372 0

@github-actions github-actions Bot added the web This relates to changes on the web. label Nov 2, 2026
@codecov

codecov Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.77%. Comparing base (5e3edb5) to head (69cab74).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #45340   +/-   ##
=======================================
  Coverage   89.77%   89.77%           
=======================================
  Files        6239     6239           
  Lines      390902   390917   +15     
  Branches    79285    79285           
=======================================
+ Hits       350933   350952   +19     
+ Misses      33870    33866    -4     
  Partials     6099     6099           
Flag Coverage Δ
api 89.64% <100.00%> (+<0.01%) ⬆️
cli 94.26% <ø> (ø)
dify-ui 92.39% <ø> (ø)
web 89.75% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-11-02 14:13:33.023314244 +0000
+++ /tmp/pyrefly_pr.txt	2026-11-02 14:13:18.843231808 +0000
@@ -2522,6 +2522,10 @@
    --> tests/unit_tests/controllers/openapi/test_human_input_form.py:203:22
 ERROR Object of class `FunctionType` has no attribute `__handler__` [missing-attribute]
    --> tests/unit_tests/controllers/openapi/test_human_input_form.py:229:20
+ERROR Object of class `FunctionType` has no attribute `__handler__` [missing-attribute]
+   --> tests/unit_tests/controllers/openapi/test_model_providers.py:330:20
+ERROR Object of class `FunctionType` has no attribute `__handler__` [missing-attribute]
+   --> tests/unit_tests/controllers/openapi/test_model_providers.py:351:20
 ERROR Object of class `FunctionType` has no attribute `view_class` [missing-attribute]
   --> tests/unit_tests/controllers/openapi/test_oauth_device.py:91:16
 ERROR Object of class `FunctionType` has no attribute `view_class` [missing-attribute]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files. web This relates to changes on the web.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

difyctl: delete a model (and its credentials) added under a provider

1 participant