Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion backend/adapter_processor_v2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ class AdapterInstance(DefaultOrganizationMixin, BaseModel):
null=False,
blank=False,
db_comment="Name of the Adapter Instance",
help_text="Display name for the adapter instance",
)
adapter_id = models.CharField(
max_length=ADAPTER_ID_LENGTH,
default="",
db_comment="Unique identifier of the Adapter",
help_text="Adapter type identifier (e.g. openai|uuid)",
)

# TODO to be removed once the migration for encryption
Expand All @@ -77,29 +79,34 @@ class AdapterInstance(DefaultOrganizationMixin, BaseModel):
adapter_type = models.CharField(
choices=[(tag.value, tag.name) for tag in AdapterTypes],
db_comment="Type of adapter LLM/EMBEDDING/VECTOR_DB",
help_text="Adapter category: LLM, EMBEDDING, VECTOR_DB, or X2TEXT",
)
created_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
related_name="adapters_created",
null=True,
blank=True,
help_text="User who created this resource",
)
modified_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
related_name="adapters_modified",
null=True,
blank=True,
help_text="User who last modified this resource",
)

is_active = models.BooleanField(
default=False,
db_comment="Is the adapter instance currently being used",
help_text="Whether the adapter is active",
)
shared_to_org = models.BooleanField(
default=False,
db_comment="Is the adapter shared to entire org",
help_text="Whether shared with entire organization",
)

is_friction_less = models.BooleanField(
Expand Down Expand Up @@ -131,7 +138,12 @@ class AdapterInstance(DefaultOrganizationMixin, BaseModel):
# Introduced field to establish M2M relation between users and adapters.
# This will introduce intermediary table which relates both the models.
shared_users = models.ManyToManyField(User, related_name="shared_adapters_instance")
description = models.TextField(blank=True, null=True, default=None)
description = models.TextField(
blank=True,
null=True,
default=None,
help_text="Optional description",
)

objects = AdapterInstanceModelManager()

Expand Down
29 changes: 14 additions & 15 deletions backend/adapter_processor_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ class BaseAdapterSerializer(AuditSerializer):
class Meta:
model = AdapterInstance
fields = "__all__"
extra_kwargs = {
"adapter_name": {
"help_text": "Display name for the adapter",
},
"adapter_id": {
"help_text": "Adapter type identifier (e.g. openai|uuid)",
},
"adapter_type": {
"help_text": "LLM, EMBEDDING, VECTOR_DB, or X2TEXT",
},
"description": {
"help_text": "Optional description",
},
}


class DefaultAdapterSerializer(serializers.Serializer):
Expand All @@ -36,11 +50,6 @@ class DefaultAdapterSerializer(serializers.Serializer):


class AdapterInstanceSerializer(BaseAdapterSerializer):
"""Inherits BaseAdapterSerializer.

Used for CRUD other than listing
"""

def to_internal_value(self, data: dict[str, Any]) -> dict[str, Any]:
if data.get(AdapterKeys.ADAPTER_METADATA, None):
encryption_secret: str = settings.ENCRYPTION_KEY
Expand Down Expand Up @@ -127,11 +136,6 @@ def get_context_window_size(self, obj: AdapterInstance) -> int:


class AdapterListSerializer(BaseAdapterSerializer):
"""Inherits BaseAdapterSerializer.

Used for listing adapters
"""

class Meta(BaseAdapterSerializer.Meta):
model = AdapterInstance
fields = (
Expand Down Expand Up @@ -184,11 +188,6 @@ def to_representation(self, instance: AdapterInstance) -> dict[str, str]:


class SharedUserListSerializer(BaseAdapterSerializer):
"""Inherits BaseAdapterSerializer.

Used for listing adapter users
"""

shared_users = UserSerializer(many=True)
created_by = UserSerializer()

Expand Down
66 changes: 66 additions & 0 deletions backend/adapter_processor_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.db.models import ProtectedError, QuerySet
from django.http import HttpRequest
from django.http.response import HttpResponse
from drf_spectacular.utils import extend_schema, extend_schema_view
from permissions.permission import (
IsFrictionLessAdapter,
IsFrictionLessAdapterDelete,
Expand Down Expand Up @@ -52,10 +53,15 @@
logger = logging.getLogger(__name__)


@extend_schema(tags=["Adapters"])
class DefaultAdapterViewSet(ModelViewSet):
versioning_class = URLPathVersioning
serializer_class = DefaultAdapterSerializer

@extend_schema(
summary="Configure default adapter triad",
description="Sets the default LLM, embedding, and vector DB adapters for the current user.",
)
def configure_default_triad(
self, request: Request, *args: tuple[Any], **kwargs: dict[str, Any]
) -> HttpResponse:
Expand All @@ -66,6 +72,11 @@ def configure_default_triad(
AdapterProcessor.set_default_triad(default_triad, request.user)
return Response(status=status.HTTP_200_OK)

@extend_schema(
summary="Get default adapter triad",
description="Returns the current user's default adapter configuration "
"(LLM, embedding, vector DB, text extractor).",
)
def get_default_triad(
self, request: Request, *args: tuple[Any], **kwargs: dict[str, Any]
) -> HttpResponse:
Expand All @@ -84,10 +95,16 @@ def get_default_triad(
return Response(status=status.HTTP_200_OK, data={})


@extend_schema(tags=["Adapters"])
class AdapterViewSet(GenericViewSet):
versioning_class = URLPathVersioning
serializer_class = TestAdapterSerializer

@extend_schema(
summary="List supported adapters by type",
description="Returns all supported adapter implementations for the given type. "
"Query param: adapter_type (LLM|EMBEDDING|VECTOR_DB|X2TEXT|OCR).",
)
def list(
self, request: Request, *args: tuple[Any], **kwargs: dict[str, Any]
) -> HttpResponse:
Expand All @@ -107,6 +124,11 @@ def list(
else:
raise InValidType

@extend_schema(
summary="Get adapter JSON schema",
description="Returns the JSON configuration schema for a specific adapter. "
"Query param: id (adapter identifier).",
)
def get_adapter_schema(
self, request: Request, *args: tuple[Any], **kwargs: dict[str, Any]
) -> HttpResponse:
Expand All @@ -117,6 +139,11 @@ def get_adapter_schema(
json_schema = AdapterProcessor.get_json_schema(adapter_id=adapter_name)
return Response(data=json_schema, status=status.HTTP_200_OK)

@extend_schema(
summary="Test adapter connection",
description="Tests adapter connectivity with the provided credentials. "
"Returns {is_valid: true/false}.",
)
def test(self, request: Request) -> Response:
"""Tests the connector against the credentials passed."""
serializer: AdapterInstanceSerializer = self.get_serializer(data=request.data)
Expand All @@ -135,6 +162,33 @@ def test(self, request: Request) -> Response:
)


@extend_schema_view(
list=extend_schema(
summary="List adapter instances",
description="Returns adapter instances. "
"Supports `?adapter_type=LLM|EMBEDDING|VECTOR_DB|X2TEXT` filter.",
tags=["Adapters"],
),
create=extend_schema(
summary="Create an adapter instance",
description="Create a new adapter with credentials. "
"Automatically sets as default if no default exists for the adapter type.",
tags=["Adapters"],
),
retrieve=extend_schema(summary="Get adapter details", tags=["Adapters"]),
update=extend_schema(summary="Update an adapter", tags=["Adapters"]),
partial_update=extend_schema(
summary="Partially update an adapter",
description="Update specific fields. Commonly used for `shared_users` updates.",
tags=["Adapters"],
),
destroy=extend_schema(
summary="Delete an adapter",
description="Deletes the adapter. Cannot delete a default adapter "
"or one in use by workflows.",
tags=["Adapters"],
),
)
class AdapterInstanceViewSet(ModelViewSet):
serializer_class = AdapterInstanceSerializer

Expand Down Expand Up @@ -380,6 +434,12 @@ def partial_update(

return response

@extend_schema(
summary="List shared users for adapter",
description="Returns the list of users the adapter instance is shared with.",
tags=["Adapters"],
responses={200: SharedUserListSerializer},
)
@action(detail=True, methods=["get"])
def list_of_shared_users(self, request: HttpRequest, pk: Any = None) -> Response:
adapter = self.get_object()
Expand Down Expand Up @@ -433,6 +493,12 @@ def update(
# For non-platform-key cases, use the default update behavior
return super().update(request, *args, **kwargs)

@extend_schema(
summary="Get adapter info",
description="Returns metadata about the adapter instance including "
"availability and deprecation status.",
tags=["Adapters"],
)
@action(detail=True, methods=["get"])
def adapter_info(self, request: HttpRequest, pk: uuid) -> Response:
adapter = self.get_object()
Expand Down
67 changes: 67 additions & 0 deletions backend/api_v2/api_deployment_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
from configuration.models import Configuration
from django.db.models import F, OuterRef, QuerySet, Subquery
from django.http import HttpResponse
from drf_spectacular.utils import (
OpenApiResponse,
extend_schema,
extend_schema_view,
)
from permissions.permission import IsOwner, IsOwnerOrSharedUserOrSharedToOrg
from plugins import get_plugin
from prompt_studio.prompt_studio_registry_v2.models import PromptStudioRegistry
Expand Down Expand Up @@ -48,6 +53,7 @@
logger = logging.getLogger(__name__)


@extend_schema(tags=["API Deployments"])
class DeploymentExecution(views.APIView):
def initialize_request(self, request: Request, *args: Any, **kwargs: Any) -> Request:
"""To remove csrf request for public API.
Expand All @@ -61,6 +67,14 @@ def initialize_request(self, request: Request, *args: Any, **kwargs: Any) -> Req
request.csrf_processing_done = True
return super().initialize_request(request, *args, **kwargs)

@extend_schema(
summary="Execute an API deployment",
description="Submit files for processing through a deployed workflow.",
responses={
200: OpenApiResponse(description="Extraction completed successfully"),
422: OpenApiResponse(description="Processing error"),
},
)
@DeploymentHelper.validate_api_key
def post(
self,
Expand Down Expand Up @@ -158,6 +172,15 @@ def post(
# Success
return Response({"message": response}, status=status.HTTP_200_OK)

@extend_schema(
summary="Get execution status",
description="Poll for the result of an async API deployment execution.",
responses={
200: OpenApiResponse(description="Execution completed"),
406: OpenApiResponse(description="Result already acknowledged"),
422: OpenApiResponse(description="Execution error"),
},
)
@DeploymentHelper.validate_api_key
def get(
self,
Expand Down Expand Up @@ -245,6 +268,32 @@ def get(
)


@extend_schema_view(
list=extend_schema(
summary="List API deployments",
description="Returns paginated list of API deployments the caller has access to. "
"Supports `?workflow=<uuid>` and `?search=<name>` query filters.",
tags=["API Deployments"],
),
create=extend_schema(summary="Create an API deployment", tags=["API Deployments"]),
retrieve=extend_schema(
summary="Get API deployment details",
description="Returns full details for a single API deployment by UUID.",
tags=["API Deployments"],
),
update=extend_schema(summary="Update an API deployment", tags=["API Deployments"]),
partial_update=extend_schema(
summary="Partially update an API deployment",
description="Update specific fields. Commonly used to toggle `is_active` "
"or update `shared_users`.",
tags=["API Deployments"],
),
destroy=extend_schema(
summary="Delete an API deployment",
description="Permanently removes the deployment and associated API keys.",
tags=["API Deployments"],
),
)
class APIDeploymentViewSet(viewsets.ModelViewSet):
pagination_class = CustomPagination

Expand Down Expand Up @@ -320,6 +369,12 @@ def create(
headers=headers,
)

@extend_schema(
summary="Get deployments by Prompt Studio tool",
description="Returns API deployments linked to workflows that use the specified "
"Prompt Studio tool. Query param: tool_id.",
tags=["API Deployments"],
)
@action(detail=False, methods=["get"])
def by_prompt_studio_tool(self, request: Request) -> Response:
"""Get API deployments for a specific prompt studio tool."""
Expand Down Expand Up @@ -357,6 +412,12 @@ def by_prompt_studio_tool(self, request: Request) -> Response:
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)

@extend_schema(
summary="Download Postman collection",
description="Downloads a Postman Collection JSON file for the deployment, "
"pre-configured with the active API key.",
tags=["API Deployments"],
)
@action(detail=True, methods=["get"])
def download_postman_collection(
self, request: Request, pk: str | None = None
Expand All @@ -379,6 +440,12 @@ def download_postman_collection(
)
return response

@extend_schema(
summary="List shared users for deployment",
description="Returns the list of users the deployment is shared with.",
tags=["API Deployments"],
responses={200: SharedUserListSerializer},
)
@action(detail=True, methods=["get"], permission_classes=[IsOwner])
def list_of_shared_users(self, request: Request, pk: str | None = None) -> Response:
"""List users who have access to this API deployment."""
Expand Down
Loading