170 lines
7.0 KiB
Python
170 lines
7.0 KiB
Python
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# See the LICENSE file for details.
|
|
|
|
from rest_framework import serializers
|
|
|
|
from plane.db.models import Project, WorkspaceAICredential, WorkspaceAISettings, WorkspaceMember
|
|
from plane.license.utils.encryption import encrypt_data
|
|
|
|
from .base import BaseSerializer
|
|
|
|
|
|
class WorkspaceAISettingsSerializer(BaseSerializer):
|
|
default_project_id = serializers.UUIDField(required=False, allow_null=True)
|
|
enabled_project_ids = serializers.ListField(child=serializers.UUIDField(), required=False, write_only=True)
|
|
enabled_member_ids = serializers.ListField(child=serializers.UUIDField(), required=False, write_only=True)
|
|
openai_api_key = serializers.CharField(required=False, allow_blank=True, write_only=True, trim_whitespace=False)
|
|
credential = serializers.SerializerMethodField(read_only=True)
|
|
|
|
class Meta:
|
|
model = WorkspaceAISettings
|
|
fields = [
|
|
"id",
|
|
"workspace_id",
|
|
"voice_tasker_enabled",
|
|
"provider",
|
|
"transcription_model",
|
|
"structuring_model",
|
|
"default_project_id",
|
|
"access_mode",
|
|
"enabled_project_ids",
|
|
"enabled_member_ids",
|
|
"max_audio_duration_seconds",
|
|
"per_user_hourly_limit",
|
|
"workspace_hourly_limit",
|
|
"per_user_daily_limit",
|
|
"workspace_daily_limit",
|
|
"project_daily_limit",
|
|
"workspace_concurrency_limit",
|
|
"sensitive_data_retention_days",
|
|
"credential",
|
|
"openai_api_key",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "workspace_id", "provider", "created_at", "updated_at", "credential"]
|
|
|
|
def get_credential(self, obj):
|
|
credential = WorkspaceAICredential.objects.filter(workspace=obj.workspace, provider=obj.provider).first()
|
|
return {
|
|
"provider": obj.provider,
|
|
"has_key": bool(credential and credential.encrypted_api_key and credential.is_active),
|
|
"key_last4": credential.key_last4 if credential else "",
|
|
"updated_at": credential.updated_at if credential else None,
|
|
}
|
|
|
|
def to_representation(self, instance):
|
|
data = super().to_representation(instance)
|
|
data["enabled_project_ids"] = [str(project_id) for project_id in instance.enabled_projects.values_list("id", flat=True)]
|
|
data["enabled_member_ids"] = [str(member_id) for member_id in instance.enabled_members.values_list("id", flat=True)]
|
|
return data
|
|
|
|
def validate_default_project_id(self, value):
|
|
if value is None:
|
|
return None
|
|
|
|
workspace = self.context["workspace"]
|
|
if not Project.objects.filter(workspace=workspace, id=value, archived_at__isnull=True).exists():
|
|
raise serializers.ValidationError("Default project must belong to this workspace.")
|
|
return value
|
|
|
|
def validate_enabled_project_ids(self, value):
|
|
workspace = self.context["workspace"]
|
|
project_ids = list(dict.fromkeys(value))
|
|
existing_ids = set(
|
|
Project.objects.filter(workspace=workspace, id__in=project_ids, archived_at__isnull=True).values_list(
|
|
"id", flat=True
|
|
)
|
|
)
|
|
if len(existing_ids) != len(project_ids):
|
|
raise serializers.ValidationError("All selected projects must belong to this workspace.")
|
|
return project_ids
|
|
|
|
def validate_enabled_member_ids(self, value):
|
|
workspace = self.context["workspace"]
|
|
member_ids = list(dict.fromkeys(value))
|
|
existing_ids = set(
|
|
WorkspaceMember.objects.filter(
|
|
workspace=workspace,
|
|
member_id__in=member_ids,
|
|
is_active=True,
|
|
).values_list("member_id", flat=True)
|
|
)
|
|
if len(existing_ids) != len(member_ids):
|
|
raise serializers.ValidationError("All selected members must be active workspace members.")
|
|
return member_ids
|
|
|
|
def validate_max_audio_duration_seconds(self, value):
|
|
if value < 10 or value > 600:
|
|
raise serializers.ValidationError("Max audio duration must be between 10 and 600 seconds.")
|
|
return value
|
|
|
|
def validate_per_user_hourly_limit(self, value):
|
|
if value < 1 or value > 1000:
|
|
raise serializers.ValidationError("Per-user hourly limit must be between 1 and 1000.")
|
|
return value
|
|
|
|
def validate_workspace_hourly_limit(self, value):
|
|
if value < 1 or value > 10000:
|
|
raise serializers.ValidationError("Workspace hourly limit must be between 1 and 10000.")
|
|
return value
|
|
|
|
def validate_per_user_daily_limit(self, value):
|
|
if value < 1 or value > 10000:
|
|
raise serializers.ValidationError("Per-user daily limit must be between 1 and 10000.")
|
|
return value
|
|
|
|
def validate_workspace_daily_limit(self, value):
|
|
if value < 1 or value > 100000:
|
|
raise serializers.ValidationError("Workspace daily limit must be between 1 and 100000.")
|
|
return value
|
|
|
|
def validate_project_daily_limit(self, value):
|
|
if value < 1 or value > 50000:
|
|
raise serializers.ValidationError("Project daily limit must be between 1 and 50000.")
|
|
return value
|
|
|
|
def validate_workspace_concurrency_limit(self, value):
|
|
if value < 1 or value > 50:
|
|
raise serializers.ValidationError("Workspace concurrency limit must be between 1 and 50.")
|
|
return value
|
|
|
|
def validate_sensitive_data_retention_days(self, value):
|
|
if value < 1 or value > 365:
|
|
raise serializers.ValidationError("Sensitive data retention must be between 1 and 365 days.")
|
|
return value
|
|
|
|
def update(self, instance, validated_data):
|
|
api_key = validated_data.pop("openai_api_key", None)
|
|
default_project_id = validated_data.pop("default_project_id", serializers.empty)
|
|
enabled_project_ids = validated_data.pop("enabled_project_ids", serializers.empty)
|
|
enabled_member_ids = validated_data.pop("enabled_member_ids", serializers.empty)
|
|
|
|
if default_project_id is not serializers.empty:
|
|
instance.default_project_id = default_project_id
|
|
|
|
for key, value in validated_data.items():
|
|
setattr(instance, key, value)
|
|
|
|
instance.save()
|
|
|
|
if enabled_project_ids is not serializers.empty:
|
|
instance.enabled_projects.set(enabled_project_ids)
|
|
|
|
if enabled_member_ids is not serializers.empty:
|
|
instance.enabled_members.set(enabled_member_ids)
|
|
|
|
if api_key:
|
|
cleaned_api_key = api_key.strip()
|
|
credential, _ = WorkspaceAICredential.objects.get_or_create(
|
|
workspace=instance.workspace,
|
|
provider=instance.provider,
|
|
)
|
|
credential.encrypted_api_key = encrypt_data(cleaned_api_key)
|
|
credential.key_last4 = cleaned_api_key[-4:] if len(cleaned_api_key) >= 4 else cleaned_api_key
|
|
credential.is_active = True
|
|
credential.save()
|
|
|
|
return instance
|